How to structure CMake projects - cmake

I started using CMake but am not sure how to use it in the following setup.
I have some library-cmake-projects (source code), which might depend on each other.
Libs
-A
-B
-C
(Lets say A depends on C and B depends on C)
How to make cmake be aware of the needed modules in the other directories?

With regards to add_subdirectory, you should structure it like your source code is structured.
So, Let's say your folder structure looks like this:
/
-- App
-- libs
-- A
-- B
-- C
Then you would put CMakeLists.txt like this:
/
-- CMakeLists.txt
-- App
-- CMakeLists.txt
-- libs
-- CMakeLists.txt
-- A
-- CMakeLsts.txt
-- B
-- CMakeLists.txt
-- C
-- CMakeLists.txt
/CMakeLists.txt
add_subdirectory(App)
add_subdirectory(libs)
/App/CMakeLists.txt
add_executable(App ...)
target_link_libraries(App A B)
/libs/CMakeLists.txt
add_subdirectory(A)
add_subdirectory(B)
add_subdirectory(C)
/libs/A/CMakeListst.txt
add_library(A ...)
target_link_libraries(A C)
/libs/B/CMakeListst.txt
add_library(B ...)
target_link_libraries(B C)
/libs/C/CMakeListst.txt
add_library(C ...)
It's not good to think about add_subdirectory as having anything to with dependency management, it is about how your source and build files are organized. It also has scoping, but most of the time you don't need to worry about it. You don't necessarily need the /libs/CMakelists.txt if you directly put all three add_subdirectory calls in the top-level CMakeLists.txt but it can help with organizing.
I would refrain from using add_subdirectory to include a neighboring folder, for example. Assume the target is there and include from somewhere higher up.
Note that this is what I would recommend in general because it seems to be what the designers had in mind, but if you need something else for some reason, or have different opinion about what is correct design, then feel free to structure it differently.

Is add_subdirectory the correct way to do this?
No, add_subdirectory() is used to traverse to other CMakeLists.txt files within your CMake project. It doesn't specify dependencies. In modern CMake, you really only need target_link_libraries to specify dependencies, as suggested in this answer.
How to add C to A to make this build in one call possible?
I do not want to add C directly to App.
You can make A depend on C by linking the previously-defined target C to A
add_library(C SHARED ... )
...
add_library(A SHARED ... )
target_link_libraries(A PUBLIC C)
Note, the use of PUBLIC here ensures C is added to the link interface of A, so it will be propagated to consuming targets. Now, when you link A to App, you will get C along with it, and there is no need to specify C explicitly.
# Link A (and C) to App.
target_link_libraries(App PRIVATE A)
How to handle the case that App also depends on B? With add_subdirectory and no caution this would try to define C twice. Are include guards the correct solution?
In my experience, this hasn't been an issue, and the linker was able to figure out the duplicated linked C library. If it does become an issue, you can simply make the scope of the B dependency PRIVATE. This way, C will not be propagated to App through B:
target_link_libraries(B PRIVATE C)
...
target_link_libraries(App PRIVATE B A)
I encourage you to read through the linked target_link_libraries() documentation, which has many examples for recommended CMake usage with these types of dependencies.

Related

How to include target include directories transitive through multiple linked libraries

