CMake command copy for symlink - cmake

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 )

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})

Using generator expression in `cmake -E copy` command

I am trying to copy dll files from my bin folder to a different folder. I want to copy files from bin/Debug when building in Debug and from bin/Release when building in Release. This is what I currently use (and which does not work).
file(GLOB library_files_debug ${outputdirectory_root}/Debug/*.dll)
file(GLOB library_files_release ${outputdirectory_root}/Release/*.dll)
add_custom_target(copy_dlls_to_wheel ALL
DEPENDS setup.py
COMMAND ${CMAKE_COMMAND} -E echo "Debug files: $<$<CONFIG:Debug>:${library_files_debug}>"
COMMAND ${CMAKE_COMMAND} -E echo "Release files: $<$<CONFIG:Release>:${library_files_release}>"
COMMAND ${CMAKE_COMMAND} -E echo "Destination dir: ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}"
COMMAND ${CMAKE_COMMAND} -E copy $<$<CONFIG:Debug>:${library_files_debug}> $<$<CONFIG:Release>:${library_files_release}> ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}
)
I am running on Windows 10, and use Visual Studio to build. When the above target copy_dlls_to_wheel is built in Debug, the first echo statement prints out the correct dll files, and the second echo is empty. However, no files are copied. Instead I get the error message The system cannot find the path specified.
I have also tried to replace the last line with
COMMAND ${CMAKE_COMMAND} -E copy $<$<CONFIG:Debug>:${library_files_debug}> ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}
, but I get the same result.
However, when I remove the generator expression, and use
COMMAND ${CMAKE_COMMAND} -E copy ${library_files_debug} ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}
the files are copied correctly to my output folder. I am pretty confident my generator expression is correct, since I get the expected output from the echo commands. Are generator expressions not supported when using cmake -E copy, or is there something else I am doing wrong?
CMake's command line copy is able to process multiple files when you simply provide a list, which is why this works:
COMMAND ${CMAKE_COMMAND} -E copy ${library_files_debug} ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}
This is expanded to a space-separated list of files when the copy command is ultimately executed, which is the expected syntax.
cmake.exe -E copy mylib1.dll mylib2.dll /your/binary/dir/python/proj
However, when wrapped in a generator expression, the list will not be interpreted correctly by CMake. While the generator expression will be evaluated correctly, the list will be kept as a semicolon-separated list of files, which is the incorrect syntax:
cmake.exe -E copy "mylib1.dll;mylib2.dll" /your/binary/dir/python/proj
This causes the copy command to fail.
To work-around this issue, you could loop over each DLL file you want to copy, if there aren't too many. Something like this could work:
# Loop through the Debug files.
foreach(cur_file ${library_files_debug})
get_filename_component(file_name ${cur_file} NAME)
add_custom_target(copy_dlls_to_wheel_debug_${file_name} ALL
DEPENDS setup.py
COMMAND ${CMAKE_COMMAND} -E echo "DLL file: ${cur_file}"
COMMAND ${CMAKE_COMMAND} -E echo "Destination dir: ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}"
COMMAND ${CMAKE_COMMAND} -E copy $<$<CONFIG:Debug>:${cur_file}> ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}
)
endforeach()
# Loop through the Release files.
foreach(cur_file ${library_files_release})
get_filename_component(file_name ${cur_file} NAME)
add_custom_target(copy_dlls_to_wheel_release_${file_name} ALL
DEPENDS setup.py
COMMAND ${CMAKE_COMMAND} -E echo "DLL file: ${cur_file}"
COMMAND ${CMAKE_COMMAND} -E echo "Destination dir: ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}"
COMMAND ${CMAKE_COMMAND} -E copy $<$<CONFIG:Release>:${cur_file}> ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME}
)
endforeach()
A quicker solution might be to bundle up your DLLs, using CMake's tar command line utility, copy them, then extract them, as suggested in this answer. CMake's tar command does not seem to accept lists wrapped in generator expressions either, so the list of files to bundle together is written to a file.
file(GLOB library_files_debug ${outputdirectory_root}/Debug/*.dll)
file(GLOB library_files_release ${outputdirectory_root}/Release/*.dll)
# Write the filenames (not full path) of the files to pack to file
set(debug_content "")
set(release_content "")
foreach(lib_file ${library_files_debug})
get_filename_component(file_name ${lib_file} NAME)
set(debug_content "${debug_content}${file_name}\n")
endforeach(lib_file ${library_files_debug})
foreach(lib_file ${library_files_release})
get_filename_component(file_name ${lib_file} NAME)
set(release_content "${release_content}${file_name}\n")
endforeach(lib_file ${library_files_release})
set(filenames_debug ${outputdirectory_root}/debug_files.txt)
set(filenames_release ${outputdirectory_root}/release_files.txt)
file(WRITE ${filenames_debug} ${debug_content})
file(WRITE ${filenames_release} ${release_content})
# Read either the list of debug or release files, and pack files
add_custom_command(
TARGET bdist PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E tar "cfj" ${outputdirectory_root}/temp.tar --files-from="$<IF:$<CONFIG:Debug>,${filenames_debug},${filenames_release}>"
WORKING_DIRECTORY ${outputdirectory_root}/$<CONFIG>)
# Unpack the files in the folder building the python wheel
add_custom_command(
TARGET bdist PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E rename ${outputdirectory_root}/temp.tar temp.tar
COMMAND ${CMAKE_COMMAND} -E tar "xfj" temp.tar
COMMAND ${CMAKE_COMMAND} -E remove temp.tar
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/python/${PROJECT_NAME})

cmake and how to avoid unzip an archive multiple times

I am using the following cmake command to extract a zip file
${CMAKE_COMMAND} -E tar xkf
This unzip the file as it is.
I'd like to AVOID unzipping when the file has been already unzipped.
How can I do that? It seems that there is no option for that purpose.
Add an dependency.
The result of the unzip operation has to be used within a later build step. Add the dependency between the unzip and that build step.
Use add_custom_command to unzip files and copy them to the expected folder
Extend your target by a dependency to the extracted files
The following example shows this for the target TTNLIB.
Example:
add_custom_command(OUTPUT ${CMAKE_SOURCE_DIR}/src/ttnbitvector.cpp
COMMAND ${CMAKE_COMMAND} -E tar xkf ${CMAKE_SOURCE_DIR}/external/ttnbitvector.cpp.zip -C ${CMAKE_SOURCE_DIR}/external
COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/external/ttnbitvector.cpp ${CMAKE_SOURCE_DIR}/src/ttnbitvector.cpp
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/external
COMMENT Extract ${CMAKE_SOURCE_DIR}/src/ttnbitvector.cpp
)
add_library(TTNLIB SHARED ${SOURCES} ${CMAKE_SOURCE_DIR}/src/ttnbitvector.cpp)
Note: Read the documentation of add_custom_command
See cmake solutions for further information.

Empty RUNTIME_OUTPUT_DIRECTORY

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.

copy directory to another from add_custom_target

I have this target code:
add_custom_target (
dist
COMMAND ${CMAKE_COMMAND} -E make_directory "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}"
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/CMakeLists.txt ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/src ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/data ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}
COMMAND ${CMAKE_COMMAND} -E copy ${PROJECT_SOURCE_DIR}/po ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}
COMMAND ${7Z} a -t7z ${PACKER_PACKAGE_FILE_NAME_EXT} ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}
COMMAND ${CMAKE_COMMAND} -E remove_directory "${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}"
COMMENT "${PACKER_PACKAGE_FILE_NAME_EXT} created"
)
My goal is to copy directory (and its contents) to my directory ${PROJECT_BINARY_DIR}/${PROJECT_NAME}-${PROJECT_VERSION}. The only file in the directory is CMakeLists.txt, the rest are just bunch of empty "src", "data" and "po" files, any ideas
You do it wrong. There is a better way to produce a distribution tarball.
Use install command and CPack module.
Here you may find a brief tutorial on CPack with example project.
If you want to copy a directory you should use cmake -E copy_directory.
But if you want to create a source package, please have a look into cpack, it can also create source packages.