cmake run script for install target? - cmake

I have a project where "installing" the code is not quite as simple as just copying some files. With a traditional Makefile, I would just create a make install target that runs a series of shell commands to do what I need.
But googling around has resulting in no examples of this (some things close, but not quite... i think). So basically, I want a custom command, that depends on the target executables, but produces nothing and runs a script that need not be portable to accomplish the "install"
Anyone have any examples of something like this?

CMake's install command allows for custom scripts. See the official documentation: install - Custom Installation Logic:
install([[SCRIPT <file>] [CODE <code>]]
[COMPONENT <component>] [...])
The SCRIPT form will invoke the given CMake script files during installation. If the script file name is a relative path it will be interpreted with respect to the current source directory. The CODE form will invoke the given CMake code during installation. Code is specified as a single argument inside a double-quoted string. For example, the code
install(CODE "MESSAGE(\"Sample install message.\")")
will print a message during installation.
To run custom shell script (or whatever program), combine install(CODE ...) with execute_process:
install(CODE "execute_process(COMMAND my_script.sh)")

This worked for me: use add_custom_target, then add the main target as a dependency to the custom target target.
# create custom target for setcap to be executed
add_custom_target(setcap ALL
WORKING_DIRECTORY ${OUTPUT_DIR}/bin
COMMAND ${CMAKE_COMMAND} -E 'sudo setcap cap_net_raw,cap_net_admin+eip ${}/bin/<executable name>)
# create a dependency on the custom target for main target, setcap depends on ${proj_name}
add_dependencies(setcap ${proj_name})

Related

Custom command / target run only for default build not install

I want to build my documentation (doxygen) alongside my project with CMake by default. I let CMake generate Makefiles. I know of these approaches so far:
1)
add_custom_target(doc ALL
COMMAND ${DOXYGEN_COMMAND})
2)
add_custom_command(
TARGET my-executable
POST_BUILD
COMMAND ${DOXYGEN_COMMAND})
The issue I have with the first approach is that the target is build both during build and install (i.e. when running make and when running make install). This is a no-go.
The issue with the second approach is that it is not actually correct: the documentation doesn't have to be built after the executable (in fact, it can be done in parallel even). Moreover I cannot build only the executable now.
Is there a way to create this custom command / target such that it is run only during make (building) and not during make install (installing) but independently from other targets such as executables?
Basically I'd need a "default target". Also I really want to avoid having to execute CMake recursively or make directly.
Just create a file, which existence means that documentation file have been built:
set(documentation_file ${CMAKE__BINARY_DIR}/docs_ready)
# Documentation target
add_custom_target(docs ALL DEPENDS ${documentation_file})
# Documentation command
add_custom_command(OUTPUT ${documentation_file}
COMMAND ${DOXYGEN_COMMAND}
# And mark that documentation is created
COMMAND ${CMAKE_COMMAND} -E touch ${documentation_file}
)
The solution given by #Tsyvarev works perfectly except for one issue:
Running make docs manually doesn't cause a re-build of the documentation because the "output file" ${documentation_file} is always considered up to date (there are no dependencies specified that could be newer).
Three workarounds I found:
Run make -B docs. The -B flags causes GNU Make to consider all targets out of date, and thus causes the documentation to be build.
Add a "manual docs" target which depends on first a docs-clean target which removes ${documentation_file} and then on docs itself (to build the documentation and the file), and run make docs-manual:
add_custom_target(docs-clean
COMMAND ${CMAKE_COMMAND} -E remove ${documentation_file})
add_custom_target(docs-manual)
add_dependencies(docs-manual docs-clean docs)
Note that I fear that this is just a dirty hack: AFAIK the order in which the dependencies (docs-clean and docs) are build isn't specified, and as they don't have a dependency between them, they could even be built in parallel.
Using add_custom_command with a TARGET docs-manual and PRE_BUILD (which runs the command before building dependencies) instead of the docs-clean target would've been perfect, but this only works for Visual Studio 7 or later.
Add a custom target that depends on docs but removes the generated ${documentation_file}:
add_custom_target(docs-manual
COMMAND ${CMAKE_COMMAND} -E remove ${documentation_file})
add_dependencies(docs-manual docs)
This works except for when running it the first time after a make docs, because then the generated file will still be there when docs is built.
edit: Use add_dependencies instead of DEPENDS of add_custom_target because the later is only intended for files, not for dependencies on targets.

How to configure external cmake libraries?

