Finding the source root directory in CMake for ROS - cmake

Using CMake with ROS, one can end up with a directory tree like this:
+src
-CMakeLists.txt (symbolic link to toplevel.cmake)
-CMakeExtras.txt (I want to use this)
+computing
+perception
+per1
-CMakeLists.txt
+per2
-CMakeLists.txt
+detection
+det1
-CMakeLists.txt
+sensing
+dev1
-CMakeLists.txt
In the CMakeExtras.txt I want to set up CMake variable, load common packages just once, etc like for example:
set(CMAKE_CXX_FLAGS "-O3 -Wall ${CMAKE_CXX_FLAGS}")
find_package(Boost REQUIRED COMPONENTS thread)
How can I do this? The INCLUDE() command is obvious, but I cannot edit the root level CMakeLists.txt and there doesn't seem to be a variable that will allow something like INCLUDE("${OVERALL_SRC_DIR}/CMakeExtras.txt). I suppose:
catkin_make -DOVERALL_SRC_DIR=~/project/src
Would be one solution, but is there a neater way?

Disclaimer: the question is still a bit unclear to me, so apologies in advance if the answer does not reflect your needs.
If the problem you want to solve is that you have a lot of directories like src in which the same top-level CMakeLists.txt is linked and you want to include a specific CMakeExtras.txt that resides in each particular directory, then you can solve this by adding:
include(${CMAKE_CURRENT_SOURCE_DIR}/CMakeExtras.txt)
to your top-level CMakeLists.txt. According to the documentation:
This the full path to the source directory that is currently being processed by cmake
and should solve this issue (i.e. the variable refers to the directory being processed, not to the CMakeLists.txt where it is used).
If instead your issue is how to compute the src path, given that a link to the top level CMakelists.txt is there, then you simply want to use PROJECT_SOURCE_DIR which:
is the source directory of the most recent project() command.

Related

Can you have a common cmake minimum included from another file?

I am quite new to cmake with a makefile background.
I like to use things like include(cmake_utils/header.cmake) to include common snippets of cmake files so that I can include them in my projects but only change them in one once in one place. Where cmake_utils is a git repo.
This is working nicely, but every single CMakeLists.txt I write has to have a cmake_minimum_required.
That is fine, but I may want to change this one day - lets say when one of my common files uses a feature from a newer version of cmake. In that case I don't want to go around changing all the CMakeLists.txt - I just want to change it in one place (ideally).
Here is my current CMakeFile.txt:
cmake_minimum_required(VERSION 3.10.2)
# Include common elements
include(cmake_utils/header.cmake)
include(cmake_utils/cpp_flags.cmake)
# Include path
include_directories(
inc
inc/log4cpp
)
# Include source files by wild card
file(GLOB SOURCES "src/log4cpp/*.cpp")
# Setup output and libs
include(cmake_utils/output_lib_shared.cmake)
include(cmake_utils/common_libs.cmake)
I really want to move the line cmake_minimum_required(VERSION 3.10.2) into my cmake_utils/header.cmake file.
But when I do this I get the following error right at the end of calling cmake:
CMake Error in CMakeLists.txt:
No cmake_minimum_required command is present. A line of code such as
cmake_minimum_required(VERSION 3.10)
should be added at the top of the file. The version specified may be lower
if you wish to support older CMake versions for this project. For more
information run "cmake --help-policy CMP0000".
Is this just a limitation of cmake that I have to live with, or is there a way to archive this?
It's also possible that I am still thinking like a gnu make writer and I have this all horribly wrong :o
Per the documentation cmake_minimum_required: Call the cmake_minimum_required() command at the beginning of the top-level CMakeLists.txt file even before calling the project() command. It is important to establish version and policy settings before invoking other commands whose behavior they may affect.
There is no way of getting around this.

How to pass variable to cpack?

