Where to set CMAKE_CONFIGURATION_TYPES in a project with subprojects
Asked Answered
D

2

7

Lets say I have a project with two independent subprojects. If I understood cmake correctly, the idea would be to have one root CMakeLists.txt defining a project(...) and then using add_subdirectory(...) to include the subprojects. Each subproject would have its own CMakeLists.txt defining its own project. This way projects can be build either together (using the root cmake file) or individually (using the subprojects cmake file).

I now would like to change the CMAKE_CONFIGURATION_TYPES. Should I do this in the root CMakeLists.txt or in each subproject, or both?

Changing it in the root would mean that building a subproject individually would offer the wrong configuration types; the other options would duplicate the cmake code. I think I'm missing something here.

Datolite answered 21/7, 2015 at 17:55 Comment(5)
Why you need to change CMAKE_CONFIGURATION_TYPES, how would you like to modify it?Haunch
Instead of Release, Debug, and RelWithDebugInfo, I would like to have Release, Debug, and Profile.Datolite
I currently do this: set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Profile") set(CMAKE_CONFIGURATION_TYPES "${CMAKE_CONFIGURATION_TYPES}" CACHE STRING "List of supported configurations.") mark_as_advanced(CMAKE_CONFIGURATION_TYPES) if(NOT CMAKE_BUILD_TYPE) message("Defaulting to release build.") set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: ${CMAKE_CONFIGURATION_TYPES}." FORCE) endif()Datolite
Then you really need to do the same thing both in the root and in the subproject. Only thing you can do is to factorize out the config-machinery to an external macro called from each CMakeLists.txt'sHaunch
Thanks. Would there be any problems with defining things twice, i.e. do I have to take care not to overwrite things? Do you want to turn your comment into an answer?Datolite
H
14

Factorize out the code that sets up configuration-dependent settings. Create a file, say, SetUpConfigurations.cmake with this content:

if(NOT SET_UP_CONFIGURATIONS_DONE)
    set(SET_UP_CONFIGURATIONS_DONE TRUE)

    # No reason to set CMAKE_CONFIGURATION_TYPES if it's not a multiconfig generator
    # Also no reason mess with CMAKE_BUILD_TYPE if it's a multiconfig generator.
    get_property(isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
    if(isMultiConfig)
        set(CMAKE_CONFIGURATION_TYPES "Debug;Release;Profile" CACHE STRING "" FORCE) 
    else()
        if(NOT CMAKE_BUILD_TYPE)
            message("Defaulting to release build.")
            set(CMAKE_BUILD_TYPE Release CACHE STRING "" FORCE)
        endif()
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY HELPSTRING "Choose the type of build")
        # set the valid options for cmake-gui drop-down list
        set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug;Release;Profile")
    endif()
    # now set up the Profile configuration
    set(CMAKE_C_FLAGS_PROFILE "...")
    set(CMAKE_CXX_FLAGS_PROFILE "...")
    set(CMAKE_EXE_LINKER_FLAGS_PROFILE "...")
endif()

Then include(..) this file at the beginning of the CMakeLists.txt's.

You have two choices about where to put SetUpConfigurations.cmake, it depends on how you organize your projects, repositories:

  1. The quick'n'dirty way: Copy and commit this script into each project that needs it. Its location will be fixed, relative to the CMakeLists.txt of the project. So you can include it, for example, with include(${CMAKE_CURRENT_SOURCE_DIR}/<...>/SetUpConfigurations.cmake)

  2. The disciplined way: Maintain a repository with your custom CMake scripts, like this one. Each time you generate a project with the cmake command, you pass the path to this repository in the CMAKE_MODULE_PATH variable:

    cmake -DCMAKE_MODULE_PATH=<dir-of-cmake-script-repo> ...
    

In this case include the script with include(SetUpConfigurations) (no .cmake extension).

A note about what a multiconfig generator is:

