Multiple sub-projects depending on the same library - cmake

I have a project main_project. It depends on two libraries - libA and libB, which in turn depend on lib_common each.
Graphically project's structure looks like this:
main_project
|
+-libA
| |
| +-CMakeLists.txt
|
+-libB
| |
| +-CMakeLists.txt
|
+-lib_common
| |
| +-CMakeLists.txt
|
+-CMakeLists.txt
Since libA uses lib_common it's pretty natural to call add_subdirectory(lib_common) inside libA's CMakeLists.txt. But the libB also depends on lib_common. If I put it in a subdirectory again it will compile twice. And I don't want that. Also I want to keep all my subprojects clean and self-contained - that's why I don't want to put lib_common in top-level main_project's CMakeLists.txt (because lib_common have nothing with main_project).
Which options do I have?

I would add one more abstraction level to your project structure:
app - this is for executables and for everything that belongs to them,
lib - libraries for the application.
The top-level CMakeLists.txt will only know that there is two different thing, the app sub-project and the lib sub-project. An example directory structure could look like follows:
FooProject
|-- app
|   |-- CMakeLists.txt
|   `-- src
|   `-- foo.cpp
|-- CMakeLists.txt
`-- lib
|-- CMakeLists.txt
|-- libA
|   |-- CMakeLists.txt
|   |-- inc
|   |   `-- bar.h
|   `-- src
|   `-- bar.cpp
|-- libB
|   |-- CMakeLists.txt
|   |-- inc
|   |   `-- baz.h
|   `-- src
|   `-- baz.cpp
`-- lib_common
|-- CMakeLists.txt
|-- inc
|   `-- common_tool.h
`-- src
`-- common_tool.cpp
The top-level CMakeLists.txt contains only the following:
cmake_minimum_required(VERSION 2.8)
project(FooProject)
add_subdirectory(lib)
add_subdirectory(app)
The app/CMakeLists.txt should know that there is a lib folder from where you can include headers and that the executable depends on the libA and the libB:
project(foo)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../lib)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src FOO_SOURCES)
add_executable(foo ${FOO_SOURCES})
add_dependencies(foo libA libB)
target_link_libraries(foo libA libB)
install(TARGETS foo RUNTIME DESTINATION bin ARCHIVE DESTINATION bin)
The lib/CMakeLists.txt exists to tie together the libA, libB and lib_common:
cmake_minimum_required(VERSION 2.8)
add_subdirectory(lib_common)
add_subdirectory(libA)
add_subdirectory(libB)
Let's say that lib_common has no dependencies, so the lib/lib_common/CMakeLists.txt won't see outside of its folder:
project(lib_common)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src LIBCOMMON_SOURCES)
add_library(lib_common ${LIBCOMMON_SOURCES})
In this example the lib/libA/CMakeLists.txt and lib/libB/CMakeLists.txt have almost identical content and only they deal with the lib_common:
project(libA)
include_directories(${CMAKE_CURRENT_SOURCE_DIR}/inc ${CMAKE_CURRENT_SOURCE_DIR}/..)
aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/src LIBA_SOURCES)
add_library(libA ${LIBA_SOURCES})
add_dependencies(libA lib_common)
target_link_libraries(libA lib_common)
install(TARGETS libA LIBRARY DESTINATION lib ARCHIVE DESTINATION lib)
In this example the lib sub-project is what you can call as clean and self-contained.

I think this a fairly standard CMake solution:
# toplevel CMakeLists.txt
add_subdirectory(libA)
add_subdirectory(libB)
add_subdirectory(lib_common)
# libA CmakeLists.txt
add_library(libA ${LIBA_SOURCES})
target_link_libraries(libA lib_common)
and similar for libB.
You do not have to call add_subdirectory(lib_common) before add_subdirectory(libA), CMake handles the dependency management automatically.

Related

Is it possible to set output directory of EXPORT_COMPILE_COMMANDS without changing the BINARY_DIR?

CMake generates compile_commands.json in cmake build directory which means that my installation of YouCompleteMe or YCM cannot find it. For YCM to use it I need to move it from the build directory to the source or top level of the project.
Project
|- CMakeLists.txt
|- compile_commands.json (there is what i want)
|- build (this iw where i build my project)
|-- compile_commands.json (this is what i have)
|-- OtherCmakeGenerateStuff
|- src
|-- CMakeLists.txt
|-- main.cc (includes library not in project)
|-lib
|-- CMakeLists.txt
|-- math
|--- CMakeLists.txt
|--- math.cc
|--- math.hh
|-include
|-- globals.hh
|-- definitions.hh
There may be mistakes in these files as I just typed it and haven't tested it. however, my focus is on the top level CMakeLists.txt file.
CMakeLists.txt (Top level)
cmake_minimum_require(VERSION 3.8)
project(just_some_project LANGUAGES CXX VERSION 1.0)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
add_subdirectory(src)
add_subdirectory(lib)
include_directories(include)
src/CMakeLists.txt
cmake_minimum_require(VERSION 3.8)
project(just_some_project LANGUAGES CXX VERSION 1.0)
find_project(someotherlibrary REQUIRED)
add_executable(main main.cc)
target_include_directories(main PUBLIC math someotherlibraryfound)
lib/CMakeLists.txt (Top level)
cmake_minimum_require(VERSION 3.8)
file(GLOB Headers "*.hh")
file(GLOB Sources "*.cc")
add_library(math STATIC $Sources $Headers)

CMake: avoid building tests by default [duplicate]

I have a small project with cmake. I build a lib and an executable. on the development machine I want also an executable that cannot be build on other machines/environments.
e.g.:
<my-lib>
| -- CMakeLists.txt
|
+ -- src/ -> build the lib/archive
| |-- lib.c
| |-- lib.h
| |-- CMakeLists.txt
|
+ -- tool -> build the tool
| |-- tool.c
| |-- CMakeLists.txt
|
+ -- tests -> build the unit tests
| |-- tests.c
| |-- CMakeLists.txt
I added CMakeLists.txt to all directories. Also an add_executable to the tests. Now the unit-test executable is build by default. But I want to exclude it from default target.
CMakeLists.txt in tests:
find_library (CUNIT_LIB cunit)
include_directories (${Cunit_INCLUDE_DIRS} "${PROJECT_SOURCE_DIR}/src")
set (CMAKE_C_FLAGS "-O2 -Wall -Werror")
add_executable (unit-test tests.c)
target_link_libraries (unit-test my-lib cunit)
Has anyone a hint how to handle this? I don't want to build unit-test always!
There is a property EXCLUDE_FROM_ALL for such task.
You can write:
set_target_properties(unit-test PROPERTIES EXCLUDE_FROM_ALL TRUE)
This is very simple: protect creation of executable by introducing an option/variable.
if (DEFINED WITH_UNIT_TEST)
find_library (CUNIT_LIB cunit)
include_directories (${Cunit_INCLUDE_DIRS} "${PROJECT_SOURCE_DIR}/src")
set (CMAKE_C_FLAGS "-O2 -Wall -Werror")
add_executable (unit-test tests.c)
target_link_libraries (unit-test my-lib cunit)
endif ()
Now when invoking CMake, one would have to explicitly specify -DWITH_UNIT_TEST, so that unit-test target is built, while by default it will never be build. For alternative approach, see comments.

Src folder without CMakeLists.txt

I'm starting with cmake I'm following a tutorial that has as an example the following structure:
$ tree -L 2 ExampleProject/
ExampleProject/
|-- build/
|-- CMakeLists.txt
|-- inc/
| |-- Formula.h
|-- src/
| |-- CMakeLists.txt
| |-- Formula.cpp
| |-- main.cpp
To include the header files that are in the inc folder I just have to include_directories inside CMakeLists.txt in the src folder. However, I would not want to put CMakeLists files in the source folder, because I think that in this folder should be only source files. I don't like to mix build files with application source files. So my idea is to use only top level CMakeLists, ie:
$ tree -L 2 ExampleProject/
ExampleProject/
|-- build/
|-- CMakeLists.txt
|-- inc/
| |-- Formula.h
|-- src/
| |-- Formula.cpp
| |-- main.cpp
That is, I don't want to treat the src folder as a subdirectory.
It is possible? Is there something like source_directories?
Here is how far I managed to get:
cmake_minimum_required(VERSION 3.10)
project(ExampleProject)
set(CMAKE_CXX_STANDARD 14)
include_directories(inc)
set(BINARY ${CMAKE_PROJECT_NAME})
file(GLOB_RECURSE SOURCES LIST_DIRECTORIES true *.h)
set(SOURCES ${SOURCES})
add_executable(${BINARY}_run ${SOURCES})
But it would not work because it has to include the .cpp files.
You can add executable from top level CMakelists.txt file. With add_executable(target src/main.cpp). You can try something like this:
add_executable(output src/main.cpp)
add_library(formula src/Formula.cpp)
target_include_directories(formula PUBLIC inc)
target_link_libraries(output PUBLIC formula)

CMake's find_package does not find library added with add_subdirectory

I'm building a test project to learn libraries zeromq with cppmq, and I want to include both libraries as subdirectories. I currently have the following structure:
|-- CMakeLists.txt
|-- deps
| |-- cppzmq-4.3.0
| | |-- CMakeLists.txt
| | `-- rest of files
| |-- zeromq-4.3.1
| | |-- CMakeLists.txt
| | `-- rest of files
`-- main.cpp
I've tried with the following CMakeLists:
cmake_minimum_required(VERSION 3.14)
project(PruebaZeroMQ)
set(CMAKE_CXX_STANDARD 11)
add_subdirectory(deps/zeromq-4.3.1)
add_subdirectory(deps/cppzmq-4.3.0)
add_executable(PruebaZeroMQ main.cpp)
target_link_libraries(PruebaZeroMQ
libzmq
cppzmq)
When I run cmake, I get the following error:
-- Detected CPPZMQ Version - 4.3.0
-- CMake libzmq package not found, trying again with pkg-config (normal install of zeromq)
CMake Error at deps/cppzmq-4.3.0/CMakeLists.txt:20 (message):
ZeroMQ was not found, neither as a CMake package nor via pkg-config
cppmq depends on zeromq, and looks like it tries to load it using find_package, so I tried to modify CMAKE_MODULE_PATH so it could find the ZeroMQConfig.cmake file, but it fails too, with the same error:
add_subdirectory(deps/zeromq-4.3.1)
list (APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_BINARY_DIR}/deps/zeromq-4.3.1 ")
add_subdirectory(deps/cppzmq-4.3.0)
Is there a way of achieving this? I'd rather not install the libraries system-wide.
After trying to manually find_package, CMake showed the following error message:
Add the installation prefix of "ZeroMQ" to CMAKE_PREFIX_PATH or set
"ZeroMQ_DIR" to a directory containing one of the above files.
So I tried that, using:
set(ZeroMQ_DIR ${CMAKE_CURRENT_BINARY_DIR}/deps/zeromq-4.3.1)
And it worked.

