CMake: How to create alias for installing different targets? - cmake

Suppose that I've got the following libraries:
add_library(myLib_static STATIC ${SRC_FILES})
add_library(myLib SHARED ${SRC_FILES})
# installing header files
install(FILES ${H_FILES} DESTINATION ${INSTDIRHEADER})
# installing binaries
install(TARGETS myLib_static
DESTINATION ${INSTDIRBIN})
install(TARGETS myLib
DESTINATION ${INSTDIRBIN})
If I execute the following command, both shared and static libraries will be installed:
make install
How can I have separate install commands for each of them? Something like this:
make install-static
make install-shared
Update:
Header files should also be installed when needed:
install(FILES ${H_FILES} DESTINATION ${INSTDIRHEADER})

Put them each in a different component and set up custom targets for the installations.
add_library(foo_static STATIC foo.cpp)
add_library(foo SHARED foo.cpp)
install(TARGETS foo_static
DESTINATION bin
COMPONENT static)
install(TARGETS foo
DESTINATION bin
COMPONENT shared)
add_custom_target(foo-install
DEPENDS foo
COMMAND
"${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=shared
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
)
add_custom_target(foo_static-install
DEPENDS foo_static
COMMAND
"${CMAKE_COMMAND}" -DCMAKE_INSTALL_COMPONENT=static
-P "${CMAKE_BINARY_DIR}/cmake_install.cmake"
)
install(FILES foo.h DESTINATION include COMPONENT static)
install(FILES foo.h DESTINATION include COMPONENT shared)
Then invoke the custom targets.
stephen#hal:~/dev/src/playground/cmake/build{master}$ cmake .. -DCMAKE_INSTALL_PREFIX=prefix
-- The C compiler identification is GNU 4.8.1
-- The CXX compiler identification is GNU 4.8.1
-- Check for working C compiler: /usr/lib/icecc/bin/cc
-- Check for working C compiler: /usr/lib/icecc/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working CXX compiler: /usr/lib/icecc/bin/c++
-- Check for working CXX compiler: /usr/lib/icecc/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/stephen/dev/src/playground/cmake/build
stephen#hal:~/dev/src/playground/cmake/build{master}$ make foo_static-install
makeobj[0]: Entering directory `/home/stephen/dev/src/playground/cmake/build'
Scanning dependencies of target foo_static
[100%] Building CXX object CMakeFiles/foo_static.dir/foo.cpp.o
Linking CXX static library libfoo_static.a
[100%] Built target foo_static
Scanning dependencies of target foo_static-install
-- Install configuration: ""
-- Installing: /home/stephen/dev/src/playground/cmake/build/prefix/bin/libfoo_static.a
-- Installing: /home/stephen/dev/src/playground/cmake/build/prefix/include/foo.h
[100%] Built target foo_static-install
makeobj[0]: Leaving directory `/home/stephen/dev/src/playground/cmake/build'
stephen#hal:~/dev/src/playground/cmake/build{master}$ make foo-install
makeobj[0]: Entering directory `/home/stephen/dev/src/playground/cmake/build'
Scanning dependencies of target foo
[100%] Building CXX object CMakeFiles/foo.dir/foo.cpp.o
Linking CXX shared library libfoo.so
[100%] Built target foo
Scanning dependencies of target foo-install
-- Install configuration: ""
-- Installing: /home/stephen/dev/src/playground/cmake/build/prefix/bin/libfoo.so
-- Up-to-date: /home/stephen/dev/src/playground/cmake/build/prefix/include/foo_p.h
[100%] Built target foo-install
makeobj[0]: Leaving directory `/home/stephen/dev/src/playground/cmake/build'
Note that components are used by cpack to allow the user installing a package to decide which components to install. So, for a library, the headers might be part of the Development component. In this case we install the header with both the shared and static component. It might make sense to additionally install it with a Development component if cpack is to be used in that way.

Related

cmake cannot find an existing directory on mingw64 (msys2)

