Defining Variable in Custom Target for CMake - cmake

I have a CMake file with two targets (say, target1 and target2) defined via "add_custom_target" at the top level of my source directory. I have some external projects in lower level directory. In the extneral project, a "TEST_COMMAND ${Test_Command_Variable}" is defined as part of an ExternalProject_Add().
I would like to change that vaiarble ${Test_Command_Variable} depending on whether I am using custom target1 or target2. Currently, ${Test_Command_Variable} is defined at the top level CMakeLists.txt before either custom targets are defined. I simply want to vary that variable depending on whether target1 or target2 is called. Is there any way to redefine that variable? Maybe do a conditional if statement dependent on whether target1 or target 2 is chosen (this seems like a trivial thing, but I can't find the way to access the "name" of the custom target!).
To clarify: I have two collections of tests. I want to type "make target1" and it runs my first collection of tests. I want to type "make target2" and it tests my second collection of tests. The problem is I also have an external project, where some of the tests are there. The external projects have a TEST_COMMAND(test_command_variable) that does not differentiate between target1 tests and target2 tests. I would like to be able to change that variable depending on whether I run "make target1" or "make target2".

Well, I got it working finally. I did something completely different to meet my objective. I abandoned the goal of trying to toggle variables depending on the target. To be clear, here was my objective:
"make target1" runs only tests in Group 1.
"make target2" runs only tests in Group 2.
I configured each of the tests as per as per this question I asked earlier. My problem was that I had external projects I was trying to pass that variable down so it would toggle Group1 or Group 2 tests depending on the configuration defined at the top level. I knew passing ctest -C group1_configuration flag (or group2_configuration flag for group2 tests)would work, but I couldn't seem to pass down to my external projects. Fortunately, my solution was "simple".
I wrote a shell script that would execute the correct ctest ... -C group1_configuration (or ctest ... -C group2_configuration) and called it test_script1.sh and test_script2.sh. These scripts would move through the build directory and call the aforementioned ctest command in each of the external projects build directories. Then, in my custom target's I defined in the CMakeLists.txt I have it call the shell script and each custom target executes the right shell script, which move through the build directories calling the correct ctest command with the correct configuration flag in each project and external project.

Related

Why do we need one more add_custom_command in cmake? [duplicate]

In CMake semantics there is some sort of distinction between "targets" and commands" that is baffling me. In Makefiles, there is no such distinction:
targetname:dependency
command
i.e. Targets correspond to a generated file of the same name.
In CMake you have commands like "add_custom_command" and "add_custom_target" that have overlapping functionality, and even in the official documentation the semantics are confused, i.e. in "Mastering CMake, 5th edition", page 110 under "Adding a custom target":
The DEPENDS argument sets up a dependency between the custom target
and the custom commands.
My understanding is that targets (generated files) have dependencies (other files, generated or no), and a command to actually do the generation. It is nonsensical to say a target depends on a command. To make matters worse, there are two flavors of "add_custom_command" that either attach an additional command to an existing target, or spit the command out into the ether.
Can someone please explain why this distinction even exists?
Targets
In general, targets comprise executables or libraries which are defined by calling add_executable or add_library and which can have many properties set.
They can have dependencies on one another, which for targets such as these just means that dependent ones will be built after their dependencies.
However, you can also define "custom targets" via add_custom_target. From the docs:
Adds a target with the given name that executes the given commands. The target has no output file and is ALWAYS CONSIDERED OUT OF DATE even if the commands try to create a file with the name of the target. Use ADD_CUSTOM_COMMAND to generate a file with dependencies. By default nothing depends on the custom target. Use ADD_DEPENDENCIES to add dependencies to or from other targets.
So these are different from "normal" targets in that they don't represent things which will produce an exe or lib, but they still benefit from all the properties that targets can have, including having or being dependencies. They appear as a target which can be built (e.g. make MyCustomTarget or msbuild MyCustomTarget.vcxproj). When you build them, you're simply invoking the commands that have been set for them. If they have dependencies on other targets (normal or custom), then these will be built first.
Custom Commands
A custom command defined via add_custom_command is quite different in that it's not a "buildable" object, and doesn't have settable properties in the way that a target does - it's not a named object which can be explicitly referred to again after it's added in the CMakeLists.txt.
It is basically a command (or set of commands) which will be invoked before building a dependent target. That's all that "depends" really means here (at least that's how I view it) - it's just saying that if A depends on B, then B will be built/executed before A is built.
The dependees of a custom command can be either set explicitly using the add_custom_command(TARGET target ... form, or implicitly by creating targets which include the files generated via the add_custom_command(OUTPUT output1 ... form.
In the first case, every time target is built, the custom command is executed first.
In the second case, it's a little more complex. If the custom command has targets which depend on its output file (and the output file doesn't already exist), it is invoked before these dependent objects are built. The dependencies are implicitly created when you do e.g. add_library(MyLib output1.h ... ) where output1.h is a file generated via add_custom_command(OUTPUT output1.h ... ).
add_custom_command adds a callable function that can have defined outputs (using the OUTPUT and BYPRODUCTS arguments). It can also have dependencies that will be run before the function is called.
Notice that it does NOT do things that you may think it does due to strange documentation (the makefile examples are very misleading). In particular, it does not have any guarantees about numbers of times it executes. For example, imagine this:
add_custom_command(OUTPUT /tmp/touched COMMAND echo touch COMMAND touch /tmp/touched)
add_custom_target(touched-one ALL DEPENDS /tmp/touched)
add_custom_target(touched-two ALL DEPENDS /tmp/touched)
How many times will "touch" be printed? You don't know, since it's not specified anywhere; make -j2 will print it twice, probably, but it's timing-dependent:
Scanning dependencies of target touched-two
Scanning dependencies of target touched-one
[ 50%] Generating touched
touch
[100%] Generating touched
touch
[100%] Built target touched-two
[100%] Built target touched-one
But Ninja will only print it once, probably:
# rm -rf * && cmake -GNinja ../c ; cmake --build . -- -j 5
[1/1] Generating touched
touch
Usually, you'll do an add_custom_command to do some work and that defines an OUTPUT, and then you'll have an add_custom_target that depends on the output of the custom command. Anyone who wants the output depends on the target, and that does give you the guarantees you want.
Caveat: see this bug for an great example of why building cross-platform metabuild tools is REALLY HARD.

Code generator generating its own CMake files and targets

Let's assume I have a script that generates a set of source files forming a target I want to link against in a CMakeLists.txt. If the file names are known to the latter then the usual add_custom_target() and add_custom_command() commands will make it possible to use the generated files as target sources.
Let's assume, though, that only the generator script knows the file names and locations. How can a target library be generated so that the parent CMakeLists.txt can link against it without its knowing the actual file names?
Note that the dependency topic isn't in this question's scope as the script knows itself when to regenerate or not. It's not the finest use of CMake, but it's sufficient in this use case.
Idea #1
The script also generates a generated.cmake file included by the parent one using include(generated.cmake). Problem: CMake doesn't find generated.cmake as it isn't existing at configuration time.
Idea #2
Similar to idea #1, but the script is called with the execute_process() so that generated.cmake is present at configuration time. Problem: The script is not called anymore at subsequent builds, thus ignoring possible changes to its input.
Idea #3
The script passes back a list of targets and files that is somehow considered by the parent CMakeLists.txt. So far I couldn't find a way to do so.
The solution I came with is eventually a mixture of all three ideas.
Solution to idea #1's problem
execute_process() actually ensures that generated_targets.cmake is present at configure time.
Solution to idea #2's and #3's problems
As stated in this answer to "Add dependency to the CMake-generated build-system itself", the CMAKE_CONFIGURE_DEPENDS directory property can be edited to add files whose touching re-triggers the configure step.
The key success factor is that this property can be set after the initial execute_process() call so that the script can identify and list its input dependencies (in an output file) that are then added to CMAKE_CONFIGURE_DEPENDS, hence also solving the input dependency problem.
Resulting pseudo code
# The script generates:
# - <output_dir>/cmake/input_files
# - <output_dir>/cmake/generated_targets.cmake
execute_process(
COMMAND myScript
--output-dir ${CMAKE_CURRENT_BINARY_DIR}/generated
)
# Mark the input files as configure step dependencies so that the execute_process
# commands are retriggered on input file change.
file(STRINGS ${CMAKE_CURRENT_BINARY_DIR}/generated/cmake/input_files _input_files)
set_property(
DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS
${_input_files}
)
# Add the generated CMake targets.
include(${CMAKE_CURRENT_BINARY_DIR}/generated/cmake/generated_targets.cmake)

CMake: circular dependency on different targets

My project has a CMakeLists in a libs/ directory like this:
add_subdirectory(foo)
add_subdirectory(bar)
With bar depending on foo.
I recently added new functionalities to foo, and appropriate tests. However those tests need functionalities from bar. This means I currently need to run CMake twice to be able to compile.
Is there a way to get around this issue or am I stuck with re-running cmake multiple times?
Is there a way to declare my dependencies in a specific way maybe? ie right now I do this:
add_library(${BAR} ${SRC_FILES} ${PUBLIC_HEADERS} ${PRIVATE_HEADERS})
which means that linking against ${BAR} in a test (located in the foo module) will expand the variable to an empty string. Can I tell CMake that this should be a target and not just a target name?

How can I control build order in cmake without adding static dependencies?

I'm using OpenGL Loader Generator to generate GL function loading code for my project. It generates gl_loader.h and gl_loader.c. Some of my other source files depend on gl_loader.h, but not all of them, so I need cmake to (re)run glLoadGen if necessary, before compiling the rest of the code, otherwise the build stops with errors because it's trying to compile a file that depends on gl_loader.h before gl_loader.h has been built. I have added the GENERATED property to gl_loader.h/.c but this isn't quite the problem the property is designed to solve and it didn't work.
In this case I can simply add an OBJECT_DEPENDS property to all of my source files, because I will hardly ever need to rerun glLoadGen. But in other situations this would cause too much unnecessary recompilation, such as when several C source and header files are generated by GObjectBuilder (gob2). Manually working out which C files depend on which generated header is impractical.
Another possibility in this case is to run glLoadGen at configure time instead of build time, but that wouldn't be appropriate for the other scenario with gob2.
Is there any other way to tell cmake that it has to run glLoadGen before compiling the other code?
This seems like the ideal scenario for the GENERATED source file property. It basically tells CMake to not worry about the source file not existing at configure time (when CMake runs); it will be available when it's needed at build time (when make/vc/etc. runs).
I'm not familiar with OpenGL or its Loader Generator, but if you use the OUTPUT form of add_custom_command to invoke glLoadGen, the GENERATED property is automatically applied to the output files:
add_custom_command(OUTPUT gl_loader.h gl_loader.c
COMMAND glLoadGen [whatever args are needed]
COMMENT "Generating gl_loader.h and gl_loader.c")
...
add_executable(MyExe ${OtherSources} gl_loader.h gl_loader.c)
WIth this setup, when you build MyExe, the custom command should execute first (if required) producing the appropriate sources (gl_loader.h/.c)

Why does CMake make a distinction between a "target" and a "command"?

In CMake semantics there is some sort of distinction between "targets" and commands" that is baffling me. In Makefiles, there is no such distinction:
targetname:dependency
command
i.e. Targets correspond to a generated file of the same name.
In CMake you have commands like "add_custom_command" and "add_custom_target" that have overlapping functionality, and even in the official documentation the semantics are confused, i.e. in "Mastering CMake, 5th edition", page 110 under "Adding a custom target":
The DEPENDS argument sets up a dependency between the custom target
and the custom commands.
My understanding is that targets (generated files) have dependencies (other files, generated or no), and a command to actually do the generation. It is nonsensical to say a target depends on a command. To make matters worse, there are two flavors of "add_custom_command" that either attach an additional command to an existing target, or spit the command out into the ether.
Can someone please explain why this distinction even exists?
Targets
In general, targets comprise executables or libraries which are defined by calling add_executable or add_library and which can have many properties set.
They can have dependencies on one another, which for targets such as these just means that dependent ones will be built after their dependencies.
However, you can also define "custom targets" via add_custom_target. From the docs:
Adds a target with the given name that executes the given commands. The target has no output file and is ALWAYS CONSIDERED OUT OF DATE even if the commands try to create a file with the name of the target. Use ADD_CUSTOM_COMMAND to generate a file with dependencies. By default nothing depends on the custom target. Use ADD_DEPENDENCIES to add dependencies to or from other targets.
So these are different from "normal" targets in that they don't represent things which will produce an exe or lib, but they still benefit from all the properties that targets can have, including having or being dependencies. They appear as a target which can be built (e.g. make MyCustomTarget or msbuild MyCustomTarget.vcxproj). When you build them, you're simply invoking the commands that have been set for them. If they have dependencies on other targets (normal or custom), then these will be built first.
Custom Commands
A custom command defined via add_custom_command is quite different in that it's not a "buildable" object, and doesn't have settable properties in the way that a target does - it's not a named object which can be explicitly referred to again after it's added in the CMakeLists.txt.
It is basically a command (or set of commands) which will be invoked before building a dependent target. That's all that "depends" really means here (at least that's how I view it) - it's just saying that if A depends on B, then B will be built/executed before A is built.
The dependees of a custom command can be either set explicitly using the add_custom_command(TARGET target ... form, or implicitly by creating targets which include the files generated via the add_custom_command(OUTPUT output1 ... form.
In the first case, every time target is built, the custom command is executed first.
In the second case, it's a little more complex. If the custom command has targets which depend on its output file (and the output file doesn't already exist), it is invoked before these dependent objects are built. The dependencies are implicitly created when you do e.g. add_library(MyLib output1.h ... ) where output1.h is a file generated via add_custom_command(OUTPUT output1.h ... ).
add_custom_command adds a callable function that can have defined outputs (using the OUTPUT and BYPRODUCTS arguments). It can also have dependencies that will be run before the function is called.
Notice that it does NOT do things that you may think it does due to strange documentation (the makefile examples are very misleading). In particular, it does not have any guarantees about numbers of times it executes. For example, imagine this:
add_custom_command(OUTPUT /tmp/touched COMMAND echo touch COMMAND touch /tmp/touched)
add_custom_target(touched-one ALL DEPENDS /tmp/touched)
add_custom_target(touched-two ALL DEPENDS /tmp/touched)
How many times will "touch" be printed? You don't know, since it's not specified anywhere; make -j2 will print it twice, probably, but it's timing-dependent:
Scanning dependencies of target touched-two
Scanning dependencies of target touched-one
[ 50%] Generating touched
touch
[100%] Generating touched
touch
[100%] Built target touched-two
[100%] Built target touched-one
But Ninja will only print it once, probably:
# rm -rf * && cmake -GNinja ../c ; cmake --build . -- -j 5
[1/1] Generating touched
touch
Usually, you'll do an add_custom_command to do some work and that defines an OUTPUT, and then you'll have an add_custom_target that depends on the output of the custom command. Anyone who wants the output depends on the target, and that does give you the guarantees you want.
Caveat: see this bug for an great example of why building cross-platform metabuild tools is REALLY HARD.