How to parameterize cmake add_custom_command and add_custom_target? - cmake

I'm creating a library for micro-controller which as part of it's source will have couple of usage examples.
All CMakeList.txt files for the examples look very similar:
set(ESP_TARGET_FW1 "${CMAKE_CURRENT_BINARY_DIR}/${ESP_FW1}.bin")
set(ESP_TARGET_FW2 "${CMAKE_CURRENT_BINARY_DIR}/${ESP_FW2}.bin")
add_executable(esp_main main.c ${ESP_USER_CONFIG})
target_include_directories(esp_main PUBLIC include)
target_link_libraries(esp_main esp_sdo phy pp net80211)
# Create ESP8266 binary files.
add_custom_command(
OUTPUT
${ESP_TARGET_FW1} ${ESP_TARGET_FW2}
COMMAND
${ESPTOOL_PATH} elf2image $<TARGET_FILE:esp_main> -o ${CMAKE_CURRENT_BINARY_DIR}/
DEPENDS
esp_main
)
# Flash binary files to the device.
add_custom_target(esp_main_flash
COMMAND
${ESPTOOL_PATH} -p ${ESP_PORT} -b ${ESP_BAUD} write_flash ${ESP_FW1} ${ESP_TARGET_FW1} ${ESP_FW2} ${ESP_TARGET_FW2}
DEPENDS
${ESP_TARGET_FW1} ${ESP_TARGET_FW2}
)
Only the name of the example (esp_main) changes and where the generated binary files are created.
I'm looking to somehow parameterize add_custom_command and add_custom_target in case like this. Ideally include some file and have it define esp_main_flash target and custom command.

In CMake the most direct way for parameterize some sequence of actions (for later reuse) is creating a macro or a function. Both of them are allowed to perform any operation which can be written in a plain CMakeLists.txt.

Related

How can I get dependency tracking for a copy of a target file when target-based genex for add_custom_command's OUTPUT param is not supported?

This is a follow-up to How can I get a target's output-file's name during CMake's configuration phase (not the generation phase)?
I want to achieve something similar to the below:
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/to_zip/$<TARGET_FILE_NAME:Foo>
DEPENDS Foo
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:Foo>
${CMAKE_BINARY_DIR}/to_zip/$<TARGET_FILE_NAME:Foo>)
list(APPEND ALL_FILES_IN_TO_ZIP
${CMAKE_BINARY_DIR}/to_zip/$<TARGET_FILE_NAME:Foo>)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/myzip.zip
DEPENDS ${ALL_FILES_IN_TO_ZIP}
COMMAND <zip everything up in ${CMAKE_BINARY_DIR}/to_zip>)
add_custom_target(create-zip DEPENDS ${CMAKE_BINARY_DIR}/to_zip/myzip.zip)
Basically, when I invoke the create-zip target, I want to copy a lot of files to ${CMAKE_BINARY_DIR}/to_zip, and then zip up everything in it. The issue with the above code is that I am not allowed to use target based generator expression for OUTPUT in add_custom_command, so the above code doesn't work. From the CMake docs:
New in version 3.20: Arguments to OUTPUT may use a restricted set of generator expressions. Target-dependent expressions are not permitted.
Is there a way to workaround this problem to make it work?
I'm pretty sure you don't need to use the name of the target's output file as the OUTPUT of your custom command which does the copy. All you're really using the OUTPUT field for is CMake's dependency mechanisms (where it tracks what things have changed and does things again if anything in DEPENDS has changed (modification time in the filesystem)). You can just create a dummy file that you touch (update the modification time for in the filesystem) whenever the target has changed and once its copy has been made.
set(targets target_foo target_bar target_baz)
foreach(target "${targets}")
set(indicator "${CMAKE_BINARY_DIR}/to_zip/rezip_indicators/${target}")
add_custom_command(
OUTPUT "${rezip_indicator_file}"
DEPENDS "${target}"
COMMAND "${CMAKE_COMMAND}" -E copy $<TARGET_FILE:${target}>
"${CMAKE_BINARY_DIR}/to_zip/$<TARGET_FILE_NAME:${target}>"
COMMAND "${CMAKE_COMMAND}" -E touch "${rezip_indicator_file}"
)
list(APPEND NEEDS_REZIP_INDICATOR_FILES "${rezip_indicator_file}")
endforeach()
add_custom_command(
OUTPUT "${CMAKE_BINARY_DIR}/myzip.zip"
DEPENDS "${NEEDS_REZIP_INDICATOR_FILES}"
COMMAND <zip everything up in ${CMAKE_BINARY_DIR}/to_zip>
)
add_custom_target(create-zip DEPENDS "${CMAKE_BINARY_DIR}/to_zip/myzip.zip")
Or something to that effect.
The indicator file will get its timestamp updated by the touch command in the custom command when the target file has changed. The target file and the needs-rezip indicator file get "synced" when the custom command runs, which will be whenever you run the buildsystem after the target has gotten rebuilt. At least- I'm pretty sure that's how things work.

