How to write a custom command that runs whenever a file A is newer than file B? - cmake

I need to write a custom command that runs whenever file A is newer than file B.
How do I do this in CMake?

Sounds like you want something similar to this:
add_custom_command(OUTPUT B
COMMAND ${CMAKE_COMMAND} -Dinput=A -P script_that_generates_B.cmake
DEPENDS A
)
Where "B" is the full path to the output file, "A" is the full path to some input file, and the command is something that runs at build time to produce B whenever A changes.
In order for the rule producing B to be executed at build time, something else must depend on B also. It should appear either as a DEPENDS of an add_custom_target that is in "all" or as a source file to an add_library or add_executable command to trigger the command to run.
EDIT:
You can also use the
if(file1 IS_NEWER_THAN file2)
construct at CMake configure time, if necessary. The documentation of the IF command is rather lengthy, but searching on this page for IS_NEWER_THAN yields this nugget:
"True if file1 is newer than file2 or if one of the two files doesn't exist. Behavior is well-defined only for full paths."

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 run post build commands in meson?

How can I do in meson to run a command after building a target?
Eg. I have an executable:
executable('target.elf', 'source1.c', 'source2.c')
And after target.elf built I want to execute a command (eg. chmod -x target.elf) on it.
I tried custom_target(), but that requires an output. I don't have new output, I just have target.elf. I tried run_command() but I didn't know how to execute it after the building.
executable now has an argument install_mode (added 0.47.0) to specify the file mode in symbolic format and optionally the owner/uid and group/gid for the installed files.
I just noticed that yasushi-shoji has provided this answer already.
The following code should do.
project('tutorial', 'c')
exec = executable('target.elf', 'main.c', build_by_default : false)
custom_target('final binary',
depends : exec,
input : exec,
output : 'fake',
command : ['chmod', '+x', '#INPUT#'],
build_by_default : true)
Note that because I want to always run the fake target, I'm using custom_target(). However, the command chmod + x demo doesn't generate the file fake specified in custom_target(), successive ninja command will always run the target.
If you don't want this behaviour, there are two ways:
You can write a script which chmod the target.elf and then copies it to target, thus effectively creates the target file. Make sure to change the output file in the meson.build if you do so.
If you don't mind typing ninja chmod instead of ninja, you can use run_target().
# optional
run_target('chmod',
command : ['chmod', '+x', exec])
Another alternative is to use install_mode for executable().
Also note that you should always use find_program() instead of plain chmod. This example doesn't use it for simplicity.

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

How to use the program's exit status at compile time?

This question is subsequent to my previous one: How to integrate such kind of source generator into CMake build chain?
Currently, the C source file is generated from XS in this way:
set_source_files_properties(${CMAKE_CURRENT_BINARY_DIR}/${file_src_by_xs} PROPERTIES GENERATED 1)
add_custom_target(${file_src_by_xs}
COMMAND ${XSUBPP_EXECUTABLE} ${XSUBPP_EXTRA_OPTIONS} ${lang_args} ${typemap_args} ${file_xs} >${CMAKE_CURRENT_BINARY_DIR}/${file_src_by_xs}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
DEPENDS ${file_xs} ${files_xsh} ${_XSUBPP_TYPEMAP_FILES}
COMMENT "generating source from XS file ${file_xs}"
)
The GENERATED property let cmake don't check the existence of this source file at configure time, and add_custom_target let the xsubpp always re-run at each compile. The reason for always rerun is because xsubpp will generate an incomplete source file even if it fails, so there are possibility that the whole compiling continues with an incomplete source file.
I found it is time consuming to always re-run source generator and recompile it. So I want to have it re-run only when dependent XS files are modified. However, if I do so, the incomplete generated source file must be deleted.
So my question is: is there any way to remove the generated file, only when the program exit abnormally at compile time?
Or more generic: is there any way to run a command depending on another command's exit status at compile time?
You can always write a wrapper script in your favorite language, e.g. Perl or Ruby, that runs xsubpp and deletes the output file if the command failed. That way you can be sure that if it exists, it is correct.
In addition, I would suggest that you use the OUTPUT keyword of add_custom_command to tell CMake that the file is a result of executing the command. (And, if you do that, you don't have to set the GENERATED property manually.)
Inspired by #Lindydancer's answer, I achieved the purpose by multiple COMMANDs in one target, and it don't need to write an external wrapper script.
set(source_file_ok ${source_file}.ok)
add_custom_command(
OUTPUT ${source_file} ${source_file_ok}
DEPENDS ${xs_file} ${xsh_files}
COMMAND rm -f ${source_file_ok}
COMMAND xsubpp ...... >${source_file}
COMMAND touch ${source_file_ok}
)
add_library(${xs_lib} ${source_file})
add_dependencies(${xs_lib} ${source_file} ${source_file_ok})
The custom target has 3 commands. The OK file only exists when xsubpp is success, and this file is added as a dependency of the library. When xsubpp is not success, the dependency on the OK file will force the custom command to be run again.
The only flaw is cross-platform: not all OS have touch and rm, so the name of these two commands should be decided according to OS type.

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.