Cmake install target triggering - cmake

I'm writing build configuration with cmake: besides main project with own code there are some external libraries. For sake of easy updating of these libraries (zlib, libpng, ...) I don't want to modify its cmakelists files, but I need specific libraries targets (to use in target_link_libraries() for example).
Another restrictions is that I can't just say that my code needs external libraries installed, all things must be located in one source code tree and must be built together.
To keep everything libraries provide structured (libs, headers) I want to install (like make install does) libraries to local building folder and then include generated cmake-file to import needed targets to my project.
I suppose flow as follows:
build external library with add_subdirectory()
install external library files into local directory
import targets using generated cmake-file
use imported targets in primary project cmake files
Problem is to automate step 2 (need to trigger install target after add_subdiretory inside main project CMakeLists.txt). I can build and install all libraries and then build own code, but this isn't convenient.
So question is how can I tell cmake to do intermediate install during build?
A little working example here:
file structure:
prj/CMakeLists.txt
prj/src/main.cpp
lib/CMakeLists.txt
lib/include/libheader.h
lib/src/libsource.cpp
prj/CMakeLists.txt
project(TestProject)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_VERBOSE_MAKEFILE on)
set(WORK_DIR ${CMAKE_CURRENT_SOURCE_DIR})
# supposed to use add_subdirectory here (with forced install).
# and then include prespecified include-file as here.
include_directories(${WORK_DIR}/../lib/build/install/include)
include(${WORK_DIR}/../lib/build/install/lib/libtargets.cmake)
add_executable(main ${WORK_DIR}/src/main.cpp)
target_link_libraries(main library_target)
lib/CMakeLists.txt
project(TestLib)
cmake_minimum_required(VERSION 2.8)
set(CMAKE_VERBOSE_MAKEFILE on)
set(WORK_DIR ${CMAKE_CURRENT_SOURCE_DIR})
include_directories(${WORK_DIR}/include)
add_library(library_target STATIC ${WORK_DIR}/src/libsource.cpp)
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
install(FILES ${WORK_DIR}/include/libheader.h DESTINATION include)
install(TARGETS library_target DESTINATION lib EXPORT libtargets)
install(EXPORT libtargets DESTINATION lib)
prj/src/main.cpp
#include <iostream>
#include "libheader.h"
using std::cout;
int main()
{
cout << getRandomNumber() << "\n";
return 0;
}
lib/include/libheader.h
#ifndef _LIBHEADER_H_
#define _LIBHEADER_H_
int getRandomNumber();
#endif
lib/src/libsource.cpp
#include <iostream>
#include "libheader.h"
int getRandomNumber()
{
return 4; // guaranteed to be random.
}
You can use following command to build all:
pushd . && mkdir lib/build && cd lib/build && cmake .. && make && make install && popd && pushd . && mkdir prj/build && cd prj/build && cmake .. && make || popd
EDIT: desired cmakelists in main project:
set(CMAKE_INSTALL_PREFIX ${CMAKE_CURRENT_BINARY_DIR}/install)
add_subdirectory(${WORK_DIR}/../lib ${CMAKE_CURRENT_BINARY_DIR}/lib.d)
# force install here, somehow
# because if not we will get "include could not find load file" here.
include(${CMAKE_CURRENT_BINARY_DIR}/install/lib/libtargets.cmake)
include_directories(${CMAKE_CURRENT_BINARY_DIR}/install/include)

sorry, I have no privilege to put comment
So, I put the reference link here
https://cmake.org/Bug/view.php?id=14311
this is also useful when including prebuilt library binaries in the
project.
add_library(prebuilt STATIC IMPORTED) set_property(TARGET prebuilt
PROPERTY IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libprebuilt.a)
install(TARGETS other_built_targets prebuilt
EXPORT project-export
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib )
Making prebuilt an imported target makes in convenient to use in the
project and treats it further like any other target.

Related

CMake: Install an export file in Debug/Release alternatively?

