cmake_minimum_required(VERSION 3.0) get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) if(NOT _isMultiConfig AND NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE Debug CACHE STRING "Choose the type of build" FORCE) endif() project(ConfigSources CXX) if("${CMAKE_CXX_COMPILER_ID};${CMAKE_CXX_SIMULATE_ID}" STREQUAL "Intel;MSVC") string(APPEND CMAKE_CXX_FLAGS_DEBUG " -Z7") string(APPEND CMAKE_CXX_FLAGS_RELWITHDEBINFO " -Z7") endif() # Source file(s) named with the configuration(s). file(GENERATE OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/config_$.cpp" CONTENT [[ #if defined(_WIN32) && defined(OBJ_SHARED) __declspec(dllexport) #endif void config_$() {} ]] ) # Custom command outputs named with the configuration(s). add_custom_command( OUTPUT "custom1_$.cpp" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom1.cpp.in" "custom1_$.cpp" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom1.cpp.in VERBATIM ) # Output path starts in a generator expression. add_custom_command( OUTPUT "$<1:custom2_$.cpp>" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom2.cpp.in" "custom2_$.cpp" DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom2.cpp.in VERBATIM ) # Source file generated as a custom command's byproduct. add_custom_command( OUTPUT custom3.txt BYPRODUCTS "$<1:custom3_$.cpp>" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom3.cpp.in" "custom3_$.cpp" COMMAND ${CMAKE_COMMAND} -E touch custom3.txt DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom3.cpp.in VERBATIM ) # Source file generated as a custom target's byproduct. add_custom_target(custom4 BYPRODUCTS "custom4_$.cpp" COMMAND ${CMAKE_COMMAND} -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/custom4.cpp.in" "custom4_$.cpp" VERBATIM ) # Source file generated by appended custom command. add_custom_command( OUTPUT "custom5_$.cpp" COMMAND ${CMAKE_COMMAND} -E echo custom5_$.cpp DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/custom5.cpp.in VERBATIM ) add_custom_command(APPEND OUTPUT "custom5_$.cpp" COMMAND ${CMAKE_COMMAND} -E copy "${CMAKE_CURRENT_SOURCE_DIR}/custom5.cpp.in" "custom5_$.cpp.in" VERBATIM ) # Appending through any configuration's output affects all configurations. if(CMAKE_CONFIGURATION_TYPES MATCHES ";([^;]+)$") set(last_config "${CMAKE_MATCH_1}") else() set(last_config ${CMAKE_BUILD_TYPE}) endif() add_custom_command(APPEND OUTPUT "custom5_${last_config}.cpp" COMMAND ${CMAKE_COMMAND} -E copy "custom5_$.cpp.in" "custom5_$.cpp" VERBATIM ) foreach(n RANGE 1 5) foreach(other ${CMAKE_BUILD_TYPE} Release RelWithDebInfo MinSizeRel) set_property(SOURCE custom${n}_${other}.cpp PROPERTY COMPILE_DEFINITIONS CUSTOM_CFG_OTHER) endforeach() set_property(SOURCE custom${n}_Debug.cpp PROPERTY COMPILE_DEFINITIONS CUSTOM_CFG_DEBUG) endforeach() add_library(Custom STATIC custom1_$.cpp custom2_$.cpp custom3_$.cpp custom3.txt custom4_$.cpp custom5_$.cpp ) # Per-config sources via INTERFACE_SOURCES. add_library(iface INTERFACE) target_sources(iface INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp" "$<$:${CMAKE_CURRENT_SOURCE_DIR}/iface_debug_src.cpp>" "$<$>:${CMAKE_CURRENT_SOURCE_DIR}/iface_other_src.cpp>" "$<$:${CMAKE_CURRENT_SOURCE_DIR}/does_not_exist.cpp>" ) target_compile_definitions(iface INTERFACE "$<$:CFG_DEBUG>" "$<$>:CFG_OTHER>" ) add_executable(ConfigSources $<$:main_debug.cpp> $<$>:main_other.cpp> $<$:does_not_exist.cpp> ${CMAKE_CURRENT_BINARY_DIR}/config_$.cpp ) target_link_libraries(ConfigSources Custom iface) # Per-config sources via LINK_LIBRARIES. add_library(iface_debug INTERFACE) target_sources(iface_debug INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/iface_debug_src.cpp" ) add_library(iface_other INTERFACE) target_sources(iface_other INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp" "${CMAKE_CURRENT_SOURCE_DIR}/iface_other_src.cpp" ) add_executable(ConfigSourcesLink main.cpp) target_compile_definitions(ConfigSourcesLink PRIVATE "$<$:CFG_DEBUG>" "$<$>:CFG_OTHER>" ) target_link_libraries(ConfigSourcesLink PRIVATE Custom "$<$:iface_debug>" "$<$>:iface_other>" "$<$:iface_does_not_exist>" ) # Per-config sources via INTERFACE_LINK_LIBRARIES. add_library(ConfigSourcesIface INTERFACE) target_link_libraries(ConfigSourcesIface INTERFACE "$<$:iface_debug>" "$<$>:iface_other>" "$<$:iface_does_not_exist>" ) add_executable(ConfigSourcesLinkIface main.cpp) target_compile_definitions(ConfigSourcesLinkIface PRIVATE "$<$:CFG_DEBUG>" "$<$>:CFG_OTHER>" ) target_link_libraries(ConfigSourcesLinkIface Custom ConfigSourcesIface) # A target with sources in only one configuration that is not the # first in CMAKE_CONFIGURATION_TYPES. if(CMAKE_CONFIGURATION_TYPES MATCHES ";([^;]+)") set(one_config "${CMAKE_MATCH_1}") else() set(one_config "${CMAKE_BUILD_TYPE}") endif() add_library(OneConfigOnly OBJECT "$<$:${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp>") set_property(TARGET OneConfigOnly PROPERTY LINKER_LANGUAGE CXX) add_executable(ConfigSourcesUseOne main_one_config.cpp) target_compile_definitions(ConfigSourcesUseOne PRIVATE "$<$:CFG_ONE>") target_link_libraries(ConfigSourcesUseOne PRIVATE "$<$:OneConfigOnly>") add_library(OneConfigOnlyIface INTERFACE) target_sources(OneConfigOnlyIface INTERFACE "$<$:${CMAKE_CURRENT_SOURCE_DIR}/iface_src.cpp>") add_executable(ConfigSourcesUseOneIface main_one_config.cpp) target_compile_definitions(ConfigSourcesUseOneIface PRIVATE "$<$:CFG_ONE>") target_link_libraries(ConfigSourcesUseOneIface PRIVATE "$<$:OneConfigOnlyIface>") # --------------------------------------------------------------------------- # Makes sure that each configuration uses the correct generated file. add_library(ObjLibFromGeneratedSources OBJECT) set_property(TARGET ObjLibFromGeneratedSources PROPERTY POSITION_INDEPENDENT_CODE 1) target_compile_definitions(ObjLibFromGeneratedSources PRIVATE OBJ_SHARED) target_sources(ObjLibFromGeneratedSources PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config_$.cpp) add_library(SharedLibFromObjLibFromGeneratedSources SHARED shared.cpp) target_link_libraries(SharedLibFromObjLibFromGeneratedSources PRIVATE ObjLibFromGeneratedSources) # --------------------------------------------------------------------------- # Make sure that additional build-events do not confuse CMake when using generated files. add_library(SharedLibFromGeneratedSources SHARED) set_property(TARGET SharedLibFromGeneratedSources PROPERTY POSITION_INDEPENDENT_CODE 1) target_sources(SharedLibFromGeneratedSources PRIVATE shared.cpp ${CMAKE_CURRENT_BINARY_DIR}/config_$.cpp ) add_custom_command(TARGET SharedLibFromGeneratedSources POST_BUILD COMMAND "${CMAKE_COMMAND}" "-E" "echo" "$" )