This is a small example of how you can write a plugin that will get tacked onto the build of casm-utilities
.
Only one file in here really matters: Makesocket.am
.
This is the the segment that will connect with the rest of the build.
You can link against libcasmutils.la
for all the casm-utilities
functions and classes, and use libgtests
for your tests.
The plugin will be compiled as if it was part of the casm-utilities
repository, which allows you to link against anything defined in that repository.
This also means that all the paths you use need to be relative to that repository.
You can extend the compilation with basically any automake feature, including binaries, scripts, headers and libraries.
Each of the following sections describes different kinds of things you can create as a plugin. A plugin does not need all of these things to work, this is just a comprehensive list of examples of what's possible.
This section will cover the minimum amount of entries you'll need to make a binary compile as a plugin.
This specifies the name of your binary once it gets installed on your system.
You can call your binary anything you want, but if you want it to be detected by casmutils
, you must follow a particular format.
casmutils
scans your PATH
to detect which plugins have been installed.
The way it does this is by searching for the pattern _casmutils-*
.
For the example in this repo, we've set
bin_PROGRAMS += _casmutils-hello
This will result in a binary called _casmutils-hello
to be installed, which you can call directly, or by calling casmutils hello
.
Use this to specify what the necessary source files for your program are.
The complete name is determined by the value you gave to bin_PROGRAMS
.
Simply prepend the name of your program to _SOURCES
, and give it a list of source files.
If the name in your program has dash characters (-
), replace them with underscores (_
):
bin_PROGRAMS += _casmutils-hello
_casmutils_hello_SOURCES = plugins/casm-utilities-plugin/src/hello.cpp
Note that the path is relative to the root of the casm-utilities
repository.
You can specify multiple files separated by spaces, or on several lines, escaping with \
.
This will specify which libraries your program needs to link against.
If you're using features in casm-utilities
, you'll need to link it against libcasmutils.la
.
For more complicated programs you might be writing, you can create your own libraries to link against as well.
As before, prepend with the name of your program for the full variable name.
bin_PROGRAMS += _casmutils-hello
_casmutils_hello_SOURCES = plugins/casm-utilities-plugin/src/hello.cpp
_casmutils_hello_LDADD = libcasmutils.la
Sometimes c++ is overkill, and you can do what you need through a python script.
The steps for that are explained here.
As with c++ binaries, the name of the utility should follow the format _casmutils-*
.
If we wanted to call a utility hellopy
, we could have a python script called _casmutils-hellopy
:
#!/usr/bin/python
print("hello")
Suppse this was located under src/_casmutils-hellopy
.
We can specify that we want this script installed by having the following in the Makesocket.am
file:
dist_bin_SCRIPTS += plugins/casm-utilities-plugin/src/_casmutils-hellopy
This will work for you, but not for other people that might be using your plugin.
When writing an executable python script, the first line is always the path to the interpreter:
#!/usr/bin/python
You should not do this.
Different systems will have python installed in different locations, so your utility won't work.
Instead, your script should be called _casmutils-hellopy.in
, and instead of a hard coded path to the interpreter, it should say:
#!@PYTHON@
You should include this file in EXTRA_DIST
:
dist_bin_SCRIPTS += plugins/casm-utilities-plugin/src/_casmutils-hellopy
EXTRA_DIST += plugins/casm-utilities-plugin/src/_casmutils-hellopy.in
Note that we're keeping dist_bin_SCRIPTS
as it was before.
Also notice that _casmutils-hellopy.in
is being tracked by the repository, but _casmutils-hellopy
is not.
Once you have the *.in
script, you need to specify what @PYTHON@
should transform into during the configure step.
To do this, have a file called configure.ac
in the root of your plugin.
The bootstrap.sh
script of the parent casm-utilities
repository will include whatever you say in this file in a similar manner to the Makesocket.am
files.
The following line in the plugin configure.ac
specifies that the *.in
file should be converted to the final script with the correct path to the interpreter:
AC_CONFIG_FILES([plugins/casm-utilities-plugin/src/_casmutils-hellopy],[chmod +x plugins/casm-utilities-plugin/src/_casmutils-hellopy])
You should be aware that any editing to configure.ac
files will require you to run boostrap.sh
in the parent repository.
If your plugin is expanding on the casmutils
library, you may have some headers you'd like to be installed alongside the parent repository.
First you must decide where the path where your headers should go.
This will define what the #include
directive will look like for the user.
Suppose you've created a new symmetry related header called extension.hpp
, which should be included via #include <casmutils/sym/extension.hpp>
.
The *_includedir
value is $(includedir)/casmutils/sym
.
Create a descriptive variable name, and add it to your Makesocket.am
file:
casmutils_plugin_includedir=$(includedir)/casmutils/sym
It's very important to choose a name that won't collide with other plugins, so definitely don't steal this variable name. Come up with one that makes sense for your repository name.
Now you're ready to specify which headers should be installed.
As we said before, we have a header called extension.hpp
, we specify that we want that installed now:
casmutils_plugin_includedir=$(includedir)/casmutils/sym
casmutils_plugin_include_HEADERS = plugins/casm-utilities-plugin/include/casmutils/sym/extension.hpp
Note that the variable names of *includedir
and *include_HEADERS
must have the same prefix.
You should always store your headers in an include
directory which mirrors the final post installation layout.
The parent casm-utilities
library has a switch for disabling header installation, which you should follow as well.
Wrap the segment we just wrote in the CASMUTILS_LIBRARY
conditional:
if CASMUTILS_LIBRARY
casmutils_plugin_includedir=$(includedir)/casmutils/sym
casmutils_plugin_include_HEADERS = plugins/casm-utilities-plugin/include/casmutils/sym/extension.hpp
endif
Do not indent the if
statement.
Unless your library module is header only, you will also have sources that you need to compile, and should be linked together with the rest of libcasmutils
.
This example will cover how to compile extension.cxx
, which relies on the extension.hpp
header.
Paths to these files relative to the plugin root are as follows:
You can simply tack on the extensions.cxx
file as an additional source file in your Makesocket.am
:
libcasmutils_la_SOURCES +=\
plugins/casm-utilities-plugin/include/casmutils/sym/extension.hpp\
plugins/casm-utilities-plugin/lib/extension.cxx
You shoud include both the source and header file.
An issue you might encounter is how to specify the location of extension.hpp
within the extension.cxx
file.
The simplest approach is to just use the relative path:
#include "../include/casmutils/sym/extension.hpp"
If you have a nicely organized include
directory (you should), you can make use AM_CPPFLAGS
to make the directive more general:
#include <casmutils/sym/extension.hpp>
This is especially desirable for cases where you intend to distribute the headers.
In order for this to work, specify the path to your include
directory as an additional place to search:
AM_CPPFLAGS += -I$(srcdir)/plugins/casm-utilities-plugin/include
You can also bind classes and standalone functions declared in extension.hpp
to python using pybind11
. Currently, extensions may only be added as new modules, separate from the casmutils
python module.
Bindings for the extension.hpp
example are specified in py/extension-py.cxx
:
PYBIND11_MODULE(utilsplugin, m) {
py::class_<cu::sym::PluginExtension>(m, "PluginExtension")
.def("say_hello", &cu::sym::PluginExtension::say_hello)
.def("say_hello_again", &cu::sym::PluginExtension::say_hello_again);
m.def("plug_extension", &cu::sym::plugin_extension);
}
In this example, the new python module utilsplugin
will contain a class PluginExtension
(binding of cu::sym::PluginExtension
) with two member functions say_hello
and say_hello_again
, as well as a standalone function plug_extension
(binding of cu::sym::plugin_extension
).
Note: You may need to install python3-dev
for this to work.
To install the new python module properly, we must append to the Makesocket.am
file:
utilsplugindir=$(pythondir)
utilsplugin_LTLIBRARIES = utilsplugin.la
utilsplugin_la_SOURCES = plugins/casm-utilities-plugin/py/extension-py.cxx
utilsplugin_la_LIBADD = libcasmutils.la
utilsplugin_la_LDFLAGS = -module
These flags will ensure that the python module is compiled as a library named utilsplugin.la
and installed in $(pythondir)
alongside other python modules. As usual, utilsplugin_la_SOURCES
specifies source files and utilsplugin_la_LIBADD
specifies libraries to link against.
The parent casm-utilities
library has a switch for disabling python installation, which you should follow as well.
Wrap the segment we just wrote in the CASMUTILS_PYTHON
conditional:
if CASMUTILS_PYTHON
utilsplugindir=$(pythondir)
utilsplugin_LTLIBRARIES = utilsplugin.la
utilsplugin_la_SOURCES = plugins/casm-utilities-plugin/py/extension-py.cxx
utilsplugin_la_LIBADD = libcasmutils.la
utilsplugin_la_LDFLAGS = -module
endif
Do not indent the if
statement.