Add an extra compile step to a custom compiler/language in CMake - cmake

This is a bit of follow-up to an earlier question I posted. My basic problem was to build a application with Gambit Scheme.
While the solution suggested in the question mentioned above works, it is kinda cumbersome so I decided to try and add Gambit Scheme as custom compiler/language to CMake. Following the suggestions in this question, I created the following files:
cmake/CMakeDetermineGambitCompiler.cmake:
# Find the compiler
find_program(
CMAKE_Gambit_COMPILER
NAMES "gambitc"
HINTS "${CMAKE_SOURCE_DIR}"
DOC "Gambit Scheme compiler"
)
mark_as_advanced( CMAKE_Gambit_COMPILER )
set( CMAKE_Gambit_SOURCE_FILE_EXTENSIONS scm;six )
# Remember this as a potential error
set( CMAKE_Gambit_OUTPUT_EXTENSION .c )
set( CMAKE_Gambit_COMPILER_ENV_VAR "" )
# Configure variables set in this file for fast reload later on
configure_file( ${CMAKE_CURRENT_LIST_DIR}/CMakeGambitCompiler.cmake.in
${CMAKE_PLATFORM_INFO_DIR}/CMakeGambitCompiler.cmake )
cmake/CMakeGambitInformation.cmake:
# This file sets the basic flags for the GAMBIT compiler
# Generate the C files
set( CMAKE_Gambit_COMPILE_OBJECT
"<CMAKE_Gambit_COMPILER> -o <OBJECT> -c <SOURCE>"
)
# Build a executable
set( CMAKE_Gambit_LINK_EXECUTABLE
"<CMAKE_Gambit_COMPILER> -o <TARGET> -exe <OBJECTS>"
)
set( CMAKE_Gambit_INFORMATION_LOADED 1 )
cmake/CMakeGambitCompiler.cmake.in:
set( CMAKE_Gambit_COMPILER "#CMAKE_Gambit_COMPILER#" )
set( CMAKE_Gambit_COMPILER_LOADED 1 )
set( CMAKE_Gambit_SOURCE_FILE_EXTENSIONS #CMAKE_Gambit_SOURCE_FILE_EXTENSIONS# )
set( CMAKE_Gambit_OUTPUT_EXTENSION #CMAKE_Gambit_OUTPUT_EXTENSION# )
set( CMAKE_Gambit_COMPILER_ENV_VAR "#CMAKE_Gambit_COMPILER_ENV_VAR#" )
cmake/CMakeTestGambitCompiler.cmake:
# For now do nothing
set( CMAKE_Gambit_COMPILER_WORKS 1 CACHE INTERNAL "" )
Then, in my project root I have two more files:
CMakeTexts.txt:
cmake_minimum_required( VERSION 3.10...3.18 )
if( ${CMAKE_VERSION} VERSION_LESS 3.12 )
cmake_policy( VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} )
endif()
# Give the project a name
project( cmake-scheme-template NONE )
# Build simple Gambit Scheme program
list( APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
enable_language( Gambit )
add_executable( ${PROJECT_NAME} main.scm )
The actual code to build, main.scm:
;;; Simple Scheme example
(begin (write "Hello, Schemer!")
(newline))
Giving the following structure:
project_root/
cmake/
CMakeDetermineGambitCompiler.cmake
CMakeGambitCompiler.cmake.in
CMakeGambitInformation.cmake
CMakeTestGambitCompiler.cmake
CMakeLists.txt
main.scm
While this works for a single file, as soon as I added another source file, I need Gambit to first create a link file for all C files that are generated from Scheme sources. Here's a simple example:
Say I add a second file, factmodule.scm:
;;; This is a simple Scheme module that provides a functions that will
;;; calculate the factorial of a number n
(define fact
(lambda (n)
(if (zero? n)
1
(* n (fact (- n 1))))))
And update main.scm:
(begin (write "Hello, Schemer!")
(newline)
(write "10! = ")
(write (number->string (fact 10)))
(newline))
To build this "by hand", I do the following:
$ gambitc -c factmodule.scm main.scm # generate C files from Scheme
$ gambitc -o link_file.c -link factmodule.c main.c # generate a link file
$ gambitc -obj factmodule.c main.c link_file.c # compile the C files in object files
$ gcc -o myexec -factmodule.o main.o link_file.o -lgambit # link the final executable
My problem is the second step, creating the link file. Ideally, I'd like to add to cmake/CMakeGambitInformation.cmake something like:
# Generate the C, link, and object files
set( CMAKE_Gambit_COMPILE_OBJECT
"<CMAKE_Gambit_COMPILER> -o <OBJECT> -c <SOURCE>" # generate C files
"<CMAKE_Gambit_COMPILER> -o link_file.c -link <OBJECTS>" # generate link file
"<CMAKE_Gambit_COMPILER> -obj <OBJECTS>" # compile C and link files
)
But there are two problems to this. One, <OBJECTS> holds the generated C files; it is my understand that the commands given in CMAKE_Gambit_COMPILE_OBJECT are executed on a per source file base. Two, I obviously want to run the last two commands only once, but before the command given to CMAKE_Gambit_LINK_EXECUTABLE are invoked.
Is there a way to execute custom commands after the objects are created but before they're linked?
I've looked into some other compilers in CMake/Modules but couldn't really find a language that does that. And this entry seems to be the most complete documentation for adding a new language, the official doc seems not to really mention it.

The following hacks should achieve what you are after. The solution releaves that gsc and cmake don't always play nice with each other, as they both have their own way of handling file extensions implicitly. Anyway, let's get started.
The series of commands I intend to reproduce from within cmake (with slightly different filenames) is
gsc -c linkstub.scm
gsc -c factmodule.scm
gsc -c main.scm
gsc -obj linkstub.scm
gsc -obj factmodule.scm
gsc -obj main.scm
gsc -o linkstub.c -link factmodule.c main.c
gsc -obj linkstub.c
gsc -o test -exe factmodule.o main.o linkstub.o
Here, linkstub.scm is an empty (generated) dummy file. We need this for the final linking, because we can't modify the <OBJECTS> list that is passed to CMAKE_Gambit_LINK_EXECUTABLE. The corresponding linkstub.c file will be the actual link file generated in by gsc -o linkstub.c -link .... And that is achieved with add_custom_command and its PRE_LINK option (execute a command every time a target is supposed to be linked). The same custom command also compiles the linkstub.c into an object file; the previous linkstub.o is overwritten by the newly created one, before the final linking starts. This way we can trick CMake to swallow a linkstub.o that is updated whenever all other objects files have been compiled.
Let get's started. We need to change the compiler command and the object extension in their respective files:
set(CMAKE_Gambit_COMPILE_OBJECT
"<CMAKE_Gambit_COMPILER> -o <SOURCE>.c -c <SOURCE>"
"<CMAKE_Gambit_COMPILER> -o <OBJECT> -obj <SOURCE>.c")
set(CMAKE_Gambit_OUTPUT_EXTENSION .o)
This is somewhat dirty, as it will leave build artifacts in your source tree (maybe you could just prepend a path to the build tree - here, we'll simply delete them automatically later). Note that compiling .scm into .o on a per-source basis also scales better, as the linking step in your original attempt can get quite large (all .c files are compiled down into an executable). Next, the placeholder for the link file:
set(linkstub ${CMAKE_CURRENT_BINARY_DIR}/linkstub.scm)
file(WRITE ${linkstub} "")
Again, it this is only a mean to get the corresponding object file into the object dependencies of the target. Now all the rest:
set(sources
factmodule.scm
main.scm)
list(TRANSFORM sources
APPEND ".c"
OUTPUT_VARIABLE cSources)
list(TRANSFORM cSources
PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/"
OUTPUT_VARIABLE cSources)
list(REMOVE_ITEM cSources
linkstub.scm.c)
add_executable(test ${sources} ${linkstub})
add_custom_command(TARGET test
PRE_LINK
COMMAND ${CMAKE_Gambit_COMPILER}
-o
${CMAKE_CURRENT_BINARY_DIR}/linkstub.scm.c
-link
${cSources}
COMMAND ${CMAKE_Gambit_COMPILER}
-o
${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/test.dir/linkstub.scm.o
-obj
${CMAKE_CURRENT_BINARY_DIR}/linkstub.scm.c
COMMAND ${CMAKE_COMMAND} -E remove ${cSources})
The last command does all the magic. It re-writes linkstub.scm.c, compiles it into an object file, and finally removes all generated .scm.c files in the source tree. Note that there is an ugly, hard-coded CMakeFiles/test.dir/ in one of the paths, there should be a way to query this path from the target to get around this.
Note again that the file extensions are somewhat crucial here, and the solution is brittle. CMake seems to absolutely need object files with .o appended, i.e. .scm.o. gsc however, will - if not told otherwise - generate a .c instead of the .scm.c, which causes the -link step to produce symbols that don't match the ones compiled into .scm.o.
As a side note, this approach obviously doesn't handle any dynamic/implicit dependencies between scheme sources - if you change factmodule.scm in a way that main.scm needs to be re-trans- and re-compiled, this won't be resolved for you. As far as I know, there is currently no way to teach CMake that you would like to register a custom dependency scanner that parses .scm files.
It seems that a plain old makefile might do a better job.

Related

Ninja Make system not accepting GENERATED command

I have a set of assembly files which should be compiled by a special compiler. After this it should be added to the library created by the compiler i have set in CMAKE_C_COMPILER. it was working fine with Mingw Makefile system but not working with Ninja Make.
Below is the code in cmakelists.txt
add_custom_target(
special_asm
COMMAND
${SPECIAL_ASM} ${src_file1}
-I${INCLUDE_PATH} -o file1.o
COMMAND
${SPECIAL_ASM} ${src_file2}
-I${INCLUDE_PATH} -o file2.o
)
add_custom_target(special_asm_cmd COMMAND cmd.exe special_asm*.bat)
add_dependencies(special_asm_cmd special_asm)
add_library(
mylib STATIC
file1.o
file2.o
${mylib_src})
add_dependencies(mylib special_asm_cmd)
set_source_files_properties(
file1.o
file2.o
PROPERTIES EXTERNAL_OBJECT true GENERATED true)
file1.o and file2.o are generated by different assembler. i have set the properties for these files also.
Problem 1:
custom target special_asm is not directly generating the object files. It is generating a batch script. That's why i have created one more custom target called special_asm_cmd to run the batch script which will generate the object files. The Mingw make system was directly generating the object file from special_asm but Ninja is not doing like that.
Problem 2
I have set the property GENERATED true for the special generated object files. But ninja is giving the below error. But Mingw Make was able to solve the dependency and no errors
ninja: error: '<path>/spt_init.o', needed by '<path>/libmylib.a', missing and no known rule to make it
There's no point in using GENERATED here - cmake will know it, when you tell him, how you generate the files anyway.
add_custom_command(
# Put generated files in binary dir, where they belong
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file1.o
# Enumerate all dependencies.
DEPENDS
${src_file1}
${INCLUDE_PATH}/header1.h ${INCLUDE_PATH}/header2.h etc..
COMMAND
${SPECIAL_ASM} ${src_file1} -I${INCLUDE_PATH}
-o ${CMAKE_CURRENT_BINARY_DIR}/file1.o
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file2.o
DEPENDS
${src_file2}
${INCLUDE_PATH}/header1.h ${INCLUDE_PATH}/header2.h etc..
COMMAND
${SPECIAL_ASM} ${src_file2}
-I${INCLUDE_PATH}
-o ${CMAKE_CURRENT_BINARY_DIR}/file2.o
)
add_custom_target(special_asm_cmd
# Enumerate the dependencies.
DEPENDS
${CMAKE_CURRENT_BINARY_DIR}/file2.o
${CMAKE_CURRENT_BINARY_DIR}/file1.o
# Glob in add_custom target? Use glob with `file(GLOB ...`
COMMAND cmd.exe special_asm*.bat
)
add_library(mylib STATIC
${CMAKE_CURRENT_BINARY_DIR}/file2.o
${CMAKE_CURRENT_BINARY_DIR}/file1.o
${mylib_src}
)
Cmake is a build system, specifically designed to know what comes from what. Files comes from other files plus commands. Tell cmake what files come from which commands and which source files - cmake will manage the dependencies and do all the rest like parallelizing the work and proper dependency tracking.

CMAKE custom compiler not overwriting

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!

add_custom_command is not generating a target

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

CMake Compiling Generated Files

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.

cmake add_custom_command

I'm struggling with add_custom_command. Let me explain the problem in detail.
I've these set of cxx files and hxx files. I run a perl script on each of them to generate a certain kind of translation file. The command looks like
perl trans.pl source.cxx -o source_cxx_tro
and similarly for header.hxx files as well.
So I'll end up with some multiple commands (each for a file)
Then I run another perl scripn on the output generated from these commands (source_cxx_tro, header_hxx_tro)
perl combine.pl source_cxx_tro header_hxx_tro -o dir.trx
dir.trx is the output file.
I've something like this.
Loop_Over_All_Files()
Add_Custom_Command (OUTPUT ${trofile} COMMAND perl trans.pl ${file} -o ${file_tro})
List (APPEND trofiles ${file_tro})
End_Loop()
Add_Custom_Command (TARGET LibraryTarget POST_BUILD COMMAND perl combine.pl ${trofiles} -o LibraryTarget.trx)
What I expect is when building the post build target, the trofiles will be built first. but it is not the case. The ${trofiles} are not getting built and hence the post build command ends in a failure.
Is there any way I can tell the POST_BUILD command depend on the previous custom command ?
Any suggestions ?
Thanks in advance,
Surya
Use add_custom_command's to create a file transformation chain
*.(cxx|hxx) -> *_(cxx|hxx)_tro
*_(cxx|hxx)_tro -> Foo.trx
and make the last transformation an first class entity in cmake by using add_custom_target. By default this target won't be build, unless you mark it with ALL or let another target that is built depend on it.
set(SOURCES foo.cxx foo.hxx)
add_library(Foo ${SOURCES})
set(trofiles)
foreach(_file ${SOURCES})
string(REPLACE "." "_" file_tro ${_file})
set(file_tro "${file_tro}_tro")
add_custom_command(
OUTPUT ${file_tro}
COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/trans.pl ${CMAKE_CURRENT_SOURCE_DIR}/${_file} -o ${file_tro}
DEPENDS ${_file}
)
list(APPEND trofiles ${file_tro})
endforeach()
add_custom_command(
OUTPUT Foo.trx
COMMAND perl ${CMAKE_CURRENT_SOURCE_DIR}/combine.pl ${trofiles} -o Foo.trx
DEPENDS ${trofiles}
)
add_custom_target(do_trofiles DEPENDS Foo.trx)
add_dependencies(Foo do_trofiles)
You want to create a custom target that consumes the output of the custom commands. Then use ADD_DEPENDENCIES to make sure the commands are run in the right order.
This might be sort of close to what you want:
https://gitlab.kitware.com/cmake/community/-/wikis/FAQ#how-do-i-use-cmake-to-build-latex-documents
Basically one add_custom_command for each file generated, collect a list of those files (trofiles), then use add_custom_target with a DEPENDS on the list trofiles. Then use add_dependencies to make the LibraryTarget depend on the custom target. Then the custom target should be built before the library target is built.