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

Michael Wild themiwi at gmail.com
Thu Oct 6 01:04:18 EDT 2011


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?
  )
###########################################################


HTH

Michael W.


More information about the CMake mailing list