catkin / ROS: How-to specify include path correctly when using submodules - git-submodules

I am building a project using ROS and thus, catkin_make to build my ROS nodes and libraries.
The problem I'm facing is:
I am using a git submodule in one package (package A) (and thus, I have a hierarchical include folder structure) and I have difficulties referencing a header file within that submodule.
In order to build the package B, which is dependent on package A, I have added the INCLUDE_DIRS statement to the catkin_package command in package A:
catkin_package(
INCLUDE_DIRS my-submodule/include
...
)
The content of that directory is:
my-submodule/my-header.h
(I have put the header files under a folder, named after the submodule, as many tutorials stated that within ROS you should use this convention).
The include statement in a file from package-B reads like this:
...
#include <my-submodule/my-header.h>
...
This works fine - package B is being built (as I am using one combined workspace to build this).
But: When I switch to the target system, where I only install package A, and then try to build package B (on that target system), it does not build because the include paths are not setup correctly.
The INSTALL statement for package A looks like this
install(DIRECTORY my-submodule/include
DESTINATION ${CATKIN_PACKAGE_INCLUDE_DESTINATION}
FILES_MATCHING PATTERN "*.h"
PATTERN ".svn" EXCLUDE
)
This is mainly, because the installed folder structure on the target system looks like this:
.../ros/include/my-package-A/include/my-submodule/my-header.h
So, the install process actually puts that submodule's include-path under the package-A-include path (which is a different path structure compared to when I build the packages directly in one combined workspace).
And the CFLAGS for compilation only set the include directory to the folder:
.../ros/include
And thus, breaking my include statement in my package-B file:
#include <my-submodule/my-header.h>
Do you have any idea how to solve this?
I am sure there are more people than me, trying to reference header files from a submodule within a package.

Assuming you have a file my-submodule/include/my-submodule/my-header.h inside your package A, then two small changes to your install() statement should fix this:
install(DIRECTORY my-submodule/include/
DESTINATION ${CATKIN_GLOBAL_INCLUDE_DESTINATION}
FILES_MATCHING PATTERN "*.h"
PATTERN ".svn" EXCLUDE
)
First, add a slash to the path (.../include/ instead of .../include), which causes the contents of the include folder to be installed instead of the include folder itself (Otherwise you'll end up with ../ros/install/include/include/my-submodule/my-header.h)
Secondly, use ${CATKIN_GLOBAL_INCLUDE_DESTINATION} (which points to .../ros/install/include/) instead of ${CATKIN_PACKAGE_INCLUDE_DESTINATION} (which points to .../ros/install/my-package-A/include/) as destination.
The alternative would be to fix catkin, as
catkin_package(
INCLUDE_DIRS my-submodule/include
...
)
should theoretically already export my-submodule/include, so you can pick it up in package B with
find_package(catkin REQUIRED DEPENDS my-package-A)
catkin_package(
CATKIN_DEPENDS my-package-A
)
include_directories(${catkin_INCLUDE_DIRS})
Unfortunately, for some reason this is explicitely not possible when using catkin config --install. See https://answers.ros.org/question/335846/install_dirs-not-working-as-expected-when-using-install/.

Related

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

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)

Install a target defined in an included directory [duplicate]

