cmake ignoring CMAKE_INSTALL_PREFIX inside CMakeLists.txt - cmake

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.

Related

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

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/"
)

Catkin cannot find gtest

Since yesterday none of my packages containing tests build. Catkin complains it cannot find gtest when using catkin_add_gtests(), since GTEST_FOUND is FALSE. You can see this in the error msg below, with the custom output I added to my CMakeLists. Up to yesterday, GTEST_FOUND was TRUE when catkin_add_gtests() was called.
This is the error I always get. In this case I'm trying to build a mockup package on a clean catkin workspace:
Errors << silly_pkg:cmake /home/paco/catkin_ws2/logs/silly_pkg/build.cmake.002.log
Not searching for unused variables given on the command line.
Re-run cmake no build system arguments
-- Using CATKIN_DEVEL_PREFIX: /home/paco/catkin_ws2/devel/.private/silly_pkg
-- Using CMAKE_PREFIX_PATH: /home/paco/catkin_ws2/devel;/opt/ros/kinetic
-- This workspace overlays: /home/paco/catkin_ws2/devel;/opt/ros/kinetic
-- Using PYTHON_EXECUTABLE: /usr/bin/python
-- Using Debian Python package layout
-- Using empy: /usr/bin/empy
-- Using CATKIN_ENABLE_TESTING: ON
-- Call enable_testing()
-- Using CATKIN_TEST_RESULTS_DIR: /home/paco/catkin_ws2/build/silly_pkg/test_results
-- Using Python nosetests: /usr/bin/nosetests-2.7
-- catkin 0.7.11
-- GTEST_FOUND: FALSE
CMake Warning at /opt/ros/kinetic/share/catkin/cmake/test/gtest.cmake:149 (message):
skipping gtest 'test_silly_pkg' in project 'silly_pkg' because gtest was
not found
Call Stack (most recent call first):
/opt/ros/kinetic/share/catkin/cmake/test/gtest.cmake:79 (_catkin_add_executable_with_google_test)
/opt/ros/kinetic/share/catkin/cmake/test/gtest.cmake:28 (_catkin_add_google_test)
CMakeLists.txt:28 (catkin_add_gtest)
CMake Error at /home/paco/catkin_ws2/src/silly_pkg/CMakeLists.txt:33 (target_link_libraries):
Cannot specify link libraries for target "test_silly_pkg" which is not
built by this project.
-- Configuring incomplete, errors occurred!
See also "/home/paco/catkin_ws2/build/silly_pkg/CMakeFiles/CMakeOutput.log".
See also "/home/paco/catkin_ws2/build/silly_pkg/CMakeFiles/CMakeError.log".
cd /home/paco/catkin_ws2/build/silly_pkg; catkin build --get-env silly_pkg | catkin env -si /usr/bin/cmake /home/paco/catkin_ws2/src/silly_pkg --no-warn-unused-cli -DCATKIN_DEVEL_PREFIX=/home/paco/catkin_ws2/devel/.private/silly_pkg -DCMAKE_INSTALL_PREFIX=/home/paco/catkin_ws2/install; cd -
I am using catkin 0.7.11, libgtest-dev 1.7.0 and cmake 3.5.1. I use ROS Kinetic with Ubuntu 16.04. The only thing I did yesterday was reinstalling ROS Kinetic, but the package versions are exactly the same. Did anybody have this problem? Do you have any ideas on what could be happening?
EDIT 3/09/18:
By comparing with a functional catkin+gtest workspace in a different computer, I found out that the main difference is in the results of /opt/ros/kinetic/share/catkin/cmake/test/gtest.cmake. In the functional workspace, line 292 evaluates to TRUE (gtest/gmock is not a target) while in my workspace it evaluates to FALSE. This is because in my workspace running find_package(GMock QUIET) (line 287) sets gmock and gtest as imported targets, which does not happen in the other computer. Why is this different?
Thanks TikO for your help!
Since you wrote that cmake does not find the libraries and that you have reinstalled Kinetic, I assume that you have a freshly installed machine or wiped out gtest libraries by accident.
If you install libgtest-dev, you only get the sources which you need to build and install like this:
sudo apt-get install libgtest-dev
mkdir /tmp/gtest_build && cd /tmp/gtest_build
cmake /usr/src/gtest
make
#copy or symlink libgtest.a and ligtest_main.a to /usr/lib folder
sudo cp *.a /usr/lib
After this routine, you should be able to build again without cmake complaining.
Optional
If you have limited rights on your machine and you are not allowed to install the libraries in that way, just copy them into some home folder like
mkdir ~/lib && cp *.a ~/lib
But be aware of the fact, that you have to call catkin in the following way:
LIBRARY_PATH=~/lib GTEST_ROOT=~/lib catkin_make
LIBRARY_PATH tells the linker where to find the libraries, while GTEST_ROOT gives cmake the location hints for it's checks.
Reference: https://github.com/tik0/gtest_ros_example
SOLUTION FOUND
gmock and gtest were being set to imported target because the suggested manual compilation of libgtest had created a FindGMock.cmake file inside /usr/share/cmake-3.5/Modules. This file was being called by the find(GMock QUIET)
in catkin_add_gtests(), therefore setting the imported target. Just deleting FindGMock.cmake solved the issue.

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

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

CMake putting build files in source directory

I am very new to CMake. A friend wrote a simple CMakeLists.txt for the project I am coding myself. I am using svn and have just checked out an old version on the same machine into a different folder. Now, in the original source directory (where CMakeLists.txt is located) I create the directory 'build', cd into there, and for the time being run the code
cmake -DCMAKE_BUILD_TYPE=Debug ..
This nicely puts all of the files in the build directory
-- Build files have been written to: ~/MixedFEMultigrid/build
Now when I check out to another directory, create another 'build' directory in that one and then run the CMake command I get the following
-- Build files have been written to: ~/oldCode
where oldCode is actually the parent directory. I have no idea why this is happening. Can someone explain this to me? The full CMakeLists.txt file is given below,
cmake_minimum_required (VERSION 2.6)
project (MixedFEMultigrid)
FIND_PACKAGE(LAPACK REQUIRED)
set( SRC_FILES multigrid.c
gridHandling.c
interpolation.c
linApprox.c
params.c
sparseMatrix.c
testing.c
richardsFunctions.c
definitions.c
newtonIteration.c
)
#Adds the executable with all the dependencies
add_executable (multigrid ${SRC_FILES})
#Specifies the libraries to link to the target
TARGET_LINK_LIBRARIES(multigrid ${LAPACK_LIBRARIES} m)
# Update if necessary
set(CMAKE_C_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wno-long-long -pedantic -fstrict-aliasing -std=c99 -O3")
As per the comment by escrafford I am updating to show what I do on the command line.
cd ~
mkdir oldCode
cd oldCode
svn co <repository>
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
The build files are then put into the directory 'oldCode' instead of the 'build' directory. The following, on the other hand, puts the build files into the 'build' directory
cd ~
mkdir MixedFEMultigrid
cd MixedFEMultigrid
svn co <repository>
mkdir build
cd build
cmake -DCMAKE_BUILD_TYPE=Debug ..
That comes from a in-source cmake execution
Remember to remove cmake cache:
$ rm CMakeCache.txt
$ mkdir debug
$ cd debug
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
That has another benefit, given that cmake do not provide a clean target
$ cd ..
$ rm -rf debug
is the equivalent of make clean or more precisely make distclean