we are working on an embedded project in C/C++ and currently some special needs appeared. Background is there are two compiled libraries which define the same symbols. The compiler allows to create relocatable output modules (with partial linking) and to hide symbols for other compilation units when linking. This also means the output module does not need to have all the symbols defined, this will be done in the final linking. Compiler used is TI LTS1.3.0. I will link directly to the relocatable-section of the manual: https://software-dl.ti.com/codegen/docs/tiarmclang/rel1_3_0_LTS/compiler_manual/linker_description/04_linker_options/linker-output-options.html#stdz0756429
The other part of the project is hardly built on CMake with static libraries which are linked against each other via target_link_libraries.
To get this working I created an "add_executable"-target for each of those both output modules with the same symbols. To those I pass the static-libraries by CMake and get the linked with target_link_libraries.
But now I have a problem. All contents of the static libraries are compiled in each of those output modules. This is unwanted behaviour since as said the final linking does the job of linking the missing stuff - so the static-libraries - to it. This should be done with another add_executable command via CMake as well.
using the target include directories property is not suitable since it only adds the include directories of the given target itself but not of the target the target will include and link against.
So e.g. if you have (pseudo code):
#library A
function( create_libA )
add_library( libA src/A.c )
target_include_directories( libA PUBLIC /inc ) #contains A.h
endfunction()
#library B. different location
function( create_libB LIBA )
add_library( libB src/B.c )
target_link_libraries( libB PUBLIC ${LIBA} )
target_include_directories( libB PUBLIC /inc ) #contains B.h
endfunction()
#target output module with partial linking. Only should link and compile LIBTOBELINKEDIN, not libB. different location.
function( build_part_module LIBB LIBTOBELINKEDIN )
add_executable( outputModuleA src/func.c ) #func.c does include A.h
#following would cause libA and libB also to be compiled and linked in the output due to transitive stuff as I understood, which is unwanted.
target_link_libraries( outputModuleA PUBLIC ${LIBB} ${LIBTOBELINKEDIN} )
#trying this
get_target_property(libBInc ${LIBB} INTERFACE_INCLUDE_DIRECTORIES)
#will only include B.h but not A.h. compilation will fail.
target_include_directories(outputModuleA /inc ${libBInc})
I did not find any solution in Cmake itself to solve this problem. It's confusing me since all the include-directories must be known when the libraries are passed transitive, which is stated in the documentation. But I understand that getting the target include directories of just the passed lib does not include the other ones.
Since target_link_libraries does also not work this way I can only think of a maybe recursive solution? But for that my knowledge is just non-existent.
target_link_libraries with something like HEADERS_ONLY would be helpfull for this job.
Also one can say: if the output module contains all the definitions it won't be a problem, since the linker then knows them and will do its magic.
But this is also unwanted, since we use the generated static-libraries to place them into sections in different regions of the RAM directly. This would then mean to create another linker-script for partial linking which defines sections which then can be again moved. But the more we go this direction, the less we need CMake for it.
Instead of get_target_property use $<TARGET_PROPERTY> generator expression: the property's value, extracted by that expression, already includes transitive propagation:
target_include_directories(outputModuleA PRIVATE
$<TARGET_PROPERTY:libB,INTERFACE_INCLUDE_DIRECTORIES>
)
Note, that generator expressions has limited usage: not all functions expects them. Documentation for target_include_directories clearly states that the command supports generator expressions.

CMake: Is there a way to get a list of imported targets that belong to a package

