CMake share library with multiple executables
My project contains several executables that share some common code. I would like to put the common code in a static library that the executables can link to. (The common code is pretty small and I prefer not to deal with shared libraries).
The source tree looks something like this:
- project
- CMakeLists.txt
- common
- CMakeLists.txt
- src
- include
- app1
- src
- CMakeLists.txt
- app2
- src
- CMakeLists.txt
app1 and app2 both depend on the code in common.
This common code is very application specific and will never need to be used by another project outside this directory tree. For that reason I would prefer not to install the library in any sort of global location.
The top-level CMakeLists.txt file just adds the subdirectories:
project(toplevel)
cmake_minimum_required(VERSION 3.1)
add_subdirectory(common)
add_subdirectory(app1)
add_subdirectory(app2)
The common library's CMakeLists.txt file creates the static library and sets include directories:
add_library(common STATIC common.cpp)
target_include_directories(common PUBLIC "${CMAKE_CURRENT_LIST_DIR}/include")
And the file for the executables looks like this:
project(app1)
cmake_minimum_required(VERSION 3.1)
add_executable(${PROJECT_NAME} main.cpp)
target_link_libraries(${PROJECT_NAME} common)
Now for my question. If I run CMake from the top level project directory, I can build app1 and app2 and they build successfully. However, if I want to build a single one of these projects (by running CMake from app1, for example) instead of building from the top level directory, I get an error because common/include
is not added to the header search path.
I can see why this happens. There is nothing in the CMakeLists.txt file for app1 or app2 that "pulls in" common. This is only done at the top level.
Is there a way around this, or is this behavior generally considered acceptable? Is something about my setup sub-optimal? I'm just thinking it would be nice to be able to build the projects individually instead of from the top level in the event that we start to develop more and more executables that use this common library, but perhaps this is something I shouldn't be concerned about.
When you setup your build environment, you should put some thought into the following three topics (beside others, but for this discussion/answer I reduced it to the three I feel are relevant here):
- Dependecies / Coupling
- Deployment
- Teams
"Strong Coupling"
I came to think of the add_subdirectory()
command as supporting "strong coupling" and your current setup implicitly supports:
- A frequently changing
common
library - A single deployment (process and timing) for all your apps
- A single team working on the complete source base
- An IDE would show it all in one solution
- You generate one build environment for everything
"Loose Coupling"
If you want a more "loose coupling" you could make use of external scripts in other languages or the use of CMake's ExternalProject_Add()
macro. So if you setup the common
library (maybe even including "binary delivery") and each app
as a separate project you do support:
- A less often changing
common
library- Probably with its own release cycles
- An independent development/deployment cycle for each app
- A team of different developers working on each
app
A Mixture of Both
So as you can see there a lot of things to consider and CMake can give you support for all kind of approaches. Considering that your project might be in the early stages, you probably go by a mixed approach (not right away de-coupling the common
library):
CMakeLists.txt
project(toplevel)
cmake_minimum_required(VERSION 3.1)
include(ExternalProject)
ExternalProject_Add(
app1
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app1"
PREFIX app1
INSTALL_COMMAND ""
)
ExternalProject_Add(
app2
SOURCE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/app2"
PREFIX app2
INSTALL_COMMAND ""
)
app1/CMakeLists.txt
project(app1)
cmake_minimum_required(VERSION 3.1)
add_subdirectory(../common common)
add_executable(${PROJECT_NAME} src/main.cpp)
target_link_libraries(${PROJECT_NAME} common)
This will actually generate three build environments. One directly in your binary output directory and one each in app1
and app2
sub-directories.
And in such approaches you may want to think about common CMake toolchain files.
References
- Use CMake-enabled libraries in your CMake project (II)
- CMake: How to setup Source, Library and CMakeLists.txt dependencies?
You should use project()
command in subdirectories only if this subproject is intended to be built both as standalone and as a part of toplevel project. This is the case for LLVM and Clang, for example: Clang can be compiled separately, but when LLVM build system detects Clang source, it includes its targets too.
In your case you don't need subprojects. To compile only app1
or app2
target issue make app1
/make app2
in projects build dir.