How to configure cmake to get an executable not by default

I have a small project with cmake. I build a lib and an executable. on the development machine I want also an executable that cannot be build on other machines/environments.
e.g.:
<my-lib>
| -- CMakeLists.txt
|
+ -- src/ -> build the lib/archive
| |-- lib.c
| |-- lib.h
| |-- CMakeLists.txt
|
+ -- tool -> build the tool
| |-- tool.c
| |-- CMakeLists.txt
|
+ -- tests -> build the unit tests
| |-- tests.c
| |-- CMakeLists.txt
I added CMakeLists.txt to all directories. Also an add_executable to the tests. Now the unit-test executable is build by default. But I want to exclude it from default target.
CMakeLists.txt in tests:
find_library (CUNIT_LIB cunit)
include_directories (${Cunit_INCLUDE_DIRS} "${PROJECT_SOURCE_DIR}/src")
set (CMAKE_C_FLAGS "-O2 -Wall -Werror")
add_executable (unit-test tests.c)
target_link_libraries (unit-test my-lib cunit)
Has anyone a hint how to handle this? I don't want to build unit-test always!
There is a property EXCLUDE_FROM_ALL for such task.
You can write:
set_target_properties(unit-test PROPERTIES EXCLUDE_FROM_ALL TRUE)
This is very simple: protect creation of executable by introducing an option/variable.
if (DEFINED WITH_UNIT_TEST)
find_library (CUNIT_LIB cunit)
include_directories (${Cunit_INCLUDE_DIRS} "${PROJECT_SOURCE_DIR}/src")
set (CMAKE_C_FLAGS "-O2 -Wall -Werror")
add_executable (unit-test tests.c)
target_link_libraries (unit-test my-lib cunit)
endif ()
Now when invoking CMake, one would have to explicitly specify -DWITH_UNIT_TEST, so that unit-test target is built, while by default it will never be build. For alternative approach, see comments.