Proper way to call a found executable in a custom command? - cmake

I have a program on my computer, let's say C:/Tools/generate_v23_debug.exe
I have a FindGenerate.cmake file which allows CMake to find that exact path to the executable.
So in my CMake code, I do:
find_program(Generate)
if (NOT Generate_FOUND)
message(FATAL_ERROR "Generator not found!")
So CMake has found the executable. Now I want to call this program in a custom command statement. Should I use COMMAND Generator or COMMAND ${GENERATOR_EXECUTABLE}? Will both of these do the same thing? Is one preferred over the other? Is name_EXECUTABLE a variable that CMake will define (it's not in the FindGenerate.cmake file), or is it something specific to someone else's example code I'm looking at? Will COMMAND Generator be expanded to the correct path?
add_custom_command(
OUTPUT blahblah.txt
COMMAND Generator inputfile1.log
DEPENDS Generator
)

find_program stores its result into the variable given as a first argument. You can verify this by inserting some debug output:
find_program(GENERATOR Generate)
message(${GENERATOR})
Note that find_program does not set any additional variables beyond that. In particular, you mentioned Generate_FOUND and GENERATOR_EXECUTABLE in your question and neither of those gets introduced implicitly by the find_program call.
The second mistake in your program is the use of the DEPENDS option on the add_custom_command. DEPENDS is used to model inter-target dependencies at build time and not to manipulate control flow in the CMakeLists. For example, additional custom command can DEPEND on the output of your command (blahblah.txt), but a custom command cannot DEPEND on the result of a previous find operation.
A working example might look something like this:
find_program(GENERATOR Generate)
if(NOT GENERATOR)
message(FATAL_ERROR "Generator not found!")
endif()
add_custom_command(
OUTPUT blahblah.txt
COMMAND ${GENERATOR} inputfile1.log
)
P.S.: You asked why the code examples were not properly formatted in your question. You indented everything correctly, but you need an additional newline between normal text and code paragraphs. I edited your question accordingly.

Related

Expand variable name containing a generator expression in cmake

In the build process, I set directories where I gather the build output of different sub-projects. The directories are set as :
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_LIST_DIR}/../build/bin/debug" )
set( CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_LIST_DIR}/../build/bin/release" )
Now, I'd like to copy some files (a directory of qt plugins) to that directory dependent on the configuration which it is built for.
I tried:
# copy qt plugins
add_custom_command( TARGET mytarget POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_directory
"${QT_DIR}/../../../plugins"
"${$<UPPER_CASE:CMAKE_RUNTIME_OUTPUT_DIRECTORY_$<CONFIG> >}/plugins"
COMMAND_EXPAND_LISTS)
thus, I try to build a string that equals the variable name and then try to expand that as described here: CMake interpret string as variable. In other words: I would like to have a generator expression that evaluates to the content of CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG or CMAKE_RUNTIME_OUTPUT_DIRECTOR_RELEASE dependent on the current build configuration.
However running cmake with the statement above results in an error:
"CMakeLists.txt:112: error: Syntax error in cmake code at [..] when parsing string ${$<UPPER_CASE:CMAKE_RUNTIME_OUTPUT_DIRECTORY_$<CONFIG> >}/plugins Invalid character ('<') in a variable name: '$'
So my question is, how can I use a generator-expression to access the corresponding variable? (Bonus question: is there another/better way to achieve the same goal?)
So my question is, how can I use a generator-expression to access the corresponding variable?
You cannot. There is currently (CMake <=3.23) no way to expand a variable whose name is determined by the value of a generator expression.
Bonus question: is there another/better way to achieve the same goal?
Yes, and you are almost there! You can use $<TARGET_FILE_DIR:...>:
add_custom_command(
TARGET mytarget POST_BUILD
COMMAND
${CMAKE_COMMAND} -E copy_directory
"${QT_DIR}/../../../plugins"
"$<TARGET_FILE_DIR:mytarget>/plugins"
VERBATIM
)
This works because TARGET_FILE_DIR evaluates to the actual directory containing the executable or library file for mytarget, no matter the active configuration, property values, etc.
Docs: https://cmake.org/cmake/help/latest/manual/cmake-generator-expressions.7.html#genex:TARGET_FILE_DIR
CMAKE_RUNTIME_OUTPUT_DIRECTORY_<CONFIG> is already relative to the binary directory so you should not try to compute the binary directory in its definition. Also, it supports generator expressions. Thus, the following will be much more robust:
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "bin/$<LOWER_CASE:$<CONFIG>>"
CACHE STRING "Common output directory for runtime artifacts")
This has a bunch of concrete benefits:
No need to set CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG or CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE
This will work for MinSizeRel and RelWithDebInfo, plus any custom configurations one might add down the line.
Since it's defined as a cache variable, it can be overridden for debugging / working around name clashes, etc.
A bit more context for (3): most CMAKE_* variables are intended to be either read-only or user-configurable (i.e. at the command line, from the GUI, etc.). Overriding their defaults via set(CACHE) is a polite compromise. A notable exception to this rule is the collection of Qt codegen flags (CMAKE_AUTO{MOC,RCC,UIC}). These must typically be set for the build to produce usable binaries.

