Why no 'install' rules generated by cmake install()? - cmake

I'm running cmake v 3.18.0 on ubuntu 20.04
My install() command doesn't generate any install rules within the generated Makfile. CMakeLists.txt looks like this:
# Static library
add_library(mbaux
STATIC
mb_cheb.c mb_delaun.c mb_intersectgrid.c mb_readwritegrd.c
mb_surface.c mb_track.c mb_truecont.c mb_zgrid.c)
target_include_directories(mbaux
PUBLIC
.
${X11_INCLUDE_DIR}
${GMT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/src/mbio)
# Static library
add_library(mbxgr
STATIC
mb_xgraphics.c)
target_include_directories(mbxgr
PUBLIC
.
${X11_INCLUDE_DIR}
${GMT_INCLUDE_DIR}
${GDAL_INCLUDE_DIR}
${CMAKE_SOURCE_DIR}/src/mbio)
# Install
message("set up mbaux install targets")
install(TARGETS mbaux DESTINATION cmake-example/lib)
install(TARGETS mbxgr DESTINATION cmake-example/lib)
Yet when I look in the generated Makefile I see my message "set up mbaux install targets" but there is no reference to the destination 'cmake-example/lib', and no specific 'install' target that I can see.
Thanks!

Just because you seem new to CMake, a couple words of advice re: install rules...
First, avoid hard-coding destinations. You always want to include(GNUInstallDirs) (even on Windows). This module defines a number of standard cache variables for configuring installation destinations.
See the documentation for the full list, but the following are the most relevant here:
CMAKE_INSTALL_INCLUDEDIR - the path below the installation prefix where the include structure should exist. Typically, and by default, include.
CMAKE_INSTALL_LIBDIR - the path below the installation prefix where libraries should be stored. Typically, and by default, lib. Often overridden on multiarch systems to include the target architecture, such as lib/aarch64-linux-gnu.
CMAKE_INSTALL_DATADIR - the path below the installation prefix where package-specific data should be stored. Typically, and by default, share.
In CMake 3.14+ these are used as the default destinations for install(TARGETS). This should be all you need:
include(GNUInstallDirs)
install(
TARGETS mbaux mbxgr ...
EXPORT mb-targets
RUNTIME COMPONENT mb-runtime
LIBRARY COMPONENT mb-runtime
NAMELINK_COMPONENT mb-development
ARCHIVE COMPONENT mb-development
INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}"
)
Don't forget to guard your include paths with $<BUILD_INTERFACE:...> in the main build (to allow INCLUDES DESTINATION above to overwrite them):
target_include_directories(
mbaux
PUBLIC
"$<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/src/mbio>"
)
You almost certainly do not want to use the ${<PACKAGE_NAME>_INCLUDE_DIRS} variables here, either. Instead use target_link_libraries with the relevant imported targets; this will set up include paths automatically.
target_link_libraries(mbaux PRIVATE X11::X11) # likely similar for GMT/GDAL
When you are required to specify a destination, you generally want to use the TYPE argument instead of DESTINATION. If you must use DESTINATION, use a project-specific cache variable as its argument; for example:
# will install headers to ${CMAKE_INSTALL_INCLUDEDIR}/**/*.h
# without mbio prefix, remove the trailing / to include mbio
# in the path
install(DIRECTORY "${PROJECT_SOURCE_DIR}/src/mbio/"
TYPE INCLUDE
COMPONENT mb-development
FILES_MATCHING PATTERN "*.h")
set(MB_INSTALL_CMAKEDIR "${CMAKE_INSTALL_DATADIR}/cmake/mb"
CACHE STRING "Installation directory for CMake package files")
install(EXPORT mb-targets
DESTINATION "${MB_INSTALL_CMAKEDIR}"
NAMESPACE mb::
FILE mb-targets.cmake)
Finally, you should never read or set CMAKE_INSTALL_PREFIX from within the CMakeLists.txt file. Instead you should rely on relative paths (as done above) or the $<INSTALL_PREFIX> generator expression (in exceptional circumstances) to avoid making your installation script non-relocatable.

As pointed out by Stephen Newell, cmake spreads rules and logic across multiple files - and I now see where they are (in this case, cmake_install.cmake)

