4

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.

Grief
  • 1,839
  • 1
  • 21
  • 40
  • 1
    Project layout is mostly up to the author. Each of your requirement has many possible decisions, and not only from the cmake side. E.g., reusing of `C` in other projects could be task for version-control system. As you are new to cmake, you better start from **any simple layout** just for **make project working**. – Tsyvarev Nov 05 '15 at 08:09
  • @Tsyvarev project is already working, but with the simple cmake configuration I've made, it is pretty monolithic. If there are many options for achieving the goal, which option is the best? If the information in the question is not enough, what are additional conditions? – Grief Nov 05 '15 at 09:41
  • 1
    **There is no "the best option"**. There are many "addition conditions" which may affects on "goodness" order of different decisions. And some of these conditions, like your 3d one, are *subjective*. You question could be good for *collect different opinions* about possible layouts, but SO site is of *question-answer* kind, and such questions are simply not suited for it. – Tsyvarev Nov 05 '15 at 09:57

1 Answers1

7

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
  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.

Community
  • 1
  • 1
Florian
  • 39,996
  • 9
  • 133
  • 149