How to use CMake to create a package that installs to '/etc' and '/var'? - cmake

I've got CMake (3.02) installing to a DESTDIR when I invoke:
$ make -C build DESTDIR=$(pwd)/build/rootfs install
This results in a file layout that I'm happy with: binaries are located in the .../build/rootfs/usr/local/bin directory and the init scripts are in .../build/rootfs/etc/init.d. To accomplish that, I used a mixture of relative and absolute paths in my CMakeLists.txt file:
set(CPACK_SET_DESTDIR ON)
...
INCLUDE(CPack)
...
set(ROOTFS_BIN_DIR bin)
set(ROOTFS_ETC_INITD_DIR /etc/init.d)
...
INSTALL(TARGETS myDaemon DESTINATION ${ROOTFS_BIN_DIR})
INSTALL(PROGRAMS myDaemon.sh RENAME myDaemon DESTINATION ${ROOTFS_ETC_INITD_DIR})
With that, I think 'working', I'm trying to create a simple tarball package which will eventually become a debian package (with pre/post install/remove scripts) but when I invoke:
$ make -C build DESTDIR=$(pwd)/build/rootfs package
I'm getting errors because cpack is attempting to write my init scripts to the system's /etc/init.d directory (instead of $(pwd)/build/rootfs/etc/init.d). If I wanted that, then $sudo !! would solve the problem. The error (replaced full path with ...):
CMake Error at .../build_src/cmdServer/cmake_install.cmake:44 (file):
file INSTALL cannot copy file
".../src/cmdServer/cmdServer.sh" to
"/etc/init.d/cmdServer".
I'm not using a lot of CPACK directives: in my top level CMakeLists.txt file I have:
SET(CPACK_SET_DESTDIR ON)
SET(CPACK_GENERATOR TGZ)
INCLUDE(CPack)
How can I package my init scripts correctly?
I've been referencing:
https://cmake.org/pipermail/cmake/2008-April/020833.html
and https://cmake.org/pipermail/cmake/2006-November/011890.html

Related

What's the use of configure_package_config_file option INSTALL_DESTINATION

