Elektra
0.9.8
|
In this section, we will explain the how to write a language binding for Elektra.
Writing bindings for the high-level API is described in a different document, since it is a bit less straightforward and needs additional considerations.
To add the subdirectory containing our binding to the build, we have to modify src/bindings/CMakeLists.txt
.
At first we want to make sure that the build tools and compilers we need for the binding are installed. We can use find_program (BUILD_TOOL_EXECUTABLE build_tool)
to find our build_tool
program. The result of the search will be stored in BUILD_TOOL_EXECUTABLE
, so now we can use an if block to include the bindings in the build, if the program exists or exclude it, if it doesn't. To do that, we use add_binding
which adds ours to the list of bindings that will be built. For more provided functions, see here.
If, for example, our bindings only support linking against a dynamic library we can express that, by using the BUILD_*
variables in if blocks or by passing ONLY_SHARED
to add_binding
. You can read more in the compile doc.
Elektra uses out-of-source builds, so we have to copy all the needed files over to the build directory. The ${CMAKE_CURRENT_SOURCE_DIR} variable refers to the source directory, while ${CMAKE_CURRENT_BINARY_DIR} refers to the build directory. The copy is as simple as
However for some files we may want to use some CMakes variables. Say we're writing a build script for our project and want to include the version number and the directory that libelektra.so
resides in, so our build tool can find and link against it. We create our script named build.script.in
. It looks like this
Back in our CMake script, we tell CMake to replace the variables with their associated values.
Note how we leave off the .in
ending on the target file.
Since we're building a foreign language project, it will most likely have its own build tool or compiler. So we have to tell CMake how to invoke it, in order to build the project. First we specify what file we expect to be generated by that command. In this example it's a .lib
file that is generated in some target directory. We then call our build tool using the variable BUILD_TOOL_EXECUTABLE
we created earlier with the build
subcommand and the --release
option. We can also specify one or multiple files that this command depends on, such that CMake can make sure they are built or generated before. Finally, we add a custom target that depends on the .lib
file. To built this target, CMake will invoke our custom command and build the specified file.
We can then explicitly include the bindings using cmake -DBINDINGS="our_binding" ..
in the build directory and follow the further steps for compilation.
To invoke our tests through CMake, we have to follow similar steps as in the build. We add a test by specifying a command that runs our tests. In our case, we're calling the same program for testing as in the building step.
We may have to specify an additonal environment variable to tell the test command, where libelektra.so
resides, so that the dynamic linker can find it.
Now our bindings can be tested through ctest
alongside all other tests.
See the Java binding for examples.
Since v0.9.0, Elektra has a new error code system. You might want to take a look in the design decision first to understand the concept of the error codes. These codes are hierarchically structured and are therefore perfectly suitable for inheritance if the language supports it.
Some error codes like the Permanent Errors
are generalizations and used for developers who want to catch all specific types of errors (e.g., it does not matter if it is a Resource or Installation Error but the developer wants to check for both). Such errors should not be able to "instantiate" or emitted back to Elektra as we want to force developers to take a more specific category. In case of Java for example the Permanent Error
is an abstract class. Which errors are instantiable or not can be seen in the error-categorization guideline in the respective title saying either abstract
or concrete
. Here is an example of how Java has implemented it:
All error codes as well as the hierarchy itself is depicted in the design decision.
If you have a language which does not support inheritance this way like GoLang, you can still use the error code itself since the hierarchy is integrated in it. For example you can check if the code starts with C01...
to catch all Permanent Errors
.
In Elektra every error has a predefined format. You can take a look at the related design decision to see how it looks like.
Every Exception/Error struct/etc. should have separate accessors to individual parts of the message. These include:
In case of an error at least the following part has to be returned:
Please also keep the wording identical for consistency. Take a look how the Java binding implemented it in the KDBException