CMake: execute a macro/function as the command of add_custom_command - cmake

I'm using an external library which provides a CMake function for automatic code generation, to be used in my CMakeLists. The problem is that whenever I modify a CMakeLists then the function is run again, triggering the recompilation of the newly generated but unchanged sources. I'd need something like add_custom_command with the possibility to specify the CMake function as COMMAND instead of an executable, so that the function is run only if the automatically generated files are not already present.
Is this feasible? If not, does it exist another way to obtain the same result?
Thanks.

Take a look to this SO post.
You can call your function in a separate CMake script, call this script with add_custom_target and cmake -P then add a dependency to your binary :
add_custom_target(run_script COMMAND ${CMAKE_COMMAND} -P separate_script.cmake)
add_executable(your_binary ...)
# or add_library(your_binary ...)
add_dependencies(your_binary run_script)

Is there a way to pass a parameter to the separate_script.cmake?
You can use the cmake variables to pass values when you call the script e.g.
"COMMAND ${CMAKE_COMMAND} -DPARAM=value -P separate_script.cmake"

To prevent that function to run, just wrap it into if:
if(NOT EXISTS ${CMAKE_BINARY_DIR}/blah-blah/generated.cpp)
run_your_provided_command(BLAH_BLAH)
endif()
Easy!
Update: To run it when config file has changed just use little more complicated condition:
if(
NOT EXISTS ${CMAKE_BINARY_DIR}/blah-blah/generated.cpp OR
${CMAKE_SOURCE_DIR}/blah-blah.config IS_NEWER_THAN ${CMAKE_BINARY_DIR}/blah-blah/generated.cpp
)
...
and use add_dependencies command to make sure your binary will be rebuild in case of config file modifications:
add_executable(
YourBinary
...
${CMAKE_BINARY_DIR}/blah-blah/generated.cpp
)
add_dependencies(YourBinary ${CMAKE_SOURCE_DIR}/blah-blah.config)

Related

How to make directory by generator expressions in CMake?

I want to create a directory by using the file(MAKE_DIRECTORY but it doesn't work with the generator expressions.
I'm trying to use the generator expression inside another CMake module, as a rough code snippet:
function_from_another_module(target_name)
and in that module:
file(MAKE_DIRECTORY $<TARGET_FILE_DIR:${target_name}>/foo/bar)
And in my real case, I'm trying to do some management on my exe targets, copy assets, generate files and some other platform based configurations.
The file() command is executed at configuration time and at that time and at that time generator expressions aren't evaluated yet. Furthermore the result may depend which is never available during the configuration process, just during the build.
You may be able to get the desired outcome by using adding build event logic via add_custom_command though:
add_custom_command(TARGET ${target_name} # correct target to attach logic to?
PRE_BUILD # or PRE_LINK/POST_BUILD ?
COMMAND ${CMAKE_COMMAND} -E make_directory "$<TARGET_FILE_DIR:${target_name}>/foo/bar")
Depending on your asset management logic you may want to create a cmake script doing the copying, execute it using ${CMAKE_COMMAND} -P script_file.cmake ... and pass necessary parameters using -D options, see CMake Command Line Tool: Run a Script

CMake call add_subdirectory within custom command

I'm working with a code generator that produces C++ and a CMakeLists.txt file, unfortunately I cannot use this in my main CMakeLists.txt file for testing purposes.
For example you have the following CMakeLists.txt file:
project(SomeProject CXX C)
add_custom_command(OUTPUT ${SRCS}
COMMAND ${CODEGEN_CLI_PATH} -i "${INPUT}" -o "${OUT}"
COMMENT "Generating sources"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM
)
add_custom_target(CODEGEN
DEPENDS
${SRCS}
)
# Needs to be executed after the custom command
add_subdirectory(${GENERATED_CMAKE_LISTS_LOCATION})
Is it possible to use functions such as add_subdirectory only after you execute custom commands for a particular target, such as CODEGEN?
I've already tried to execute it by adding an extra line to the existing custom command:
COMMAND ${CMAKE_COMMAND} -D DIR=${GENERATED_CMAKE_LISTS_LOCATION} -P add_subdirectories.cmake
Unfortuantly this doesn't work because it isn't allowed to execute functions like add_subdirectory in script mode.
Neither I can manage to call custom made functions (that are executing add_subdirectory) from add_custom_command that are located in the same file.
Nope, it is not possible. The add_subdirectory command is run during configuration step, while CODEGEN is a target that runs during build.
You seem to be doing something wrong, so the only advice I can give you is to use execute_process command to run commands you need. The execute_process command is executed during configuration stage, so it will be able to generate files you need before add_subdirectory.
But again, please describe your problem, why do you want CMake to do that.
I have a huge fixed unsigned char array that I compiled into a static library. The way I work around it is by:
if(NOT EXISTS ${PATH_TO_FOLDER}/smeagol.a)
add_subdirectory(smeagol)
endif()
I'm still looking for a nicer kung-fu way to do it using cmake. I feel that its out there, and I will update this answer once i find it.

Run custom shell script with CMake

I am having the following directory structure:
/CMakeLists.txt
/component-a/CMakeLists.txt
/...
/component-b/CMakeLists.txt
/...
/doc/CMakeLists.txt
/create-doc.sh
The shell script create-doc.sh creates a documentation file (doc.pdf). How can I use CMake to execute this shell script at build time and copy the file doc.pdf to the build directory?
I tried it by using add_custom_command in the CMakeLists.txt file inside the directory doc:
add_custom_command ( OUTPUT doc.pdf
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/create-doc.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/)
Unfortunately the command is never run.
I also tried execute_process:
execute_process ( COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/create-doc.sh
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/ )
Now the script is executed during the configuration phase, but not at build time.
You got almost there with add_custom_command. This is indeed the correct way to tell CMake how to generate a file. However, CMake will only run that when something depends on that file.
When, as in your case, the file itself is the end product and is not used any further by subsequent build steps, the usual approach is to create a custom target to drive the relevant custom command(s):
add_custom_target(
BuildDocs ALL
DEPENDS doc.pdf
)
This (custom target driver for custom commands) is a very common idiom in CMake.
You can of course play around with arguments for add_custom_target (e.g. ALL, COMMENT) as it suits you.

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}"
)

