Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

preferred cmake project structure [closed]

Tags:

c++

cmake

I would like to have the following structure A -> B -> C, where:

  • C is boilerplate code, wrappers for third-party libraries, very basic code etc.
  • B is the common classes, functions and data structures specific to the project's domain.
  • A is the project itself.

I would like to make it easy to reuse C or B(+C) in future in my other projects. In addition, I have the following requirements:

  1. As all three projects are in-progress, I would like to have an ability to build C, C+B and C+B+A in one shot.
  2. I would prefer the static linkage over dynamic, so that C and C+B would be static libraries, and C+B+A would be the executable
  3. I would like to keep cmake lists and config files simple and clean. Examples which I found in the official wiki and over the internet are pretty big and monstrous.
  4. It would be great if it won't require changing more than a couple of lines if I'd change the locations of A, B or C in the filesystem.
  5. All these three components are using google-test, but I'm not sure if it is important for the project layout.

I am pretty new to cmake and I don't even understand is it better to write XXXConfig.cmake or FindXXX.cmake files. Also, I am not sure, how should I pass relative paths from subcomponent to the parent component using X_INCLUDE_DIRS.

like image 608
Grief Avatar asked Oct 16 '25 08:10

Grief


1 Answers

First I have to admit that I agree with @Tsyvarev. Your CMake environment should fit to your processes/workflow and should take project sizes and team structure into account. Or generally speaking the environment CMake will be used in. And this tends to be - in a positive way - very alive.

So this part of your question is difficult to answer and I'll concentrate on the technical part:

  1. CMake has to know the location of the dependencies - relative or absolute - by
    • having a monolithic source tree (the one you don't want anymore)
      • CMake share library with multiple executables
      • CMake: How to setup Source, Library and CMakeLists.txt dependencies?
    • a common directory location for includes/libraries/binaries
      • Custom Directory for CMake Library Output
      • cmake install not installing libraries on windows
    • getting the paths via config files/variable definitions
      • How can I get cmake to find my alternative boost installation?
      • How to add_custom_command() for the CMake build process itself?
    • using registration in or installation from a database provided on the host
      • Making cmake library accessible by other cmake packages automatically
      • cmake wont run build_command in ExternalProject_Add correctly
  2. To keep your CMake files as simple as possible I would recommend to group your CMake code into separate dedicated files:
    • Prefer toolchain files over if(SomeCompiler) statements
    • Move common/repeating code parts as function() bodies into a shared CMake include file
    • Move complex non-target specific code parts into their own (CMake) script files

Example Code

Since you have specifically asked for the find_package() variant, taking Use CMake-enabled libraries in your CMake project and the things listed above:

MyCommonCode.cmake

cmake_policy(SET CMP0022 NEW)

function(my_export_target _target _include_dir)
    file(
        WRITE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Config.cmake"
        "
            include(\"\$\{CMAKE_CURRENT_LIST_DIR\}/${_target}Targets.cmake\")
            set_property(
                TARGET ${_target} 
                APPEND PROPERTY 
                    INTERFACE_INCLUDE_DIRECTORIES \"${_include_dir}\"
            )
        "
    )

    export(
        TARGETS ${_target} 
        FILE "${CMAKE_CURRENT_BINARY_DIR}/${_target}Targets.cmake"
        EXPORT_LINK_INTERFACE_LIBRARIES
    )
    export(PACKAGE ${_target}) 
endfunction(my_export_target)

C/CMakeLists.txt

include(MyCommonCode.cmake)
...
my_export_target(C "${CMAKE_CURRENT_SOURCE_DIR}/include")

B/CMakeLists.txt

include(MyCommonCode.cmake)

find_package(C REQUIRED)
...
target_link_libraries(B C)
my_export_target(B "${CMAKE_CURRENT_SOURCE_DIR}/include")

A/CMakeLists.txt

include(MyCommonCode.cmake)

find_package(B REQUIRED)
...
target_link_libraries(A B)

This keeps all 3 build environments separate, only sharing the relatively static MyCommonCode.cmake file. So in this approach I have so far not covered your first point, but would recommend the use of a external script to chain/trigger your build steps for A/B/C.

like image 100
Florian Avatar answered Oct 18 '25 22:10

Florian



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!