What I wanted to do is call
add_subdirectory(ext/oglplus)
and be done with it. Unfortunately it is not that simple. There is a huge buildscript which detects various opengl settings. So I tried the following
ExternalProject_Add(liboglplus
SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/ext/oglplus
CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/ext/oglplus/configure.py --use-glew
BUILD_COMMAND ${MAKE})
The problem that I have is don't really want to build it like that. It also doesn't build correctly because for some reason it wants to install the library and because there is no install target it will abort the compilation.
But the build script is calling cmake under the hood.
So what I want to do is to tell cmake to use "cofigure.py" instead of "cmake .." and then use it like any other cmake library.
Is this possible?
I used to call Linux Kernel KBuild from CMake using
ADD_CUSTOM_COMMAND() and ADD_CUSTOM_TARGET()
This way you can run arbitrary commands (like your config.py) and use the output.
First setup the command with all command-line options as CMake-variables, in tou case this would be calling the config.py script ${CMAKE_CURRENT_SOURCE_DIR}/ext/oglplus/.
Instead of encoding the Path to your script in the command (adding ext/oglplus) I think it may be better adding WORKING_DIRECTORY to the custom command:
add_custom_command
SET(KBUILD_CMD ${CMAKE_MAKE_PROGRAM}
-C ${KERNEL_BUILD_DIR}
CROSS_COMPILE=${CROSS_COMPILE} ARCH=${ARCH}
EXTRA_CFLAGS=${KBUILD_EXTRA_CFLAGS}
INSTALL_MOD_PATH=${INSTALL_MOD_PATH}
M=${CMAKE_CURRENT_SOURCE_DIR}
KBUILD_EXTRA_SYMBOLS=${depends_module_ksyms}
)
Add a custom command that calls your build-script and creates a file in the CMAKE_CURRENT_BINARY_DIRECTORY (note the second COMMAND to touch a file)
ADD_CUSTOM_COMMAND(
OUTPUT ${module_name}.built
COMMAND ${KBUILD_CMD} modules
COMMAND cmake -E touch ${module_name}.built
COMMENT "Kernel make modules ${module_name}"
VERBATIM
)
Add a custom target, its always out of date, but if you want it to be called automatically add ALL otherwise you have to explicityly call make module_build, I guess this is what you want.
ADD_CUSTOM_TARGET("${module_name}_build" ALL
DEPENDS ${depends_module_ksyms}
${CMAKE_CURRENT_BINARY_DIR}/${module_name}.built
COMMENT "Building Kernel Module ${module_name}"
)

Is there way to tell CMake to generate several install rules?

Using CMake to generate GNU/Makefile as an example, I would like to be able to run different install rules, say make install and make install-doc.
Is there a way to tell CMake to generate this ?
You can add a custom target which invokes the cmake_install.cmake script in the outermost binary directory. This script is also invoked when you run the default install target.
add_custom_target(install-doc
COMMAND "${CMAKE_COMMAND}" "-DBUILD_TYPE=$<CONFIGURATION>"
"-DCOMPONENT=doc" "-P" "${CMAKE_BINARY_DIR}/cmake_install.cmake"
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}")
The desired installation component has to be passed as the CMake variable COMPONENT. The build configuration can be set with the variable BUILD_TYPE. $<CONFIGURATION> is a generator expression which will be replaced by the currently active build configuration.

CMake Header Generator Updates

In CMake I currently have a simple Python script to generate a header, but if I update the script itself CMake won't re-run the script. Is there a way I can get CMake to do this?
It seems you are directly invoking your code generation script when cmake is run. While it is possible solution but it is definitely not a right way to use code generators with cmake.
I recommend you to use add_custom_command for your case:
add_custom_command(
OUTPUT generated.h
COMMAND ${PYTHON_EXECUTABLE} generator.py
DEPENDS generator.py
)
And next you can simple put your header to the list of source files passed to add_library/add_executable commands. cmake will automatically track all the dependencies and invoke your script.
Term DEPENDS generator.py informs cmake that it should regenerate header if script is changed.
With this approach file generated.h will be generated only at build time (when you run make or execute a build command in IDE). In contrast if you are running your script at cmake time (with execute_process command) then you have to rerun cmake to regenerate your file. Which is possible but you need to use some tricks to introduce a non-standard dependency.

Compile-time wildcards in cmake install targets

I'm new to cmake and I'm finding it very frustrating. I am trying to use wildcards in file paths that are evaluated when the build runs, not when the build is generated.
I have created a build that uses SWIG to generate Java wrappers for some C++ code. I can write the commands to generate the native code, compile it, and produce a working shared library, and even use the INSTALL command to install that shared library correctly. What I can't figure out how to do is to write an INSTALL command that can copy all *.java files generated by SWIG into that same install location.
It seems that cmake's FILE GLOB command does the globbing when cmake is executed, and not when the build actually runs. Of course, when cmake is executed, SWIG hasn't run yet, and the Java files don't exist.
Is there a way to do what I want? Am I going about things wrong? It seems like this is such a fundamental part of what Makefiles need to do, I'm really surprised not to find an easy way to do it.
Assuming that the Java wrappers are located in the current binary directory, you can use the following install command to copy the Java files upon install:
install(
CODE "file( GLOB _GeneratedJavaSources \"${CMAKE_CURRENT_BINARY_DIR}/*.java\" )"
CODE "file( INSTALL \${_GeneratedJavaSources} DESTINATION \"$ENV{HOME}\" )"
)
The CODE form of the install command is used to execute two CMake commands upon running the install target. The first one collects all generated Java files in a helper variable. The second one uses the INSTALL form of the file command to copy the files.
you can use install(SCRIPT swigInstaller.cmake) or install(DIRECTORY) both of which supports doing file globing at install time. You can read more about the install command at:
http://cmake.org/cmake/help/cmake-2-8-docs.html#command:install