How do I add a dependency on a script to a target in CMake? - cmake

After my program is linked I need to perform some post-processing on it. I added a add_custom_command(TARGET ... and that worked fine. However, this extra custom command runs a script (that is not generated; it's checked into the codebase), and I want the target to be considered out of date if that script changes so it will be rebuilt properly.
The add_dependencies rule seems to only work between top-level elements, which this is not (it's just a script), and there's no DEPENDS element in this form of the add_custom_command that I can use.
How do I do this?

It's unfortunately a bit convoluted, but you can use add_custom_target to invoke CMake in script-processing mode via -P.
You need to use add_custom_target here since it will always execute, even if everything's up to date.
Having made this decision, we need to have the custom target execute commands which will check for a new version of your post-processing script file (let's call this "my_script" and assume it's in your root dir), and if it's changed cause your dependent target to go out of date.
This would comprise:
Compare a previous copy of "my_script" to the current "my_script" in the source tree. If the current "my_script" is different, or if the copy doesn't exist (i.e. this is the first run of CMake) then...
Copy "my_script" from the source tree to the build tree, and...
Touch a source file of the dependent target so that it goes out of date.
All of the commands required inside the CMake script can be achieved using execute_process to invoke cmake -E.
So the CMake script (called e.g. "copy_script.cmake") would be something like:
execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files
${OriginalScript} ${CopiedScript} RESULT_VARIABLE Result)
if(Result)
execute_process(COMMAND ${CMAKE_COMMAND} -E copy
${OriginalScript} ${CopiedScript})
execute_process(COMMAND ${CMAKE_COMMAND} -E touch_nocreate ${FileToTouch})
endif()
The CMake script needs to have required variables passed in via the -D args before calling -P, so the calling CMakeLists.txt would have something like:
set(FileToTouch ${CMAKE_SOURCE_DIR}/src/main.cpp)
add_custom_target(CopyScript ALL ${CMAKE_COMMAND}
-DOriginalScript=${CMAKE_SOURCE_DIR}/my_script
-DCopiedScript=${CMAKE_BINARY_DIR}/my_script
-DFileToTouch=${FileToTouch}
-P ${CMAKE_SOURCE_DIR}/copy_script.cmake)
add_executable(MyExe ${FileToTouch})
This will cause a full rebuild of the executable, since it thinks a source file has been modified. If you only require to force relinking there may be a better way to achieve this.

Related

Cause CMake to force recompile a file, but only if project is otherwise recompiled?

I have generally the same question as in Can CMake always force the compilation/build of a specific file?
I have a C++ file using __DATE__ to display the build date of my app. But if this file is not modified, it will not be rebuilt and the date will not be updated.
Can CMake always rebuild that specific file?
... except I want something slightly different:
In the CMake project I have (for C, transpiles to Makefile which I use), sometimes there are no actual changes to the code when I run make, which is detected nicely, in the sense that there is no recompilation (or relinking) of the program.
Obviously, in this case, I do not want to update the timestamp, and end up with a new executable, which is otherwise identical to the previous one - apart from the build date.
I have seen in the quoted post, that one simply has to ensure a changed timestamp on the file, to force a recompilation. So, assuming my __DATE__ usage is in use_date.c, what I'd want, is that the timestamp of use_date.c is updated (forcing recompilation), only if any other file in the project (say, main.c) has been changed, so it forces project recompilation and linking (obviously, this should also work if I just change use_date.c manually, and no other file).
So, assuming my project just generates an executable (no libraries):
add_executable(my_project use_date.c other_file.c main.c)
... is it possible to add a CMake step, that updates the timestamp of use_date.c (and thus causes its recompilation), only if otherwise the project is getting recompiled and relinked?
OK, found a way: it seems kind of a squeaky solution - hopefully someone more knowledgeable in CMake will post a proper solution eventually. But in the meantime:
I've found that add_custom_command with POST_BUILD runs the custom command only if a new binary (.elf for me) is generate, and otherwise does not run the custom command.
So, basically, we could have a small bash script in the custom command:
If this bash script is called, we can assume that POST_BUILD has called it, meaning that a new .elf (for whatever other reasons) has been built
So we can touch the use_date.c, and while in the bash script still, descend into the build directory, and explicitly run make again - which should recompile only use_date.c, and then link with the rest from the previous build.
This essentially works - except, it causes an infinite loop.
This can get solved with creating a temp file:
If this bash script is called, we can assume that POST_BUILD has called it, meaning that a new .elf (for whatever other reasons) has been built
check for a temp file:
if it does not exist, create it - and then we can touch the use_date.c, and while in the bash script still, descend into the build directory, and explicitly run make again - which should ...
if it does exist, then we've been called from the second .elf. build (for the touched use_date.c), so we want to remove the temp file, and just exit - stopping the infinite loop
This seems to work fine:
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
DEPENDS ALL
#COMMAND bash ARGS -c "touch ${CMAKE_CURRENT_SOURCE_DIR}/use_date.c && cd build && make" ## infinite loop!
COMMAND bash ARGS -c "if [ ! -f .refresh ]; then echo '.refresh' | tee .refresh && touch ${CMAKE_CURRENT_SOURCE_DIR}/use_date.c && cd build && make; else rm -v .refresh; fi" # OK!
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "remake with refreshed __DATE__ in use_date.c (${CMAKE_CURRENT_SOURCE_DIR})"
VERBATIM
)

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.

add_custom_command depending on another add_custom_command

We have two add_custom_command clauses, where one depends on the other:
The first command compiles an .osl source file into an .oso object file using the oslc compiler:
set (oslc ${PROJECT_SOURCE_DIR}/sandbox/bin/oslc)
add_custom_command (
OUTPUT "${oso_dir}/${oso_filename}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${oso_dir}"
COMMAND "${oslc}" -I"${osl_include_path}" -o "${oso_dir}/${oso_filename}" "${osl_src_abs}"
MAIN_DEPENDENCY ${osl_src_abs}
DEPENDS ${${headers}} ${osl_src_abs} "${oslc}"
)
Notice the dependency on ${oslc}: we explicitly depend on ${oslc} because we need to make sure it exists before we can execute this command.
The second command "builds" (really, deploys) the oslc compiler by copying it from somewhere else:
add_custom_command (
OUTPUT "${PROJECT_SOURCE_DIR}/sandbox/bin/oslc"
COMMAND ${CMAKE_COMMAND} -E copy ${OSL_COMPILER} ${PROJECT_SOURCE_DIR}/sandbox/bin/
)
While this setup works, it has the side effect that both commands are always executed (the second command followed by the first command) even when the .osl input file hasn't been modified.
It seems that this behavior is specific to Windows. It appears to work fine on Linux.
If the dependency to ${oslc} is removed from the first command, the second command is no longer executed at all, even when the oslc compiler is missing; but on the other hand, .osl files are now only recompiled when they have changed since the last build, as desired (as long as oslc is present).
Is there anything wrong with this setup? If not, what is the right way to combine both features: compiling .osl files only when they have changed since the last build, and "building" the oslc compiler (required by the first step) when it doesn't yet exist?
The actual CMake script is available on GitHub:
Entire CMakeLists.txt file
First add_custom_command clause
Second add_custom_command clause
A simple solution, at least for Windows, is to change the second command to
add_custom_command (
TARGET appleseed.shaders
PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${OSL_COMPILER} ${PROJECT_SOURCE_DIR}/sandbox/bin/
)
(notice the PRE_BUILD keyword)
and to remove the explicit dependency to ${oslc} from the first command.

Why add_custom_command does not work

I am trying to copy one file ${PROJECT_SOURCE_DIR}/abc.h to another location {PROJECT_SOURCE_DIR}/src with the following command:
add_custom_command(
TARGET MyTarget
POST_BUILD
COMMAND -E copy ${PROJECT_SOURCE_DIR}/abc.h
$<"${PROJECT_SOURCE_DIR}/src":MyTarget>)
However, no matter how I try, it seems that the created VC Studio project will not perform the file copy. Any ideas? Thanks.
There are a couple of issues.
First, you want to execute cmake -E copy ... inside the custom command. To do this, you can provide the path to the CMake executable via the variable CMAKE_COMMAND.
Next, you don't need generator expressions in this case. You can just copy from "${PROJECT_SOURCE_DIR}/abc.h" to "${PROJECT_SOURCE_DIR}/src"
So, your final command should be more like:
add_custom_command(
TARGET MyTarget
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy "${PROJECT_SOURCE_DIR}/abc.h"
"${PROJECT_SOURCE_DIR}/src")
As an aside, if the copied file is forming part of your build, you might be better to use cmake -E copy_if_different ... rather than just copy since this won't update the copied file's timestamp needlessly. (If the file is seen as "updated" all sources that #include it will be recompiled when a rebuild happens).