Take the following example as a start:
...
add_library(Foo ...)
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib
...
)
install(EXPORT FooTargets
FILE lib/cmake/Foo
...
)
Running this with
$ mkdir build; cd build
$ cmake -DCMAKE_BUILD_TYPE=Release .. (or in Debug)
$ cmake --build .
$ cmake --install . --prefix my/custom/prefix
This will create the files:
my/custom/prefix/lib/cmake/Foo/FooTargets.cmake
my/custom/prefix/lib/cmake/Foo/FooTargets-release.cmake (Or debug, respectively)
my/custom/prefix/lib/libFoo.a
And from what I managed to understand from the FooTargets.cmake file, it globs for all FooTargets-*.cmake files and includes() them all.
In turn, the FooTargets-release.cmake file is the one that references the libFoo.a file.
In the docs about the install command, it says that you can add the CONFIGURATIONS option to the install TARGETS command, so that if we change the above:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
This will install the libFoo.a file in my/custom/path/lib/Debug/libFoo.a. Now let's say I want the Release library to be installed in lib/Release and the Debug library be installed in lib/Debug, and that when the downstream project will consume my package, it will have the right library depending on its configuration - i.e. - debug build will link against the Debug library, same for release.
What I thought I can do is:
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Debug
LIBRARY DESTINATION lib/Debug
ARCHIVE DESTINATION lib/Debug
)
install(TARGETS Foo EXPORT FooTargets
CONFIGURATIONS Release
LIBRARY DESTINATION lib/Release
ARCHIVE DESTINATION lib/Release
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
And what will happen is that when building in Debug, the FooTargets-debug.cmake will be generated, pointing to the lib/Debug/libFoo.a, and when building in Release, the FooTargets-release.cmake will be generated, pointing to the lib/Release/libFoo.a. The FooTargets.cmake will then check what configuration is the consuming project is building with, and include the right configuration.
When I try doing the above, I get:
-- Configuring done
CMake Error: install(EXPORT "FooTargets" ...) includes target "Foo" more than once in the export set.
-- Generating done
CMake Generate step failed. Build files cannot be regenerated correctly.
How is this should be done??
EDIT
I found out, in a not-very-straightforward way, that when I build the consuming project as such:
$ cmake -DCMAKE_BUILD_TYPE=Debug ..
and like this:
$ cmake -DCMAKE_BUILD_TYPE=Release ..
I am linking against only the relevant library. Basically how this works is that the FooTargets.cmake file is included by the FooConfig.cmake file, which is called by find_package. The FooTargets.cmake file is the one that defines the imported target:
add_library(Foo IMPORTED)
This file then calls all the FooTargets-*.cmake, which adds the relevant library to some list called _IMPORT_CHECK_FILES_FOR_FOO.
What this file also does, is:
set_property(TARGET Foo::Foo APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE)
Apparently there is some property IMPORTED_CONFIGURATIONS that holds the imported configurations.
I suppose that somewhere down the road, find_package takes this list and filters it according to the CMAKE_BUILD_TYPE variable, and this way links only the relevant library.
What I still don't understand, is how to make it so that upon Release builds (of Foo), the FooTargets-release.cmake will be created, pointing to lib/Release/Foo.a, and the same for debug builds.
It seems that CMake went half-way with this, unless I'm seriously missing something.
It seems per-CONFIGURATIONS installs are not easily fit to EXPORT semantic.
However, in simple cases per-configuration's specific can be achieved by using generator expressions in DESTINATION:
install(TARGETS Foo EXPORT FooTargets
LIBRARY DESTINATION lib/$<CONFIG>
ARCHIVE DESTINATION lib/$<CONFIG>
)
install(EXPORT FooTargets
DESTINATION lib/cmake/Foo
...
)
The code above will install libraries into lib/Debug for Debug configuration, and into lib/Release for Release configuration.

Create CMake/CPack <Library>Config.cmake for shared library

I have the simplest possible c-library which builds and is packed using the following CMakeLists.txt:
cmake_minimum_required(VERSION 3.5)
project (libfoo C)
add_library(foo SHARED impl.c)
target_link_libraries(foo)
install(TARGETS foo LIBRARY DESTINATION lib/)
install(FILES public_header.h DESTINATION include/libfoo)
set(CPACK_GENERATOR "TGZ")
include(CPack)
Working example is located here: https://github.com/bjarkef/cmake-simple/tree/master/libfoo
I execute mkdir -p build; (cd build/; cmake ../; make all package;) to build a .tar.gz package with the compiled shared library along with its public header file. This is all working fine.
Now I wish to modify the CMakeLists.txt to create the FooConfig.cmake and FooConfigVersion.cmake files needed for CMake find_package in a different project to find the foo library. How do I do this?
I have discovered I should used the CMakePackageConfigHelpers: configure_package_config_file and write_basic_package_version_file, and I should create a FooLibraryConfig.cmake.in file. However I cannot figure out how to put it all together.
Note that it is important the the resulting .cmake files only contains relative paths.
I have cmake module included in the top level CmakeList.txt:
# Generate and install package config files
include(PackageConfigInstall)
Within the generic PackageConfigInstall.cmake file, the config files are created from the cmake.in files, and installed. This module can be reused for other packages.
include(CMakePackageConfigHelpers)
# Generate package config cmake files
set(${PACKAGE_NAME}_LIBRARY_NAME ${CMAKE_SHARED_LIBRARY_PREFIX}${PACKAGE_NAME}${CMAKE_STATIC_LIBRARY_SUFFIX})
configure_package_config_file(${PACKAGE_NAME}-config.cmake.in
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake
INSTALL_DESTINATION ${CMAKE_INSTALL_DIR}/${PACKAGE_NAME}
PATH_VARS LIB_INSTALL_DIR INCLUDE_INSTALL_DIR APP_INCLUDE_INSTALL_DIR )
configure_file(${PACKAGE_NAME}-config-version.cmake.in ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake #ONLY)
# Install package config cmake files
install(
FILES
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config.cmake
${CMAKE_CURRENT_BINARY_DIR}/${PACKAGE_NAME}-config-version.cmake
DESTINATION
${CMAKE_INSTALL_DIR}/${PACKAGE_NAME}
COMPONENT
devel
)
You'll need a package file for your library, such as your_lib-config.cmake.in, which will become your_lib-config.cmake. This will contain the include and library variables that can be used.
get_filename_component(YOUR_LIB_CMAKE_DIR "${CMAKE_CURRENT_LIST_FILE}" PATH)
# flag required by CMakePackageConfigHelpers
#PACKAGE_INIT#
set_and_check(YOUR_LIB_INCLUDE_DIR #PACKAGE_YOUR_LIB_INCLUDE_INSTALL_DIR#/hal)
set_and_check(YOUR_LIB_LIBRARY #PACKAGE_LIB_INSTALL_DIR#/#CMAKE_STATIC_LIBRARY_PREFIX##PROJECT_NAME_LIB##CMAKE_STATIC_LIBRARY_SUFFIX#)
set_and_check(YOUR_LIB_LIBRARIES #PACKAGE_LIB_INSTALL_DIR#/#CMAKE_STATIC_LIBRARY_PREFIX##PROJECT_NAME_LIB##CMAKE_STATIC_LIBRARY_SUFFIX#)
You'll also want a config-version.cmake.in file like this:
set(PACKAGE_VERSION #PACKAGE_VERSION#)
# Check whether the requested PACKAGE_FIND_VERSION is compatible
if("${PACKAGE_VERSION}" VERSION_LESS "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_COMPATIBLE FALSE)
else()
set(PACKAGE_VERSION_COMPATIBLE TRUE)
if ("${PACKAGE_VERSION}" VERSION_EQUAL "${PACKAGE_FIND_VERSION}")
set(PACKAGE_VERSION_EXACT TRUE)
endif()
endif()
There's quite a bit to the packaging scripts to get it all to work just right. I went through a lot of trial and error to finally get something that works on different targets (both linux server and embedded target). I might have left something out, so please just comment and I'll update answer.

How to copy the binary output of one target into the binary folder of another target?

I'm building a dynamic library that is meant to be loaded dynamically like a plugin. When present, the library is loaded. When not present, it can't.
Naturally, I made a test app...and it doesn't work.
CMakeLists.txt
cmake_minimum_required(VERSION 2.8)
PROJECT(MYLIB)
enable_testing()
INCLUDE(CPack)
SET(SRCS
src/source1.cpp
src/source2.cpp
)
ADD_LIBRARY(mylib SHARED ${SRCS})
ADD_SUBDIRECTORY(test)
test/CMakeLists.txt
ADD_EXECUTABLE(test_loader main.c)
TARGET_LINK_LIBRARIES(test_loader dl)
ADD_TEST(NAME test_loader COMMAND test_loader)
test/main.c
#include <dlfcn.h>
#include <stdio.h>
int main(int argc, char *argv[])
{
void* handle;
handle = dlopen("./mylib.so", RTLD_LAZY);
if (handle == 0)
{
fprintf(stderr, "%s\n", dlerror());
}
return 1;
}
and then to build
mkdir build
cd build
cmake ..
make
The result is that there is a mylib.so file in /build. There is a test_loader executable in /build/test.
And this doesn't work out.
What I need is for there to be a copy of mylib.so under /build/test/ so that I can dynamically load it with the test app.
While the answers given helped me greatly (and anyone reading this should review those answers as well as the comments), I found an even better solution:
add_custom_command(TARGET test_loader POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different "$<TARGET_FILE:mylib>" $<TARGET_FILE_DIR:test_loader>)
This solution results in two copies of the library. One copy is next to the test_loader, so that the unit tests can work. The other is in the default output location. Since it's still in the default output location, other projects that depend on mylib know where to find them, by default.
There are cmake variables that let you control where libraries, shared libraries, and executables are placed. See cmake documentation Variables that Control the Build. I do not think it is a good idea to mix executables and libraries. (Of course this is my opinion--you can use whatever layout you want.) I use the following:
# Directory for output files
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CACHE PATH "Output directory for static libraries.")
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
CACHE PATH "Output directory for shared libraries.")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
CACHE PATH "Output directory for executables and DLL's.")
If you used this, then you would need to adjust your dlopen statement to handle = dlopen("../lib/libmylib.so", RTLD_LAZY); Note that I adjusted the name of mylib because I believe cmake will, by default on Linux, use a lib prefix when naming libraries.

How to use CMake to find and link to a library using install-export and find_package?

You have a CMake-enabled library project. You need to use it in another library or executable. How to use CMake to find and link to the library? You may have the following preferences:
write the least possible amount of boiler-plate code
decouple the internal details of the linked library from the consuming target
Ideally, the usage of the library should look like this:
add_executable(myexe ...)
target_link_libraries(myexe mylib::mylib)
Let me demonstrate a possible solution on a concrete example:
The myapp project
We have an executable target myapp. We're linking it with mylib, which is built in its own build tree. In the CMakeLists.txt of myapp we find and specify mylib as a dependency of myexe:
find_package(mylib REQUIRED)
...
add_executable(myexe ...)
target_link_libraries(myexe mylib::mylib)
Let's see how to set up mylib and the build of myexe to make this work.
The mylib project
The directory layout of mylib:
mylib
- CMakeLists.txt
- mylib.c
+ include
- mylib.h # single public header
In the CMakeLists.txt of mylib we need to create the target and specify its source files:
add_library(mylib mylib.c include/mylib.h)
The public header mylib.h will be included as #include "mylib.h" both by mylib and the clients of mylib:
mylib itself and other targets built in mylib's CMake project (for example tests) need to find include/mylib.h from the mylib source tree
clients of mylib built in their own projects (like myexe) need to find include/mylib.h at its installed location
CMake allows us to specify both include paths for mylib:
target_include_directories(mylib PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>)
We're using the PUBLIC option here since this header is needed on the public interface of mylib. Use PRIVATE for include paths internal to mylib.
The INSTALL_INTERFACE specifies a path relative to the install root, that is, CMAKE_INSTALL_PREFIX. To actually install the public header:
set_target_properties(mylib PROPERTIES PUBLIC_HEADER include/mylib.h")
We also need to install the library itself and the so-called config-module and related files. The config-module is the file which will be used by consuming projects, like myapp to find mylib and get all the parameters needed to link with it. It is similar to the pkg-config's .pc files.
We need two, related install commands. The first one:
install(TARGETS mylib
EXPORT mylib-targets
PUBLIC_HEADER DESTINATION include
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin)
The list of destinations needed to cover all the standard install locations of static libraries, dll's and so's. If you're sure your library will be built exclusively as a static lib, a single DESTINATION lib would make it.
The interesting part is the EXPORT option. It assigns the list of targets (currently, it's only mylib) to the identifier mylib-targets. This identifier will be used in the next command to generate and install some special files which make find_package(mylib) work in the consuming projects:
install(EXPORT mylib-targets
NAMESPACE mylib::
FILE mylib-config.cmake
DESTINATION lib/cmake/mylib)
This command generates multiple files:
one file for each build configuration (Debug, Release, etc..) which describes the library file and configuration-dependent parameters
a file which describes the configuration-agnostic parameters and also includes all the config-dependent files. Since this file can also be used as a config-module on its own we simply rename it as mylib-config.cmake
The files will be installed into ${CMAKE_INSTALL_PREFIX}/lib/cmake/mylib which is one of the many standard locations the find_package(mylib) command will search for mylib-config.cmake.
Building mylib
We need to specify an install location in the variable CMAKE_INSTALL_PREFIX:
mkdir build
cd build
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
and build and install the library:
cmake --build . --target install
Building myexe
myexe needs to know where to look for mylib. The variable CMAKE_PREFIX_PATH can be a list of paths. We need to specify the previous install location:
mkdir build
cd build
cmake -DCMAKE_PREFIX_PATH=$PWD/../out ../myexe
cmake --build .
A note on building multiple configurations
Usually we need to build multiple configurations (Debug, Release). A critical issue is to specify configuration-dependent filenames or install locations. For example, you can set the default value of the DEBUG_POSTFIX property for the library project:
set(CMAKE_DEBUG_POSTFIX d)
The debug version of the mylib library file will be named libmylibd.lib (or mylibd.lib on Windows). The generated EXPORT files will contain the modified filenames.
If you're using makefile-style CMake generators you can control the build configuration by setting the CMAKE_BUILD_TYPE variable:
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
cmake --build . --target install
You may need seperate build directories for each configuration or you can re-use the same build dir. In that case, to play it safe it's best to explicitly clean before build:
cmake --build . --target install --clean-first
If you're using a multiconfig IDE generator, like Xcode or Visual Studio, you need to specify the configuration in build time:
cmake -DCMAKE_INSTALL_PREFIX=$PWD/../out ../mylib
cmake --build . --target install --config Release
References
You can clone and build this repository which contains the mylib and myexe projects (tested on Windows and Linux).
Check out the CMake documentation. The most important related commands are:
add_library
target_link_libraries
find_package
install
target_include_directories
target_compile_definitions
and two detailed articles:
Build System
Packages

Writing a CMakeLists.txt for ROS

I am new to ROS (Indigo), and I want to write a C++ program to use the MoveIt! package.
However, I am confused as to what I need to have in my CMakeLists.txt file, such that the header and library files can be found.
For example, here is the file so far:
cmake_minimum_required(VERSION 2.8.1)
project(MicoMoveIt)
set(CMAKE_CXX_FLAGS "-std=c++11")
file(GLOB srcs *.cpp)
include_directories(/opt/ros/indigo/include)
link_directories(/opt/ros/indigo/lib)
add_executable(${PROJECT_NAME} ${srcs})
And my main.cpp file is:
#include <ros/ros.h>
#include <moveit/move_group_interface/move_group.h>
int main()
{
moveit::planning_interface::MoveGroup group("right_arm");
return 0;
}
But compiling this gives me the error:
/opt/ros/indigo/include/moveit/robot_model/joint_model.h:47: error: Eigen/Geometry: No such file or directory
#include <Eigen/Geometry>
^
So, I need to specify where to find these Eigen header files. But given that Eigen is a dependency of MoveIt!, then I shouldn't have to manually include all these header file directories.
How should I be writing the CMakeLists.txt file so that all header and library directories of dependencies are included?
Try with this changes:
cmake_minimum_required(VERSION 2.8.3)
project(<project_name>)
set(CMAKE_CXX_FLAGS "-std=c++11")
find_package(catkin REQUIRED COMPONENTS
<other_packages_you_need>
moveit_ros_planning_interface
)
catkin_package(CATKIN_DEPENDS)
include_directories(include ${catkin_INCLUDE_DIRS})
set(BINNAME <executable_name>)
set(SOURCES <cpp_source_files>)
add_executable (${BINNAME} ${SOURCES})
target_link_libraries(${BINNAME} ${catkin_LIBRARIES})
The key factor for your problem is the inclusion of package moveit_ros_planning_interface in the find_package() directive. Everytime you have to build some code, you have to add every package dependancies to your project (in this case you need the moveit interface package). Notice that the package.xml file plays a crucial role too.
Lastly you have to tell the linker to retrieve and link the libraries you are using in your project. To do so use target_link_libraries() and the useful catkin_LIBRARIES variable which almost does what you expected (you could manually add libraries, but it could be messy in big projects).
Have a look here and here for more info.
EDIT: in this case you do not need the link_directories() directive, which has to be used only when the linker has to know where to retrieve the external library.