How to rerun cmake custom command, when one of its dependencies becomes "dirty"?

I have python script that is run through add_custom_command in my project, this script analyzes target sources and generate additional one for the target.
get_target_property(TARGET_SOURCES ${target} SOURCES)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/static_init/generated/${target}/static_init.cpp
COMMAND ${Python3_EXECUTABLE} myscript.py
DEPENDS ${TARGET_SOURCES}
VERBATIM)
target_sources( "${target}" PRIVATE "${CMAKE_BINARY_DIR}/static_init/generated/${target}/static_init.cpp" )
I need my script to rerun when one of sources becomes "dirty" (e.g. one of included headers is changed, probably this file is in another target and it is not guaranteed that another target will be rebuild on header change). As we use Ninja, IMPLICIT_DEPENDS isn't available for us, so it looks DEPFILE property is the way to do it, but I cannot understand, how can I get one for all ${TARGET_SOURCES}?
It doesn't seem to me that ninja will generate something like that automatically, I cannot write it by hands, as it will require to recursively analyze all includes during cmake run. Or maybe there is another way to solve this problem?
To get dependencies of a source you have to compile it. Just depend on the target. I would do:
add_custom_target(target_objects OBJECT all_source_files_for_your_target)
add_custom_command(
OUTPUT ${CMAKE_BINARY_DIR}/static_init/generated/${target}/static_init.cpp
COMMAND ${Python3_EXECUTABLE} myscript.py
DEPENDS $<TARGET_OBJECTS:target_objects>
VERBATIM
)
add_custom_library/executable("${target}" PRIVATE
${CMAKE_BINARY_DIR}/static_init/generated/${target}/static_init.cpp
)
link_target_libraries("${target}" PRIVATE target_objects)
That way the dependencies are linear.

CMake's objects output folder variable

