Check permissions of the destination directory before installing - c++

I've been searching in the documentation and SO but I haven't found an answer for this issue.
Using cmake, I'm trying to check the permission of the DESTINATION directory before installing some libraries.
Is there some command in cmake to do this? Do I need to make the checks with custom commands?
As an example, this is my code in my CMakeLists.txt:
INSTALL( TARGETS ${LIBRARY_NAME}
DESTINATION lib/plugins/
PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE )
The idea is to check if the user has the required permissions to write in lib/plugins/ before installing the plugins.

You can use install(SCRIPT ...) command flow for execute some CMake script at install stage. For example:
check_script.cmake.in:
EXECUTE_PROCESS(COMMAND test -w #CMAKE_INSTALL_PREFIX#/lib/plugins
RESULT_VARIABLE res)
IF(res)
MESSAGE(FATAL_ERROR "No write permissions on plugins directory")
ENDIF()
CMakeLists.txt:
CONFIGURE_FILE(check_script.cmake.in check_script.cmake #ONLY)
INSTALL(SCRIPT ${CMAKE_CURRENT_BINARY_DIR}/check_script.cmake)
INSTALL( TARGETS ${LIBRARY_NAME} DESTINATION lib/plugins/ ...)
As you can see, it is too many work to check file permissions on install stage. Actually, you rarely need such checks: if installing of file fails, whole installation process stops immediately, and appropriate message is shown to the user.

Related

CMake - Alternate option to FetchContent when the source file already exists locally

I tried building the ORTools github package locally using cmake and it builds without any errors. However the environment which we are planning to ultimately use does not have an outbound network connection. The problem I see is that https://github.com/google/or-tools/blob/v9.4/cmake/dependencies/CMakeLists.txt performs a Git Clone to download the dependencies and add them. Since there is no outbound network access this step fails and I'm unable to build the dependency. To circumvent this we are planning to manually download the dependencies and add them to https://github.com/google/or-tools/blob/v9.4/cmake/dependencies/ folder.
I'm pretty new to CMake and I'm not sure what changes have to be made to accommodate this.
For example, what would the following snippet need to be changed to if I cloned Zlib v1.2.11 repository and added it to https://github.com/google/or-tools/blob/v9.4/cmake/dependencies/?
# ##############################################################################
# ZLIB
# ##############################################################################
if(BUILD_ZLIB)
message(CHECK_START "Fetching ZLIB")
list(APPEND CMAKE_MESSAGE_INDENT " ")
FetchContent_Declare(
zlib
GIT_REPOSITORY "https://github.com/madler/ZLIB.git"
GIT_TAG "v1.2.11"
PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/ZLIB.patch")
FetchContent_MakeAvailable(zlib)
list(POP_BACK CMAKE_MESSAGE_INDENT)
message(CHECK_PASS "fetched")
endif()
Can FetchContent_Declare be used to point to a directory that already contains the source files? What's the alternative?
You could specify SOURCE_DIR parameter for FetchContent_Declare and omit download options:
FetchContent_Declare(
zlib
SOURCE_DIR <path/to/existing/directory>
PATCH_COMMAND git apply --ignore-whitespace "${CMAKE_CURRENT_LIST_DIR}/../../patches/ZLIB.patch"
)
This works in the same way as in ExternalProject_Add command, which options are accepted by FetchContent_Declare.

CMake Install/CPack Problems: Improper Permissions AND Wrong Executables

I'm currently working on a project that is being built using CMake; it is then subsequently packaged up with CPack into an RPM.
The following is packaged into the RPM:
Several Executables
Configuration files
Some more context:
This project is running using OpenMPI, and there are X amount of nodes, depending on user input AND based on the # of nodes the user specifies, X docker containers are spawned.
There are instances of each executable running in tandem on each container
There is an OpenMPI crash and it is difficult to debug without GDB
The executables have been compiled as Release. However, if I want to make debugging possible I have been compiling the CMake project as Debug.
The executables are created as Debug. I have the VERBOSE=1 flag on when making the package and can confirm that -g flag is present, and the executables have debug symbols when loading them into GDB locally
Some lines in the CMakeLists.txt:
# Bunch of compilation lines above
install(TARGETS executable_1 executable_2 executable_3 DESTINATION bin) # Installs the executables into a local bin dir
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_PREFIX /usr)
install(DIRECTORY bin/ DESTINATION /usr/bin)
install(DIRECTORY config/ DESTINATION /etc/PROJECT_NAME FILES_MATCHING PATTERN "*")
# Making of the package
set(CPACK_PACKAGE_NAME "PROJECT_NAME")
set(CPACK_PACKAGE_VERSION 0.1)
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_VENDOR "ME")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_RPM_PACKAGE_AUTOREQ 0)
set(CPACK_RPM_PACKAGE_RELOCATABLE True)
include(CPack)
The problem:
When CPack is doing its thing and creating the RPM, the executables can be packaged into the RPM, but the problem is they have improper permissions; aka when the executables are installed on the Containers, they cannot be executed. I can address this by adding:
# Bunch of compilation lines above
install(TARGETS executable_1 executable_2 executable_3 DESTINATION bin) # Installs the executables into a local bin dir
set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
set(CMAKE_INSTALL_PREFIX /usr)
# Permissions Fix
set(PROGRAM_PERMISSIONS_DEFAULT
OWNER_WRITE OWNER_READ OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
install(DIRECTORY bin/ PERMISSIONS ${PROGRAM_PERMISSIONS_DEFAULT} DESTINATION /usr/bin)
install(DIRECTORY config/ DESTINATION /etc/PROJECT_NAME FILES_MATCHING PATTERN "*")
# Making of the package
set(CPACK_PACKAGE_NAME "PROJECT_NAME")
set(CPACK_PACKAGE_VERSION 0.1)
set(CPACK_PACKAGE_RELEASE 1)
set(CPACK_GENERATOR "RPM")
set(CPACK_PACKAGE_VENDOR "ME")
set(CPACK_PACKAGING_INSTALL_PREFIX ${CMAKE_INSTALL_PREFIX})
set(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME}-${CPACK_PACKAGE_VERSION}-${CPACK_PACKAGE_RELEASE}.${CMAKE_SYSTEM_PROCESSOR}")
set(CPACK_RPM_PACKAGE_AUTOREQ 0)
set(CPACK_RPM_PACKAGE_RELOCATABLE True)
include(CPack)
After adding the permissions, the permission are fixed, but for some reason the executables that are copied do not have debug symbols anymore; my local executables in the bin/ folder are the proper executables which have symbols, but the executables packaged into the RPM DOT NOT have debug symbols. Meaning somewhere along the way, something really odd is happening.
My question is, why? Either way results in a different problem. I'm wondering if there is a way to make sure the permissions are correct AND the proper executables get copied and packaged into the RPM. Any suggestions would be much appreciated.
Thanks!
Do not use absolute install destinations and do not read CMAKE_INSTALL_PREFIX, if you can avoid it. Using absolute install destinations (possibly indirectly through the use of CMAKE_INSTALL_PREFIX) forces you to run the install target with privileges that allow the process to modify those absolute paths. This usually means you have to use sudo to run the install target, even when installing to a directory owned by the standard user.
Since running cpack involves running the install target with the install prefix replaced with a directory inside the build directory (basically cmake --install ... --prefix ...), you can avoid the need for root privileges, if you use relative install locations.
My preferred the install logic here would be:
# allow the installation into any directory below the file system root
set(CMAKE_INSTALL_PREFIX /)
# set the default executable install location, see https://cmake.org/cmake/help/latest/command/install.html#installing-targets
# Note: could be set "globally", i.e. from the toplevel CMakeLists.txt allowing installation of individual targets
set(CMAKE_INSTALL_BINDIR usr/bin)
install(TARGETS executable_1 executable_2 executable_3 RUNTIME)
#preferrably replace this with install(FILES ... TYPE BIN) to install individual files
set(PROGRAM_PERMISSIONS_DEFAULT
OWNER_WRITE OWNER_READ OWNER_EXECUTE
GROUP_READ GROUP_EXECUTE
WORLD_READ WORLD_EXECUTE)
install(DIRECTORY bin/ PERMISSIONS ${PROGRAM_PERMISSIONS_DEFAULT} DESTINATION usr/bin)
# preferrably replace this with install(FILES ... TYPE SYSCONFIG)
install(DIRECTORY config/ DESTINATION etc/PROJECT_NAME FILES_MATCHING PATTERN "*")
# Making of the package
...
I assume in your scenario somewhere along the way you got the file permissions incorrect the cmake build / install / package process.

I'm stuck trying to clone a git repository which requires cmake files and dependancies

I want to download and use this repo:(https://github.com/rstebbing/subdivision-regression)
I have downloaded this repo, it's dependencies and their dependencies. Once downloaded, I've changed the CMakeList files (as instructed) with the new locations of the packages but when I try and 'sudo make install' it can't find the packages and won't install.
I am on a linux machine.
I downloaded these dependencies: ceres, common, gflags, rapidjson and believed they are install correctly.
When installing the subdivision I follow the git instructions and change the paths and ran cmake fine. When I use 'sudo make install' i get the error:
In file included from subdivision/doosabin/doosabin_pyx.h:12:0,
from subdivision/doosabin/doosabin_.cpp:615:
cpp/doosabin/include/doosabin.h:20:10: fatal error: Eigen/Dense: No such file or directory
#include "Eigen/Dense"
^~~~~~~~~~~~~
Even though I have specified the path to this file in the cpp/doosabin/CMakeLists and site.cfg:
site.cfg:
[Include]
EIGEN_INCLUDE ="/home/hert5584/RStebbing/eigen-git-mirror/"
COMMON_CPP_INCLUDE ="/home/hert5584/RStebbing/common/cpp/"ccd
CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8.0)
PROJECT(DOO-SABIN)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin)
SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin)
SET ( CMAKE_CXX_FLAGS "-std=c++11" )
MACRO(EXPECT_FILES VARIABLE)
FOREACH(EXPECTED_FILE ${ARGN})
IF (NOT EXISTS ${${VARIABLE}}/${EXPECTED_FILE})
MESSAGE(FATAL_ERROR
"Caller defined ${VARIABLE}: ${${VARIABLE}} does not contain "
"${EXPECTED_FILE}.")
ENDIF (NOT EXISTS ${${VARIABLE}}/${EXPECTED_FILE})
ENDFOREACH()
ENDMACRO(EXPECT_FILES)
SET(EIGEN_INCLUDE_DIR "/home/hert5584/RStebbing/eigen-git-mirror/")
EXPECT_FILES(EIGEN_INCLUDE_DIR Eigen/Dense)
INCLUDE_DIRECTORIES(${EIGEN_INCLUDE_DIR})
I also tested this without changing subdivision CMake files and only changing subdivions-regression and got a similar error about not finding functions.
Any help on how to install this properly, or any ideas about what I am doing wrong would be amazing!
Thank you
I made a mistake in setting up CMake. I was changing the paths manually inside the CMakeList files whereas it should be done with cmake:
cmake -DCOMMON_CPP_INCLUDE_DIR=/home/RStebbing/common/cpp -DEIGEN_INCLUDE_DIR=/home/RStebbing/eigen-git-mirror ../subdivision/cpp/doosabin/
Now working

