In a CMAKE project, I need to define a custom command for a file type (.osl) which is compiled by a tool (oslc) which compiles to another file type (.oso)
I managed to do it by a function that I can run on a list of source files:
find_program(OSLC_EXECUTABLE oslc)
function(compile_osl out_var)
set(result)
foreach(osl_f ${ARGN})
file(RELATIVE_PATH osl_f_base ${CMAKE_CURRENT_SOURCE_DIR} ${osl_f})
string(REGEX REPLACE "\\.osl$" ".oso" oso_f ${osl_f_base})
set(oso_f "${CMAKE_CURRENT_BINARY_DIR}/${oso_f}")
get_filename_component(oso_f_dir ${oso_f} DIRECTORY)
file(MAKE_DIRECTORY ${oso_f_dir})
add_custom_command(OUTPUT ${oso_f}
COMMAND ${OSLC_EXECUTABLE} ${osl_f} -o ${oso_f}
DEPENDS ${osl_f}
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
COMMENT "Creating compiled OSL file ${oso_f}"
VERBATIM
)
list(APPEND result ${oso_f})
endforeach()
set(${out_var} "${result}" PARENT_SCOPE)
endfunction()
Thanks to the DEPENDS directive, the compiler is only run if the sources are newer than the output files. However, the compiler does not overwrite output files, so it does not work.
Deleting with file(REMOVE ...) just before the custom command does not work, as it deletes all files, not only the ones requiring recompilation. Also, it deletes at cmake execution, not at make time.
I could maybe define another custom command with "rm" but this not cross platform (I would need to add specific lines for Windows, which I do not like).
Any idea?
Thanks!
Related
I need to generate extra files for an existing CMake target that is already defined with an add_executable(); I don't know how many files there are in advance, and in addition those files are not compiled/part of the executable itself. These files should be build or updated whenever I build that target, but only if their dependent files have been updated.
These extra files are generated and/or updated from an existing file with a Python script. So the natural choices are add_custom_target() and add_custom_command(), but I run into two issues with these:
add_custom_target() works and I can add that as an additional depency of the main target, but the scripts are always executed.
add_custom_command() has proper dependency tracking, but I cannot add the files as dependencies of the main target, CMake simply won't allow it.
So what does not work:
function(register_translation_files)
## determine TARGET and INPUT_FILES ...
foreach (LANG IN LISTS TRANSLATION_LANGUAGES)
message ("Add translation '${LANG}' for target ${TARGET}")
set (XLF_FILE "${TARGET}_${LANG}.xlf")
add_custom_command (
OUTPUT ${XLF_FILE}
COMMAND scripts/cet2xlf.py --language ${LANG} ${XLF_FILE} ${INPUT_FILES}
DEPENDS ${INPUT_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_dependencies (${TARGET} ${XLF_FILE}) <<--- fails with '('the dependency target of TARGET does not exist')
endforeach()
endfunction()
(....)
add_executable (MainTarget foo.cpp bla.cpp)
register_translation_files (TARGET MainTarget INPUT file1 file2)
add_custom_target works but is always executed (as CMake considers it always outdated):
function(register_translation_files)
## determine TARGET and INPUT_FILES ...
foreach (LANG IN LISTS TRANSLATION_LANGUAGES)
message ("Add translation '${LANG}' for target ${TARGET}")
set (XLF_FILE "${TARGET}_${LANG}.xlf")
add_custom_target (
${XLF_FILE}
COMMAND scripts/cet2xlf.py --language ${LANG} ${XLF_FILE} ${INPUT_FILES}
DEPENDS ${INPUT_FILES}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_dependencies (${TARGET} ${XLF_FILE}) <<--- builds, but script is executed every time!
endforeach()
endfunction()
(....)
add_executable (MainTarget foo.cpp bla.cpp)
register_translation_files (TARGET MainTarget INPUT file1 file2)
I tried all kinds if variations, including a custom_target with dependencies on custom_command output, but I either end up with 'this dependency does not exist' or a script that is always executed.
Surely, one can add files with add_depencies()?
You could combine both add_custom_target and add_custom_command to accomplish this:
List the files generated by add_custom_command in via the DEPENDS option of add_custom_target and then use add_dependencies.
I strongly recommend keeping the source directories free of any files generated during build/cmake configuration. Not doing this prevents projects you set up based on the same source directory from working properly.
function(register_translation_files)
## determine TARGET and INPUT_FILES ...
set(OUTPUT_DIR ${CMKAE_CURRENT_BINARY_DIR}/generated_translation_files)
file(MAKE_DIRECTORY ${OUTPUT_DIR})
set(GENERATED_FILES)
foreach (LANG IN LISTS TRANSLATION_LANGUAGES)
message ("Add translation '${LANG}' for target ${TARGET}")
set (XLF_FILE "${OUTPUT_DIR}/${TARGET}_${LANG}.xlf")
add_custom_command (
OUTPUT ${XLF_FILE}
COMMAND "${CMAKE_CURRENT_SOURCE_DIR}/scripts/cet2xlf.py" --language ${LANG} ${XLF_FILE} ${INPUT_FILES}
DEPENDS ${INPUT_FILES}
WORKING_DIRECTORY ${OUTPUT_DIR}
)
list(APPEND GENERATED_FILES ${XLF_FILE})
endforeach()
add_custom_target("${TARGET}_generate_translations" DEPENDS ${GENERATED_FILES})
add_dependencies(${TARGET} "${TARGET}_generate_translations")
endfunction()
Surely, one can add files with add_depencies()?
No, this is not possible. This is hinted at in the documentation of add_dependencies alongside hinting at the solution presented above:
add_dependencies( [<target-dependency>]...)
[...]
See the DEPENDS option of add_custom_target() and add_custom_command() commands for adding file-level dependencies in custom rules.
I have certain files (.hlsl) in one of my targets that are compiled using a custom compiler (glslc). After compilation, I use a different tool to embed the compiled binary in a c-file containing c-array like described here.
This workflow is summarized in the following code which invokes 2 custom commands:
function(build_hlsl_shader shader_file)
get_source_file_property(shader_type ${shader_file} ShaderType)
get_filename_component(shader_name ${shader_file} NAME_WE)
# special command: .hlsl file to a .c / .h file
add_custom_command(
TARGET shaders_custom_target # a custom target
#OUTPUT ${shader_name}.cpp # maybe this?
#DEPENDS ${shader_file} # maybe that?
MAIN_DEPENDENCY ${shader_file} # or maybe thee?
COMMAND # special compiler
${glslc_executable}
-fshader-stage=${shader_type}
${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
-o
${CMAKE_CURRENT_SOURCE_DIR}/${shader_name}.spv
COMMAND # special tool that generates the c header.
bin2h
${CMAKE_CURRENT_SOURCE_DIR}/${shader_name}.spv
)
endfunction()
I then define a custom target that that generates the said files whenever it is built.
add_custom_target(
shaders_custom_target
DEPENDS
"shader.hlsl"
)
build_hlsl_shader("shader.hlsl")
# this library consumes the headers generated by the first target
add_library(library2 STATIC "file1.cpp""file1.hpp")
add_dependencies(library2 shaders_custom_target)
The main issue is that the custom target build process is triggered every time I build my project regardless of the fact the none of the .hlsl files are changed. This is undesirable, I want my custom command to work just like when compiling regular .c or cpp files. They are compiled only when changed.
My requirements are as follows:
Each time one of the .hlsl files are changed, the custom command should trigger.
The custom command should not get triggered whenever I build the target / project.
So how can this achieved?
I tried several methods like using DEPENDS, MAIN_DEPENDENCY or using OUTPUT instead of TARGET. None worked. I also tried the solution offered here and here. They all suffer from the same issue.
As requested in the comments, Here is my attempt to use OUTPUT instead of TARGET:
function(build_hlsl_shader shader_file)
get_source_file_property(shader_type ${shader_file} ShaderType)
get_filename_component(shader_name ${shader_file} NAME_WE)
add_custom_command(
#TARGET shaders_custom_target
OUTPUT
#${shader_name}.hpp # the generated header, tried with or without
${shader_file} # tried with and without
DEPENDS ${shader_file} # tried with and without
#MAIN_DEPENDENCY ${shader_file} # tried with and without
COMMAND
${glslc_executable}
-fshader-stage=${shader_type}
${CMAKE_CURRENT_SOURCE_DIR}/${shader_file}
-o
${CMAKE_CURRENT_SOURCE_DIR}/${shader_name}.spv
COMMAND
bin2h
${CMAKE_CURRENT_SOURCE_DIR}/${shader_name}.spv
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
endfunction()
It depends on whether you consider the shaders to be "part of the library" or just some data you need at runtime.
For my projects that produce HLSL shaders, I use a custom target similar to what you are doing:
add_custom_target(shaders)
set_source_files_properties(VertexShader.hlsl PROPERTIES ShaderType "vs")
set_source_files_properties(PixelShader.hlsl PROPERTIES ShaderType "ps")
foreach(FILE VertexShader.hlsl PixelShader.hlsl)
get_filename_component(FILE_WE ${FILE} NAME_WE)
get_source_file_property(shadertype ${FILE} ShaderType)
add_custom_command(TARGET shaders
COMMAND dxc.exe /nologo /Emain /T${shadertype}_6_0 $<IF:$<CONFIG:DEBUG>,/Od,/O3> /Zi /Fo ${CMAKE_BINARY_DIR}/${FILE_WE}.cso /Fd ${CMAKE_BINARY_DIR}/${FILE_WE}.pdb ${FILE}
MAIN_DEPENDENCY ${FILE}
COMMENT "HLSL ${FILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM)
endforeach(FILE)
add_dependencies(${PROJECT_NAME} shaders)
This essentially sets up a 'post build' event. If any of the shader source files change, then they all get rebuilt.
The other way to set it up is to use an output file, but here you need some extra cmake logic so you can add the output files to the add_library or add_executable:
# SOURCES variable is all the C/C++ source files in the library
# Build HLSL shaders
set_source_files_properties(VertexShader.hlsl PROPERTIES ShaderType "vs")
set_source_files_properties(PixelShader.hlsl PROPERTIES ShaderType "ps")
foreach(FILE VertexShader.hlsl PixelShader.hlsl)
get_filename_component(FILE_WE ${FILE} NAME_WE)
get_source_file_property(shadertype ${FILE} ShaderType)
list(APPEND CSO_FILES ${CMAKE_BINARY_DIR}/${FILE_WE}.cso)
add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/${FILE_WE}.cso
COMMAND dxc.exe /nologo /Emain /T${shadertype}_6_0 $<IF:$<CONFIG:DEBUG>,/Od,/O3> /Zi /Fo ${CMAKE_BINARY_DIR}/${FILE_WE}.cso /Fd ${CMAKE_BINARY_DIR}/${FILE_WE}.pdb ${FILE}
MAIN_DEPENDENCY ${FILE}
COMMENT "HLSL ${FILE}"
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
VERBATIM)
endforeach(FILE)
add_library(library2 ${SOURCES} ${CSO_FILES})
This second version only builds a specific output file if it's source changed, but generally speaking you don't need this level of granularity with shaders.
I want to copy qml files from source directory to build directory. Following script works fine only first time. When I change any of the *.qml files and run make, they are not copied to build folder, they are not updated. What am I doing wrong?
file(GLOB_RECURSE SOURCES *.cpp)
file(GLOB_RECURSE QMLS *.qml)
add_library(MyLib SHARED ${SOURCES} ${QMLS})
foreach(QmlFile ${QMLS})
add_custom_command(TARGET MyLib POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${QmlFile} $<TARGET_FILE_DIR:MyLib>)
endforeach()
While Angew's answer is good, it is possible to eliminate usage of additional target. For doing this, add_library call should use copied .qml files (instead of original ones, like in the script in the question post):
# This part is same as in Angew's answer
set(copiedQmls "")
foreach(QmlFile ${QMLS})
get_filename_component(nam ${QmlFile} NAME)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${nam}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QmlFile} ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${QmlFile}
COMMENT "Copying ${QmlFile}"
VERBATIM
)
list(APPEND copiedQmls ${CMAKE_CURRENT_BINARY_DIR}/${nam})
endforeach()
# But instead of creating new target, we reuse library one.
add_library(MyLib SHARED ${SOURCES} ${copiedQmls})
When library target is built, it triggers non-sources files in add_library call to be updated (if there is corresponded add_custom_command call), but updating non-source files doesn't force library file to be rebuilt. This is why your original code doesn't work as expected.
Note, because .qml files are not recognized by CMake as sources, you doesn't need to set GENERATED property for them, as stated here.
Your are using the TARGET signature of add_custom_command, which means the commands are executed as part of building the TARGET. In your case, POST_BUILD, which means the commands will be run after the build of MyLib finishes. If MyLib is up to date and does not need to be re-built, the commands will not run.
You might want to use the output-generating signature (OUTPUT) instead. Something like this:
set(copiedQmls "")
foreach(QmlFile ${QMLS})
get_filename_component(nam ${QmlFile} NAME)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${nam}
COMMAND ${CMAKE_COMMAND} -E copy_if_different ${QmlFile} ${CMAKE_CURRENT_BINARY_DIR}
DEPENDS ${QmlFile}
COMMENT "Copying ${QmlFile}"
VERBATIM
)
list(APPEND copiedQmls ${CMAKE_CURRENT_BINARY_DIR}/${nam})
endforeach()
add_custom_target(
CopyQMLs ALL
DEPENDS ${copiedQmls}
)
Note that unfortunately, the OUTPUT argument of add_custom_command does not support generator expressions, so $<TARGET_FILE_DIR> cannot be used there. I used ${CMAKE_CURRENT_BINARY_DIR} in the example above, you might need to customise this to suit your needs. ${CMAKE_CFG_INTDIR} can be used to specify the per-configuration directory for multiconfiguration generators.
Note the presence of the additional custom target in my code above. That's necessary, because CMake will only execute an output-producing custom command if something depends on that output. The custom command does that, by listing the outputs in its DEPENDS section.
Perhaps this is impossible and I'm misreading the cmake 3.2 documentation, but I though creating a custom command would create a custom "target" in the Makefile so that I could build the target by invoking the name of the output file. The CMake docs says:
In makefile terms this creates a new target in the following form:
OUTPUT: MAIN_DEPENDENCY DEPENDS
COMMAND
so I thought I could then run make OUTPUT. Perhaps the documentation is confusing CMake targets with Makefile targets?
For example,
add_custom_command(OUTPUT foo_out
COMMAND post_process foo_in > foo_out
DEPENDS foo_in
)
I would like to do
make foo_out
and it will make foo_out. However, if I do this, I get
make: **** No rule to make target `foo_out`. Stop.
and sure enough, the word "foo_out" doesn't exist anywhere in any file in the cmake binary output directory. If I change it to this
add_custom_target(bar DEPENDS foo_out)
add_custom_command(OUTPUT foo_out COMMAND post_process foo_in > foo_out)
Then I can do
make bar
and I can do
make foo_in
but I still can't do
make foo_out
The problem with make bar is that it is unintuitive, as the actual file output is foo_out not bar.
How do I do this?
In my case, I need to run a special processing step to the standard executable target which inserts optional resources into the ELF file. I would like the ability to have both executables as Makefile targets, so I can build the naked ELF executable as well as the resource-injected ELF executable.
If I was writing a custom Makefile, this is trivial to do!
foo_in: foo.c
$(CC) $< -o $#
foo_out: foo_in
post_process $< > $#
And I can do make foo_in and make foo_out.
add_custom_command does not create a new target. You have to define targets explicitly by add_executable, add_library or add_custom_target in order to make them visible to make.
If you have to fix things up for deployment, you could
1. use the install command (somewhere in your CMakeLists.txt) like this:
install(SCRIPT <dir>/post_install.cmake)
to store commands which are executed only when you run make install in a separate .cmake file. Or if the install target is already reserved for other things or you have more complex stuff going on:
2. manually define a deploy target. Once you got that, you can create a custom post-build command which is only executed when you explicitly run make on your deploy target. This allows you to execute commands through a separate target.
In your CMakeLists.txt this could look like:
cmake_minimum_required(VERSION 3.0)
add_executable("App" <sources>)
# option 1: do deployment stuff only when installing
install(SCRIPT <dir>/post_install.cmake)
# option 2: define a deploy target and add post-build commands
add_custom_target("deploy")
add_custom_command(TARGET "deploy" POST_BUILD <some command>)
Both approaches allow you to separate dev builds from expensive ready-to-deploy builds (if I understand correctly, that's the goal here). I would recommend option 1 since it's just cleaner.
Hope this helps!
Documentation Unclear
CMake's documentation is unclear here. The Makefiles generators of CMake do create the source file make rules in sub Makefiles which are not visible in the main Makefile. In the main Makefile you will find only the PHONY rules for your CMake targets. The only exception I know of is the Ninja
Makefiles generator which puts all build rules into single file.
Translating Post-Processing Steps into CMake
From my experience - if post_process is a script - you should probably think about rewriting your post-processing steps with/inside the CMake scripts, because CMake should know about all the file dependencies and variables used for post-processing (it then will e.g. handle all the necessary re-build or clean-up steps for you).
Here is a simplified/modified version of what I do:
function(my_add_elf _target)
set(_source_list ${ARGN})
add_executable(${_target}_in ${_source_list})
set_target_properties(
${_target}_in
PROPERTIES
POSITION_INDEPENDENT_CODE 0
SUFFIX .elf
)
add_custom_command(
OUTPUT ${_target}_step1.elf
COMMAND some_conversion_cmd $<TARGET_FILE:${_target}_in> > ${_target}_step1.elf
DEPENDS ${_target}_in
)
add_custom_target(
${_target}_step1
DEPENDS
${_target}_step1.elf
)
add_custom_command(
OUTPUT ${_target}_out.elf
COMMAND final_post_process_cmd ${_target}_step1.elf > ${_target}_out.elf
DEPENDS ${_target}_step1
)
add_custom_target(
${_target}_out
DEPENDS
${_target}_out.elf
)
# alias / PHONY target
add_custom_target(${_target} DEPENDS ${_target}_out)
endfunction(my_add_elf)
and then call
my_add_elf(foo foo.c)
It's only an example, but I hope it gives the idea: you could call make foo for the final ELF output, make foo_in or make foo_step1 for one of the other steps. And I think all steps are transparent for the user and CMake.
Can't give your Target the same name as one of the Outputs
When you're trying to give a custom target the same name as one of its outputs e.g. like this:
add_executable(foo_in foo.c)
add_custom_command(
OUTPUT foo_out
COMMAND post_process foo_in > foo_out
DEPENDS foo_in
)
add_custom_target(foo_out DEPENDS foo_out)
You end-up with invalid make files. I've raised an issue about this in the hope that there could be a possible solution by extending CMake itself and got the following reply:
CMake is not intended to produce specific content in a Makefile.
Top-level target names created by add_custom_target are always logical
(i.e. phony) names. It is simply not allowed to have a file of the
same name.
Posible Workarounds
So there are some workarounds, but they all have one or the other disadvantage.
1. Shortest Version:
macro(my_add_elf_we _target)
add_executable(${_target}_in ${ARGN})
add_custom_target(
${_target}_out
COMMAND post_process $<TARGET_FILE:${_target}_in> > ${_target}_out
DEPENDS ${_target}_in
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${_target}_out)
endmacro(my_add_elf_we)
You can't declare OUTPUTs in the add_custom_target() itself, but in this case you don't want to (because you don't want to have any naming confusions). But if you don't declare any outputs:
The target will always considered out-of-date
You need to add the "invisible" outputs the clean build rule
2. Force Output Name Version
Here is a version of the above macro that forces target and output names to given values:
macro(my_add_elf_in_out _target_in _target_out)
add_executable(${_target_in} ${ARGN})
set_target_properties(
${_target_in}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_in}"
)
add_custom_target(
${_target_out}
COMMAND post_process ${_target_in} > ${_target_out}
DEPENDS ${_target_in}
)
set_property(DIRECTORY APPEND PROPERTY ADDITIONAL_MAKE_CLEAN_FILES ${_target_out})
endmacro(my_add_elf_in_out)
You call it with:
my_add_elf_in_out(foo_in.elf foo_out.elf foo.c)
3. Object Libary Version
The following version uses object libraries, but the system will not reuse the foo_in target linkage:
macro(my_add_elf_obj_in_out _target_in _target_out)
add_library(${_target_in}_obj OBJECT ${ARGN})
add_executable(${_target_in} $<TARGET_OBJECTS:${_target_in}_obj>)
set_target_properties(
${_target_in}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_in}"
)
add_executable(${_target_out} $<TARGET_OBJECTS:${_target_in}_obj>)
set_target_properties(
${_target_out}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_out}"
EXCLUDE_FROM_ALL 1
)
add_custom_command(
TARGET ${_target_out}
POST_BUILD
COMMAND post_process ${_target_in} > ${_target_out}
)
endmacro(my_add_elf_obj_in_out)
4. Last and Final Version
And one final version that definitely works only for with Makefile generators and that got me posting the issue at CMake's bug tracker:
macro(my_add_elf_ext_in_out _target_in _target_out)
add_executable(${_target_in} ${ARGN})
set_target_properties(
${_target_in}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_in}"
)
add_executable(${_target_out} NotExisting.c)
set_source_files_properties(
NotExisting.c
PROPERTIES
GENERATED 1
HEADER_FILE_ONLY 1
)
set_target_properties(
${_target_out}
PROPERTIES
SUFFIX ""
OUTPUT_NAME "${_target_out}"
RULE_LAUNCH_LINK "# "
)
add_custom_command(
TARGET ${_target_out}
POST_BUILD
COMMAND post_process ${_target_in} > ${_target_out}
)
add_dependencies(${_target_out} ${_target_in})
endmacro(my_add_elf_ext_in_out)
Some references
CMake add_custom_command/_target in different directories for cross-compilation
CMake: how do i depend on output from a custom target?
cmake add_custom_command
How do I use CMake to build LaTeX documents?
Turning around the dependencies, and using the second signature of add_custom_command, this should work:
add_custom_target(foo_out DEPENDS foo_in)
add_custom_command(TARGET foo_out POST_BUILD COMMAND post_process foo_in > foo_out)
Note: Adding BYPRODUCTS foo_out will cause (for example) ninja to say
multiple rules generate foo_out. builds involving this target will not be correct; continuing anyway
I have a list of files that get generated during the CMake build process. I want to compile these files using "add_library" afterward, but I won't know which files get generated until after they get generated. Is there anyway to build this into a CMake script?
Well, I think it is possible, so I'll share what I've done. My problem was that I had to compile several CORBA idls to use as part of a project's source and I didn't want to manually list every file. I thought it would be better to find the files. So I did it like this:
file(GLOB IDLS "idls/*.idl")
set(ACE_ROOT ${CMAKE_FIND_ROOT_PATH}/ace/ACE-${ACE_VERSION})
foreach(GENERATE_IDL ${IDLS})
get_filename_component(IDLNAME ${GENERATE_IDL} NAME_WE)
set(OUT_NAME ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/${IDLNAME})
list(APPEND IDL_COMPILED_FILES ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp)
add_custom_command(OUTPUT ${OUT_NAME}C.h ${OUT_NAME}C.cpp ${OUT_NAME}S.h ${OUT_NAME}S.cpp
COMMAND ${ACE_ROOT}/bin/tao_idl -g ${ACE_ROOT}/bin/ace_gperf -Sci -Ssi -Wb,export_macro=TAO_Export -Wb,export_include=${ACE_ROOT}/include/tao/TAO_Export.h -Wb,pre_include=${ACE_ROOT}/include/ace/pre.h -Wb,post_include=${ACE_ROOT}/include/ace/post.h -I${ACE_ROOT}/include/tao -I${CMAKE_CURRENT_SOURCE_DIR} ${GENERATE_IDL} -o ${CMAKE_CURRENT_SOURCE_DIR}/idls_out/
COMMENT "Compiling ${GENERATE_IDL}")
endforeach(GENERATE_IDL)
set_source_files_properties(${IDL_COMPILED_FILES}
PROPERTIES GENERATED TRUE)
set(TARGET_NAME ${PROJECT_NAME}${DEBUG_SUFFIX})
add_executable(
${TARGET_NAME}
${SOURCE}
${IDL_COMPILED_FILES}
)
The GENERATED properties is useful in case one of my idl compilation outputs (*C.cpp, *C.h, *S.cpp and *S.h) is not created, so that the build command doesn't complain that the file doesn't exist.
Well, it is possible to do so with CMake's CMAKE_CONFIGURE_DEPENDS directory property. This forces CMake to reconfigure if any of the given files changed.
Simple solution
The following code shows the approach for a single model file, that is used as input for the code generation:
set(MODEL_FILE your_model_file)
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
set(GENERATED_SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})
file(REMOVE_RECURSE ${GENERATED_SOURCE_DIR})
file(MAKE_DIRECTORY ${GENERATED_SOURCE_DIR})
execute_process(COMMAND your_code_generation_tool -o ${GENERATED_SOURCE_DIR} ${MODEL_FILE})
file(GLOB LIBGENERATED_FILES ${GENERATED_SOURCE_DIR}/*)
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated ${GENERATED_SOURCE_DIR})
With the above approach, each time the model file has changed CMake will reconfigure which results in the model being regenerated.
Advanced solution
The problem with the simple solution is that even for the smallest possible change in the model the entire dependencies of the generated files have to be rebuilt.
The advanced approach uses CMake's copy_if_different feature to let only generated files that are affected by the model change to appear modified which results in better build times. To achieve that we use a staging directory as destination for the generator and sync the contents subsequently with the generator output of the previous compile run:
set(MODEL_FILE your_model_file)
set(GENERATOR_STAGING_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE}.staging)
set(GENERATOR_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${MODEL_FILE})
set_directory_properties(PROPERTIES CMAKE_CONFIGURE_DEPENDS ${MODEL_FILE})
# Create fresh staging/final output directory
file(REMOVE_RECURSE ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_STAGING_DIR})
file(MAKE_DIRECTORY ${GENERATOR_OUTPUT_DIR})
# Run code generation
execute_process(COMMAND your_code_generation_tool -o ${GENERATOR_STAGING_DIR} "${CMAKE_CURRENT_SOURCE_DIR}/${MODEL_FILE}")
# Remove stale files from final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_OUTPUT_DIR}/" "${GENERATOR_OUTPUT_DIR}/*")
foreach(FILE ${GENERATED_FILES})
if(NOT EXISTS "${GENERATOR_STAGING_DIR}/${FILE}")
file(REMOVE "${GENERATOR_OUTPUT_DIR}/${FILE}")
endif()
endforeach()
# Copy modified files from staging to final generator output directory
file(GLOB GENERATED_FILES RELATIVE "${GENERATOR_STAGING_DIR}/" "${GENERATOR_STAGING_DIR}/*")
foreach(FILE ${GENERATED_FILES})
execute_process(COMMAND ${CMAKE_COMMAND} -E copy_if_different "${GENERATOR_STAGING_DIR}/${FILE}" "${GENERATOR_OUTPUT_DIR}")
endforeach()
file(GLOB LIBGENERATED_FILES "${GENERATOR_OUTPUT_DIR}/*")
add_library(libgenerated ${LIBGENERATED_FILES})
target_include_directories(libgenerated PUBLIC ${GENERATOR_OUTPUT_DIR})
If you don't know the name of the files that will be generated, you can "glob" the folders where they reside.
file( GLOB_RECURSE MY_SRC dest_folder/*.cpp )
add_library( libname SHARED ${MY_SRC} )
Now I'm not sure what triggers the generation of these files. The "globbing" will happen only when you manually run cmake: it will not be able to detect automatically that new files are present.
Treat this as a non-answer, just more info:
I recently had to do something for one case where I had a .cpp file that was auto-generated, but I could not figure out how to get CMake to construct the Visual Studio project file that would then compile it. I had to resort to something quite stinky: I had to #include <the_generated.cpp> file from another file that resided under the ${CMAKE_CURRENT_SOURCE} directory. That won't help you much in your case because I suspect you have several .cpp files, so this approach is not scalable.
Also, I found that the GENERATED source file property, when added to the file, did not help at all.
I consider this condition either a bug in Visual Studio (in my case this was VS2008 SP1), or in how CMake generates the .vcproj files, or both.