Introduction to Emacs modules

Table of Contents

1 How it works

A new feature was added to Emacs 25 that lets you load dynamic modules. This means you are no longer limited to Emac Lisp, you can extend Emacs by using native compiled code. The two main advantages are better integration and speed (if you know what you are doing).

Modules work as "plugins" in many other programs. Each module is compiled to a shared library (with .so, .dylib or .dll extension depending on your system). This shared library implements functions that will get called by the main program. The plugin API or module interface describes what the main program expects as entry points and what the module can use to interact with the main program.

Concretely this API is accessible through the emacs-module.h header file. This is the only thing you need to make an Emacs module, you don't have to link against any Emacs binaries or libraries.

The most important things this file defines is:

  • An emacs_value opaque type which is used to pass Lisp values to and from Emacs.
  • An emacs_runtime structure which is used to get an environment pointer.
  • An emacs_env structure that contains function pointers to the API.
  • The function prototype of the module main entry point.
extern int emacs_module_init (struct emacs_runtime *ert);

This is the function Emacs will call when it loads your module. It will pass you a pointer to a runtime structure, from which you can get an environement pointer:

emacs_env *env = ert->get_environment(ert);

This environement pointer lets you interact with the Lisp interpreter thru function pointers. All those function pointers take the environment as the first parameter.

2 Basic module

We're going to define a new module that defines a new module mymod that defined a function mymod-test that returns the integer 42. First get a recent copy of the Emacs sources. You need to build Emacs with module support (it is disabled by default) and you need the emacs_module.h header.

http://alpha.gnu.org/gnu/emacs/pretest/emacs-25.0.92.tar.xz

You need to run ./configure --with-modules && make. You can test your freshly built Emacs by running ./src/emacs -Q. The -Q option prevents Emacs from reading your usual init file (you get a clean Emacs).

2.1 Makefile

# path to the emacs source dir
# (you can provide it here or on the command line)
#ROOT    =
CC      = gcc
LD      = gcc
CFLAGS  = -ggdb3 -Wall
LDFLAGS =

all: mymod.so

# make shared library out of the object file
%.so: %.o
        $(LD) -shared $(LDFLAGS) -o $@ $<

# compile source file to object file
%.o: %.c
        $(CC) $(CFLAGS) -I$(ROOT)/src -fPIC -c $<

2.2 mymod.c

Emacs modules must be GPL compatible. To enforce that Emacs checks for the presence of the plugin_is_GPL_compatible symbol before it loads a module.

#include <emacs-module.h>

/* Declare mandatory GPL symbol.  */
int plugin_is_GPL_compatible;

/* New emacs lisp function. All function exposed to Emacs must have this prototype. */
static emacs_value
Fmymod_test (emacs_env *env, int nargs, emacs_value args[], void *data)
{
  return env->make_integer (env, 42);
}

/* Bind NAME to FUN.  */
static void
bind_function (emacs_env *env, const char *name, emacs_value Sfun)
{
  /* Set the function cell of the symbol named NAME to SFUN using
     the 'fset' function.  */

  /* Convert the strings to symbols by interning them */
  emacs_value Qfset = env->intern (env, "fset");
  emacs_value Qsym = env->intern (env, name);

  /* Prepare the arguments array */
  emacs_value args[] = { Qsym, Sfun };

  /* Make the call (2 == nb of arguments) */
  env->funcall (env, Qfset, 2, args);
}

/* Provide FEATURE to Emacs.  */
static void
provide (emacs_env *env, const char *feature)
{
  /* call 'provide' with FEATURE converted to a symbol */

  emacs_value Qfeat = env->intern (env, feature);
  emacs_value Qprovide = env->intern (env, "provide");
  emacs_value args[] = { Qfeat };

  env->funcall (env, Qprovide, 1, args);
}

int
emacs_module_init (struct emacs_runtime *ert)
{
  emacs_env *env = ert->get_environment (ert);

  /* create a lambda (returns an emacs_value) */
  emacs_value fun = env->make_function (env,
              0,            /* min. number of arguments */
              0,            /* max. number of arguments */
              Fmymod_test,  /* actual function pointer */
              "doc",        /* docstring */
              NULL          /* user pointer of your choice (data param in Fmymod_test) */
  );

  bind_function (env, "mymod-test", fun);
  provide (env, "mymod");

  /* loaded successfully */
  return 0;
}

2.3 Testing

Compile your module by providing the path to the Emacs source dir as the ROOT variable (edit the Makefile or do it like this):

$ make ROOT=$HOME/prog/emacs-25.0.92
gcc -ggdb3 -Wall -I/home/aaptel/prog/emacs-25.0.92/src -fPIC -c mymod.c
gcc -shared  -o mymod.so mymod.o
rm mymod.o

You can now run your modules-enabled Emacs load and test your module. Use -L to append the directory containing your compiled module to the load-path list.

$ ~/prog/emacs-25.0.92/src/emacs -Q -L $PWD

Load your module and try to call mymod-test by typing this in the scratch buffer and evaluating it with C-j (it evaluates the form right before the point and prints it back).

If you have done everything right you should get this:

(require 'mymod) <C-j>
mymod

(mymod-test) <C-j>
42

Author: Aurélien Aptel

Created: 2016-03-31 jeu. 16:44

Emacs 24.5.1 (Org mode 8.2.10)

Validate