I am trying to compile a project under MSYS2 and CLANG64 environment.
I have previously compiled dependencies in /usr/local.
$ ls /usr/local/include
boost compat-5.3.c cryptopp lauxlib.h libmongoc-1.0 lua.hpp luajit.h mongocxx yaml-cpp
bsoncxx compat-5.3.h gtest libbson-1.0 lua.h luaconf.h lualib.h tsl
$ ls /usr/local/lib
cmake libboost_filesystem-mt-s-x64.a libbson-static-1.0.a libmongoc-1.0.dll.a
libboost_atomic-mt-s-x64.a libboost_program_options-mt-s-x64.a libbsoncxx-static.a libmongoc-static-1.0.a
libboost_atomic-mt-x64.a libboost_regex-mt-s-x64.a libcryptopp.a libmongocxx-static.a
libboost_chrono-mt-s-x64.a libboost_system-mt-s-x64.a libgtest.a libyaml-cpp.a
libboost_container-mt-s-x64.a libboost_thread-mt-s-x64.a libgtest_main.a pkgconfig
libboost_context-mt-s-x64.a libbson-1.0.dll.a liblua-compat.a
But when I create the project, I explicitly set the location of binaries with interface libraries as I don't want to rely on the find mechanism that has hurt me badly in the past - linking to unintended, old system libraries.
project(test)
cmake_minimum_required( VERSION 3.0 )
add_library( cryptopp STATIC IMPORTED GLOBAL )
set_target_properties( cryptopp PROPERTIES
IMPORTED_LOCATION "/usr/local/lib/libcryptopp.a"
INTERFACE_INCLUDE_DIRECTORIES "/usr/local/include"
INTERFACE_COMPILE_DEFINITIONS "HAVE_CRYPTOPP"
)
add_executable( test test.cpp )
target_link_libraries( test cryptopp )
This works perfect under all Linux distros - Redhat, Ubuntu, etc but fails in MSYS2.
However when I run cmake, I get an error stating that /usr/local/include does not exist.
$ cmake ..
-- Building for: Ninja
-- The C compiler identification is Clang 14.0.4
-- The CXX compiler identification is Clang 14.0.4
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: G:/msys64/clang64/bin/cc.exe - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: G:/msys64/clang64/bin/c++.exe - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
CMake Error in CMakeLists.txt:
Imported target "cryptopp" includes non-existent path
"/usr/local/include"
in its INTERFACE_INCLUDE_DIRECTORIES. Possible reasons include:
* The path was deleted, renamed, or moved to another location.
* An install or uninstall procedure did not complete successfully.
* The installation package was faulty and references files it does not
provide.
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
I just cannot figure out why this is happening. Any clues?
Maybe it's a Windows path issue. Try replacing /usr/local with the output of cygpath -m /usr/local.

Setting RPATH of Static Library in Executable

I have the following setup for my CMake project:
add_executable(exeA ${SOURCES})
target_link_libraries(exeA PRIVATE libB)
libB is a static library built separately in a different project and depends on shared libraries libC OR libD which provide equivalent API's but implemented differently and have different performance profiles.
In exeA's build I want to be able to link against either of libC or libD depending on a condition.
From reading RPATH handling, it seems I could use RPATH related CMake properties, however its unclear if I can use these properties to set the RPATH of an existing static library
Unfortunately there is no easy and fast way to change the RPATH of an
existing executable or shared library.
Is something like this possible to do in CMake?
Appreciate any recommendations to handle this use case.
Create a STATIC IMPORTED library for libB and SHARED IMPORTED libraries for libC and libD. Put the relevant one in the INTERFACE_LINK_LIBRARIES property of libB. CMake will set the RPATH correctly when exeA is built.
cmake_minimum_required(VERSION 3.23)
project(test)
# --------------------------------------------------
# The following code should probably be factored out
# into a proper Find module.
find_library(LIBB_LIBRARY NAMES B REQUIRED)
find_library(LIBC_LIBRARY NAMES C REQUIRED)
find_library(LIBD_LIBRARY NAMES D REQUIRED)
add_library(third-party::libC SHARED IMPORTED)
set_target_properties(
third-party::libC
PROPERTIES
IMPORTED_LOCATION "${LIBC_LIBRARY}"
)
add_library(third-party::libD SHARED IMPORTED)
set_target_properties(
third-party::libD
PROPERTIES
IMPORTED_LOCATION "${LIBD_LIBRARY}"
)
add_library(third-party::libB STATIC IMPORTED)
set_target_properties(
third-party::libB
PROPERTIES
IMPORTED_LOCATION "${LIBB_LIBRARY}"
)
option(LIBB_USES_LIBC "dummy option for demo" ON)
if (LIBB_USES_LIBC)
target_link_libraries(third-party::libB INTERFACE third-party::libC)
else ()
target_link_libraries(third-party::libB INTERFACE third-party::libD)
endif ()
# --------------------------------------------------
# --------------------------------------------------
# Project code
add_executable(exeA main.cpp)
target_link_libraries(exeA PRIVATE third-party::libB)
You can test this out with dummy files like so:
$ ls
CMakeLists.txt
$ mkdir -p prefix{1,2}/lib
$ touch prefix1/lib/lib{C,D}.so prefix2/lib/libB.a main.cpp
$ cmake -G Ninja -S . -B build "-DCMAKE_PREFIX_PATH=$PWD/prefix1;$PWD/prefix2"
-- The C compiler identification is GNU 9.4.0
-- The CXX compiler identification is GNU 9.4.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build
$ cmake --build build -- -nv # dry run (n), verbose (v)
[1/2] /usr/bin/c++ -MD -MT CMakeFiles/exeA.dir/main.cpp.o -MF CMakeFiles/exeA.dir/main.cpp.o.d -o CMakeFiles/exeA.dir/main.cpp.o -c /home/alex/test/main.cpp
[2/2] : && /usr/bin/c++ CMakeFiles/exeA.dir/main.cpp.o -o exeA -Wl,-rpath,/home/alex/test/prefix1/lib ../prefix2/lib/libB.a ../prefix1/lib/libC.so && :
As you can see, the RPATH is set correctly to the directory containing libC.so.

