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

Michael Wild themiwi at gmail.com
Thu Oct 6 04:29:31 EDT 2011


On 10/06/2011 08:14 AM, Michael Hertling wrote:
> 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

You still could compile b_or_c.cpp into a static library first, no
trickery with IMPORTED required. However, if that static library is
going to be linked to a shared library, you would need to make sure that
CMAKE_SHARED_LIBRARY_CXX_FLAGS is included in the COMPILE_FLAGS of the
static library.

OTOH, putting the content of b_or_c.cpp into a file that is
unconditionally compiled would solve the whole problem in one go... That
file would naturally be the client code of what is defined in b.cpp or
c.cpp.

Michael


More information about the CMake mailing list