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?