qt creator kit can not configure my projects by use of cmake file [duplicate]

cmake build failed with
CMAKE_AR-NOTFOUND cr libperfutils.a
When i checked build folder ar is not set.
x86_64-linux\gtest-native\1.7.0-r5\build\CMakeCache.txt
CMAKE_AR:FILEPATH=CMAKE_AR-NOTFOUND
//CXX compiler.
CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/g++
How to set ar in cmake ?
Wheni see the cmake config below output . cmake automatically finding the g++
& why it not detecting ar
-- The CXX compiler identification is GNU 4.8.4
-- Check for working CXX compiler: g++
-- Check for working CXX compiler: g++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
When i check ar in machine manually its available.
/usr/bin/ar
CMake should have been able to detect /usr/bin/ar by itself, but you can manually override this, with the -D CMAKE_AR=/usr/bin/ar option.
Here's the list of useful variables:
https://gitlab.kitware.com/cmake/community/wikis/doc/cmake/Useful-Variables
cmake ar should set in cmake lib build ,
temperorly solved the issue by manally setting in my local Cmake file
set( CMAKE_AR "${AR}" CACHE FILEPATH "Archiver" )

cmake move build files in a subdirectory after configuring and building [duplicate]

I'm using cmake to compile a C++ project, and I want cmake to generate all the output files(metafiles like Makefile used to create binaries) in the build folder. I've checked all the answers in How do I make cmake output into a 'bin' dir?, none of them worked for me(suprisingly!). Files are generated in the root folder instead of in the build folder, what's wrong here? I guess I must have missed something.
Code Structure
➜ cmake-test tree .
.
├── CMakeLists.txt
└── hello.cpp
0 directories, 2 files
CMakeLists.txt
# Specify the minimum version for CMake
cmake_minimum_required(VERSION 3.11)
# Project's name
project(hello)
# Set the output folder where your program will be created
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/build)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# The following folder will be included
include_directories("${PROJECT_SOURCE_DIR}")
add_executable(hello ${PROJECT_SOURCE_DIR}/hello.cpp)
Build Commands and Outputs
➜ cmake-test cmake .
-- The C compiler identification is GNU 8.2.0
-- The CXX compiler identification is GNU 8.2.0
-- Check for working C compiler: /usr/bin/cc
-- Check for working C compiler: /usr/bin/cc -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/c++
-- Check for working CXX compiler: /usr/bin/c++ -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/searene/CLionProjects/cmake-test
➜ cmake-test ls
bin CMakeCache.txt CMakeFiles cmake_install.cmake CMakeLists.txt hello.cpp Makefile
cmake version
➜ cmake-test cmake --version
cmake version 3.11.4
CMake suite maintained and supported by Kitware (kitware.com/cmake).
OS
Linux
The usual way to do this, rather than changing variables to set the path, is simply to create the output directory, change to it, and run cmake from there. So instead of cmake . you usually have cmake .. or similar.
I understand the initial impulse to say "But I expect my build system to write output somewhere else." But CMake is not usually used in the way you were initially expecting, and other people who run your CMake build won't expect what you were expecting, so it's probably best to just use the built-in, default behavior, which is to put the output wherever cmake was run.
Put another way: You are fighting against the tool. Don't do that.
Disclaimer: I recommend going with #john-zwinck's answer.
By default, cmake uses the current working directory as build directory and whatever path you provide as source directory. So the normal way of achieving your goal is
create the build directory (mkdir build)
go there (cd build)
call cmake with the source dir as argument (cmake path/to/source)
BUT there is another way, as far as I know not documented in the cmake docs and only kept for compatibility reasons or internal usage, that some people are using. The -B and -H flags
cmake -Hpath/to/source -Bpath/to/build
or even from the source dir
cmake . -Bbuild
Important: no space after -B.
CMake 3.19.1 (not sure how about older ones) has following option (from docs):
cmake [<options>] -S <path-to-source> -B <path-to-build>
Uses as the build tree and as the
source tree. The specified paths may be absolute or relative to the
current working directory. The source tree must contain a
CMakeLists.txt file. The build tree will be created automatically if
it does not already exist. For example:
cmake -S src -B build

