[CMake] CMake single-configuration makefiles

Michael Hertling mhertling at online.de
Mon Nov 14 10:23:05 EST 2011


On 11/13/2011 11:30 PM, Eric Noulard wrote:
> 2011/11/13 Robert Dailey <rcdailey at gmail.com>:
>> I understand that currently Makefiles generated by CMake are
>> single-configuration by-design. In other words, you can't issue a "debug" or
>> "release" command to make, you have to regenerate for a different
>> configuration.
> 
> For this purpose you can use 2 build trees that use the same source tree
> 
> cd /path/to/source
> mkdir dbuild
> cd dbuild
> cmake -DCMAKE_BUILD_TYPE=Debug ..
> cd ..
> mkdir rbuild
> cd rbuild
> cmake -DCMAKE_BUILD_TYPE=Release ..
> 
> now to build debug
> cd dbuild; make
> 
> and to build release
> cd rbuild; make
> 
>> Can someone explain the reason for this design?
> 
> Don't know in the past I used makefile that can deal with multiple config,
> everytime it was a "re-entrant" makefile scheme. May be this should be
> avoided in order to produce 'portable' makefiles?
> 
>> Are there any plans to  change it?
> 
> Don't know.
> But may be it would be nice to have a way to create
> "several build trees" at one time for that and produce a unifying
> top level makefile that drive them all.

One can nearly achieve this by now; see the following example:

# CMakeLists.txt:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(MULTICONFIG C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
# -->
IF(CMAKE_CONFIGURATION_TYPES)
    ADD_CUSTOM_TARGET(configs)
    ADD_CUSTOM_TARGET(builds)
    FOREACH(i IN LISTS CMAKE_CONFIGURATION_TYPES)
        FILE(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/${i})
        ADD_CUSTOM_TARGET(config-${i}
            COMMAND ${CMAKE_COMMAND} ${PROJECT_SOURCE_DIR}
                -DCMAKE_BUILD_TYPE=${i}
                -DCACHE_DIR=${PROJECT_BINARY_DIR}
                WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/${i})
        ADD_DEPENDENCIES(configs config-${i})
        ADD_CUSTOM_TARGET(build-${i}
            COMMAND ${CMAKE_COMMAND} --build ${PROJECT_BINARY_DIR}/${i})
        ADD_DEPENDENCIES(builds build-${i})
    ENDFOREACH()
    FOREACH(i IN LISTS FORWARD)
        ADD_CUSTOM_TARGET(${i}
            COMMAND ${CMAKE_COMMAND} --build \${CONFIG} --target ${i})
    ENDFOREACH()
    ADD_CUSTOM_TARGET(world ALL
        COMMAND ${CMAKE_COMMAND} --build \${CONFIG})
    RETURN()
ENDIF()
IF(DEFINED CACHE_DIR)
    LOAD_CACHE(${CACHE_DIR}
        EXCLUDE CMAKE_BUILD_TYPE CMAKE_CONFIGURATION_TYPES)
ENDIF()
# <--
ADD_EXECUTABLE(main main.c)
SET_TARGET_PROPERTIES(main PROPERTIES
    COMPILE_DEFINITIONS_DEBUG DEBUG
    COMPILE_DEFINITIONS_RELEASE RELEASE)

/* main.c: */
int main(void){return 0;}

The part to add in order to enable multiple build trees is between
"-->" and "<--". The basic idea is: If CMAKE_CONFIGURATION_TYPES is
defined, create a build tree for each entry along with a custom target
which configures that tree with the correct CMAKE_BUILD_TYPE. Also, add
a custom target which builds that tree via "cmake --build". These build
trees' configurations must be a separate step - therefore, driven by a
custom target - because the top-level configuration has to be finished
to enable the several build trees to load the cache via LOAD_CACHE();
otherwise, the initial -D settings would not be forwarded. For these
reasons, we need the CACHE_DIR variable, and the RETURN() statement
disables the actual part of the CMakeLists.txt file in the top-level
configuration. Obviously, CMAKE_BUILD_TYPE/CMAKE_CONFIGURATION_TYPES
must not be loaded from the cache to the build trees. A downside is
that you must implement your own, say, "target forwarding" if you
want to be able to say "main <TARGET> CONFIG=debug"; the FORWARD
variable with a list of targets to be forwarded is used for this
purpose. To see the project in action, go to an empty top-level
build directory and issue, e.g.:

cmake <srcdir> \
-DCMAKE_CONFIGURATION_TYPES="debug;release;custom" \
-DCMAKE_C_FLAGS_CUSTOM="-DCUSTOM" \
-DFORWARD="main"

This will create the subdirectories {debug,release,custom}; now

make configs

to configure the different build trees and load the cache from the
top-level configuration. Subsequently, you can say "make builds" to
build all configurations, or "cmake --build <config>" to build only
one, or "make main CONFIG=<CONFIG>" or even "make CONFIG=<CONFIG>".

Anyway, although the addition to mimic the behavior of a real multi-
configuration generator to the Makefiles ones is no big deal, I'm
in doubt if it's worth the effort. Personally, I'm fine with the
Makefiles generators' single-configuration limitation.

Regards,

Michael

>> I'm new to Makefiles so I'm curious to learn as much as possible
>> about it.
> 
> If you are new to makefile may be you can have a look at
> the chapter 4 of this book
> http://www.informit.com/store/product.aspx?isbn=0130091154
> 
> it is about GNU make but it contains valuable introduction to "makefile"
> and the book may downloaded:
> http://ptgmedia.pearsoncmg.com/images/0130091154/downloads/0130091154.zip


More information about the CMake mailing list