I am working on a project, let's call it A
. It comes with its own CMakeLists.txt
. This projects also depends on other two projects, B
and C
. Each one has its own CMakeLists.txt
. Projects C
also depends on project D
and it also has its own CMakeLists.txt
. Projects B
, C
and D
are also my projects and they reside as separate GitHub repositories. They produce some libraries that are needed by project A
. I want a solution that meets the following criteria:
I can compile everything with one command. Normally, I would do cd build/ && cmake ../B/ && make .
for each project before doing the same thing for A
. I am aiming for something like cd build/ && cmake ../A/ && make .
.
I can write code to any project I want and then recompile everything. Again, I own all projects and they all reside as GitHub submodules.
The dependencies don't need to be installed. I do not want to do that because it has already created enough confusion. I just want to be able to read my headers and libraries directly from the source tree and build
directory respectively.
Allows for different build configurations for each project. If I am working only on project A
, I want it on debug, but B
should be on release (and irrelevant for the other dependencies).
I already tried using Visual Studio Code's cmake-tools
. It creates a separate directory named build
, but this means out-of-tree compilation for all dependencies and it fails for all projects except A
. It is a tedious job to reconfigure all CMakeLists.txt
to point to build
and I still could not fix all errors. I have inherited the codebase for B
, C
and D
and do not want to mess with their CMake configurations.
This is my current directory structure:
A/
├── build/
├── CMakeLists.txt
│
├── B/
│ └── CMakeLists.txt
├── C/
│ ├── CMakeLists.txt
│ └── D/
│ └── CMakeLists.txt
|
├── include/
├── src/
│ └── CMakeLists.txt
└── test/
└── CMakeLists.txt
Are my goals achievable? Is my source tree structure correct? Is there anything smart I could do to fix these problems?
If you want to build multiple projects in a single commandline invokation, you either need to make them all part of the same generated buildsystem, or use the ExternalProject module, or write a wrapper script that invokes multiple buildsystems for you.
As far as I know, in general, a single generated single-config buildsystem can only have one overarching build configuration ("Debug", "Release", etc.), and a single generated multi-config can only have multiple "homogeneous" configs (no "mixed" configs: the "Debug" config is "Debug" through and through, etc.). If you want different projects to each be able to have their own build configs, that means you need to give them each their own generated buildsystem. And either I'm missing something, or the only way to get around that is to do some ugly hacks, which I don't know of, and wouldn't recommend even if I knew them.
When working with mixed build configurations and multi-config buildsystems, you probably want to take a look at the MAP_IMPORTED_CONFIG_<CONFIG>
target property and its associated initialization variable.
add_subdirectory()
One solution is to add_subdirectory
B and C in A, and D in C. This will cause CMake to generate a buildsystem that builds all of A, B, C, and D.
export()
+ include()
Another solution is to use export()
in each dependency project, and then include()
the target export file to add the dependency. This means a separate generated buildsystem for each project.
I think you'd be able to get everything you want with the ExternalProject module if you combine it with export()
+ include()
(you could also use it with an approach that uses installation, which actually wouldn't add manual steps on your part, which I'm assuming is what you meant you wanted to avoid). Each project would get its own buildsystem, but the invocation of configure and build would be driven by the dependent project (though you should be able to invoke individual ones too in the normal way with cmake --build <build_dir>
). Of particular interest to you would be the SOURCE_DIR
and BUILD_DIR
options. You'll probably also want to use the BUILD_ALWAYS
build step option, and since you're working with CMake projects, also pass a truthy value for CONFIGURE_HANDLED_BY_BUILD
.
Note that in the Pitchfork Layout spec (not that you necessarily care about it), your B and C directories would probably go under A/external/, and your D directory would probably go under C/external/.
The VS Code CMake Tools extension allows you to configure the build directory. See its settings docs. It also supports CMake Presets. See its CMake Presets support docs.
Out-of-source builds aren't a bad thing. They make it easier to delete an entire buildsystem and its artifacts because they separate those from the source tree.