Consider the following CMakeLists.txt file:
add_subdirectory(execA)
add_subdirectory(libB)
install(TARGETS execA libB
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
I get the following error:
install TARGETS given target "execA" which does not exist in this
directory
execA and libB have their own CMakeList.txt files and are located under project directory, as well as the build directory I'm running cmake (cmake ..):
project
|------ CMakeList.txt (the one with the code)
|----execA
| \- .cpp, .hpp and CMakelist.txt
|----libB
| \- .cpp, .hpp and CMakelist.txt
|---- lib
|---- bin
\---- build (where I´m commanding: $ cmake ..
How do I fix this error?
According to this bugreport, install(TARGETS) command flow accepts only targets created within the same directory.
So you need either move the add_library() call into the top-level directory, or split install(TARGETS) call into per-target ones, and move each of them into the corresponding subdirectory.
Since CMake 3.13 install(TARGETS) can work even with targets created in other directories.
install(TARGETS) can install targets that were created in other directories. When using such cross-directory install rules, running make install (or similar) from a subdirectory will not guarantee that targets from other directories are up-to-date.
Even though it would help seeing the CMakeLists.txt files contained in the subdirectories, I guess they contain add_executable and/or add_library statements to create your stuff.
Also, because of your example, I guess you are using the same name of your directories for your targets.
That said, you should know that symbols defined in a CMakeLists.txt file in a subdirectory are not visible by default within the context of the CMakeLists.txt file in the parent directory. Because of that, you should rather move your install statements within the CMakeLists.txt files within your subdirectories.
This should solve the problem, if my thoughts were right. Otherwise, I strongly suggest you to post in your question also the content of the other files above mentioned.
Anyway, the error is quite clear.
The file that contains the install statement for the target named X does not contain a target creation statement (add_executable and the others) that gives birth to that target, so it goes on saying that that target does not exist in that directory.
This still seems to be a pain point in CMake 3.11.
In our codebase, we have many targets defined in subdirectories and need to create an assortment of installers with different configurations and (potentially overlapping) combinations of targets.
Here's my solution:
Before calling add_subdirectory in your root CMakeLists.txt file, create a GLOBAL property with the names of the target(s) you want to include in your installer.
Wrap target creation functions (add_executable, etc.) in your own custom functions. Within those functions check if the target is present in the global property, and invoke install accordingly.
That approach allows you to centralize installer configuration.
Also: To support creation of multiple installers, we populate our global list along with other installer properties in separate .cmake files. When we invoke cmake, we pass the name of the installer configuration CMake file as a command-line argument. Our root CMakeLists.txt file simply calls include with that file.

Install add_subdirectory(A) header files before building add_subdirectory(B)

I am new to cmake and I am trying to build my code on UNIX and Windows platforms. I have add_subdirectory(A) and add_subdirectory(B). If I build only add_subdirectory(A), it installs headers files to some xyz location and next when I build add_subdirectory(B), it uses headers files from previously installed xyz location. But when I try to build both add_subdirectory(A) and add_subdirectory(B) together, it fails with missing header files from xyz location, as they are not installed. So my question is, is there a way that I can tell cmake to build and install add_subdirectory(A) and then start building add_subdirectory(B).
I tried to look online but no luck.
My master CMakeLists.txt:
add_subdirectory(TradeFlowCommonFrameworkLib/cpp/src)
add_subdirectory(TradeFlowCommonDataLib/cpp/src)
install(EXPORT Findfotmc DESTINATION ${INSTALL_FOTMC}/cmake)
----------------------------------------------------------------
Adding subdirectories does not specify dependancies between them. You have to explicitly link your second executable/library to your first library.
In the sub CMakeLists of your B project (TradeFlowCommonDataLib/cpp/src) you have to add something like this :
target_link_libraries(B A)

How do I specify the files I want CPack to pack into an RPM?

I'm making two rpms with CPack using its component feature. I want one to have .so files and the other to have all header files. I couldn't find any similar questions regarding packaging files in the component feature.
(DEVEL" is the component for my devel rpm)
Right now I have set(CPACK_RPM_DEVEL_INSTALL_FILES path/../file1
...
path/../file2)
just with all my files separated by returns but that does not work at all. What is the correct statement to provide a list of files I need in the rpm?
Currently it produces 3 rpms (I assume the third will just be a complete one with all files which I'm fine generating and not using). Two of the rpms have every file in the repo in them and the third just has two CMake files in it.
cpack_add_component(DEVEL)
//Skipping version, description, name, setting source_dir...
set(CPACK_RPM_COMPONENT_INSTALL ON)
set(CPACK_COMPONENTS_IGNORE_GROUPS 1)
set(CPACK_COMPONENTS_GROUPING ONE_PER_GROUP)
set(CPACK_COMPONENT_DEVEL_DISPLAY_NAME "devel")
set(CPACK_RPM_DEVEL_INSTALL_FILES "/usr/include/opentracing/noop.h
...
/usr/include/opentracing/version.h")
set(CPACK_COMPONENT_DIST_REQUIRED TRUE)
set(CPACK_COMPONENT_DEVEL_REQUIRED TRUE)
set(CPACK_COMPONENTS_ALL DIST DEVEL)
I am calling this from linux command line with cpack -G rpm
In your CMakelists.txt, add something like:
install(TARGETS outputfiles... RUNTIME DESTINATION bin LIBRARY DESTINATION lib)
Then you can use
make package
in your build directory.

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
)