I am using a macro to create precompiled headers for my cmake project. For gcc, this macro uses add_custom_command to create a *.h.gch file which can then be added to the target along with the other source files with add_executable/add_library. The problem is that sometimes the same *.h.gch file is used for two different targets, because some libraries are built both as static and dynamic libs.
I need to call the macro after each of the add_library calls because for MSVC/Xcode, one needs to adjust the target properties to enable PCH usage/compilation. But for gcc, this results in an error as I'm trying to use add_custom_command with an output that already has a build rule (the .gch). Currently I am avoiding this error by just skipping the add_custom_command for any target that contains "Static" in its name - this happens to work because all the static libraries in the project have a "Static" postfix, but its obviously not a very elegant solution.
Is there a way in cmake to check if a target already has a build rule, or alternatively, a way to allow add_custom_command to fail silently without causing an error? Or is there a way to change my design so that I can avoid the problem entirely? I suppose one "solution" would be to add a conditional check in each of the CMakeLists, but I really don't want to do that.
This is the code I am currently using:
The Macro:
macro(SET_PRECOMPILED_HEADER targetName PCHFile)
if(MSVC)
# PCH for MSVC
elseif(${CMAKE_GENERATOR} MATCHES "Xcode")
# PCH for Xcode
else() #gcc
if(NOT ${targetName} MATCHES "Static") ## <-- this is bad
## set the correct "compilerArgs"
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${compilerArgs}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PCHFile}
)
endif()
endmacro(SET_PRECOMPILED_HEADER targetName PCHFile)
...then in the CMakeLists, something like this:
# Dynamic version:
set(MODULE_NAME MyLib)
project(${MODULE_NAME})
## set ${sources}
add_library(${MODULE_NAME} SHARED ${sources} "src/precompiled.h.${PCH_EXT}")
set_target_properties(${MODULE_NAME} PROPERTIES COMPILE_DEFINITIONS MY_DLL_DEFINITION)
SET_PRECOMPILED_HEADER(${MODULE_NAME} "src/precompiled.h")
# Static version:
set(MODULE_NAME MyLibStatic)
project(${MODULE_NAME})
add_library(${MODULE_NAME} ${sources} "src/precompiled.h.${PCH_EXT}")
set_target_properties(${MODULE_NAME} PROPERTIES COMPILE_DEFINITIONS MY_STATIC_DEFINITION)
SET_PRECOMPILED_HEADER(${MODULE_NAME} "src/precompiled.h")
Thanks for your help! I'm sorry if this is a duplicate - there are already several questions on add_custom_command, but none of them quite seem to address what I'm after.
First, you can create target for each PCH and then use this before declaring new target:
if(TARGET ${PCHFile}.gch)
Another way:
In the root CMakeLists.txt:
set(PRECOMPILED_HEADERS "" CACHE INTERNAL "")
In the macro:
list(FIND PRECOMPILED_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch res)
if(NOT res EQUAL -1)
add_custom_command(
OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch
COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${compilerArgs}
DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/${PCHFile}
)
list(APPEND PRECOMPILED_HEADERS ${CMAKE_CURRENT_BINARY_DIR}/${PCHFile}.gch)
endif()