Why should include(CPack) go after the per project package configuration?

I'm experimenting with CPack module of CMake and got somewhat confusing behavior. I have CpackMylib.cmake that is included into a root CMakeLists.txt. It looks as follows:
include(CPack) #included on top
install (TARGETS mylib
LIBRARY
DESTINATION /usr/lib
COMPONENT mylib-all
)
install (DIRECTORY include/
DESTINATION /usr/include/mylib
COMPONENT mylib-all)
set(CPACK_PACKAGE_NAME "mylib")
set(CPACK_GENERATOR "DEB")
And when running make package it fails to create the package with the following trace:
Run CPack packaging tool...
CPack: Create package using STGZ
CPack: Install projects
CPack: - Run preinstall target for: mylib
CPack: - Install project: mylib
CMake Error at /home/krjoff/mylib/cmake_install.cmake:55 (file):
file INSTALL cannot copy file "/home/krjoff/mylib/libmylib.so" to
"/usr/lib/mylib.so".
CMake Error at /home/krjoff/mylib/cmake_install.cmake:73 (file):
file INSTALL cannot set permissions on "/usr/include/mylib"
CPack Error: Error when generating package: mylib
Makefile:129: recipe for target 'package' failed
make: *** [package] Error 1
It looks like it simply ignores all variables I put after the include(CPack) and trying to build some STGZ package and install it immediately. But if I put the include(CPack) in the end of the CpackMylib.cmake after all configuration's been made it works perfectly fine.
Can someone explain why is it necessary to put the include(CPack) after all the configuration settings?
This is how is supposed to work CPack. When you include it in your CMakeLists.txt, it reads all the variables listed in its documentation like CPACK_GENERATOR or CPACK_PACKAGE_NAME and creates the packagetarget you then call with make package.
If you include it before setting those variables, their value will be ignored.
Before including this CPack module in your CMakeLists.txt file, there are a variety of variables that can be set to customize the resulting installers. The most commonly-used variables are:
CPACK_PACKAGE_NAME
The name of the package (or application). If not specified, it defaults to the project name.
CPACK_PACKAGE_VENDOR
The name of the package vendor. (e.g., “Kitware”). The default is “Humanity”.
CPACK_PACKAGE_DIRECTORY
The directory in which CPack is doing its packaging. If it is not set then this will default (internally) to the build dir. This variable may be defined in a CPack config file or from the cpack command line option -B. If set, the command line option overrides the value found in the config file.
...
Source : CPack --- CMake

How to get the output path of a target defined by ExternalProject?

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