cmake add_custom_command + Xcode: multi output = multi command invocation - c++

I have custom command that generate several headers at once.
All works fine with make/ninja files generated by cmake.
But if I generate Xcode project via cmake -GXCode,
then instead of once gen.sh was invoked 10 times
and not only that, it also will be invoked every build,
even if timestamps of generated file are younger then gen_in.txt.
How can I fix this?
project(multi_output)
cmake_minimum_required(VERSION 3.17)
set(MANY_HEADERS test0.h test1.h test2.h test3.h test4.h test5.h test6.h test7.h test8.h test9.h)
add_custom_command(
OUTPUT ${MANY_HEADERS}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen.sh
DEPENDS gen_in.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
add_executable(foo main.cpp ${MANY_HEADERS})
where gen.sh is
#!/bin/sh
echo "gen.sh: start"
sleep 1
echo "gen.sh: hard work done"
for i in `seq 0 9`; do
cat gen_in.txt > test$i.h
sed -i bak s/placeholder/$i/g test$i.h
done

In OUTPUT option relative paths are treated as relative to the binary directory.
That is, your add_custom_command actually doesn't produce the files declared as OUTPUT.
This is why it is called more and more: the build tool finds out that an OUTPUT file does not exist, and runs the COMMAND for build that file. (Many build tool don't check whether the OUTPUT file is actually created.)
For files created in the source directory you need to specify their absolute path in OUTPUT option:
set(MANY_HEADERS test0.h test1.h test2.h test3.h test4.h test5.h test6.h test7.h test8.h test9.h)
# This will be a list of _absolute paths_ to the headers
set(MANY_HEADERS_ABS)
foreach(HEADER ${MANY_HEADERS})
list(APPEND MANY_HEADERS_ABS "${CMAKE_CURRENT_SOURCE_DIR}/${HEADER}")
endforeach()
add_custom_command(
OUTPUT ${MANY_HEADERS_ABS}
COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/gen.sh
DEPENDS gen_in.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)
# In add_executable one could use relative paths too
add_executable(foo main.cpp ${MANY_HEADERS})
Note, that add_executable command may accept relative paths to the file both in source and binary trees.
CMake checks whether a file exists or generated (e.g. with add_custom_command) in the source tree, and if it is, the source path is used. Then similar checks are performed for binary tree. (And if this check fails, CMake will emit an error).
Actually, careful inspecting of the make output may give a hint, whether make rebuilds files in the source tree or in the build tree.
This is what is produced by the original code (remember: make is called from the build directory):
[ 33%] Generating test0.h, test1.h, test2.h, test3.h, test4.h, test5.h, test6.h, test7.h, test8.h, test9.h
And this is what is produced when use absolute paths to the source tree. (Out-of-source build, use build/ subdirectory for build.)
[ 33%] Generating ../test0.h, ../test1.h, ../test2.h, ../test3.h, ../test4.h, ../test5.h, ../test6.h, ../test7.h, ../test8.h, ../test9.h

Related

CMake: How to choose which include directories get passed for each source file

I have a project with many source files, and header files. I could add every single directory that contains a header file to CMake's include directories so they are passed via -I option to the compiling of each source file.
include_directories(I/will/need/tons/of/these);
Is there any way through CMake that I can pass only the relevant include directories to the compiling of each source file?
For example, If i was writing this myself on the command line, I would use a script like this:
g++ -I $(./get_include_dirs.sh foo.cpp) -o foo.o foo.cpp
where $(./get_include_dirs.sh foo.cpp) is a script that gets expanded to only the include dirs of foo.cpp
Can this be done on CMake? I don't know how to tell cmake to use that script for each file
Normally, you should set include directories not globally but on target level:
set(my-target-sources
source1.cpp
...
sourceN.cpp
)
add_library(my-target
${my-target-sources}
)
target_include_directories(my-target
[PUBLIC|PRIVATE|INTERFACE]
directory-relevant-to-my-target
)
This way, the compiler will look for header files in directory-relevant-to-my-target when compiling my-target. I think that there is no reason for setting the include directories on source file granularity.
For executing a script from CMake you can use execute_process and collect your list of directories in the OUTPUT_VARIABLE parameter but I would not propose that since you will lose inherent portability of CMake when doing so.
You can use SET call like following;
set(HEADERS h1.h h2.h h3.h)
set(CPP_SOURCE myfunc.cpp)
add_executable(prog ${HEADERS} ${SOURCE_FILES} )
PS: It is better to review the file hierarchy, because in general you should use just include_directories call.

How to copy target files using cmake from major CMakeLists.txt?

As an example suppose four folders(app1, app2, app3 and main) such as below
main
|__ CMakeLists.txt
\__ module1
|______ CMakeLists.txt
|______ sub1.cpp
|______ sub1.h
\__ library5
|______ CMakeLists.txt
|______ sub5.cpp
|______ sub5.h
\__app1
\__app2
\__app3
Which output of module1 is module1.dll and output of library5 is lib5.dll. Folder of app1 must contain module1.dll and lib5.dll, app2 needs lib5.dll and finally app3 needs module1.dll(number of apps, modules and libs are more than this example and as I explain below we don't want to change modules/libraries's CMakeLists.txt, just main's CMakeLists.txt is ours).
PS:
I have a cmake project which has several libraries and modules. They included in my project using add_subdirectory command (note that my project just made up from multiple modules and it has not any add_library or add_target).
I need to copy outputs of libraries/modules without changing their CMakeLists.txt (add_custom_command with POST_BUILD option actually is not a good choice because at this point I need to change CMakeLists.txt of libraries/modules which they are not just belong to my project). On the other hand it must done in outer(major) CMakeLists.txt which has others(libraries/modules).
I tried some other commands such as file (COPY ) and configure_file() but I think they operate in generating cmake-cache phase and just can copy resource files which are exist in pre-build phase.
Moreover, In another approach I write a bash script file to copy the files and call it in major CMakeLists.txt via bellow command.
add_custom_target (copy_all
COMMAND ${CMAKE_SOURCE_DIR}/copy.sh ${files}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
)
The files has the list of files. But the copy not performed! I manually test the script which works as desired. But I don't have any idea why it can not operate at call in CMakeLists.txt.
What can I do to copy sub-projects outputs to some locations from major CMakeLists.txt?
The Setup
To simplify it a little, let's say you have:
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(PostBuildCopyFromRoot)
add_subdirectory(module)
module/CMakeLists.txt
file(WRITE "module.h" "int ModuleFunc();")
file(WRITE "module.cpp" "int ModuleFunc() { return 1; }")
add_library(module SHARED "module.cpp" "module.h")
target_include_directories(module PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}")
set_target_properties(module PROPERTIES WINDOWS_EXPORT_ALL_SYMBOLS 1)
app/app.mexw64
The Problem
If you now just add to following to the root CMakeLists.txt:
add_custom_command(
TARGET module
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"$<TARGET_FILE:module>"
"app/$<TARGET_FILE_NAME:module>"
)
You will get from CMake:
CMake Warning (dev) at CMakeLists.txt:8 (add_custom_command):
Policy CMP0040 is not set: The target in the TARGET signature of
add_custom_command() must exist. Run "cmake --help-policy CMP0040" for
policy details. Use the cmake_policy command to set the policy and
suppress this warning.
TARGET 'module' was not created in this directory.
Solutions
You can always overwrite command behaviors:
function(add_library _target)
_add_library(${_target} ${ARGN})
add_custom_command(
TARGET ${_target}
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
"$<TARGET_FILE:${_target}>"
"${CMAKE_SOURCE_DIR}/app/$<TARGET_FILE_NAME:${_target}>"
)
endfunction()
NOTE: Put the code snippets before the add_subdirectory() call
References
Copying executable and DLLs to User specified location using CMake in windows
Parent CMakeLists.txt overwriting child CMakeLists.txt output directory options
Proper usage of CMAKE_*_OUTPUT_DIRECTORY
Is there a way to include and link external libraries throughout my project only editing my top level CMakeList?

How to specify linux binary's rpath in cmake

I have a CMakeLists.txt file that contains such line of code:
link_directories(dir_a dir_b dir_c)
When I build an executable and perform
readelf -d
on it, I see RPATH with dir_a, dir_b dir_c.
What can I do with cmake if I want to create
Executable with empty RPATH?
Executable with some paths, but not specified in link_directories line?
The executable is not supposed to be "installed".
From this cmake-documentation you can read that:
you can use the variable CMAKE_SKIP_RPATH to have cmake not adding any RPATH to a binary ever and
that with the SET_TARGET_PROPERTIES-functions RPATHs can be controlled individually on a per-target base.

CMake: Embed ELF into executable

I have a project that needs access to an ELF file embedded into the executable in a special section.
I was handcrafting Makefiles before and simply had a shell script where I used objcopy to copy the target I wanted to embed into an .o file, then link to this file in the executable.
# Create a new section and copy the binary there ($1=input $2=output name)
objcopy --input-target binary --output-target elf64-x86-64 \
--binary-architecture i386 $1 $2.o
Now I want to get rid of the custom Makefiles and use CMake to generate them. However, I don't see an easy way to link to such a file. I am able to create and add this file, but not to link against it:
# Invoke script to package module as a library
add_custom_command(OUTPUT ${PACKAGED_FILE}
COMMAND ./package.sh ${MODULE_FILE} ${PACKAGED_FILE}
WORKING_DIRECTORY ${MODULE_DIR}
DEPENDS ${MODULE_FILE}
COMMENT packaging file into ELF object
VERBATIM
)
add_custom_target(${PACKAGED_NAME} ALL DEPENDS ${PACKAGED_FILE})
I have tried to add it with:
target_link_libraries(binary ${PROJECT_BINARY_DIR}/${PACKAGED_FILE})
However, this fails because the file isn't there yet. It will be, but CMake doesn't know that. Adding the target name as a link library doesn't help either because it can't be found. Adding it as a also dependency doesn't help. Does anyone have an idea how this could be accomplished?
We are doing a similar thing in our project - the following part of our CMakeLists.txt does the trick:
set(PROJECT_EMBED_OBJ_FILES "")
set(PROJECT_EMBED_FILES "file1.elf" "file2.elf")
foreach(FILENAME ${PROJECT_EMBED_FILES})
get_filename_component(FILENAME_ONLY ${FILENAME} NAME)
get_filename_component(FILEPATH_ONLY ${FILENAME} PATH)
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o
WORKING_DIRECTORY ${PROJECT_SOURCE_DIR}/${FILEPATH_ONLY}
COMMAND ${CMAKE_OBJCOPY}
ARGS -I binary -O elf64-x86-64 -B i386 ${FILENAME_ONLY} ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o )
list(APPEND PROJECT_EMBED_OBJ_FILES ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME_ONLY}.o)
message(STATUS "Objcopy'ing ${FILENAME}")
endforeach(FILENAME)
And then in the call to add_executable:
add_executable(projectname ${PROJECT_SOURCES} ${PROJECT_EMBED_OBJ_FILES})
You may try
add_custom_command(TARGET $(PROJECT_NAME).elf
POST_BUILD
COMMAND ${CMAKE_OBJCOPY} ARGS -O binary ${PROJECT_NAME}.elf \
${PROJECT_NAME}.bin)
Put this after your add_executable().
The POST_BUILD means execute after build.

cmake run xxd as custom command

I've a rc directory I want to run xxd -i filename recursively in that directory.and add the generated files to my project.
I was checking some answer on lua. and I did it in a similar way.
SET(RESOURCE_COMPILER "xxd")
SET(COMPILED_RESOURCES "rc/base.css rc/common.js")
FOREACH(F ${COMPILED_RESOURCES})
ADD_CUSTOM_COMMAND(
OUTPUT "${COMPILED_RESOURCES}/${F}"
COMMAND ${RESOURCE_COMPILER} -i "${COMPILED_RESOURCES}/${F}"
COMMENT "Compiling ${F} to binary")
LIST (APPEND COMPILED_RESOURCES "${COMPILED_RESOURCES}/${F}")
ENDFOREACH()
but I don't see any file generated at all.
There are a couple of reasons you may not be seeing the output files. Your xxd command isn't well formed in the add_custom_command, but also the command will only be executed if the output file is required as a source in another CMake target (exe or lib) in the same CMakeLists.txt.
I'd change your snippet to something like:
SET(RESOURCE_COMPILER xxd)
FILE(GLOB_RECURSE COMPILED_RESOURCES "rc/*")
FOREACH(INPUT_FILE ${COMPILED_RESOURCES})
SET(OUTPUT_FILE ${INPUT_FILE}.hex)
ADD_CUSTOM_COMMAND(
OUTPUT ${OUTPUT_FILE}
COMMAND ${RESOURCE_COMPILER} -i ${INPUT_FILE} ${OUTPUT_FILE}
COMMENT "Compiling ${INPUT_FILE} to binary")
LIST(APPEND COMPILED_RESOURCES ${OUTPUT_FILE})
ENDFOREACH()
Then if you have another target which depends on the ${OUTPUT_FILE}s the command will be executed when that target is being built:
ADD_EXECUTABLE(MY_EXE main.cc ${COMPILED_RESOURCES})