I have a cmake project which one of the install targets is a collection of files. This files change depending on the configuration (Release, Debug...).
I would like to be able to install the files like so:
install(DIRECTORY $<TARGET_FILE_DIR:tgt>
DESTINATION bin
COMPONENT files)
But cmake does not support that. Generator variables do not apply to DIRECTORY. So I was wondering if there is a way to either save the directory somewhere. Either the cache or a file and then load it into cpack.
So I guess the question is how to pass a variable from cmake to cpack?
This is a rather late answer, but I happened upon this question trying to solve a somewhat different problem that could also be summarized as: "How do I pass a variable to CPack?" In my case, I was making this call from a customized version of CPackDeb.cmake copied to my workspace:
find_program(OPKG_CMD NAMES opkg-build HINTS "${OPKG_HINT}")
# ^^^^^^^^^^^^
# This is what I wanted to pass to CPack
I was setting OPKG_HINT in a file included from my top-level CMakeLists.txt, but it was not getting passed through to cpack; the above find_program() invocation was seeing an empty string for OPKG_HINT.
The solution turned out to be stupid simple: just prepend CPACK_ to the variable name!
If I do this in CMakeLists.txt:
set(CPACK_OPKG_HINT "${_sysroot_top}/aarch64-poky-linux/usr/bin")
then I can put this in my CPackDeb.cmake file and it works fine:
find_program(OPKG_CMD NAMES opkg-build HINTS "${CPACK_OPKG_HINT}")
Anyway, this wound up being a bit of an X-Y problem for the OP, but... if you really need to set a variable at CMake time in such a way that it's accessible to cpack, prefixing the variable name with CPACK_ seems to do the trick nicely...
The following setup work if you use a "single-configuration generators (such as make and Ninja)" and call CMake with
cmake -DCMAKE_BUILD_TYPE=Release <source_dir>
https://cmake.org/cmake/help/v3.0/variable/CMAKE_BUILD_TYPE.html
You can define the ${dir} variable in another way if you like.
IF (CMAKE_BUILD_TYPE STREQUAL "Release")
SET(dir release_dir)
ELSE()
SET(dir debug_dir)
ENDIF()
INSTALL(DIRECTORY ${dir} DESTINATION bin COMPONENT files)
Until now this seems to be the best answer (from someone on the cmake mail list)
install(DIRECTORY path/to/Debug/dir
DESTINATION bin
CONFIGURATIONS Debug
COMPONENT files
)
install(DIRECTORY path/to/Release/dir
DESTINATION bin
CONFIGURATIONS Release
COMPONENT files
)
CMake 3.5 supports generator expressions for the DIRECTORY arguments. See installing directories.

cmake use one cmakelist.txt for a project with subdirectories