How to auto generate pkgconfig files from cmake targets

I would like to generate pkgconfig files in cmake from the targets. I started by writing something like this:
function(auto_pkgconfig TARGET)
get_target_property(INCLUDE_DIRS ${TARGET} INTERFACE_INCLUDE_DIRECTORIES)
string(REPLACE "$<BUILD_INTERFACE:" "$<0:" INCLUDE_DIRS "${INCLUDE_DIRS}")
string(REPLACE "$<INSTALL_INTERFACE:" "$<1:" INCLUDE_DIRS "${INCLUDE_DIRS}")
string(REPLACE "$<INSTALL_PREFIX>" "${CMAKE_INSTALL_PREFIX}" INCLUDE_DIRS "${INCLUDE_DIRS}")
file(GENERATE OUTPUT ${TARGET}.pc CONTENT "
Name: ${TARGET}
Cflags: -I$<JOIN:${INCLUDE_DIRS}, -I>
Libs: -L${CMAKE_INSTALL_PREFIX}/lib -l${TARGET}
")
install(FILES ${TARGET}.pc DESTINATION lib/pkgconfig)
endfunction()
This is a simplified version but it basically reads the INTERFACE_INCLUDE_DIRECTORIES properties and processes the INSTALL_INTERFACE of the generator expressions.
This works well as long as the include directories are set before calling auto_pkgconfig, like this:
add_library(foo foo.cpp)
target_include_directories(foo PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>
${OTHER_INCLUDE_DIRS}
)
auto_pkgconfig(foo)
However, sometimes properties are set after the call to auto_pkgconfig, like this:
add_library(foo foo.cpp)
auto_pkgconfig(foo)
target_include_directories(foo PUBLIC
$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>
${OTHER_INCLUDE_DIRS}
)
However, this won't properly read the include directories anymore. I would like auto_pkgconfig to run after all the target properties are set. I could use generator expressions for this, by changing auto_pkgconfig to this:
function(auto_pkgconfig TARGET)
file(GENERATE OUTPUT ${TARGET}.pc CONTENT "
Name: ${TARGET}
Cflags: -I$<JOIN:$<TARGET_PROPERTY:${TARGET},INTERFACE_INCLUDE_DIRECTORIES>, -I>
Libs: -L$<TARGET_FILE_DIR:${TARGET}> -l${TARGET}
")
install(FILES ${TARGET}.pc DESTINATION lib/pkgconfig)
endfunction()
However, this will read the BUILD_INTERFACE instead of the INSTALL_INTERFACE. So is there another way to read target properties after they have been set?
According to the CMake documentation, the contents of INSTALL_INTERFACE are only available when calling install(EXPORT). Unless they extend CMake, it will be best to do something else to generate your PkgConfig files. Ideally you would have enough control over your install layout to make this easy.
However, this doesn't mean you can't do what you ask; it's just "Tony the Pony" levels of evil. I actually hesitated to post this. Please don't take this as a recommendation.
The idea is to use install(EXPORT) to have CMake generate the appropriate scripts. Then generate a dummy CMake project that uses the file(GENERATE OUTPUT ...) code you gave above; the dummy project will see the exported, ie. INSTALL_INTERFACE properties.
I initially tried to use install(CODE [[ ... ]]) to do this, but it also sees the $<BUILD_INTERFACE:...> view. I've asked about this on the CMake Discourse.
cmake_minimum_required(VERSION 3.16)
project(example)
# Dummy library for demo
add_library(example SHARED example.cpp)
target_compile_definitions(example
PUBLIC $<BUILD_INTERFACE:BUILD>
$<INSTALL_INTERFACE:INSTALL>)
target_include_directories(example
PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:$<INSTALL_PREFIX>/include>)
# Here be dragons...
function(auto_pc TARGET)
file(CONFIGURE OUTPUT "pc.${TARGET}/CMakeLists.txt"
CONTENT [[
cmake_minimum_required(VERSION 3.16)
project(pc_#TARGET#)
find_package(pc_#TARGET# REQUIRED CONFIG)
file(GENERATE OUTPUT #TARGET#.pc
CONTENT [=[
Name: #TARGET#
Cflags: -I$<JOIN:$<TARGET_PROPERTY:INTERFACE_INCLUDE_DIRECTORIES>, -I> -D$<JOIN:$<TARGET_PROPERTY:INTERFACE_COMPILE_DEFINITIONS>, -D>
Libs: -L$<TARGET_FILE_DIR:#TARGET#> -l#TARGET#
]=] TARGET "#TARGET#")
]] #ONLY NEWLINE_STYLE LF)
install(TARGETS ${TARGET} EXPORT pc_${TARGET})
install(EXPORT pc_${TARGET} DESTINATION "_auto_pc" FILE pc_${TARGET}-config.cmake)
file(CONFIGURE OUTPUT "pc.${TARGET}/post-install.cmake"
CONTENT [[
file(REAL_PATH "${CMAKE_INSTALL_PREFIX}" prefix)
set(proj "#CMAKE_CURRENT_BINARY_DIR#/pc.#TARGET#")
execute_process(COMMAND "#CMAKE_COMMAND#" "-Dpc_#TARGET#_DIR=${prefix}/_auto_pc" -S "${proj}" -B "${proj}/build")
file(COPY "${proj}/build/#TARGET#.pc" DESTINATION "${prefix}")
]] #ONLY NEWLINE_STYLE LF)
install(SCRIPT "${CMAKE_CURRENT_BINARY_DIR}/pc.${TARGET}/post-install.cmake")
endfunction()
auto_pc(example)
# Clean up install path
install(CODE [[ file(REMOVE_RECURSE "${CMAKE_INSTALL_PREFIX}/_auto_pc") ]])
This results in the following:
alex#Alex-Desktop:~/test$ cmake -S . -B build
...
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build
alex#Alex-Desktop:~/test$ cmake --build build/
...
alex#Alex-Desktop:~/test$ cmake --install build --prefix install
-- Install configuration: ""
-- Installing: /home/alex/test/install/lib/libexample.so
-- Installing: /home/alex/test/install/_auto_pc/pc_example-config.cmake
-- Installing: /home/alex/test/install/_auto_pc/pc_example-config-noconfig.cmake
-- The C compiler identification is GNU 9.3.0
-- The CXX compiler identification is GNU 9.3.0
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Check for working C compiler: /usr/bin/cc - skipped
-- Detecting C compile features
-- Detecting C compile features - done
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- Configuring done
-- Generating done
-- Build files have been written to: /home/alex/test/build/pc.example/build
alex#Alex-Desktop:~/test$ ls install/
example.pc lib
alex#Alex-Desktop:~/test$ cat install/example.pc
Name: example
Cflags: -I/home/alex/test/install/include -DINSTALL
Libs: -L/home/alex/test/install/lib -lexample
This should make you sad. It makes me sad.
edit: off topic, since here, the pc files are generated manually
pkgconfig template files
motivation:
CMakeLists.txt should be the single source of truth (name, version)
pkgconfig files are about 10 times smaller than cmake files (cmake to pkgconfig is a lossy transformation)
template file: my_package.pc.in
prefix="#CMAKE_INSTALL_PREFIX#"
exec_prefix="${prefix}"
libdir="${prefix}/lib"
includedir="${prefix}/include"
Name: #PROJECT_NAME#
Description: #CMAKE_PROJECT_DESCRIPTION#
Version: #PROJECT_VERSION#
Cflags: -I${includedir}
Libs: -L${libdir} -l#target1#
CMakeLists.txt
cmake_minimum_required(VERSION 3.0)
project(my_library VERSION 1.1.2 LANGUAGES C
DESCRIPTION "example library")
add_library(my_library src/my_library.c)
# generate pc file for pkg-config
set(target1 my_library)
configure_file(my_package.pc.in
lib/pkgconfig/my_package.pc #ONLY)
based on: CMake generate pkg-config .pc
related: exporting targets to cmake files