Sometimes I wish I could get a list of the imported targets that belong to a package. Is there a variable that holds them?
This would allow me to write something like this
find_package(Qt5 CONFIG REQUIRED)
message("Imported Qt5 targets: ${Qt5_IMPORTED_TARGETS}") # speculative code
With my current knowledge I have to rely on the documentation of the package to give me the names of all imported targets. Reading them from a variable or property would be easier.
CMake 3.21 introduced the directory property IMPORTED_TARGETS that can be used to get a list of all imported targets. This can be used to derive a list of targets that were imported by a single find_package() call when it is queried before and after the call to find_package(). The code could look something like this:
...
get_property(importTargets DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
get_property(importTargetsAfter DIRECTORY "${CMAKE_SOURCE_DIR}" PROPERTY IMPORTED_TARGETS)
list(REMOVE_ITEM importTargetsAfter ${importTargets})
message("${importTargetsAfter}")
...
Usually it is good enough to only print the list of all imported targets and guess from the names which of them were imported by the package of interest.
Not precisely what you asked for, but for Qt5, one can do:
cmake_minimum_required(VERSION 3.14)
project(so)
find_package(Qt5 COMPONENTS Core Widgets REQUIRED)
get_cmake_property(_variableNames VARIABLES)
foreach(_variableName ${_variableNames})
if(_variableName MATCHES "^Qt5.*LIBRARIES")
message(STATUS "${_variableName}")
message(STATUS "\t${${_variableName}}")
endif()
endforeach()
Example output:
-- Qt5Core_LIBRARIES
-- Qt5::Core
-- Qt5Gui_EGL_LIBRARIES
-- Qt5::Gui_EGL
-- Qt5Gui_LIBRARIES
-- Qt5::Gui
-- Qt5Gui_OPENGL_LIBRARIES
-- Qt5::Gui_GL
-- Qt5Widgets_LIBRARIES
-- Qt5::Widgets
-- Configuring done
-- Generating done
-- Build files have been written to: /path/to/build
Caveat with approach: One needs to know the component names.

target_link_libraries and add_dependencies

Is there any use case in which
target_link_libraries(my-lib x y z)
add_dependencies(my-lib x) # this is not just a waste of bytes?
If so, can someone explain what it would be?
In current CMake releases:
After some error checking add_dependencies results in a call to Target->AddUtility(). x is added to the list of utilities for my-lib.
target_link_libraries does not result in a call to AddUtility, but it does add the arguments to the LINK_LIBRARIES target property.
Later, both the content of the LINK_LIBRARIES target property and the list of utilities are used to compute the dependencies of the target in cmComputeTargetDepends.
The list of utilities in a target can not be queried at configure time, and is only used at generate time, so the use of add_dependencies with arguments which are libraries already added with target_link_libraries is redundant.
I don't know what you're particularly interested in...
From a conceptual point of view -- I think you're right. It is a waste of bytes.
From a CMake documentation point of view -- You should prefer make so to guarantee the correct build order.
According to the documentation target_link_libraries, add_dependencies concepts was ideologically split.
Such an idea of split dependencies, and linker options is also persisted in the Makefile format in the GNU make tool.
target_link_libraries
..Specify libraries or flags to use when linking a given target..
add_dependencies
...Make a top-level <target> depend on other top-level targets to ensure
that they build before <target> does...
In modern CMake from 3.* you can omit add_dependencies if you will perform linking with an aliased target:
add_library(fooLib 1.cpp 2.cpp)
add_library(my::fooLib ALIAS fooLib)
...
target_link_libraries(fooBin my::fooLib)
It's used in cases where top-level targets depend on each other. That is, if x is a something that you add to your project (at top-level) with
add_library( x x.c y.c z.c )
you shall tell CMake about it. And you do that with add_dependencies.

With CMake, how can I use the same source for a library for multiple different builds

I'd need to find how to best use CMake to build multiple different versions of the same library.
Let's assume that I have software A, B and C. All of these use the same external library, let's call that D. Let's assume that D is huge compared to A, B & C. So just using svn:external to checkout it multiple times is a bad option.
What crossed my mind is to have a folder where I have the subfolders for A, B, C & D and just calling add_subdir(../D). But to make things complicated, I don't want to enforce this folder structure on other developers, so I ended up with the thought of making this work with find_package, somehow.
What I'd like to see is that you are able to get C and D from subversion to any folders and have C compile it's own version of D for itself and use it, with no effort from the custom folders.
This might seem like a silly dream, but not being the smartest man on the planet, maybe someone has an idea of how to achieve this.
EDIT:
note that A B & C cannot use the same binaries for D since they would use different compiler flags to build the library.
add_subdirectory works with directories located outside the main project dir, you just have to give a binary_dir.
add_subdirectory(source_dir [binary_dir] [EXCLUDE_FROM_ALL])
Add a subdirectory to the build. The source_dir specifies the directory in which the source CMakeLists.txt and code files are located. If it is a relative path it will be evaluated with respect to the current directory (the typical usage), but it may also be an absolute path. The binary_dir specifies the directory in which to place the output files. If it is a relative path it will be evaluated with respect to the current output directory, but it may also be an absolute path.
In A, B and C you could have something like that:
set(MYPROJECT_D_PATH "" CACHE PATH "Path to D")
if("${test}" STREQUAL "")
message(FATAL_ERROR "You must set MYPROJECT_D_PATH")
endif()
add_subdirectory(${MYPROJECT_D_PATH} D)
When you compile A, B or C for the first time, you provides the path to D like that:
cmake <src_dir> -DMYPROJECT_D_PATH=<path_to_D>

CMake: collecting libraries

I am using CMake to build a simple C++ project, hereafter named P. The structure of P is quite simple:
P/src/
P/src/package1
P/src/packege2
P/src/...
P/src/main-app
I would like to collect the libraries in package1, package2, ... in a variable called P_LIBS.
In a first attempt, I tried to collect the libraries available in package1, package2, ... in the variable called P_LIBS initially set in the src/CMakeLists.txt file. However, the updates to P_LIBS made in the CMakeLists.txt of the subfolders are not propagated to the parent folder.
I would rather not write a list of libraries in the main CMakeLists.txt file. I would rather modify such variable while moving in the directory tree.
After a search on the internet I could not find any valid suggestion. If I look at the various Find files, I only see long listings of libraries in their main CMakeLists.txt file.
Is there a way to do what (I hope) I explained above?
Thanks to sakra's link I was able to 'propagate' names up to the parent folder. However, the names I add to the P_LIBS variable are later interpreted as 'library' names, not as reference to CMake targets. In other words, if
P_LIBS = {a, b}
the 'a' and 'b' are interpreted as the library names, i.e. CMake generates:
gcc [...] -l a -o exe
instead of
gcc [...] /path/to/a.o -o exe
(.o or other extensions)
You are propably constructing the targets list as a string, try to make them a list instead. For example:
# in package1/CMakeLists.txt
set(P_LIBS ${P_LIBS} a b PARENT_SCOPE)