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

Michael Hertling mhertling at online.de
Thu Oct 6 02:14:33 EDT 2011


On 10/06/2011 07:04 AM, Michael Wild wrote:
> On Thu 06 Oct 2011 05:17:00 AM CEST, Michael Hertling wrote:
>> 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
> 
> I think it would be much easier to have a wrapper file, say b_or_c.cpp
> which #include's b.cpp or c.cpp at compile time depending on the current
> configuration. E.g. like this:
> 
> ///////////////////////////////////////////////////////////
> #if defined USE_B_CPP
> #  include "b.cpp"
> #elseif defined USE_C_CPP
> #  include "c.cpp"
> #else // what should happen otherwise?
> #  error Either USE_B_CPP or USE_C_CPP must be defined!
> #endif
> ///////////////////////////////////////////////////////////
> 
> 
> And then in your CMakeLists.txt you do:
> 
> ###########################################################
> set_source_files_properties(b_or_c.cpp PROPERTIES
>   COMPILE_DEFINITIONS_DEBUG USE_B_CPP
>   COMPILE_DEFINITIONS_RELEASE USE_C_CPP
>   # what should happen in a default build?
>   # Or RELWITHDEBINFO and MINSIZEREL?
>   )
> ###########################################################

Yes, this would work, too, but if neither b.cpp nor c.cpp should be
compiled if the current configuration is neither DEBUG nor RELEASE,
the b_or_c.cpp file would be effectively empty, and adding an object
file compiled from an empty source file to a binary is not 100 % the
same as dropping the object file completely - at least with gcc and
even with -Os. However, it's a quite negligible effect, but linking
against an empty static library or building a reconfigured project
means *exactly* the same as if b.cpp and c.cpp have been left out.

Regards,

Michael


More information about the CMake mailing list