|
|
The normal way to add a new command to CMake is to just define a new
|
|
|
macro or function (with the `macro` and `endmacro` or `function` and
|
|
|
`endfunction` commands).
|
|
|
|
|
|
The other way is to use the `load_command` command, but how does this
|
|
|
command work? Actually, this command allows you to code a new CMake
|
|
|
command in C or C++, and dynamically load the command into a running
|
|
|
CMake. It effectively allows you to insert your own C or C++ code into
|
|
|
CMake. The C plugin interface is actually a good example of design of a
|
|
|
plugin interface, but it is difficult to use, it has almost no
|
|
|
documentation (before the creation of this wiki page), it has several
|
|
|
quirks and limitations, it seems leftover from an ancient CMake version,
|
|
|
though it remains present in CMake 2.6.
|
|
|
|
|
|
Avoid this feature\! Coding a plugin and using `load_command` is
|
|
|
probably not a good idea. This wiki page is for those readers who wonder
|
|
|
about `load_command` or who want to experiment with it. Also, anyone can
|
|
|
edit the page and add more information.
|
|
|
|
|
|
## Overview
|
|
|
|
|
|
The
|
|
|
[cmLoadCommandCommand.cxx](http://public.kitware.com/cgi-bin/viewcvs.cgi/Source/cmLoadCommandCommand.cxx?root=CMake&view=log)
|
|
|
source file implements a `load_command` command, which takes the form of
|
|
|
|
|
|
load_command(SomeName dir1 dir2 ...)
|
|
|
|
|
|
This command prefixes "cm" to SomeName, and then loads a cmSomeName
|
|
|
module into CMake. (For ELF systems, this would
|
|
|
[dlopen(3)](http://www.freebsd.org/cgi/man.cgi?query=dlopen&sektion=3) a
|
|
|
"libcmSomeName.so" file into CMake.) It searches for the module in the
|
|
|
dir1, dir2, ... directories. Then it looks for and immediately calls the
|
|
|
SomeNameInit function in the module.
|
|
|
|
|
|
One can code the module in C or C++, but it must include the
|
|
|
[cmCPluginAPI.h](https://github.com/Kitware/CMake/blob/master/Source/cmCPluginAPI.h)
|
|
|
file, which defines the interface from the module to CMake. The
|
|
|
SomeNameInit function must use this interface to define one and only one
|
|
|
new CMake command.
|
|
|
|
|
|
## EXIT example
|
|
|
|
|
|
How to make CMake exit with status 42?
|
|
|
|
|
|
Wrap the
|
|
|
[exit(3)](http://www.freebsd.org/cgi/man.cgi?query=exit&sektion=3) C
|
|
|
function as a CMake command. For example,
|
|
|
|
|
|
# in CMakeLists.txt
|
|
|
exit(42)
|
|
|
|
|
|
should immediately cause CMake to exit with the given status. This also
|
|
|
skips the usual cleaning and error messages, so the `exit` command will
|
|
|
never be useful in any actual CMake project. The `exit` command intends
|
|
|
only a simple example of something that not any macro or function can
|
|
|
do.
|
|
|
|
|
|
### cmExitCommand.c
|
|
|
|
|
|
This is a C plugin. The entire source is in one file `cmExitCommand.c`
|
|
|
which contains the following:
|
|
|
|
|
|
/* commands/cmExitCommand.c */
|
|
|
|
|
|
#include <stdio.h>
|
|
|
#include <stdlib.h>
|
|
|
#include <string.h>
|
|
|
|
|
|
#include <cmCPluginAPI.h>
|
|
|
|
|
|
void ExitCommandInit(cmLoadedCommandInfo *);
|
|
|
static int ipass(void *, void *, int, char *[]);
|
|
|
|
|
|
void
|
|
|
ExitCommandInit(cmLoadedCommandInfo *info)
|
|
|
{
|
|
|
info->Name = "exit";
|
|
|
info->InitialPass = ipass;
|
|
|
}
|
|
|
|
|
|
int
|
|
|
ipass(void *in, void *mf, int argc, char *argv[])
|
|
|
{
|
|
|
cmLoadedCommandInfo *info;
|
|
|
int status;
|
|
|
char extra;
|
|
|
|
|
|
/* cast to avoid gcc warning */
|
|
|
info = (cmLoadedCommandInfo *) in;
|
|
|
|
|
|
if (argc != 1) {
|
|
|
info->Error =
|
|
|
strdup("called with incorrect number of arguments");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (sscanf(argv[0], "%d%c", &status, &extra) != 1) {
|
|
|
info->Error =
|
|
|
strdup("requires an integer argument");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
exit(status);
|
|
|
/* NOTREACHED */
|
|
|
return 1;
|
|
|
}
|
|
|
|
|
|
That was the entire source code for the `exit` command. This module does
|
|
|
not make any API calls back to CMake (no use of `info->CAPI` in the
|
|
|
code), but it already demonstrates how to cause a command to do
|
|
|
something, and how to report errors.
|
|
|
|
|
|
#### ExitCommandInit
|
|
|
|
|
|
CMake calls the ExitCommandInit function immediately after loading the
|
|
|
module. The rule is that a module called cm*SomeName* needs a
|
|
|
*SomeName*Init function; so a cmExitCommand module needs an
|
|
|
ExitCommandInit function. The job of this function is to take a pointer
|
|
|
to a struct, and fill the struct with information.
|
|
|
|
|
|
Here again is the code to ExitCommandInit:
|
|
|
|
|
|
void
|
|
|
ExitCommandInit(cmLoadedCommandInfo *info)
|
|
|
{
|
|
|
info->Name = "exit";
|
|
|
info->InitialPass = ipass;
|
|
|
}
|
|
|
|
|
|
How to write the ExitCommandInit function? CMake initializes all `info`
|
|
|
fields to zero (except `info->CAPI`), so this function needs only to set
|
|
|
the interesting fields. The most important field is `info->Name` to the
|
|
|
name of the command.
|
|
|
|
|
|
Every CMake command has an initial pass and a final pass. Most commands
|
|
|
need only the initial pass. So the line `info->InitialPass = ipass`
|
|
|
tells CMake to call our `ipass` function during the initial pass.
|
|
|
|
|
|
#### InitialPass
|
|
|
|
|
|
The `info->InitialPass` is a pointer to a function that takes four
|
|
|
arguments `(void *info, void *mf, int argc, char *argv[])` and returns
|
|
|
`int`. Because the first parameter declares void \* instead of
|
|
|
cmLoadedCommandInfo \*, we need to cast it. (Or we could just declare
|
|
|
`ipass` to take cmLoadedCommandInfo \* without a cast, but this would
|
|
|
cause a warning in some compilers.)
|
|
|
|
|
|
The declaration for InitialPass uses void \*info instead of
|
|
|
cmLoadedCommandInfo \*info. The code uses a cast to avoid a warning in
|
|
|
some compilers.
|
|
|
|
|
|
```
|
|
|
/* cast to avoid gcc warning */
|
|
|
info = (cmLoadedCommandInfo *) in;
|
|
|
```
|
|
|
|
|
|
The last two parameters `int argc, char *argv[]` bring in the arguments
|
|
|
to the CMake command. The `int` return value is to be nonzero if the
|
|
|
command succeeds, or zero if it fails. The "exit" command needs to check
|
|
|
that there is one argument, convert the argument from string to integer,
|
|
|
and correctly report any error.
|
|
|
|
|
|
The correct way to report an error is to set the info-\>Error string.
|
|
|
For some strange reason, CMake will try to free(3) the string later. If
|
|
|
you simply assign a string, then CMake will later pass a bogus pointer
|
|
|
to free(3). Instead you need to
|
|
|
[malloc(3)](http://www.freebsd.org/cgi/man.cgi?query=malloc&sektion=3)
|
|
|
the string somehow; this is the reason for the
|
|
|
[strdup(3)](http://www.freebsd.org/cgi/man.cgi?query=strdup&sektion=3)
|
|
|
calls. After you set the error string, you can return 0 to make the
|
|
|
command fail. Here again is the code to check the number of arguments,
|
|
|
and to check the conversion. (Though builtin CMake commands convert with
|
|
|
[atoi(3)](http://www.freebsd.org/cgi/man.cgi?query=atoi&sektion=3) and
|
|
|
no error check.)
|
|
|
|
|
|
```
|
|
|
if (argc != 1) {
|
|
|
info->Error =
|
|
|
strdup("called with incorrect number of arguments");
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
if (sscanf(argv[0], "%d%c", &status, &extra) != 1) {
|
|
|
info->Error =
|
|
|
strdup("requires an integer argument");
|
|
|
return 0;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Also, CMake prepends the name of the command to the error message, so
|
|
|
"called with incorrect number of arguments" becomes "*exit* called with
|
|
|
incorrect number of arguments".
|
|
|
|
|
|
### Compiling a module
|
|
|
|
|
|
The cmExitCommand.c file leaves a problem. CMake cannot load a C source
|
|
|
file directly, CMake can only load a compiled module.
|
|
|
|
|
|
The output of `cmake --help-command load_command` suggests using
|
|
|
`try_compile` to compile the module, but `try_compile` only compiles
|
|
|
executables (because it uses `add_executable`). So you need to create a
|
|
|
separate CMake project for the cmExitCommand module, and build this
|
|
|
project before loading the cmExitCommand module into any other project.
|
|
|
|
|
|
-
|
|
|
(It might be possible to trick `try_compile` to build a module, or
|
|
|
at least build an executable that exports its symbols like a module,
|
|
|
if you pass the correct compiler flags after the
|
|
|
COMPILE_DEFINITIONS keyword. However this would require to discover
|
|
|
the correct compiler flags.)
|
|
|
|
|
|
Also, the compiler needs to find the `cmCPluginAPI.h` header file. CMake
|
|
|
installs a copy in `${CMAKE_ROOT}/include`.
|
|
|
|
|
|
The following `CMakeLists.txt` file is enough to corectly build the
|
|
|
cmExitCommand module:
|
|
|
|
|
|
# commands/CMakeLists.txt
|
|
|
|
|
|
cmake_minimum_required(VERSION 2.6)
|
|
|
project(ExitCommand C)
|
|
|
include_directories(${CMAKE_ROOT}/include)
|
|
|
add_library(cmExitCommand MODULE cmExitCommand.c)
|
|
|
|
|
|
The name of the cmExitCommand.c file is not important; but the name of
|
|
|
the cmExitCommand target needs to match with the name of the
|
|
|
ExitCommandInit function.
|
|
|
|
|
|
To build this module, for example with Unix make:
|
|
|
|
|
|
unix$ mkdir commands/obj
|
|
|
unix$ cd commands/obj
|
|
|
unix$ cmake ..
|
|
|
-- The C compiler identification is GNU
|
|
|
-- Check for working C compiler: /usr/bin/gcc
|
|
|
-- Check for working C compiler: /usr/bin/gcc -- works
|
|
|
-- Detecting C compiler ABI info
|
|
|
-- Detecting C compiler ABI info - done
|
|
|
-- Configuring done
|
|
|
-- Generating done
|
|
|
-- Build files have been written to:
|
|
|
/home/kernigh/trunk/examples/load_command/commands/obj
|
|
|
unix$ make
|
|
|
Scanning dependencies of target cmExitCommand
|
|
|
[100%] Building C object CMakeFiles/cmExitCommand.dir/cmExitCommand.c.o
|
|
|
Linking C shared module libcmExitCommand.so
|
|
|
[100%] Built target cmExitCommand
|
|
|
unix$
|
|
|
|
|
|
### Testing the module
|
|
|
|
|
|
With the compiled module, one can try to load it in a CMake project with
|
|
|
`load_command` and use the new command. Because `load_command` is not
|
|
|
scriptable, you cannot use a cmake -P script to test a module. You must
|
|
|
create a test CMake project.
|
|
|
|
|
|
The following `CMakeLists.txt` file is such a project:
|
|
|
|
|
|
# testexit/CMakeLists.txt
|
|
|
|
|
|
cmake_minimum_required(VERSION 2.6)
|
|
|
project(testexit C)
|
|
|
load_command(ExitCommand ${CMAKE_SOURCE_DIR}/../commands/obj)
|
|
|
exit(freeway)
|
|
|
exit(19 left)
|
|
|
exit(42)
|
|
|
|
|
|
-
|
|
|
(Replace `${CMAKE_SOURCE_DIR}/../commands/obj` with the path to the
|
|
|
built cmExitCommand module.)
|
|
|
|
|
|
When this script runs, the `load_command` succeeds. The first two `exit`
|
|
|
commands cause errors. The third such command exits CMake (and skips the
|
|
|
usual error message about failing to configure the project). Unix users
|
|
|
can use the command `echo $?` to check the exit status from CMake:
|
|
|
|
|
|
unix$ mkdir testexit/obj
|
|
|
unix$ cd testexit/obj
|
|
|
unix$ cmake ..
|
|
|
-- The C compiler identification is GNU
|
|
|
-- Check for working C compiler: /usr/bin/gcc
|
|
|
-- Check for working C compiler: /usr/bin/gcc -- works
|
|
|
-- Detecting C compiler ABI info
|
|
|
-- Detecting C compiler ABI info - done
|
|
|
CMake Error at CMakeLists.txt:6 (exit):
|
|
|
exit requires an integer argument
|
|
|
|
|
|
|
|
|
CMake Error at CMakeLists.txt:7 (exit):
|
|
|
exit called with incorrect number of arguments
|
|
|
|
|
|
|
|
|
unix$ echo $?
|
|
|
42
|
|
|
unix$
|
|
|
|
|
|
That was the story of how, after coding a C plugin, compiling a module
|
|
|
and loading the module into CMake, we caused CMake to exit with status
|
|
|
42.
|
|
|
|
|
|
## Another Example?
|
|
|
|
|
|
Anyone can edit this wiki page and add more information. The person who
|
|
|
wanted to bother could post or link an example of a plugin that actually
|
|
|
used `info->CAPI` for something.
|
|
|
|
|
|
----
|
|
|
This page was initially populated by conversion from its [original location](https://public.kitware.com/Wiki/CMake/C_Plugins_for_Loadable_Commands) in another wiki. |