Related

How to install object files using CMake

There is an object library in CMake:
add_library( librevolta_runtime_x86 OBJECT
crt0.S
crti.S
crtn.S
)
install( TARGETS librevolta_runtime_x86 EXPORT revolta
OBJECTS DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
which is linked to my static library:
add_library( revolta STATIC )
target_link_libraries( revolta
PUBLIC
librevolta-runtime-x86
)
# Install all the revolta headers into include directory and copy the built library
install( TARGETS revolta EXPORT revolta
FILE_SET HEADERS DESTINATION "${CMAKE_INSTALL_PREFIX}/include"
ARCHIVE DESTINATION "${CMAKE_INSTALL_PREFIX}/lib"
)
# Export librevolta targets
install( EXPORT revolta DESTINATION "${CMAKE_INSTALL_PREFIX}/lib/cmake/revolta" )
The problem is once the library revota is build and installed, the result is:
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crt0.S.obj
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crti.S.obj
<CMAKE_INSTALL_PREFIX>/lib/objects-Debug/librevolta-runtime-x86/crtn.S.obj
But I need to make up the installation like:
<CMAKE_INSTALL_PREFIX>/lib/crt0.o
<CMAKE_INSTALL_PREFIX>/lib/crti.o
<CMAKE_INSTALL_PREFIX>/lib/crtn.o
Note: <CMAKE_INSTALL_PREFIX> is just the placeholder for the path being specified in the CMake variable.
So I need to:
Rename the object files from *.S.obj to simple *.o
Adapt the path so that all the object files are installed into pure <CMAKE_INSTALL_PREFIX>/lib/ directory
Due to target_link_libraries(...) dependency, make up the
librevolta_runtime_x86 the member of revolta export set
Many thanks to anyone willing to help me. Martin
EDIT: The simple macro as promised
macro(install_objects OBJFILE_LIST)
foreach(OBJFILE ${${OBJFILE_LIST}})
# Available since CMake 3.20 you can use get_filename_component
# To get any specific information you can wish for about a file
# Before 3.20 you would have to use string(REGEX MATCH ...)
# get_filename_component(OBJ_DIR ${OBJFILE} DIRECTORY) <-- path
# get_filename_component(OBJ_EXT ${OBJFILE} EXT) <-- extension
get_filename_component(OBJ_NAME ${OBJFILE} NAME)
# You can uncomment the message to see what you can get
# MESSAGE(STATUS "OBJ_DIR: ${OBJ_DIR} OBJ_NAME: ${OBJ_NAME} OBJ_EXT: ${OBJ_EXT}")
# This is just an example, but you can also use ${OBJ_DIR} for example to specify a better path
install(FILES ${OBJFILE} DESTINATION [wherever_you_want_them] RENAME "${OBJ_NAME}.o")
endforeach(OBJFILE)
endmacro(install_objects)
#example usage
set(MY_OBJECTS
"test.s.obj"
"somepath/test2.S.obj"
"whatever/whatever/test3.s.OBJ"
)
install_objects(MY_OBJECTS)
The above MY_OBJECTS would give you the following variables:
-- OBJ_DIR: OBJ_NAME: test.s.obj OBJ_EXT: .s.obj
-- OBJ_DIR: somepath OBJ_NAME: test2.S.obj OBJ_EXT: .S.obj
-- OBJ_DIR: whatever/whatever OBJ_NAME: test3.s.OBJ OBJ_EXT: .s.OBJ
As you can see it is pretty robust and keeps your CMakeLists.txt clean. Just make sure to create your OBJECT_LIST using pre-defined variables such as ${CMAKE_BINARY_DIR}
This will simplify your object files installs. IMO it also answers both ad1) ad2)
Ad3) I'm not sure what is your issue here or what exactly is happening, as the question itself is focused on installing objects.
Is there any specific reason why you wish to install the intermediate object files? This is an important question because a static library when linked will be unpacked and optimized - take a look at this question/answer.
The OBJECT libraries are a useful thing in CMake when you know that some parts of your codebase would be compiled multiple times (due to the fact that it uses the same code). It makes sense to couple these parts into a reusable object library.
All of this aside: What you are looking for is
INSTALL(FILES file-name.abc DESTINATION dest-folder RENAME file-name.xyz)