Xcode and Visual Studio are multiconfig generators. They respect the value of CMAKE_CONFIGURATION_TYPES but CMAKE_BUILD_TYPE has no effect since no concrete configuration is defined when the CMakeLists.txt is processed. It will be selected on the IDE's user interface later.

On the other hand, the makefile-style generators are not interested in CMAKE_CONFIGURATION_TYPES. CMAKE_BUILD_TYPE defines the configuration. It is a concrete value when the CMakeLists.txt file is processed but still: never make any decisions based on the value of CMAKE_BUILD_TYPE:

if(CMAKE_BUILD_TYPE STREQUAL "Release") # WRONG!
    ....
endif()

You project won't work as intended in multiconfig generators.

Haunch answered 21/7, 2015 at 20:9 Comment(5)
I have another follow-up question. You said to include(..) the file at the beginning of each CMakeLists.txt. Does this mean I have to use relative includes like ${CMAKE_CURRENT_SOURCE_DIR}/../SetUpConfigurations.cmake? This would mean that I can't just copy my subdirector somewhere and call cmake on the copy. Its not really a problem and I guess there is no way of avoiding it other than copying the file in each subdirectory, but just to be sure, I wanted to ask.Datolite
@Flogo, I extended the answer with advice on thisHaunch
Thanks, i may add that on Visual Studio i'm using this code to redefine the CMAKE_CXX_FLAGS_<xyz> to include the /MP compile time switch to boost the compilation to all available CPU coresUnstrained
This is an excellent answer and deals with single- and multiconfig generators in an elegant way.Anabolite
Also according to cmake co-maintainer Craig Scott's post here (gitlab.kitware.com/cmake/cmake/issues/17645#note_367457), the GENERATOR_IS_MULTI_CONFIG property should be used to check for multiconfig rather than checking the CMAKE_CONFIGURATION_TYPES variable. I'll edit the answer now to include his suggestion.Shotgun
C
1

When use add_subdirectory into subproject dir, you propagate almost all variables into that subproject, which contradicts to "subproject independency".

Instead, it is better to build and install subproject using nested cmake call inside execute_process(). If you want to make some subproject's definitions available for top-level project, you need to "export" this definitions when subproject is installed. This question/answer post describes, how to do that.

Cyperaceous answered 21/7, 2015 at 18:34 Comment(4)
This seems like a workaround to me. Do you have a link to a project where this is used? Maybe the wording in my question was misleading, too. The subprojects are independent in that they share no code and do not link to each other. But it still is the common case to compile and use both of them. I also don't mind propagating stuff down the hierarchy, in fact, I want to propagate CMAKE_CONFIGURATION_TYPES down. Preferably without having to specify it twice.Datolite
@Tsyvarev: Nested cmake calls are good things. But there are very common cases where add_subdirectory is the way to go for an otherwise independent project. Example: Imagine an app-suite consisting of many inhouse library and app repositories. Each repo is developed in its standalone project. Then these projects are put into the app-suite super-project as subdirectories to be able to edit and build their source files in one IDE solution.Haunch
@Flogo: For example, see that cmake file: github.com/euspectre/kedr/blob/master/sources/CMakeLists.txt. Branch under else (KEDR_GEN) installs subproject kedr_gen. If you want subprojects to share some code, place this code in shared .cmake file and include() it in the subprojects. @tamas.kenez: Yes, there are cases when combining subprojects is more impotant, than independency.Cyperaceous
@Tsyvarev: Thanks for the example. I'm still not convinced, though. The cmake file calls commands like mkdir, rm, and make which seems to defeat the purpose of cmake being plattform-independent. I also didn't realize before that nesting cmake calls would mean that you would no longer have all projects in one IDE solution as tamas.kenez points out. In my case, I prefer having the projects in one solution. For me, all projects are in the same repo but they are standalone apps in something like an app-suite.Datolite

© 2022 - 2024 — McMap. All rights reserved.