I have a CMakeLists.txt where I copy files in a specific order but the execution order seems random:
#First Installation
install(DIRECTORY ${SIT_AW_FIRST_WORKSPACE_DIR}/ DESTINATION ${WS_DESTINATION_DIR})
#Second Installation that should overwrite a subset of files
install(DIRECTORY ${SIT_AW_SECOND_WORKSPACE_DIR}/ DESTINATION ${WS_DESTINATION_DIR})
Output :
-- Installing: /opt/FOO/share/baz/xml/Login.xml
-- Up-to-date: /opt/FOO/share/baz/xml/Login.xml
The second file will not overwrite the first because they have the same timestamp and the same name, but they have different size. Is there a way to force the copy?
I think you may have better success by using the PATTERN and EXCLUDE options instead of trying to overwrite one file with another. You could try something like this:
#First Installation
install(DIRECTORY ${SIT_AW_FIRST_WORKSPACE_DIR}/ DESTINATION ${WS_DESTINATION_DIR}
PATTERN "Login.xml" EXCLUDE)
#Second Installation that should overwrite a subset of files
install(DIRECTORY ${SIT_AW_SECOND_WORKSPACE_DIR}/ DESTINATION ${WS_DESTINATION_DIR})
You can read the CMake docs here with the specifics here: https://cmake.org/cmake/help/v3.18/command/install.html#installing-directories
Related
I am trying to generate a CMake target for a C++ project using add_custom_target that copies the contents of the directory in which the CMakeLists.txt resides into ${CMAKE_BINARY_DIR}, but excludes a given list of files.
While it looks like a quite easy task, I'm facing problems here. My attempts so far:
1) Generate a list of all files/dirs, remove the items to be excluded and use the CMake copy command:
file(GLOB files_to_copy LIST_DIRECTORIES TRUE *)
list(REMOVE_ITEM files_to_copy
${CMAKE_CURRENT_SOURCE_DIR}/file_to_exclude.txt
${CMAKE_CURRENT_SOURCE_DIR}/dir_to_exclude
# ...
)
add_custom_target(my-target
COMMAND ${CMAKE_COMMAND} -E copy ${files_to_copy} ${CMAKE_BINARY_DIR}
)
Problems:
Removing items this way works on a string comparison level, which could lead to problems when using symbolic links, for example
The copy command line tool apparently supports copying directories, however it doesn't seem to work on my machine, therefore directories do not get copied.
2) Use the file command to copy the files, excluding some files
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/" DESTINATION ${CMAKE_BINARY_DIR}
PATTERN "file_to_exclude.txt" EXCLUDE
PATTERN "dir_to_exclude" EXCLUDE
)
Problems:
This does exactly what I want, except that the command is not bound to a target and therefore is always executed. But I only need the copy operation to be a target.
Is there any possibility to bind the file(COPY ...) command to a target? Or any other straightforward solution to achieve what I want?
Please note that I only want to use CMake built-in tools and not execute any OS-specific shell commands (working on a cross-platform project).
CMake scripting commands work only in CMake context and are executed immediately.
But a COMMAND in add_custom_command (or add_custom_target) is executed in the context of a build tool (e.g. Makefile), not in CMake context.
However, you may put CMake scripting commands into separate CMake script, and call this script from the COMMAND. This solution has the same platform-independent properties as CMakeLists.txt itself.
You may parameterize separate script either:
With configure_file CMake command.
By passing -D parameters to CMake when call the script.
The first approach is quite simple: you write the script as would you write CMakeLists.txt itself. But it generates additional files for every parametrization set.
The second approach is useful for multi-purpose (or multi-usable) scripts, as it doesn't create additional copy of the script for every usage. But it requires some design of the parameters.
Using 'configure_file'
copy_sources.cmake.in (as if commands are written in CMakeLists.txt):
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/" DESTINATION ${CMAKE_BINARY_DIR}
PATTERN "file_to_exclude.txt" EXCLUDE
PATTERN "dir_to_exclude" EXCLUDE
)
CMakeLists.txt:
# Instantiate the parameterized script in the binary directory.
configure_file(copy_sources.cmake.in copy_sources.cmake)
add_custom_target(my-target
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_BINARY_DIR}/copy_sources.cmake
)
Using '-D' parameters
copy_sources.cmake:
# Expect the script to be called from the source directory.
# This saves one parameter.
#
# DEST_DIR denotes the directory for copy to.
file(COPY "${CMAKE_SOURCE_DIR}/" DESTINATION ${DEST_DIR}
PATTERN "file_to_exclude.txt" EXCLUDE
PATTERN "dir_to_exclude" EXCLUDE
)
CMakeLists.txt:
add_custom_target(my-target
COMMAND ${CMAKE_COMMAND}
-DDEST_DIR=${CMAKE_BINARY_DIR} # all '-D' options should preceed '-P' one
-P ${CMAKE_CURRENT_SOURCE_DIR}/copy_sources.cmake
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} # The script assumes proper current directory
)
I'm building Google's FlatBuffers as a dependency for my own project and I need to compile a schema at build-time. I don't want to use BuildFlatBuffers.cmake or FindFlatBuffers.cmake because I'm using a specific version and I can't rely on it being locally installed.
This is a simplified version of my CMakeLists.txt:
ExternalProject_Add (
flatbuf
URL "https://github.com/google/flatbuffers/archive/v1.8.0.tar.gz"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
)
add_custom_target (
flatbuf_schema
PREFIX ${FLATBUF_PREFIX}
DEPENDS flatbuf
COMMAND ${FLATBUF_PREFIX}/src/flatbuf-build/flatc --cpp ${FLATBUF_SCHEMA}
)
It works fine for Make and Ninja but fails in Xcode, which builds flatc in the Debug directory.
I thought about these possible solutions:
use add_subdirectory instead of ExternalProject_Add so that I can use ${FLATBUFFERS_FLATC_EXECUTABLE} or $<TARGET_FILE:flatc>;
manually assign a RUNTIME_OUTPUT_DIRECTORY for flatbuf;
search for flatc in multiple paths (not portable; I also don't know how to make it happen at build-time).
I tried (2) and (3) but without success. As for (1), I'm not sure it's a good idea. How can I build schemas in a portable manner?
You can use ExternalProject_Get_Property, something like this...
note: I suppose you don't even need to install flatbuf, just build it and use it.
ExternalProject_Add (
flatbuf_project
URL "https://github.com/google/flatbuffers/archive/v1.8.0.tar.gz"
CMAKE_ARGS -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE}
INSTALL_COMMAND ""
)
ExternalProject_Get_Property(flatbuf_project source_dir)
ExternalProject_Get_Property(flatbuf_project binary_dir)
# Export flatbuf executable to consume schema file during build
add_executable(flatbuf::flatbuf IMPORTED)
set_target_properties(flatbuf::flatbuf PROPERTIES IMPORTED_LOCATION
"${binary_dir}/flatc")
add_dependencies(flatbuf::flatbuf flatbuf_project)
add_custom_target(flatbuf_schema
PREFIX ${FLATBUF_PREFIX}
COMMAND flatbuf::flatbuff --cpp ${FLATBUF_SCHEMA}
)
note2:
If COMMAND specifies an executable target name (created by the add_executable() command) it will automatically be replaced by the location of the executable created at build time. If set, the CROSSCOMPILING_EMULATOR executable target property will also be prepended to the command to allow the executable to run on the host. Additionally a target-level dependency will be added so that the executable target will be built before this custom target.
note3:
target ALIAS are not working on IMPORTED target unfortunately...
Apparently CMake provides a variable to solve this exact problem, i.e. CMAKE_CFG_INTDIR (docs). The path to the flatc executable should then be
ExternalProject_Get_Property(flatbuf BINARY_DIR)
set (FLATC "${BINARY_DIR}/${CMAKE_CFG_INTDIR}/flatc")
I am trying to write a game for practice in C++. I am using CMake for this, and my project gets built in a separate build directory automatically by my IDE. However, my assets folder is not being copied over to the new directory.
Currently, I am attempting to resolve this with the following:
add_custom_command(TARGET ${PROJECT_NAME}
POST_BUILD
COMMAND cp -r ${PROJECT_SOURCE_DIR}/assets ${CMAKE_BINARY_DIR}
)
However, this seems to have absolutely no effect on the outcome and the assets directory is still not present in my build directory.
How can I actually make it copy this asset directory to my build location? Is there a smarter way to point my program to my assets location (perhaps one that will also work with assets in /usr/share?)
After quite a while of more searching, I managed to find the file(COPY {files} DESTINATION {directory}) command:
file(COPY assets DESTINATION ${CMAKE_BINARY_DIR})
Two ways I've found, both ensuring that it copies on target build and stays up to date even if the target doesn't need to re-build.
The first uses the CMake command line tool copy_directory. It's unclear from the documentation whether this copy always happens, regardless if files in the assets directory were updated or not, or if it only copies when the asset files don't exist or an asset file was updated:
add_custom_target(copy_assets
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets
)
add_dependencies(mytarget copy_assets)
The second requires a small additional CMake script file, but it allows us to use the file(COPY ...) CMake comamnd, which states in the documentation that "copying preserves input file timestamps, and optimizes out a file if it exists at the destination with the same timestamp."
First, create the CMake script file copy-assets.cmake in the same directory as your CMakeLists.txt, with the contents being (similar to Ethan McTague's answer)
file(COPY ${CMAKE_CURRENT_LIST_DIR}/assets DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
And then in your CMakeLists.txt, add the following:
add_custom_target(copy_assets
COMMAND ${CMAKE_COMMAND} -P ${CMAKE_CURRENT_LIST_DIR}/copy-assets.cmake
)
add_dependencies(mytarget copy_assets)
The solution above using add_custom_target is suitable for most cases, but has a few problems.
Does not support dependent generator expressions (e.g. $<TARGET_FILE_DIR:tgt>)
Re-copies every build (slow for numerous/large files)
Is not particularly readable, especially for copying from multiple locations
Instead, I've been using file GENERATE
set(ASSETS
assetfile
...)
foreach(ASSET ${ASSETS})
file(GENERATE OUTPUT ${ASSET} INPUT ${ASSET})
endforeach()
Note I haven't needed generator expressions yet, but it looks like the latest version of CMake (3.19) adds generator expression support to this function.
Here is what I do with vulkan shaders. Simply replace the compilation step with a copy command:
macro(add_shaders)
cmake_parse_arguments("MY" "TARGET" "SOURCES" ${ARGN})
foreach(src ${MY_SOURCES})
set(OUTF "${CMAKE_CURRENT_BINARY_DIR}/shaders/${src}.spv")
get_filename_component(PARENT_DIR "${OUTF}" DIRECTORY)
add_custom_command(
OUTPUT "${OUTF}"
COMMAND ${CMAKE_COMMAND} -E make_directory "${PARENT_DIR}"
COMMAND "${Vulkan_GLSLC_EXECUTABLE}" "${CMAKE_CURRENT_SOURCE_DIR}/${src}" -o "${OUTF}"
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${src}"
VERBATIM
)
# src in order to get listed in the IDE, OUTF to declare the build relationship
target_sources("${MY_TARGET}" PRIVATE "${src}" "${OUTF}")
endforeach()
endmacro()
add_shaders(TARGET my_target SOURCES asset1 asset2 asset3 ...)
You'll have to specify each asset on its own (or loop over a list). Including wildcards into a build is a bad idea [tm]. This will also only copy/process stuff if it has changed.
In a CMake project, I am using git tags for version numbers. Using the approach from https://github.com/iPenguin/version_git, I am able to get the version number into the source codes (that is, CMake creates a version.cpp file with a const string containing the version number that I can then use in my C++ source codes). The problem is that when I create a source package using CPack, I exclude the .git directory and when compiling the sources extracted from the package, the version number is not available.
How to make CPack put the version number into the source package?
I have the following requirements:
I do not want the build directory to be included in my source package. It is important to note that I do out-of-source builds from a directory under the project root. That is, if my toplevel CMakeLists.txt is in directory my_project, then I run "cmake .." and "make" in directory my_project/build.
I do not want to include the .git directory in my source package.
I want all the files generated by cmake and "make package_source" to be inside the build directory. Additionally, I don't want cmake to modify or delete any file outside of the build directory.
I do not want to use undocumented behavior of cmake and cpack, in particular the paths used by cpack.
So far I was not able to find a solution that would satisfy all my requirements. Here is what I have tried:
I let cmake create a file versionForSourcePackage.cmake, which, when later included by cmake, will set the VERSION variable. Then I want to put this file into the source package, so that when cmake is run in the extracted package, it will have the VERSION variable set. This file is created under the build directory, but I do not know how to properly let CPack copy it to the source packages.
The first possibility is a slight modification of https://github.com/iPenguin/version_git, but it does not satisfy my requirement number 4. Full example is at https://github.com/josefcibulka/version_git.
When building from repository, I get the version number from git and save it in ${PROJECT_BINARY_DIR}/versionForSourcePackage.cmake. Then I use CPACK_INSTALL_COMMANDS to let CPack copy this file to ${PROJECT_BINARY_DIR}/_CPack_Packages/Linux-Source/${CPACK_SOURCE_GENERATOR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}/ so that it is included in the package. This violates the requirement number 4 and moreover, if I want to create both TGZ and ZIP packages in the future, I need to make some changes. Is there some variable that I could use within CPACK_INSTALL_COMMANDS to get the path to the directory where CPack is preparing the contents of the package?
CMakeLists.txt looks like:
cmake_minimum_required(VERSION 2.8)
project("version_from_git")
# Appends the cmake/modules path to MAKE_MODULE_PATH variable.
set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
# When in the extracted package, use the previously generated file, otherwise get the current version from git.
if(EXISTS "${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake")
include("${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake")
else()
include(GetGitRevisionDescription)
git_describe(VERSION --tags --dirty=-dirty)
endif()
# Parse the version information into pieces.
string(REGEX REPLACE "^v([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${VERSION}")
string(REGEX REPLACE "^v[0-9]+\\.[0-9]+\\.[0-9]+(.*)" "\\1" VERSION_SHA1 "${VERSION}")
set(VERSION_SHORT "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}")
set(CPACK_SOURCE_GENERATOR "TGZ")
set(CPACK_PACKAGE_VERSION_MAJOR "${VERSION_MAJOR}")
set(CPACK_PACKAGE_VERSION_MINOR "${VERSION_MINOR}")
set(CPACK_PACKAGE_VERSION_PATCH "${VERSION_PATCH}")
# The following will copy the version file to the source package.
set(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${VERSION_SHORT}-Source")
set(CPACK_INSTALL_COMMANDS "${CMAKE_COMMAND} -E make_directory \
${PROJECT_BINARY_DIR}/_CPack_Packages/Linux-Source/${CPACK_SOURCE_GENERATOR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}/"
"${CMAKE_COMMAND} -E copy \
${PROJECT_BINARY_DIR}/versionForSourcePackage.cmake \
${PROJECT_BINARY_DIR}/_CPack_Packages/Linux-Source/${CPACK_SOURCE_GENERATOR}/${CPACK_SOURCE_PACKAGE_FILE_NAME}/")
# Exclude the build and .git directory from the source package.
set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_SOURCE_DIR}/.git/;${PROJECT_BINARY_DIR}/;${CPACK_SOURCE_IGNORE_FILES}")
include (CPack)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/version.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
# Prepare the versionForSourcePackage.cmake file that will be included in the source package.
configure_file(
${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake.in
${PROJECT_BINARY_DIR}/versionForSourcePackage.cmake #ONLY)
set(version_file "${CMAKE_CURRENT_BINARY_DIR}/version.cpp")
set(source_files "${CMAKE_CURRENT_SOURCE_DIR}/src/main.cpp")
#Add the version_file to the project build.
add_executable(${PROJECT_NAME} ${source_files} ${version_file})
The file versionForSourcePackage.cmake.in:
set(VERSION "v#VERSION_SHORT#")
The second possibility is the approach used at https://github.com/lcw/cmake_git_version. Full example is in the second_possibility branch in https://github.com/josefcibulka/version_git. The file versionForSourcePackage.cmake is placed to the subdirectory version_file of the build directory and this directory is added among CPACK_SOURCE_INSTALLED_DIRECTORIES. This works well when the project and build directory are at the same level.
The problem is that if the build directory is under the project directory, I add the build directory to CPACK_SOURCE_IGNORE_FILES to satisfy requirement 1. Then, even when I set CPACK_SOURCE_INSTALLED_DIRECTORIES to include the version_file directory, it will be ignored. It can be done so that I ignore only everything in the build directory except for the version_file directory. Then the build directory in the source package contains only the version_file directory, which is better than the whole build directory, but still not perfect.
Update: It seems that requirement 4 is not satisfied either, because I cannot find CPACK_SOURCE_INSTALLED_DIRECTORIES in the CMake documentation.
The differences from the first possibility are, in CMakeLists.txt:
# The following will transfer the version from git to the source package.
set(CPACK_SOURCE_INSTALLED_DIRECTORIES "${PROJECT_SOURCE_DIR};/;${PROJECT_BINARY_DIR}/version_file;/")
# Exclude the build and .git directory from the source package.
set(CPACK_SOURCE_IGNORE_FILES "${PROJECT_SOURCE_DIR}/.git/;${PROJECT_BINARY_DIR}/([^v].*|v[^e].*|ve[^r].*|ver[^s].*|vers[^i].*|vers[^i].*|versi[^o].*|versio[^n].*|version[^_].*|version_[^f].*|version_f[^i].*|version_fi[^l].*|version_fil[^e].*);${CPACK_SOURCE_IGNORE_FILES}")
include (CPack)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules/version.cpp.in
${CMAKE_CURRENT_BINARY_DIR}/version.cpp)
configure_file(
${PROJECT_SOURCE_DIR}/versionForSourcePackage.cmake.in
${PROJECT_BINARY_DIR}/version_file/versionForSourcePackage.cmake #ONLY)
At the end, I am using a modification of the first possibility, but the issues are solved only partially.
Instead of CPACK_INSTALL_COMMANDS, I use CPACK_INSTALL_SCRIPT and a CMake script. This script is executed for every package that is being created and the nice thing is that inside of this script, CMAKE_CURRENT_BINARY_DIR points to the directory where the contents of the package are being gathered. That is, whatever I copy to that directory will get inside the package.
This, however, looks to be an undocumented behavior of CMAKE_CURRENT_BINARY_DIR. For example, the CMAKE_BINARY_DIR and PROJECT_BINARY_DIR are empty when used in a CMake script executed by CPack. So maybe in some other version of CMake, CMAKE_CURRENT_BINARY_DIR will also be empty when executed by CPack.
Another issue is that I do want the version file to be copied only to source packages and not to binary packages. The way how I do the distinction is that if CMAKE_CURRENT_BINARY_DIR ends with "Source", I assume that the script is running during the preparation of a source package.
Full example is in the using_cpack_install_script branch of https://github.com/josefcibulka/version_git. The changed parts of CMakeLists.txt:
# The following will transfer the version from git to the source package.
set(CPACK_INSTALL_SCRIPT "${CMAKE_BINARY_DIR}/versionNumberToPackage.cmake")
configure_file(${CMAKE_SOURCE_DIR}/versionNumberToPackage.cmake.in
${CMAKE_BINARY_DIR}/versionNumberToPackage.cmake #ONLY)
The contents of versionNumberToPackage.cmake.in are:
# Copies VersionForSourcePackage.cmake to the source package (it should not be copied to binary package).
# Detection if this is the source package is that the path ends with "Source" and an optional slash.
if("${CMAKE_CURRENT_BINARY_DIR}" MATCHES "Source/?$")
file(COPY #CMAKE_BINARY_DIR#/versionForSourcePackage.cmake DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
endif()
I would like to use CMake for a project, but I have the following two requirements:
The final output of the project should be a set of object files (*.o).
The location of the object files is important. I want to select which directory the files are outputted.
Does CMake support this type of behavior? If so, how? Can I do it with move commands after the object file is build?
First create an object library.
Now the problem is that:
Object libraries cannot be imported, exported, installed, or linked.
http://www.cmake.org/cmake/help/v2.8.11/cmake.html#command:add_library
I would try to use the install(DIRECTORY ...).
Using the options:
DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} #Probably you have to check more precisely where the object files are built
DESTINATION #it's something relative to DESTDIR, if set, or CMAKE_INSTALL_PREFIX otherwise
FILES_MATCHING
PATTERN "*.o"
A flaw of this solution will be in the output directory name, that will be basically decided by cmake, I wonder if anything can be done in that respect.