CMake target_sources and install

I am having a hard time figuring out how to install PUBLIC headers specified in target_sources().
It appears that target_sources() is somewhat of a mystery for anything other than adding private sources to an executable.
After reading a lot of material, where especially this blog entry was helpful, I managed to understand & bypass the issue with target_sources() and PUBLIC files. A CMakeLists.txt in one of the many subdirectories of my C++ library project looks like this:
target_sources(mylib
PRIVATE
foo.cpp
PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/foo.hpp>
$<INSTALL_INTERFACE:foo.hpp>
)
This allows me to successfully build the library. However, upon installation, the header file(s) listed in the PUBLIC section of target_sources() are never installed.
My installation looks like this:
install(
TARGETS
mylib
EXPORT mylib-targets
LIBRARY
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT lib
ARCHIVE
DESTINATION ${CMAKE_INSTALL_LIBDIR}
COMPONENT lib
RUNTIME
DESTINATION ${CMAKE_INSTALL_BINDIR}
COMPONENT bin
INCLUDES
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
However, this doesn't install any of the headers.
This stackoverflow answer mentions the use of PUBLIC_HEADER but reading the documentation doesn't give me the feeling that that is relevant in my case.
Question: What is the proper way of installing PUBLIC or INTERFACE headers using install()?
Background: My goal is to have a separate CMakeLists.txt in every subdirectory of my source tree. Each of these lists is supposed to use target_sources() to add PRIVATE and PUBLIC files to the build. All PUBLIC (and INTERFACE) sources should be installed during installation.
PUBLIC section of target_sources command has nothing common with public headers installed with PUBLIC_HEADER option of install command.
The headers you want to treat as public for your library should be listed in PUBLIC_HEADER property for the target.
Documentation for install(TARGETS) has a nice example of setting and installing private headers. In your case this would be:
# This defines `foo.hpp` located in the current source directory as a 'public header'.
set_target_properties(mylib PROPERTIES PUBLIC_HEADER foo.hpp)
# This install public headers among with the library.
install(
TARGETS
mylib
PUBLIC_HEADER
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
# ... other options
)
See also that related question: How to configure CMakeLists.txt to install public headers of a shared library?.
Note also, that INCLUDES and PUBLIC_HEADERS clauses for install(TARGETS) command are different things:
INCLUDES specifies include directory for the installed library
PUBLIC_HEADERS specifies directory where all target's public headers will be copied upon installation.
E.g. if you want your header file to be used via
#include <mylib/foo.h>
then you need provide following options for install() command:
PUBLIC_HEADER
# The header will be places at ${CMAKE_INSTALL_INCLUDEDIR}/mylib/foo.h
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/mylib
INCLUDES
# This directory will be used as include directory.
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}
So <include-directory> joined (via /) with the relative path in #include<> directive will give absolute path to the header file.

Should I modify CMAKE_MODULE_PATH in PackageConfig.cmake

