1

We are using CMake to manage our codes, including multiple products and libraries, in one monolithic repository. At current, these projects are used via add_subdirectory. For instance:

Repo layout:

Foo
Bar
Baz
Product

Whereas Bar and Baz depends on Foo, and Product depends on Bar and Baz.

In file /Foo/CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
include_guard(GLOBAL)
project(Foo)
add_library(foo ......)
# ......

In file /Bar/CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
include_guard(GLOBAL)
project(Bar)
add_subdirectory(../Foo sibling/Foo)
add_library(bar ......)
target_link_libraries(bar foo)
# ......

In file /Baz/CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
include_guard(GLOBAL)
project(Baz)
add_subdirectory(../Foo sibling/Foo)
add_library(baz
target_link_libraries(baz foo)
# ......

And in final product cmake /Product/CMakeLists.txt:

cmake_minimum_required(VERSION 3.10)
include_guard(GLOBAL)
project(Product)
add_subdirectory(../Bar sibling/Bar)
add_subdirectory(../Baz sibling/Baz)
add_executable(product ......)
target_link_libraries(product bar baz)
# ......

This layout solves diamond requirement, and allows each project being generated separately. However it has following disadvantages:

  • The CMakeLists.txt become more complexed. It contains more lines and uses more CMake concepts, making it more difficult to train new members.
  • When project number increases, the binary directory become messy and it is very hard to locate a base project's actual binary dir. For example, the actual binary dir of Base may be located in ${CMAKE_BINARY_DIR}/Some/sibling/Very/sibling/Deep/sibling/Dependencies/sibling/Base, or ${CMAKE_BINARY_DIR}/Another/sibling/Damn/sibling/Deep/sibling/Dependencies/sibling/Base.

We also tried more monolithic way: use a root CMake file that includes all projects:

In file /CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)
project(MegaProject)

add_subdirectory(Base)
add_subdirectory(Foo)
add_subdirectory(Bar)
add_subdirectory(Product)

This don't need to write complexed CMake file for each project, you only need things like this:

project(Product)
add_executable(product ......)
target_link_libraries(product bar baz)

which reduced many lines.

However, it has a critical problem: it requires to generate whole code base, and when there are massive projects, the generation time became tooooo long.

So, how to manage big monolithic code with CMake?

jiandingzhe
  • 1,881
  • 15
  • 35
  • Why do you care about a subproject's binary directory? BTW, you may make inner build directories not so lengthy, by using common root for them: `add_subdirectory(../Bar ${CMAKE_BINARY_DIR}/sibling/${PROJECT_NAME}/Bar)`. (Unique part `${PROJECT_NAME}` is needed for make binary directories for `Bar` different. While you use `include_guard`, CMake still needs to step into the subdirectory to find that guard, and with same binary directories stepping into would lead to an error). – Tsyvarev Dec 26 '18 at 08:22
  • @Tsyvarev Your suggestion would solve much, although it still needs to find actual `Bar` binary dir in a series of directories (it may be in `sibling/SomeThing/Bar` or `sibling/Another/Bar`). And I tried your last mentioned way (by `add_subdirectory(../Bar ${CMAKE_BINARY_DIR}/Bar)`), and it do raise the error as you say. – jiandingzhe Dec 26 '18 at 12:49
  • 1
    Ok, if you want to have the predefined binary directory for subprojects, you may elaborate new, "guarded", method instead of `add_subdirectory`. It is possible to hide all magic inside the method, so it could be called like `add_subdirectory_guarded(Bar)`, but one require to additional `include()` for make this method avaiable for his/her project. – Tsyvarev Dec 26 '18 at 13:39
  • 1
    Some approaches to implement such "guarded" `add_subdirectory`: https://stackoverflow.com/questions/42978414/how-to-handle-a-transitive-dependency-conflict-using-git-submodules-and-cmake/42985142#42985142. – Tsyvarev Dec 27 '18 at 20:19

0 Answers0