Empty RUNTIME_OUTPUT_DIRECTORY - cmake

Working on a project using CMake. This project contains an executable and a python script. These two files should be compiled/copied in the same directory at build.
I try something like :
add_executable( ${myTarget} ${all_c_sources} )
add_custom_command(
TARGET ${myTarget} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy ${pythonScriptPath} ${RUNTIME_OUTPUT_DIRECTORY}/
)
The executable is well build in a default location like ${CMAKE_BINARY_DIR}/Debug but the copy of the python script failed :
c:\Program Files (x86)\CMake\bin\cmake.exe" -E copy C:/.../script.py /\r
My goal is to use CMake default behavior as mush as possible.
Is there a simple way to get the default output path ?
Thanks !

Something like this:
add_custom_command(TARGET ${LIBRARY_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<TARGET_FILE:${LIBRARY_NAME}>
$<TARGET_FILE_DIR:${CMAKE_PROJECT_NAME}>/path/to/where/module/should/be
COMMENT "Copying file to Runtime directory: " $<TARGET_FILE:${LIBRARY_NAME}>
)
Adjust it to your needs.
Read about CMake Generator Expressions if you need to know more.

Related

How to copy a file if modified and avoid rebuilding executables and libraries when running make all?

I want to copy a file from the source directory to the binary directory when running make or make all if the file has been modified. So if only this file has been modified, then no libraries or executables should be rebuilt when running make. Only the file should be copied. I tried several approaches, for example:
cmake_minimum_required(VERSION 3.18)
project(copy-file VERSION 1.0 DESCRIPTION "testing copy file if modified on make all")
set(FILE_PATH "some_dir/file.txt")
add_executable(hello hello.c)
#add_custom_command(
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
# COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
# MAIN_DEPENDENCY ${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH})
add_custom_command(
TARGET hello
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH})
#configure_file(${FILE_PATH} ${FILE_PATH} COPYONLY)
Unfortunately, this only copies file.txt (when running make) if hello.c has been modified. If hello.c has not been modified, but file.txt has been modified, nothing happens when I run make (whereas I expected the file to be copied from the source directory to the binary directory)
Here is a link to the source files I used for this minimal example.
Any ideas what I am missing?
Do not use POST_BUILD on a custom target. Specify input and output and let CMake take care of the dependency.
add_custom_command(
OUTPUT
${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH}
COMMAND
${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH}
${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH}
)
add_custom_target(copy DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH})
Following the suggestion of #vre and adding copy_if_different instead of copy and then adding add_custom_target() that can be used with the custom command like this seems to work now:
cmake_minimum_required(VERSION 3.18)
project(copy-file VERSION 1.0 DESCRIPTION "testing copy file if modified on make all")
set(FILE_PATH "some_dir/file.txt")
add_executable(hello hello.c)
add_custom_target(copy ALL )
add_custom_command(
TARGET copy
PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${CMAKE_CURRENT_SOURCE_DIR}/${FILE_PATH} ${CMAKE_CURRENT_BINARY_DIR}/${FILE_PATH})

Split CMake generator expression into function arguments

I have the following CMake segment to copy some DLLs I require into the output folder of my executable:
file(GLOB Debug_DLLS "${SDK_DIR}/Libs/*.dll")
file(GLOB Release_DLLS "${SDK_DIR}/Libsr/*.dll")
add_custom_command(TARGET myApp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
$<$<CONFIG:Debug>:${Debug_DLLS}>
$<$<NOT:$<CONFIG:Debug>>:${Release_DLLS}>
$<TARGET_FILE_DIR:myApp>
)
copy_if_different is supposed to support multiple arguments.
I am 100% sure that SDK_DIR is a valid folder and also that the Release_DLLS and Debug_DLLS variable is valid. The code works if I just put in a simple filepath into Release_DLLs.
But when building I simply get the error: "The system cannot find the provided path" in my native system language. Why isn't this working with multiple files?
I needed to quote the generator expressions and add COMMAND_EXPAND_LISTS to the command.
The following code works and is probably the most flexible solution to copy different DLLs to the output directory based on the build type:
file(GLOB Debug_DLLS "${SDK_DIR}/Libs/*.dll")
file(GLOB Release_DLLS "${SDK_DIR}/Libsr/*.dll")
add_custom_command(TARGET myApp POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"$<$<CONFIG:Debug>:${Debug_DLLS}>"
"$<$<NOT:$<CONFIG:Debug>>:${Release_DLLS}>"
$<TARGET_FILE_DIR:myApp>
COMMAND_EXPAND_LISTS
)