i like to structure my code in multiple subdirs but i dont want to create a new cmakelist.txt in each new subdir.
my folder structure is something like this:
project
>cmakelist.txt
>build
>src
>main.cpp
>multiple_subdirs_or_(c|h)pp_files_with_more_subdirs_or_(c|h)pp_files
my cmakelist.txt looks like this:
...
file(GLOB_RECURSE cpps RELATIVE ${CMAKE_CURRENT_LIST_DIR} "src/*.cpp")
file(GLOB_RECURSE hpps RELATIVE ${CMAKE_CURRENT_LIST_DIR} "src/*.hpp")
#remove files with main
list(REMOVE_ITEM cpps "src/test.cpp")
#bins
add_executable(test src/test.cpp src/test.cpp ${hpps} ${cpps})
#same problem if this is used instead of the other add_executable
add_library(foo OBJECT ${cpps} ${hpps})
add_executable(test src/test.cpp $<TARGET_OBJECTS:foo>)
the problem with my file:
source files created after the execution of cmake are not compiled and the build fails if they are used.
as predicted by http://www.cmake.org/cmake/help/v3.0/command/file.html in section GLOB:
We do not recommend using GLOB to collect a list of source files from
your source tree. If no CMakeLists.txt file changes when a source is
added or removed then the generated build system cannot know when to
ask CMake to regenerate.
the question: is it possible to use a single cmakelist.txt for a project with multiple sub directories? (without the problems of file(GLOB ...) )
You have two totally unrelated things here.
First, can you use only a single CMakeLists.txt file for your whole project? Yes, of course you can (although I'd personally not go this way after a project has reached a certain size), and you're already doing this.
Second, the problem with GLOB. You already quoted the part of the documentation where it states what problems the use of GLOB has. This cannot really be avoided at the moment if you want to continue using GLOB, as this is part of the cmake design where they distinguish between what is done during configure and build time. The alternative is to list all files manually. Whether you do this in a single CMakeLists.txt file in your projects main directory, or in multiple files across your subdirectories does not matter.
To answer your question: yes, it is possible to handle a project with multiple sub-directories and one CMakeLists.txt. I have two considerations for you to take into account:
I strongly recommend you not using file(GLOB ...) for sources.
You have to list the files manually. For example (src/ is the source-subdirectory):
set(cpps src/file1.cpp src/file2.cpp src/file3.cpp)

CMake: How to get subdirectories to use source files from the root directory

I have this variable set in the root CMakeLists.txt
set(${LIBNAME}_srcs
File1.cpp
File2.cpp
File3.cpp
File4.cpp
File5.cpp
)
add_subdirectory(A)
and I want to add the variable as a source for the executable in the subdirectory A
add_executable(${TEST}}
What is the cleanest way to do this? without having to create a new variable with ../ on all the source files? Or is there a macro I can use?
You can insert the absolute path to each of the values in ${LIBNAME}_srcs by doing something like:
foreach(${LIBNAME}_src ${${LIBNAME}_srcs})
list(APPEND abs_${LIBNAME}_srcs ${CMAKE_SOURCE_DIR}/${${LIBNAME}_src})
endforeach()
add_executable(${TEST} ${abs_${LIBNAME}_srcs})
Jumping to conclusions here, it looks like what you're doing may be a bit unusual.
Normally the add_executable call would be made in the same place where the list of source files is gathered - usually in the same directory.
Going by the fact that you've named your sources variable ${LIBNAME}_srcs, I'd guess that you're already creating a library from these sources. If so, it'd be better to just link that library in your test subdirectory rather than recompiling all the library's sources into the executable.
Something like:
add_executable(${TEST} test_main.cpp)
target_link_libraries(${TEST} ${LIBNAME})
When I add sources, I do something like this:
set(${LIBNAME}_srcs
${SRC}/File1.cpp
${SRC}/File2.cpp
${SRC}/File3.cpp
${SRC}/File4.cpp
${SRC}/File5.cpp
)
Where ${SRC} is the absolute path to the source directory found using ${CMAKE_SOURCE_DIR}.
Then, you can simply use add_executable(${TEST} ${LIBNAME}_srcs) in your subdirectory. CMake will automatically import the scope of parent directories into child directories.

Changing cmake directories

I'm writing a cmake file for a project which has the following structure
project/ (root)
libraries/ (contains (precompiled) libraries
src/
code/ (contains a set of fortran files)
My CMakeLists.txt file is currently in project/ and effectively is just
cmake_minimum_required(VERSION 2.6)
enable_language(Fortran)
project(project1)
set(projsrc src/code)
set(libdir lib/)
find_library(PROJ_LIBRARY pr10 PATHS ${libdir})
add_executable (sc1 sc1.f90)
target_link_libraries(sc1 ${PROJ_LIBRARY})
This creates my binary in the same folder as the source code, when I actually want it in the level above (i.e. in the src folder - this structure will be changed so we have a bin folder eventually), but haven't worked out how to do it.
Some answers on SO say you have to have a CMakeLists.txt file in every folder - is this correct? Is it possible to set an environment variable or use a CMake variable (e.g. http://cmake.org/cmake/help/v2.8.8/cmake.html#command:set). It's also not very clear from some answers whether the solutions they have posted are C++ specific (as that is what language CMake most often seems to be used for).
Edit
I found out that I can change it to the behaviour I want by modifying it slightly:
cmake_minimum_required(VERSION 2.6)
enable_language(Fortran)
project(project1)
set(projsrc src/code)
set(libdir lib/)
find_library(PROJ_LIBRARY pr10 PATHS ${libdir})
add_executable (src/sc1 ${projsrc}/sc1.f90)
target_link_libraries(src/sc1 ${PROJ_LIBRARY})
However, this doesn't explain why my behaviour is different to how it should be, according to arrowdodger below. I'm also still trying to work out how to display the values of environment variables; I've tried the following with no luck:
message(${RUNTIME_OUTPUT_DIRECTORY})
message($ENV{RUNTIME_OUTPUT_DIRECTORY})
message(${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
message($ENV{CMAKE_RUNTIME_OUTPUT_DIRECTORY})
By default, binaries will appear in respective subdirectory of your build dir. By respective i mean the directory that contains CMakeLists.txt with add_executable() call.
For example, if you have following CMakeLists.txt
add_executable(tgt1 src1.f90)
add_executable(tgt2 subdir/src2.f90)
in the root folder, you will get both binaries in ${CMAKE_BINARY_DIR}. So if you wish tgt2 to be in ${CMAKE_BINARY_DIR}/subdir, you need to add CMakeLists.txt there and call add_executable(tgt2 src2.f90) from there.
You can change this behavior:
CMAKE_LIBRARY_OUTPUT_DIRECTORY, CMAKE_RUNTIME_OUTPUT_DIRECTORY and others.
You can also set respective target properties.