I'm trying to understand how to use CPack. I want to produce a binary and a source package. The binary one is perfectly fine, including only what's necessary. In the source distribution, though, there are basically all files present in the project's root directory - including my CMake build directory from where I ran the CPack command.
Since one might have several build directories, I can't see how I can exclude them all from being put into the source package.
set(CPACK_SOURCE_IGNORE_FILES "*.git*")
works fine but ideally I would like to restrict the set of included files only to those that are tracked by version control, e.g. git, in the first place. And, no, I don't consider making sure that my project directory is completely clean before a cpack a viable option.
(Packing an entire directory into an archive is something I don't need CPack for.)
This is possible with CPACK_PROJECT_CONFIG_FILE
. This file is executed by cpack before the CPACK_*
variables are queried, so you can use this file to dynamically add entries to the CPACK_SOURCE_IGNORE_FILES
list.
In the top-level CMakeLists.txt add the following before the call to include(CPack)
:
find_package(Git)
configure_file(
"${CMAKE_SOURCE_DIR}/ProjectCPackConfig.cmake.in"
"${CMAKE_BINARY_DIR}/ProjectCPackConfig.cmake"
@ONLY
)
set(CPACK_PROJECT_CONFIG_FILE "${CMAKE_BINARY_DIR}/ProjectCPackConfig.cmake")
And create ProjectCPackConfig.cmake.in:
# If we are packaging source, then ignore files that are untracked by git.
if(CPACK_TOPLEVEL_TAG STREQUAL CPACK_SOURCE_TOPLEVEL_TAG AND "@GIT_FOUND@")
# Use `git ls-files` to list untracked files/directories
execute_process(
COMMAND "@GIT_EXECUTABLE@" ls-files --ignored --other --exclude-standard --directory
WORKING_DIRECTORY "@CMAKE_SOURCE_DIR@"
RESULT_VARIABLE status
OUTPUT_VARIABLE GIT_IGNORED_FILES
OUTPUT_STRIP_TRAILING_WHITESPACE
)
if(NOT status EQUAL 0)
message(ERROR "Failed to query git for file ignore list.")
endif()
# split the output of git ls-files into a list
string(REPLACE "\n" ";" GIT_IGNORED_FILES "${GIT_IGNORED_FILES}")
# .git directory is implicitly untracked
list(APPEND GIT_IGNORED_FILES ".git/")
foreach(IGN_FILE ${GIT_IGNORED_FILES})
# apply escaping to the filename so nothing is mistaken for a regex quantifier
string(REGEX REPLACE "." "\\\\\\0" IGN_FILE_QUOTED "@CMAKE_SOURCE_DIR@/${IGN_FILE}")
# add the quoted filename to the CPACK_IGNORE_FILES list
list(APPEND CPACK_IGNORE_FILES "^${IGN_FILE_QUOTED}")
endforeach()
endif()
It is worth noting that cpack does not directly use any CPACK_SOURCE_*
variables. Instead, CPACK_SOURCE_*
values are assigned to CPACK_*
variables in CMAKE_BINARY_DIR
/CPackSourceConfig.cmake, and cpack executes this file before executing CMAKE_BINARY_DIR
/ProjectCPackConfig.cmake. This is why I assign to CPACK_IGNORE_FILES
instead of CPACK_SOURCE_IGNORE_FILES
, and it is also why I use CPACK_TOPLEVEL_TAG STREQUAL CPACK_SOURCE_TOPLEVEL_TAG
to detect whether a source package is currently being generated.