Have CMake not complain about non-existant file?

I know there probably isn't any workaround to this, but I need to generate a source file dllmain.c for my model.dll. This is done together with an executable that extracts some essential information for me, so my current CMakeLists looks like this
add_executable(main ${SOURCE} otherListSourcesGoHere)
add_custom_command(
TARGET main
POST_BUILD
COMMAND main.exe <--- Main built from above
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/jtox.py
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/dllgen.py <--- Python script generating a
sourcefile for my DLL
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_DIR}/build/dllmain.c ${PROJECT_DIR}
)
add_library(
${CMAKE_PROJECT_NAME}
SHARED
${PROJECT_DIR}/dllmain.c
otherListSoucrsGoHereAswell
)
But of course, as the dllmain.c file is non-existant before the exe has been completed and ran, I can't use this as it will return an error. Is is possible to execute this somehow without having to run two CMakeLists?
EDIT 1
With the help of #Angew I found out that you could specify the output of a file in a add_custom_command, so currently I have this instead
add_executable(main ${SOURCE} otherListSourcesGoHere)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/dllmain.c
COMMAND main
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/jtox.py
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/dllgen.py -o ${CMAKE_CURRENT_BINARY_DIR}/dllmain.c
)
add_library(
${CMAKE_PROJECT_NAME}
SHARED
${CMAKE_CURRENT_BINARY_DIR}/dllmain.c
otherListSoucrsGoHereAswell
)
But I have the error
CMakeFiles\bil.dir\build.make:60: recipe for target '../dllmain.c' failed
CMakeFiles\Makefile2:66: recipe for target 'CMakeFiles/bil.dir/all' failed
makefile:82: recipe for target 'all' failed
I don't seem to find a solution. How can I go about debugging this issue?
EDIT 2
I fixed this issue by adding DEPENDS main on add_custom_command
add_custom_command(
OUTPUT ${PROJECT_DIR}/dllmain.c
DEPENDS main
COMMAND echo "Executing main.exe"
COMMAND main.exe
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND echo "JSON to XML with Python"
COMMAND python ${PYTHON_SOURCE_DIR}/jtox.py
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/dllgen.py -o ${PROJECT_DIR}
)
The correct way to do this is to tell CMake how to generate the file instead of doing it "manually" and keeping it secret from CMake. To do this, change your CMakeList like this:
add_executable(main ${SOURCE} otherListSourcesGoHere)
add_custom_command(
OUTPUT ${PROJECT_DIR}/dllmain.c
COMMAND main
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/jtox.py
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/dllgen.py
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_DIR}/build/dllmain.c ${PROJECT_DIR}
)
add_library(
${CMAKE_PROJECT_NAME}
SHARED
${PROJECT_DIR}/dllmain.c
otherListSoucrsGoHereAswell
)
This way, CMake will know that the file ${PROJECT_DIR}/dllmain.c is generated, and also how to generate it. It will correctly replace main with the executable built from target main, and introdce a proper build depencency.
Side note: you should consider modifying dllgen.py so that it's able to generate a file in a directory of your choice, and have it generate into the binary directory. That way, you will not pollute your source tree with build artefacts, which is a very desirable property: it allows you to revert to pristine state just by removing the binary dir. With that change, the CMakeList could look like this:
add_executable(main ${SOURCE} otherListSourcesGoHere)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/dllmain.c
COMMAND main
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/jtox.py
COMMAND ${CMAKE_COMMAND} -E sleep 1
COMMAND python ${PYTHON_SOURCE_DIR}/dllgen.py -o ${CMAKE_CURRENT_BINARY_DIR}/dllmain.c
)
add_library(
${CMAKE_PROJECT_NAME}
SHARED
${CMAKE_CURRENT_BINARY_DIR}/dllmain.c
otherListSoucrsGoHereAswell
)