Silence custom command depending on CMAKE_VERBOSE_MAKEFILE

I've a custom command in my CMake script which generates a lot of output. I'd like to take advantage of CMAKE_VERBOSE_MAKEFILE so I can decide if I want to see this output or not.
Is there a common way for doing this?
The one way I see is to redirect output to /dev/null depending on this CMake's flag, but what about Windows and other OSes?
Is there a portable or recommended way? What about default rules for C/C++ compiling commands?
Technically speaking, CMAKE_VERBOSE_MAKEFILE exists for the purpose of hiding and showing command lines, not command output.
If I had to do this, I would use a custom variable.
But on the main topic, here is how you should do:
if (COMMAND_VERBOSE)
execute_process(COMMAND "mycustom_command")
else (COMMAND_VERBOSE)
execute_process(COMMAND "mycustom_command" OUTPUT_QUIET)
endif (COMMAND_VERBOSE)
This is the most portable way to do so.
There is also an ERROR_QUIET flag, however it is a bad idea to disable error messages, else the user would be unable to see why the command failed if it failed.
If you are using add_custom_command or add_custom_target instead, such a flag does not exist.
You'll have to provide a manual redirection to /dev/null (Unix), or NUL (Windows).
As SirDarius pointed out, execute_process() has an option to silence tool output, while add_custom_command() / add_custom_target() do not.
But there is a way to work around this: By putting the actual call to your tool in a separate CMake script wrapper, using execute_process() with OUTPUT_QUIET enabled or disabled depending on a switch:
# mycustom_command.cmake
if ( OUTPUT )
execute_process( COMMAND mycustom_command )
else()
execute_process( COMMAND mycustom_command OUTPUT_QUIET )
endif()
Then, use add_custom_command() and the CMake script processing mode (-P) to call that script from your main CMakeLists.txt, with the script switch enabled / disabled by whatever variable you use for that purpose in your CMakeLists.txt file.
add_custom_command( OUTPUT outfile
COMMAND ${CMAKE_COMMAND} -P mycustom_command.cmake -DOUTPUT=${OUTPUT_DESIRED}
)
This is fully portable. If your mycustom_command is build from within your project as a target, just add it as DEPENDENCY to your add_custom_command() to have it build in time for the script call.