For the following CMakeLists.txt file
cmake_minimum_required(VERSION 3.15)
project(Testing)
include(CMakePackageConfigHelpers)
configure_package_config_file(FooConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
INSTALL_DESTINATION lib/Goo/dmake
)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
DESTINATION lib/Foo/cmake )
and the FooConfig.cmake.in file
#PACKAGE_INIT#
check_required_components(Foo)
It would finally install FooConfig.cmake to lib/Foo/cmake:
$ cmake ..
$ cmake --build .
$ cmake --install . --prefix ../install/
$ ls -R ../install/
../install/:
lib
../install/lib:
Foo
../install/lib/Foo:
cmake
../install/lib/Foo/cmake:
FooConfig.cmake
It seems that whatever value I set to the INSTALL_DESTINATION option of configure_package_config_file, it won't change FooConfig.cmake's installation directory. But if I comment INSTALL_DESTINATION lib/Goo/dmake, CMake Error prompts
No INSTALL_DESTINATION given to CONFIGURE_PACKAGE_CONFIG_FILE()
Then, what is the use of the INSTALL_DESINATION option? What its value (specifically, the above setting lib/Goo/dmake) actually affects?
The documentation:
The <path> given to INSTALL_DESTINATION must be the destination where the FooConfig.cmake file will be installed to.
I know the right way is INSTALL_DESTINATION lib/Foo/cmake. But as I deliberately set it as lib/Goo/dmake, the FooConfig.cmake file still rests at the desired destination lib/Foo/cmake. So, why this option is designed as a must?
The function configure_package_config_file only generates the file specified as the second argument. This function neither installs the generated file nor affects on its installation. So, its arguments can only affect on the content of the generated file.
If you look into the generated script FooConfig.cmake, then you could find the line like
get_filename_component(PACKAGE_PREFIX_DIR "${CMAKE_CURRENT_LIST_DIR}/../../../" ABSOLUTE)
This is how the script calculates installation prefix based on the path to the FooConfig.cmake file.
E.g. when you install the package with prefix /usr/local and your FooConfig.cmake script will be located in the directory /usr/local/lib/Foo/cmake, then the quoted path will be expanded into /usr/local/lib/Foo/cmake/../../../, which corresponds to /usr/local/.
Number of components ../ in the script equals to the number of components in INSTALL_DESTINATION parameter.
Why 'PACKAGE_PREFIX_DIR' is needed
Assume a package installs all public headers into directory include/ (relative to installation prefix), and config script needs to deliver that directory to the user via variable FOO_INCLUDE_DIR.
The most direct way would be to write in the script following:
# FooConfig.cmake.in
...
set(FOO_INCLUDE_DIR #CMAKE_INSTALL_PREFIX#/include)
so configure_package_config_file would substitute real install prefix instead of #CMAKE_INSTALL_PREFIX#.
But embedding absolute path into the config file prevents the package to be relocatable. So, once installed, the package could be used only from the installation directory, and cannot be copied elsewhere.
For relocatable package a config file computes all installation paths relative to the path of the config file itself, because it is the only path known to the script when it is called by find_package. It is like an executable can compute paths to the files located near to that executable.
If the script is installed into lib/Foo/cmake/FooConfig.cmake, then relative path to the include directory would be ../../../include, so one could use following assignment in that script:
# FooConfig.cmake.in
...
set(FOO_INCLUDE_DIR ${CMAKE_CURRENT_LIST_DIR}/../../../include)
So, when find_package(Foo) will execute this script, it expands the variable CMAKE_CURRENT_LIST_DIR to the actual directory with the possibly relocated script.
Operating with relative paths requires much attention from the coder, so CMake allows to automate this task:
# CMakeLists.txt
...
# Path to the include directory in the installation tree
set(INCLUDE_DIR 'include')
configure_package_config_file(FooConfig.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/FooConfig.cmake
INSTALL_DESTINATION lib/Foo/cmake
# Tell CMake to handle variable `INCLUDE_DIR` as a path
# under installation tree.
PATH_VARS INCLUDE_DIR
)
# FooConfig.cmake.in
#PACKAGE_INIT#
# Here we could use `#PACKAGE_INCLUDE_DIR#` as reference
# to variable 'INCLUDE_DIR' set in the CMakeLists.txt.
set_and_check(FOO_INCLUDE_DIR "#PACKAGE_INCLUDE_DIR#")
If you look into the generated file, you will find that #PACKAGE_INCLUDE_DIR#
is expanded into ${PACKAGE_PREFIX_DIR}/include, which uses the variable PACKAGE_PREFIX_DIR.

Is it possible to determine whether CMake install(CODE) is called from the "install" or "package" stage?

I'm using CMake v3.21.0 to invoke Qt's windeployqt during the install stage by the means of the install(CODE) command as follows:
install(
CODE "
execute_process(
COMMAND \"${CMAKE_COMMAND}\" -E
env PATH=\"${windeployqt_ROOT_DIR}\"
\"${windeployqt_EXECUTABLE}\"
# TODO(2021-08-25 by wolters): This is a different path when CPack is`
# used. How to check for this case and obtain the correct output path?
--dir \"${CMAKE_INSTALL_PREFIX}/${args_INSTALL_SUFFIX}\"
--no-quick-import
--no-system-d3d-compiler
--no-virtualkeyboard
--no-compiler-runtime
--no-webkit2
--no-angle
--no-opengl-sw
--verbose 0
\"\$<TARGET_FILE:${args_TARGET}>\"
)
"
COMPONENT runtime
)
This works fine if installing the project:
cmake --build . --config RelWithDebInfo --target install
But when creating a CPack package the files created by windeployqt are not part of the package (ZIP in this case):
cpack -G ZIP -C RelWithDebInfo -D CPACK_COMPONENTS_ALL="runtime"
I know that the issue is the usage of ${CMAKE_INSTALL_PREFIX} in the CODE.
For the install target this is correct.
For the package target this is not correct. Instead the build directory for the current CPack generator should be used, e.g. ${CMAKE_CURRENT_BINARY_DIR}/_CPack_Packages/win64/ZIP/${CPACK_PACKAGE_FILE_NAME}.
My questions are:
Is there a way to differentiate between install and package target in the CODE section? (pseudo-code: if(CMAKE_IS_PACKAGING))
If there is a way: Is it possible to obtain or dynamically build the directory path to the actual CPack temporary "install" directory?
If both problems can be solved the files generated by windeployqt should be part of the packages generated by CPack.
The variable CMAKE_INSTALL_PREFIX should not be expanded in the CMakeLists.txt, as you are doing. Its actual value at invocation time is available inside the install(CODE) fragments.
Consider the following snippet:
cmake_minimum_required(VERSION 3.21)
project(test NONE)
install(CODE [[message(STATUS "HERE: ${CMAKE_INSTALL_PREFIX}")]])
Note that [[ ... ]] escapes variable expansions (you could also use backslashes). Now if you configure this project with -DCMAKE_INSTALL_PREFIX=/tmp/install, you'll see the message print as you expect.
$ cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/tmp/install
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build
$ cmake --build build/ --target install
[0/1] Install the project...
-- Install configuration: ""
-- HERE: /tmp/install
If you now run the install script again without reconfiguring or rebuilding, it will still work:
$ cmake --install build/ --prefix /tmp/other-prefix
-- Install configuration: ""
-- HERE: /tmp/other-prefix
This is how CPack runs your install rules. It does not use the configuration-time value of CMAKE_INSTALL_PREFIX. It expects your project to be relocatable (i.e. bug-free).

Installing no longer existing files

I have a list of various files that I want to install, by executing a resulting INSTALL project. This works, but sometimes files are no longer available, when the install operation is executed. Environment is a build server, where files get moved around -> this causes the build to be faulty.
An easy way to fix this behaviour is in the OPTIONAL parameter of the install command. So my question is: Is there a way to output warnings at runtime, if the install command failed?
Here is my code, to recreate the issue. In the src directory there are files "1.txt" and "2.txt". I build the cmakelists.txt and then delete "2.txt". After that, I execute the INSTALL solution I got.
cmake_minimum_required(VERSION 3.0)
project(documentation)
set(SOURCEDOCUMENTATION "D:/projects/side_master/src/documentation/src")
set(TARGETDOCUMENTATION "D:/projects/side_master/src/documentation/tgt")
file (GLOB files_to_install "${SOURCEDOCUMENTATION}/*")
foreach(file_to_install ${files_to_install})
install(FILES ${file_to_install} DESTINATION ${TARGETDOCUMENTATION} OPTIONAL)
endforeach()
The error (without the OPTIONAL parameter):
-- Install configuration: "Debug"
-- Installing: D:/projects/side_master/src/documentation/tgt/1.txt
CMake Error at cmake_install.cmake:56 (file):
file INSTALL cannot find
"D:/projects/side_master/src/documentation/src/2.txt".
What I want to get is a generated message, like this:
File "D:/projects/side_master/src/documentation/src/2.txt" not found.
You could make use of the install(SCRIPT ...) or install(CODE ...) signatures of the CMake install command, to run custom installation steps specific to your use case. The custom steps here would check for the existence of the files (using CMake's EXISTS logic) to be installed, and print a warning message if the file does not exist. The custom installation command could look something like this:
install(CODE "
if(NOT EXISTS ${file_to_install})
message(WARNING \"File ${file_to_install} not found during installation.\")
endif()
")

How to understand the difference between two command line options for cmake?

Describe difference between these two command lines:
C:\xxxxx> cmake -help
Usage
$ cmake [options] <path-to-source>
$ cmake [options] <path-to-existing-build>
Specify a source directory to (re-)generate a build system for it in the
current working directory. Specify an existing build directory to
re-generate its build system.
The last description does not give me how to use the first, or the second.
Could you explain it to me?
When you use you do an in-tree build (cmake .), there is no difference.
When you do an out-of-tree build, there is a difference.
Suppose your project lives in ~/foo and your current directory is ~/foo/build
You have to run cmake .. for the first build. But for subsequent reconfigures, you can use cmake . because there is already a build there.
This command:
cmake [options] <path>
works as follows:
if <path> is not a valid (that is, already configured) CMake build directory, it is assumed to contain a CMakeList.txt. CMake will configure the current working directory as a build directory using <path>/CMakeLists.txt for source directory.
if <path> is a valid CMake build directory, the command reconfigures that directory using the source directory assigned when you first configured that build directory
So the common usage patterns are:
initial configuration:
mkdir my-build-dir
cd my-build-dir
cmake [options] my-source-dir
subsequent (re)configurations:
cmake [options] my-build-dir # current-work-dir is not important
alternative (initial) configuration using undocumented options:
cmake -Hmy-source-dir -Bmy-build-dir [options] # cwd is not important

What is cmake_install.cmake

I wrote a very simple HelloWorld.c program and ran Cmake. It created a cmake_install.cmake file in my build directory. Can somebody explain to me why CMake generated the file cmake_install.cmake? What is it's purpose and how can I use it?
CMakelists.txt :
cmake_minimum_required(VERSION 3.0)
PROJECT(FirstExample)
add_executable(prog first.c)
Thanks!
You generally don't use cmake_install.cmake directly. From the v3.12 page it states:
The install() command generates a file, cmake_install.cmake, inside
the build directory, which is used internally by the generated install
target and by CPack.
With your current CMakeLists.txt, the generated file doesn't do much. To create a useful install you would need to add more INSTALL commands to your CMakeLists.txt using the syntax below.
INSTALL(TARGETS targets... [EXPORT <export-name>]
[[ARCHIVE|LIBRARY|RUNTIME|FRAMEWORK|BUNDLE|
PRIVATE_HEADER|PUBLIC_HEADER|RESOURCE]
[DESTINATION <dir>]
[INCLUDES DESTINATION [<dir> ...]]
[PERMISSIONS permissions...]
[CONFIGURATIONS [Debug|Release|...]]
[COMPONENT <component>]
[OPTIONAL] [NAMELINK_ONLY|NAMELINK_SKIP]
] [...])
For further reading on this command, check out the documentation site and wiki.
If it's desired to manually execute the script as stated by Nic30g the 3.12 page states that cmake -P accepts the following variables:
COMPONENT
Set this variable to install only a single CPack component as opposed to all of them. For example, if you only want to install the Development component, run
cmake -DCOMPONENT=Development -P cmake_install.cmake
BUILD_TYPE
Set this variable to change the build type if you are using a multi-config generator. For example, to install with the Debug configuration, run
cmake -DBUILD_TYPE=Debug -P cmake_install.cmake.
DESTDIR
This is an environment variable rather than a CMake variable. It allows you to change the installation prefix on UNIX systems. See DESTDIR for details.
As previous answer tells, the cmake_install.cmake contains the commands generated by install command from your CMakeLists.txt.
You can execute it by cmake -P cmake_install.cmake and it performs the installation of your project even on windows.
https://cmake.org/pipermail/cmake/2007-April/013657.html