[CMake] Include source files on a per-configuration basis

Michael Hertling mhertling at online.de
Wed Oct 5 23:17:00 EDT 2011


On 10/05/2011 10:47 PM, Robert Dailey wrote:
> In my particular CMake project, I have three CPP files:
> 
>     a.cpp
>     b.cpp
>     c.cpp
> 
> I want 'a.cpp' to be compiled in all configurations (release & debug).<br>
> I only want 'b.cpp' to be compiled in DEBUG configuration.<br>
> I only want 'c.cpp' to be compiled in RELEASE configuration.
> 
> How can I do this? I need something similar to the `debug` and `optimized`
> keywords that are accepted by the `target_link_libraries()` CMake operation.

If it's okay that b.cpp and c.cpp are compiled in all configurations but
incorporated in the final binaries only in the DEBUG or in the RELEASE
configuration, respectively, you might do the following:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(IMPORTEDEMPTY C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
# Add library for DEBUG:
FILE(WRITE ${CMAKE_BINARY_DIR}/b.c "void b(void){}\n")
ADD_LIBRARY(b STATIC b.c)
# Add library for RELEASE:
FILE(WRITE ${CMAKE_BINARY_DIR}/c.c "void c(void){}\n")
ADD_LIBRARY(c STATIC c.c)
# Add empty static library:
FILE(WRITE ${CMAKE_BINARY_DIR}/empty.c "")
ADD_LIBRARY(empty STATIC empty.c)
# Reimport empty static library:
EXPORT(TARGETS empty NAMESPACE imported FILE importedempty.cmake)
INCLUDE(${CMAKE_BINARY_DIR}/importedempty.cmake)
# Impose IMPORTED_LINK_INTERFACE_LIBRARIES_{DEBUG,RELEASE} properties:
FOREACH(i IN LISTS CMAKE_CONFIGURATION_TYPES ITEMS ${CMAKE_BUILD_TYPE})
    STRING(TOUPPER "${i}" i)
    IF(i STREQUAL "DEBUG")
        SET_TARGET_PROPERTIES(importedempty PROPERTIES
            IMPORTED_LINK_INTERFACE_LIBRARIES_${i} b)
    ELSEIF(i STREQUAL "RELEASE")
        SET_TARGET_PROPERTIES(importedempty PROPERTIES
            IMPORTED_LINK_INTERFACE_LIBRARIES_${i} c)
    ENDIF()
ENDFOREACH()
# Specify required dependencies:
ADD_DEPENDENCIES(importedempty empty b c)
# Add final binary:
FILE(WRITE ${CMAKE_BINARY_DIR}/a.c "int main(void){return 0;}\n")
ADD_EXECUTABLE(a a.c)
TARGET_LINK_LIBRARIES(a importedempty)

Adventurous, but somewhat clean; see [1] for an explanation, and be
especially careful with a file named "libc.a" on *nix systems. ;-)

If you really need to avoid the compilation of b.cpp or c.cpp in
certain configurations, you might try the following approach:

CMAKE_MINIMUM_REQUIRED(VERSION 2.8 FATAL_ERROR)
PROJECT(RECONF C)
SET(CMAKE_VERBOSE_MAKEFILE ON)
FILE(WRITE ${CMAKE_BINARY_DIR}/a.c "int main(void){return 0;}\n")
FILE(WRITE ${CMAKE_BINARY_DIR}/b.c "void b(void){}\n")
FILE(WRITE ${CMAKE_BINARY_DIR}/c.c "void c(void){}\n")
STRING(TOUPPER "${CONF}" CONF)
IF(CONF STREQUAL "DEBUG")
    ADD_EXECUTABLE(a0 EXCLUDE_FROM_ALL a.c b.c)
ELSEIF(CONF STREQUAL "RELEASE")
    ADD_EXECUTABLE(a0 EXCLUDE_FROM_ALL a.c c.c)
ELSE()
    ADD_EXECUTABLE(a0 EXCLUDE_FROM_ALL a.c)
ENDIF()
ADD_CUSTOM_TARGET(a ALL
    COMMAND ${CMAKE_COMMAND}
        -DCONF="$<CONFIGURATION>"
        ${CMAKE_BINARY_DIR}
    COMMAND ${CMAKE_COMMAND}
        --build ${CMAKE_BINARY_DIR}
        --config "$<CONFIGURATION>"
        --target a0)

Effectively, when target "a" is built, the project reconfigures itself
with the current configuration passed in via "CONF" and with a helper
target "a0" which is made up from the configuration-specific sources;
finally, this target "a0" is built with the current configuration.
This can be seen working on *nix with Makefiles, but there might
be issues with other generators and IDEs.

'hope that helps.

Regards,

Michael

[1] http://www.mail-archive.com/cmake@cmake.org/msg34680.html


More information about the CMake mailing list