Create a zip archive with Cmake without parent directory? - cmake

When I do this:
add_custom_target(
mystuff
COMMAND ${CMAKE_COMMAND} -E tar "cvf" "${CMAKE_CURRENT_BINARY_DIR}/mystuff.zip" --format=zip -- ${CMAKE_CURRENT_SOURCE_DIR}/stuff/
)
against a directory organized like this:
stuff/
file1.txt
file2.txt
file3.txt
The resulting zip file contains:
stuff/
file1.txt
file2.txt
file3.txt
but I want: (no parent directory)
file1.txt
file2.txt
file3.txt
If I were doing this outside of cmake, I would use the -C argument (change directory)
How to do this with cmake?

You could change the working directory of the tar command, something like this:
add_custom_target(
mystuff
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/stuff"
COMMAND ${CMAKE_COMMAND} -E tar "cvf" "${CMAKE_CURRENT_BINARY_DIR}/mystuff.zip" --format=zip .
)

You could make the whole thing simpler by removing the nested call to CMake, and calling zip directly:
add_custom_target(
mystuff
WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/stuff"
COMMAND zip -r "${CMAKE_CURRENT_BINARY_DIR}/mystuff.zip" .
)
(Note: original didn't work, see comment below)
Tell tar to include only the contents of the directory (it will do so recursively, but will leave off the top level).
tar "cvf" "${CMAKE_CURRENT_BINARY_DIR}/mystuff.zip" --format=zip \
-- ${CMAKE_CURRENT_SOURCE_DIR}/stuff/*

Related

How to run execute_process in cmake with wildcard?

Below is the CMakeLists.txt file, that tries to execute terminal command with wildcard character *.txt. Currently it gives error ls: cannot access '*.txt': No such file or directory. But, when I try to run without the wildcard it runs as expected, ie it lists the files in the current directory.
Is there any way I could use wild cards in execute_process?
cmake_minimum_required(VERSION 3.8)
project(foo)
execute_process(COMMAND ls *.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
RESULT_VARIABLE result
OUTPUT_VARIABLE curr_out
ERROR_VARIABLE curr_out
)
message(STATUS "${result} : ${curr_out}")
Filename expansion one of expansions done by your shell before the command is executed and it causes *.txt to expand to the list of files using special rules.
You have to run the shell to cause *.txt to expand.
COMMAND sh -c "ls *.txt"
Using file(GLOB tmp "*.txt") and passing tmp would be a more cmake-ish way.

How to move a file download from configure step to build step?

Let's say I have the block below in my CMakeLists.txt.
file (DOWNLOAD http://.../file.zip "${CMAKE_BINARY_DIR}/file.zip")
execute_process (
COMMAND "${CMAKE_COMMAND}" -E tar -xf file.zip
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
add_custom_command (
OUTPUT output.txt
COMMAND "${MY_COMMAND}" file-found-in-zip.txt output.txt
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
)
The basic steps are:
Download.
Extract.
Add a custom command that uses a file that was extracted in step 2.
During the build step, the custom command may or may not be executed, but the download and extraction always will. How can I make the download and extraction conditional, such that it happens only if the custom command that needs it will be executed?
I guess something along:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
"file(DOWNLOAD http://.../file.zip ${CMAKE_CURRENT_BINARY_DIR}/file.zip"
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file.zip
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
VERBATIM
)
execute_process (
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/file-found-in-zip.txt
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/file.zip
COMMAND ${CMAKE_COMMAND} -E tar -xf file.zip
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)
add_custom_command (
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/file-found-in-zip.txt
COMMAND "${MY_COMMAND}" file-found-in-zip.txt output.txt
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}"
VERBATIM
)
Or just:
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
"file(DOWNLOAD http://.../file.zip ${CMAKE_CURRENT_BINARY_DIR}/file.zip"
)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/output.txt
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_BINARY_DIR}/download.cmake
COMMAND ${CMAKE_COMMAND} -E tar -xf file.zip
COMMAND "${MY_COMMAND}" file-found-in-zip.txt output.txt
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
VERBATIM
)

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 unzip automatically(while building) when the zip file changes

I am currently using execute_command to unzip some files before building.
I would like to unzip the folder that is in effect replace the old files with new file in the destination folder when the zip file in the source changes. Can anyone give me some suggestions?
Currently I am doing something like this,
execute_process(
COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/abc.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
)
I tried with add_custom_command() but I am executing it for every build.
I only want it to unzip if the source zip file changes.
add_custom_command( OUTPUT ${LibsList}
COMMAND ${CMAKE_COMMAND} -E tar xzf ${CMAKE_SOURCE_DIR}/libs.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${CMAKE_SOURCE_DIR}/libs.zip )
They mostly contain header files and a few shared libraries. I usually need the header files at build time and shared libraries at run time. If possible I would not give the list of files in OUTPUT as it is very large
I achieve this, at least for my scenario using the code below,
add_custom_target( unZip ALL)
add_custom_command(TARGET unZip PRE_BUILD
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_BINARY_DIR}/abc/
COMMAND ${CMAKE_COMMAND} -E tar xzf {CMAKE_SOURCE_DIR}/abc.zip
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
DEPENDS ${CMAKE_SOURCE_DIR}/abc.zip
COMMENT "Unpacking abc.zip"
VERBATIM)
Now I made all target dependent on "unZip". This way when rebuilding unZip occurs or at build time if abc.zip changes, unZip occurs.

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.