In CMake how do I deal with generated source files which number and names are not known before?

Imagine a code generator which reads an input file (say a UML class diagram) and produces an arbitrary number of source files which I want to be handled in my project. (to draw a simple picture let's assume the code generator just produces .cpp files).
The problem is now the number of files generated depends on the input file and thus is not known when writing the CMakeLists.txt file or even in CMakes configure step. E.g.:
>>> code-gen uml.xml
generate class1.cpp..
generate class2.cpp..
generate class3.cpp..
What's the recommended way to handle generated files in such a case? You could use FILE(GLOB.. ) to collect the file names after running code-gen the first time but this is discouraged because CMake would not know any files on the first run and later it would not recognize when the number of files changes.
I could think of some approaches but I don't know if CMake covers them, e.g.:
(somehow) define a dependency from an input file (uml.xml in my example) to a variable (list with generated file names)
in case the code generator can be convinced to tell which files it generates the output of code-gen could be used to create a list of input file names. (would lead to similar problems but at least I would not have to use GLOB which might collect old files)
just define a custom target which runs the code generator and handles the output files without CMake (don't like this option)
Update: This question targets a similar problem but just asks how to glob generated files which does not address how to re-configure when the input file changes.
Together with Tsyvarev's answer and some more googling I came up with the following CMakeList.txt which does what I want:
project(generated)
cmake_minimum_required(VERSION 3.6)
set(IN_FILE "${CMAKE_SOURCE_DIR}/input.txt")
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${IN_FILE}")
execute_process(
COMMAND python3 "${CMAKE_SOURCE_DIR}/code-gen" "${IN_FILE}"
WORKING_DIRECTORY ${PROJECT_BINARY_DIR}
INPUT_FILE "${IN_FILE}"
OUTPUT_VARIABLE GENERATED_FILES
OUTPUT_STRIP_TRAILING_WHITESPACE
)
add_executable(generated main.cpp ${GENERATED_FILES})
It turns an input file (input.txt) into output files using code-gen and compiles them.
execute_process is being executed in the configure step and the set_property() command makes sure CMake is being re-run when the input file changes.
Note: in this example the code-generator must print a CMake-friendly list on stdout which is nice if you can modify the code generator. FILE(GLOB..) would do the trick too but this would for sure lead to problems (e.g. old generated files being compiled, too, colleagues complaining about your code etc.)
PS: I don't like to answer my own questions - If you come up with a nicer or cleaner solution in the next couple of days I'll take yours!

Cmake generator expression TARGET_PROPERTY of external project

I need to use a property from an external project. So far I'm successfully doing that this way:
ExternalProject_Add(
some_ep_name
...
)
# Get source dir
ExternalProject_Get_Property(some_ep_name SOURCE_DIR)
# Set source dir value into less generically named variable
set(SOME_EP_NAME_SOURCE_DIR "${SOURCE_DIR}")
This works, but it seems unnecessarily verbose, and it annoys me a little. I was hoping I could use a generator expression, like so:
"$<TARGET_PROPERTY:some_ep_name,SOURCE_DIR>"
But it seems like this doesn't work. Before I give up, I wanted to check if I was doing something wrong or if anyone knows a better way.
All "properties" of ExternalProject are known at configuration time. So they don't require support of generator expressions, which main intention is usage for values not known at configuration time (but known at build time).
If you found "unnecessarily verbose" having several lines of code for save external project' property into the variable, you may create a macro/function for incorporate all these lines. Then calling the macro/function will use only single line of code:
function(ExternalProject_Property_to_Var VAR eproject prop)
ExternalProject_Get_Property(${eproject} ${eprop})
set(${VAR} ${${eprop}} PARENT_SCOPE)
endfunction()
...
ExternalProject_Property_to_Var(SOME_EP_NAME_SOURCE_DIR some_ep_name SOURCE_DIR)

CMake: add dependency to add_custom_command dynamically

I have a CMake project with many subprojects.
Each of them can use a function I provide to generate a small text file with some certain information (by calling add_custom_command).
At the final step I'd like to combine all those files into one big text file.
I've created a custom command which searches for created files (all in one place) and merges them.
The problem is that I'd like to make this final step dependend on all of small steps being made in subprojects while I don't actually know how many files will be provided.
My final command looks like:
add_custom_command(OUTPUT combination.txt
COMMAND create combination.txt from all files from /path/)
and my create-small-text-file-for-each-subproject command looks like:
add_custom_command(OUTPUT /path/${sub_project_name}.txt
COMMAND create /path/${sub_project_name}.txt)
And when I create those small files I'd like to do something like to make "combination.txt" depend on /path/${sub_project_name}.txt
So I wish I could:
add_dependency(combination.txt /path/${sub_project_name}.txt)
However this only works for targets.
I've also tried to use set_source_files_properties with OBJECT_DEPENDS, but it seems to doesn't work (maybe its intend to be used with add_target's cpp files ?)
The last way to get it work I see is to use a cache variable which would accumulate all those small files paths and then use it like this:
add_custom_command(OUTPUT combination.txt
COMMAND create combination.txt from all files from /path/
DEPENDS ${all_small_files_list})
but this is the last thing I want to do.
Instead of using add_custom_command you could use add_custom_target with a correct dependency-definition (so that is it not built every time).
add_custom_target(project
COMMAND touch project.txt)
add_custom_target(project2
COMMAND touch project2.txt)
add_custom_target(combination
COMMAND cat project.txt project2.txt > combination.txt)
add_dependencies(combination project2)
add_dependencies(combination project)
add_executable(t t.c)
add_dependencies(t combination.txt)
Again: make sure you're using the DEPENDS argument of add_custom_target to create a real dependency chain so that a project-target and thus the combination-target gets out of date.
UPDATE: I was too premature. In fact cmake (at least up to 2.8.9) works as follows for dependencies: with a call to add_dependencies you cannot add a dependency which is the OUTPUT of a custom command IOW a (generated) file. With add_dependencies you can only add target as created by add_custom_target. However in a add_custom_target you can depend on an output of add_custom_command by using the DEPENDS-directive. That said this makes it work:
add_custom_command(OUTPUT project.txt
COMMAND uptime >> project.txt MAIN_DEPENDENCY t2.c)
add_custom_target(project DEPENDS project.txt)
add_custom_target(combination
COMMAND cat project.txt project2.txt > combination.txt)
add_dependencies(combination project)
This will make the combination target always be regenerated as it has no MAIN_DEPENDENCY or DEPENDS, but the usage of add_dependencies is allowed.

How to create a C #define for a certain target using CMake?

I feel a little stupid right now. After recently converting a few smaller projects to use CMake, I decided to also get rid of a few "Platform_Config.h" files. These files contain a few preprocessing directives like #define USE_NEW_CACHE and control compilation.
How would I 'convert' these defines to be controlled with CMake? Ideally by using these "cache" variables the user can easily edit.
There are two options. You can use the add_definitions method to pass defines as compiler flags: E.g. somewhere in your projects cmakelists.txt:
add_definitions( -DUSE_NEW_CACHE )
CMake will make sure the -D prefix is converted to the right flag for your compiler (/D for msvc and -D for gcc).
Alternatively, check out configure_file. It is more complex, but may be better suited to your original approach with a Platform_Config file.
You can create an input-file, similar to your original Platform_Config.h and add "#cmakedefine" lines to it.
Let's call in Platform_Config.h.in:
// In Platform_Config.h.in
#cmakedefine USE_NEW_CACHE
// end of Platform_Config.h.in
When then running
configure_file( ${CMAKE_SOURCE_DIR}/Platform_Config.h.in ${CMAKE_BINARY_DIR}/common/Platform_Config.h )
it will generate a new Platform_Config file in your build-dir. Those variables in cmake which are also a cmakedefine will be present in the generated file, the other ones will be commented out or undefed.
Of course, you should make sure the actual, generated file is then correctly found when including it in your source files.
option command might provide what you are looking for.
use it with the COMPILE DEFINITIONS property on the target and i think you are done.
To set the property on the target, use the command set target properties
option(DEBUGPRINTS "Prints a lot of debug prints")
target(myProgram ...)
if(DEBUGPRINTS)
set_target_properties(myProgram PROPERTIES COMPILE_DEFINITIONS "DEBUGPRINTS=1")
endif()
edit:
The option i wrote in the example shows up as a checkbox in the CMake GUI.
In case you want to set defines per target: Since 2.8.11 you can use target_compile_definitions.
In earlier versions you probably don't want to use set_target_properties as is, since it overwrites any defines you set previously. Call get_target_property first instead, then merge with previous values. See add_target_definitions here.
Use target_compile_options. Do not quote your define or it not be detected as a define. CMake parses off the /define and adds the actual define to the DefineConstants section of the csproj, if there are quotes it will put the entire quoted string in the AdditionalOptions section of the csproj.
An example from one of my projects that uses generator expressions:
target_compile_options( ${LIBRARY_NAME} PRIVATE
$<${IS_ART_ITERATION_BUILD}:/define:ART_ITERATION_BUILD>
)
An example without generator expressions:
target_compile_options( ${LIBRARY_NAME} PRIVATE
/define:GRAPHICS_VULKAN
)