CMake: How to set up source, library and CMakeLists.txt dependencies?
Adding the same subdirectory multiple times is out of question, it's not how CMake is intended to work. There are two main alternatives to do it in a clean way:
Build your libraries in the same project as your app. Prefer this option for libraries you're actively working on (while you're working on the app) so they are likely to be frequently edited and rebuilt. They will also show up in the same IDE project.
Build your libraries in an external project (and I don't mean ExternalProject). Prefer this option for libraries that are just used by your app but you're not working on them. This is the case for most third-party libraries. They will not clutter your IDE workspace, either.
Method #1
- your app's
CMakeLists.txt
adds the subdirectories of the libraries (and your libs'CMakeLists.txt
's don't) - your app's
CMakeLists.txt
is responsible to add all immediate and transitive dependencies and to add them in the proper order - it assumes that adding the subdirectory for
libx
will create some target (saylibx
) that can be readily used withtarget_link_libraries
As a sidenote: for the libraries it's a good practice to create a full-featured library target, that is, one that contains all the information needed to use the library:
add_library(LibB Src/b.cc Inc/b.h)
target_include_directories(LibB PUBLIC
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/Inc>)
So the location of include directories of the library can remain an internal affair of the lib. You will only have to do this;
target_link_libraries(LibC LibB)
then the include dirs of LibB
will also be added to the compilation of LibC
. Use the PRIVATE
modifier if LibB
is not used by the public headers of LibC
:
target_link_libraries(LibC PRIVATE LibB)
Method #2
Build and install your libraries in seperate CMake projects. Your libraries will install a so-called config-module which describes the locations of the headers and library files and also compile flags. Your app's CMakeList.txt
assumes the libraries has already been built and installed and the config-modules can be found by the find_package
command. This is a whole another story so I won't go into details here.
A few notes:
- You can mix #1 and #2 as in most cases you will have both unchanging, third-party libs and your own libraries under development.
- A compromise between #1 and #2 is using the ExternalProject module, preferred by many. It's like including the external projects of your libraries (built in their own build tree) into your app's project. In way it combines the disadvantages of both approaches: you can't use your libraries as targets (because they're in a different project) and you can't call
find_package
(because the libs are not installed the time your app'sCMakeLists
is configuring). - A variant of #2 is to build the library in an external project but instead of installing the artifacts use them from their source/build locations. For more about this see the
export()
command.