5

Suppose the source tree is in this structure:

/
 |- lib1
 |  |- src.cpp
 |  |- lib1.h
 |  |- CMakeLists.txt
 |
 |- lib2
 |  |- src.cpp
 |  |- lib2.h
 |  |- CMakeLists.txt
 |
 |- lib3
 |  |- src.cpp
 |  |- lib3.h
 |  |- CMakeLists.txt
 |
 |- app
 |  |- src.cpp
 |  |- CMakeLists.txt
 |
 |- CMakeLists.txt

Suppose:

  1. lib1 has function f();
  2. lib2 has function g() which uses f();
  3. app/src.cpp uses function g();
  4. Nobody uses lib3.

I want:

  1. in app/CMakeLists.txt, it only link to lib2. The logic here is, app/src.cpp only uses g(), so when writing app/src.cpp, we can't specify the dependency to lib1 because it is implementation detail of lib2. So according to this logic, in app/CMakeLists.txt, it can't have anything related to lib1, i.e. it neither include_directories of lib1, add_subdirectory of lib1, nor target_link_libraries of lib1, etc.
  2. Since nobody uses lib3, it won't even be built. This needs to be done automatically. So manually add_subdirectory for lib1 and lib2 but not lib3 is not a clever way. You can imagine if we have a very large source tree with complicated tree structure and dependencies and hundreds of executables in hundreds of different subdirectories. If I only want to build several of them, then I don't want to bother building unused libraries at all.

So my question is: is there a way to write the CMakeLists.txt files in a scalable way to satisfy the above requirements? If not, then is there some similar tools that can do this?

Thanks.

Kan Li
  • 8,557
  • 8
  • 53
  • 93

2 Answers2

4

For the 1 question:

In lib2/CMakeLists.txt you should put this:

target_link_libraries(lib2 lib1)

And in app/CMakeLists.txt:

target_link_libraries(app lib2)

Now if you try to build app, CMake will check if lib2 is up to date and if not - rebuild lib1 and lib2.

For the 2 question:

You can guard add_subdirectory(lib3) invocation with if() block based on option() variable.

Another way - in lib3/CMakeLists.txt:

add_library(lib3 ${SRCS} EXCLUDE_FROM_ALL)

This would make CMake to not adding lib3 target into all target. This target will still be built if you are trying to build something depending on it, or issue make lib3 manually.

arrowd
  • 33,231
  • 8
  • 79
  • 110
  • This doesn't solve my problem. In your solution, you need to add_subdirectory(lib1) before add_subdirectory(lib2) in the top-level CMakeLists.txt, otherwise how could lib2/CMakeLists.txt know the existence of project lib1? This is not scalable because with tens of subdirectories, I need to figure out their dependencies and sort them correctly. Also, lib2 depending on lib1 is the implementation detail of lib2, so it is logically problematic to have top level CMakeLists.txt knowing this. Also, as the project iterates, the dependencies may get changed and you need to re-sort them. – Kan Li Feb 25 '12 at 16:45
  • 2
    You can invoke `add_subdirectory()` in any order without caring about deps. And you would need `target_link_libraries()` anyway. Or do you to completely get rid of root CMakeFiles.txt? – arrowd Feb 26 '12 at 11:49
  • 1
    As pointed out by @arrowdodger, the question #1 answer is correct and does not involve administrative overhead. Cmake will figure out the correct order to build the libraries. – CXJ Nov 19 '14 at 20:20
  • How are the lib1 header files made available to lib2 which are needed to build lib2 prior to linking? – Sam Liddicott May 28 '20 at 17:10
1

If libs belong to 3rd or 2nd party i.e. you can't or don't want to fix their build ways, than you could prefer ExternalProject_Add over add_subdirectory.

ExternalProject_Add(lib1 /home/me/projects/my_project/lib1 EXCLUDE_FROM_ALL TRUE)
ExternalProject_Add(lib2 /home/me/projects/my_project/lib2 DEPENDS lib1 EXCLUDE_FROM_ALL TRUE)
ExternalProject_Add(lib3 /home/me/projects/my_project/lib3 EXCLUDE_FROM_ALL TRUE)

Each call creates a target named by first argument. This targets can be used as dependencies. DEPENDS keyword precedes a list of external project dependencies. EXCLUDE_FROM_ALL TRUE states that target wouldn't be build by default.

Be sure that this projects are properly installed into reachable location to see one another after building.

Docs: https://cmake.org/cmake/help/latest/module/ExternalProject.html

ephemerr
  • 1,833
  • 19
  • 22