I am writing a MyPackageConfig file for my project with exported targets so that other projects can easily find MyPackage and it's dependencies. It looks like this:
include(CMakeFindDependencyMacro)
find_dependency(LIB1_WITHOUT_CMAKE_CONFIG)
find_dependency(LIB2_WITH_CMAKE_CONFIG)
include (Some/Install/Dir/MyPackageTargets.cmake)
I am wondering if it is smart to add the following lines to the MyPackageConfig.cmake before the find_dependency calls
# Find target dependencies
# Allows packages linking with MyPackage to use the find modules that
# MyPackage used to find it's dependencies. Since this path is appended to
# the existing module path, the calling package's module path will take
# precedence
list(APPEND CMAKE_MODULE_PATH #CMAKE_INSTALL_PREFIX#/lib/cmake/MyPackage/modules)
# Allows packages linking with MyPackage to find MyPacakge's dependencies if
# they don't already have them. Since this path (or these paths) are
# appended to the existing prefix path, the calling package's prefix
# path will take precedence
list(APPEND CMAKE_PREFIX_PATH #CMAKE_PREFIX_PATH#)
find_dependency(LIB1_WITHOUT_CMAKE_CONFIG)
find_dependency(LIB2_WITH_CMAKE_CONFIG)
Good idea? No?
Longer explanation of my rationale:
How does YourPackage that uses MyPackage find LIB1?
(i). You could write your own FindLIB1.cmake but that's duplication of effort
(ii). I could install my FindLIB1.cmake alongside my MyPackageConfig.cmake in a Modules dir. But you will have to include this path in your module path.
My suggestion: Add a line before find_dependency(LIB1_WITHOUT_CMAKE_CONFIG) modifying the module path like so:
list(APPEND CMAKE_MODULE_PATH #CMAKE_INSTALL_PREFIX#/lib/cmake/mstk/modules)
This will ensure that if you have a FindLIB1.cmake, it will be used but if you don't mine will be found and used.
How do you know where the LIB1 and LIB2 reside (including LIB2's Config file)?
By adding the line
list(APPEND CMAKE_PREFIX_PATH #CMAKE_PREFIX_PATH#)
I tell your package where I searched and found my dependencies (but only if you didn't already have them in the CMAKE_PREFIX_PATH you specified)
Yes, you may change variables like CMAKE_MODULE_PATH or CMAKE_PREFIX_PATH for the purpose of your config script.
Any "good" project should be prepared to prepending/appending CMAKE_PREFIX_PATH, because this variable could normally be set by a user (when call cmake). As for CMAKE_MODULE_PATH, module's names in different directories are rarely conflicted.
Some hints:
You may restore the variables at the end of your script. Such way you won't affect the calling code when changing the variables:
# Store old value of the variable
set(old_CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
# Change variable, use it, ...
# ...
# Restore the variable at the end
set(CMAKE_MODULE_PATH ${old_CMAKE_MODULE_PATH})
Note, however, that find_dependency returns from the script if an (inner) package not found, so restoring the variable won't trigger in that case. But usually find_package() is called with REQUIRED keyword, so failing to find the inner package would be fatal.
Instead of changing CMAKE_PREFIX_PATH you may set other variables which affect only specific find script and doesn't affect others. E.g many find scripts use XXX_ROOT variables for hint about location.
For find config script itself, you may use PATHS or HINTS options of find_package():
# Was:
#- list(APPEND CMAKE_PREFIX_PATH #CMAKE_PREFIX_PATH#)
#- find_dependency(LIB2_WITH_CMAKE_CONFIG)
# Replace with:
find_dependency(LIB2_WITH_CMAKE_CONFIG
PATHS #CMAKE_PREFIX_PATH# # Where to find config script
NO_DEFAULT_PATH # Do not search other places
)

set PKG_CONFIG_PATH in cmake

I have built opencv locally and installed it to a local directory (not the system default ). opencv.pc is present under a folder pkgconfig in this local folder. How can I find this opencv.pc from cmake, because I want to link and include opencv files from my program.
pkg_search_module(<PREFIX> [REQUIRED] [QUIET] <MODULE> [<MODULE>]*)
does not have any parameter in which I can force the command to use a specific path (similar to HINTS with find_package()) and not the system default.
So basically .cmake works:
find_package(OpenCV REQUIRED HINTS "my/path/to/share/OpenCVConfig.cmake")
but I would like to use a opencv.pc located under my/path/to/pkgconfig/opencv.pc.
After doing some research and a hint from the #OlivierM, I found the answer.
Here are the steps:
Method I :
CMAKE_PREFIX_PATH can be set to find the .pc files
set(CMAKE_PREFIX_PATH "${CMAKE_SOURCE_DIR}/libs/opencv-install")
Method II
A second method is to use the PKG_CONFIG_PATH, which is a system environment variable to look for .pc files.
set(ENV{PKG_CONFIG_PATH} "${CMAKE_SOURCE_DIR}/libs/opencv-install/lib/pkgconfig")
Irrespective of which method you use,
For old (traditional) CMake:
find_package(PkgConfig REQUIRED)
pkg_search_module(PKG_OPENCV REQUIRED opencv) # this looks for opencv.pc file
Please note that the PKG_OPENCV variable can be named anything. Whatever it is is named, its used as a prefix. For example if you name ABCD, then include directories will be ABCD_INCLUDE_DIRS
The variable PKG_OPENCV_INCLUDE_DIRS and PKG_OPENCV_LIBRARIES contains the header files (compile stage) and libraries (link stage) respectively.
One very important thing I noticed was that the variable PKG_OPENCV_LIBRARIES just provides the libraries and not the library path during the link stage. In order to use the library path as well in one command, one has to use
PKG_OPENCV_LDFLAGS
This variable contains the library path as well as all the libraries listed in the package config file.
for examaple:
include_directories(${PKG_OPENCV_INCLUDE_DIRS})
target_link_libraries (FINAL_BINARY ${PKG_OPENCV_LDFLAGS})
For modern CMake:
In modern CMake we don't want variables, we want targets.
find_package(PkgConfig REQUIRED)
# this looks for opencv.pc file and creates a new target
# IMPORTED_TARGET requires CMake >= 3.6.3
pkg_search_module(PKG_OPENCV REQUIRED IMPORTED_TARGET opencv)
All variables will still be created for backwards compatibility, but IMPORTED_TARGET will create a target you can use in your project which will automatically propagate all of its build and usage requirements:
target_link_libraries(my_proj PRIVATE PkgConfig::PKG_OPENCV)
You could set PKG_CONFIG_PATH with the CMake line:
set(ENV{PKG_CONFIG_PATH} "$ENV{PKG_CONFIG_PATH}:/my/path/to/pkgconfig")
I did this workaround in this file
Interestingly, it seems CMake 3.1 extends PKG_CONFIG_PATH with some CMake variable see: https://cmake.org/gitweb?p=cmake.git;a=commitdiff;h=3df51470
I would propose you to call cmake with custom PKG_CONFIG_PATH variable, like below:
PKG_CONFIG_PATH=$PKG_CONFIG_PATH:my/path/to/pkgconfig cmake <some args>
Or can make PKG_CONFIG_PATH update to be permanent for whole bash session:
$ export PKG_CONFIG_PATH=$PKG_CONFIG_PATH:my/path/to/pkgconfig
$ cmake <some args>

cmake: install header order and dependencies on target

I've a set of libraries and executables all with their own CMakeLists.txt. All libraries are building their targets to the same path (for example ../build/bin and ../build/lib)... as well as exporting their header files (../build/inc).
Now I wish to build one build system and let CMake figure out the dependencies (using add_subdirectory and global build settings).
The problem is: all libraries do export their headers to build/inc after they are build (when make install in invoked). When I do a whole system build make install is not invoked until after the end (and everything has build). So for example executable progfoo with target_link_libraries( progfoo onelib ) will fail, because CMake figures out the dependency to onelib (which builds fine), but progfoo fails because it looks for headers in build/inc... which were not exported yet. The same thing in general applies to many libraries.
What is the correct CMake-Way to handle these cases? Thank you!
Install is the final step, the one that should be visible to the user. So when you export binaries and headers you should already have binaries built against headers in their original locations.
From CMake point of view you have only 1 target at a time. For example you can build a Web Server and using as dependencies libcurl and boost::asio. It is very possible (and good) to add dependencies to current target using add_subdirectory, but when you have to add include directories you have to do that on a per dependency basis, it would be convenient in example if each of the dependencies provides already a variable with current absolute path to includes.
In example see this dummy libcurl's CMakeLists.txt that set path to absolute include directory
get_filename_component(_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
// export into parent scope libcurl header location
set (LIBCURL_INCLUDES ${_DIR}/include PARENT_SCOPE)
Then you can use it from Web Server's CMakeLists.txt for building and later use the same path again to extract files for installing them where required
add_subdirectory(PATH_TO_LIBCURL_CMAKELISTS)
# add include directories for building web server
include_directories( ${LIBCURL_INCLUDES})
#I assume you are installing headers because final user will need them,
#in case you needed them just for building you are already done here
#without even installing them
#gather headers list
file(GLOB libCurlHeadersList
"${LIBCURL_INCLUDES}/*.h"
"${LIBCURL_INCLUDES}/*.hpp"
)
#install header list
install( FILES
${libCurlHeadersList}
DESTINATION ../build/inc/libcurl
)