CMake add_custom_command ('POST_BUILD') 'DEPENDS' option is ignored

I have a library and a test projects on CMake, and I'm using this directory structure with two (project) CMakeLists.txt:
/
|- CMakeLists.txt
|- include/libName
|- src/...
|
|- test/
|- CMakeLists.txt
|- src/...
The outer project list defines the library, like:
add_library(libName ${SRC} ${INCLUDE})
And adds 'test' as subdirectory:
add_subdirectory(test)
The test project list defines the executable and a test, like:
add_executable(NameTest ${SRC})
target_link_libraries(NameTest libName)
add_test(NAME NameTest COMMAND NameTest)
The problem
I'm trying to build and execute the test program when the library is built. If any test fails, I want the build of the library fail too.
This is what I have (inside the outer lists file):
add_custom_command(
TARGET libName
POST_BUILD
COMMAND CTEST_OUTPUT_ON_FAILURE=1 ctest
DEPENDS NameTest # <- This is driving me crazy!
)
This command ignores completely if the target 'NameTest' is built, if there is a file with that name, or if not. I can't notice any difference if the whole 'DEPENDS' option is removed.
I even modified like:
add_custom_command(
TARGET libName
POST_BUILD
COMMAND ${CMAKE_COMMAND} -E echo "Bip! Bip! Bip!"
DEPENDS this_is_not_an_existent_file_nor_target
)
And the command is triggered anyway. I'm not very sure about if this is the option I need, so:
Why is this not working?
How can I achieve my real purpose?
Thank you.
Edit: ctest will execute every test (add_test), but the NameTest executable (yet listed) must be built before calling it! Now would be built after the library, but before the 'POST_BUILD' custom command. It fails, of course.
I want CMake realize NameTest is necessary for running that custom command.
Edit: I find useful the Angew's answer, so I accepted his answer and refined it a little bit:
add_custom_command(
TARGET libName
POST_BUILD
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target NameTest --config $<CONFIG>
COMMAND ${CMAKE_CTEST_COMMAND} -C $<CONFIG> --output-on-failure
)
Thank you!
1. Why is this not working?
Because you're mixing options from two distinct signatures of add_custom_command. DEPENDS comes from the form which is used to generate a file. TARGET and POST_BUILD are from the form which adds pre/post build commands to existing targets.
See the documentation of add_custom_command for more details on the two uses.
2. How can I achieve my real purpose?
I believe the following should do what you want to:
add_custom_command(
TARGET libName
POST_BUILD
COMMAND ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target NameTest --config $<CONFIG>
COMMAND CTEST_OUTPUT_ON_FAILURE=1 ${CMAKE_COMMAND} --build ${CMAKE_BINARY_DIR} --target test --config $<CONFIG>
)

CMake command copy for symlink

I have following command:
add_custom_command(
TARGET Packaging POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
source_file
target_file )
Sometimes the source_file is a symlink. However, when it is copied, the referenced file is copied not the symlink.
How can I tell CMake command to copy the symlink as it is without de-referencing it?
It seems that there is no direct solution in CMake till now https://gitlab.kitware.com/cmake/cmake/issues/14609
A work around is to call a shell script that does the trick.
instead built-in "copy" you can use linux "cp" command with corresponded switches:
add_custom_command(
TARGET Packaging POST_BUILD
COMMAND cp -a -u
source_file
target_file )