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

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

Related

cmake ignoring CMAKE_INSTALL_PREFIX inside CMakeLists.txt

I have a small project with a very simple CMakeLists.txt file. At the bottom of this file, I have the following lines:
set (CMAKE_INSTALL_PREFIX /opt/myprod)
message (STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
install (TARGETS myprod DESTINATION bin)
However, when I run:
sudo make install
I get the following:
[100%] Built target myprod
Install the project...
-- Install configuration: ""
-- Up-to-date: /usr/bin/myprod
-- Up-to-date: /usr/bin/myprod
cmake always puts my executable under /usr/bin when it should be under /opt/myprod/bin.
And, yes, the last line is always repeated. Does anyone know how I can fix this?
Using cmake 3.20.3 on Fedora 34.
Your issue cannot be reproduced with the level of detail you've given:
File CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project(myprod)
add_executable(myprod main.cpp)
set(CMAKE_INSTALL_PREFIX /opt/myprod)
message (STATUS "CMAKE_INSTALL_PREFIX: ${CMAKE_INSTALL_PREFIX}")
install (TARGETS myprod DESTINATION bin)
File main.cpp
int main() { return 0; }
Commands:
$ cmake -S . -B build
...
$ cmake --build build/
$ sudo cmake --build build/ --target install
[sudo] password for alex:
[0/1] Install the project...
-- Install configuration: "Release"
-- Installing: /opt/myprod/bin/myprod
$ sudo rm -rf /opt/myprod/
So as you can see, /opt/myprod survived to the final output.
The install() command is responsible for generating the cmake_install.cmake script in the build directory. As far as I know, the very first one reads CMAKE_INSTALL_PREFIX, so you must have another call to install() in your project that you aren't showing us.
Furthermore, you should not set CMAKE_INSTALL_PREFIX inside the CMakeLists.txt; it is designed to be set externally as a cache variable. Hard-coding it is bad because someone else might wish to install your project to a different location besides /opt. Maybe they're on a different operating system or Linux distribution. Maybe even your whims change. In any case, one shouldn't need to edit a file to change the install prefix.
Since you're using CMake 3.20, I strongly encourage you to move such settings to presets.

What is the proper way to handle `CMAKE_INSTALL_PREFIX` when using the Ninja Multi-Config cmake generator?

With the cmake generator "Ninja Multi-Config" what is the proper way to handle CMAKE_INSTALL_PREFIX. For instance, if you do:
$ cmake -DCMAKE_INSTALL_PREFIX=../install -G "Ninja Multi-Config" ..
$ cmake --build . --config Release --target install
And then afterwards do
$ cmake --build . --config Debug --target install
will the files in ../install be overwritten by the Debug install? What is the normal way to handle the install location in such cases?
By default they will be overwritten- Ie. with multi-config, the configurations' files install to the same locations.
Command-line "manual" approach
If you don't mind having to do this kind of thing manually on the command-line each time you install, you can just use the --prefix parameter for cmake --install <...>.
From the docs for CMAKE_INSTALL_PREFIX:
The CMAKE_INSTALL_PREFIX may be defined when configuring a build tree to set its installation prefix. Or, when using the cmake(1) command-line tool's --install mode, one may specify a different prefix using the --prefix option.
In that sense, CMAKE_INSTALL_PREFIX can be seen as a default value set per-generated buildsystem that can be overridden on the commandline.
So you can do something like cmake --install <build_dir> --config <config> --prefix <install_dir_unique_to_config>.
defaults in CMakeLists.txt approach
See this CMake mailing thread for various workarounds. Summarized here:
You can (with some exceptions- see the docs) use the <CONFIG>_POSTFIX target property to append a postfix to an output name of a target.
set_target_properties(my_target <more targets can be listed here> PROPERTIES
DEBUG_POSTFIX "-debug"
RELEASE_POSTFIX "-release"
# etc.
)
Workaround using install(DESTINATION) parameters:
install(TARGETS ${LIB_NAME}
CONFIGURATIONS DEBUG
EXPORT ${LIB_NAME}Config-d
PUBLIC_HEADER DESTINATION "include/${LIB_NAME}"
LIBRARY DESTINATION "bin/${LIB_NAME}/debug/"
ARCHIVE DESTINATION "lib/${LIB_NAME}/debug"
)
install(TARGETS ${LIB_NAME}
CONFIGURATIONS RELEASE
EXPORT ${LIB_NAME}Config
PUBLIC_HEADER DESTINATION "include/${LIB_NAME}"
LIBRARY DESTINATION "bin/${LIB_NAME}/release/"
ARCHIVE DESTINATION "lib/${LIB_NAME}/release/"
)

cmake, how to specify the output directory

I'm using cmake to generate a VS2017 solution (and projects...), I try to generate everything in a different folder.
I used both the command line and different variables, but no way, it generate in the "source" folder !
Here are some examples of what I tried:
cd source
cmake -B../build ...
cd build
cmake ../source
cmake --build "../build"
cmake -Dxxx=../build
Any idea ? all theses solutions are expected to generate in the build folder !
Once you have performed in-source build (in source directory), it is impossible to build the project out-of-source: every such attempt will modify in-source build.
You need to clear build files (CMakeCache.txt, probably some other ones) in source directory before using out-of-source builds.
I fixed with:
cd VSBuild
cmake ../
instead of
cd VSBuild
cmake ..
Have you tried this? This will put all the build files under "out"
$ cmake -H. -Bout
To execute:
$ cmake --build out

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