How "${PROJECT_BINARY_DIR}/CMakeFiles/project.dir/", the place object files resulted from compilation will be placed on, can be un-hardcoded?
Going straightly to the problem, we have some tests that check objects resulted from compilation on harfbuzz cmake and we use a hardcoded string there but that doesn't seem right and I hope some ${} or $<> exist for that.
I'm afraid you're out of luck here. CMake keeps this as an internal implementation detail, by design.
I'd say it's unlikely to change, but if you want to be absolutely future-proof, you could use a workaround of creating a static library out of the object files and then manually unpacking it back into object files (using the appropriate archiver/librarian) as part of the test. If the object files are also used by another target, and linking to that static library wouldn't work for that target, you can make an object library out of the files and then use that in both the original target and the for-test static library.
Here's an example of how you might achieve this workaround:
add_library(MyObjectLib OBJECT src/a.cpp src/b.cpp)
add_executable(Main src/main.cpp $<TARGET_OBJECTS:MyObjectLib>)
add_library(LibForTesting STATIC $<TARGET_OBJECTS:MyObjectLib>)
add_test(
NAME check-static-inits.sh
COMMAND ${PROJECT_SOURCE_DIR}/src/prepare-and-check-static-inits.sh $<TARGET_FILE:LibForTesting>
)
And here's what the script prepare-and-check-static-inits.sh would look like (pseudo-code):
ar -x $1 -o some_dir
./check-static-inits.sh some_dir
Turning my comment into an answer
There is at the moment no variable or generator expression to get the list of object files used for linking a archive or library target.
But you could append compiler/archiver/linker calls with any program/script and utilize CMake's expansion rules inside those calls.
Note: That will only work CMake's Command-Line Build Tool Generators. And the list(APPEND ...) calls only have to be there once in your CMake code after your project() call.
Examples
Generate a symbolic link to <OBJECT_DIR> with <TARGET_NAME>
project(MyLib)
list(
APPEND CMAKE_CXX_ARCHIVE_FINISH
"\"${CMAKE_COMMAND}\" -E create_symlink \"<OBJECT_DIR>\" \"<TARGET_NAME>\""
)
[...]
add_library(MyLib STATIC src/a.cpp src/b.cpp)
Call some program to do something with the <OBJECTS> list (e.g. echo or write to a file)
project(MyExe)
list(
APPEND CMAKE_CXX_LINK_EXECUTABLE
"\"${CMAKE_COMMAND}\" -E echo \"<OBJECTS>\""
)
[...]
add_executable(MyExe main.cpp)
Directly do something after each object file is generated. In your case where you want to call objdump for each object file it would e.g. be:
list(
APPEND CMAKE_CXX_COMPILE_OBJECT
"${CMAKE_OBJDUMP} -t \"<OBJECT>\" > $(notdir <OBJECT>.dump)"
)
Unfortunately there is no expansion rule for "output file name" hence the platform/make specific trick with $(notdir ...).
References
CMAKE_<LANG>_ARCHIVE_FINISH
CMAKE_<LANG>_LINK_EXECUTABLE
CMAKE_<LANG>_COMPILE_OBJECT
How to get path to object files with CMake for both multiconfiguration generator and makefile based ones?

Dynamically generated dependencies

I am trying to generate file that depends on a set of files that can
change throughout different make invocations.
To understand it better, let's show you the code:
cmake_minimum_required(VERSION 2.8)
project(demo-one C)
add_custom_command(
OUTPUT
"${CMAKE_BINARY_DIR}/generated.c"
COMMAND
generate -o "${CMAKE_BINARY_DIR}/generated.c"
DEPENDS
"$(shell generate-dependencies-list)"
COMMENT
"Generating generated.c"
)
add_executable(main main.c "${CMAKE_BINARY_DIR}/generated.c")
So, I want to generate the file generated.c with the generate
command and this files needs to be regenerated when the files
specified by generated-dependencies-list command changes. As you may
notice, generated-dependencies-list can generate different set of
files throughout make invocations, so is not feasible to get
the result of generated-dependencies-list at configure time to then
pass the result to add_custom_command.
Actually the above code somewhat works, but it looks like a hack that
will only work for Makefile backend, also the make rule doesn't look
as what I'm expecting, after all, it's a hack:
generated.c: ../$(shell\ generate-dependencies-list)
Basically, I want this rule or something to get the same result:
generated.c: $(shell generate-dependencies-list)
Has CMake any feature to achieve this?
when the files specified by generated-dependencies-list command changes
If the output of the command generated-dependencies-list depends only on this script and script's parameters then you can just add this script to DEPENDS sub-option:
add_custom_command(
OUTPUT
"${CMAKE_BINARY_DIR}/generated.c"
COMMAND
"${CMAKE_CURRENT_LIST_DIR}/generate-dependencies-list"
COMMAND
generate -o "${CMAKE_BINARY_DIR}/generated.c"
DEPENDS
"${CMAKE_CURRENT_LIST_DIR}/generate-dependencies-list"
COMMENT
"Generating generated.c"
)

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.