include(ucm) macro(add_precompiled_header Target PrecompiledHeader PrecompiledSource IncludedAs) # gather all source files for the target cmake_parse_arguments(PCH "" "" "EXCLUDE" ${ARGN}) get_target_property(Sources ${Target} SOURCES) # precompiled headers for C/C++ sources are different, so don't apply to .c files list(FILTER Sources EXCLUDE REGEX "\\.c$") list(FILTER Sources EXCLUDE REGEX "\\.rc$") ucm_remove_files(${PCH_EXCLUDE} ${PrecompiledSource} FROM Sources) # determine output paths set(PrecompiledOutputDir "${CMAKE_CURRENT_BINARY_DIR}/${Target}_pch") file(MAKE_DIRECTORY ${PrecompiledOutputDir}) if(MSVC) get_filename_component(PrecompiledBasename ${PrecompiledHeader} NAME_WE) set(PrecompiledBinary "${PrecompiledOutputDir}/${PrecompiledBasename}.pch") message("Setting up precompiled header for MSVC") set_source_files_properties(${PrecompiledSource} PROPERTIES COMPILE_FLAGS "/Yc\"${IncludedAs}\" /Fp\"${PrecompiledBinary}\"" OBJECT_OUTPUTS "${PrecompiledBinary}") set_source_files_properties(${Sources} PROPERTIES COMPILE_FLAGS "/Yu\"${IncludedAs}\" /FI\"${IncludedAs}\" /Fp\"${PrecompiledBinary}\"" OBJECT_DEPENDS "${PrecompiledBinary}") endif() if(CMAKE_COMPILER_IS_GNUCXX) message("Setting up precompiled header for GCC") # we need to use custom commands to compile the header # first step, copy it to the build dir set(CopiedPch "${PrecompiledOutputDir}/${IncludedAs}") set(PchDepFile "${CopiedPch}.dep") add_custom_command(OUTPUT "${CopiedPch}" COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${CMAKE_CURRENT_SOURCE_DIR}/${PrecompiledHeader}" "${CopiedPch}" MAIN_DEPENDENCY "${CMAKE_CURRENT_SOURCE_DIR}/${PrecompiledHeader}" COMMENT "Copying header") # second step, we need to gather the same compile flags as for the target so that the compiled header works set(PchFlagsFile "${PrecompiledOutputDir}/compile_flags.rsp") export_all_flags(${Target} "${PchFlagsFile}") set(CompilerFlags "@${PchFlagsFile}") # third step, compile the header get_filename_component(BaseName ${IncludedAs} NAME) set(CompiledPch "${PrecompiledOutputDir}/${BaseName}.gch") if(CMAKE_GENERATOR STREQUAL "Ninja") # tell G++ to produce a depfile for the Ninja generator to keep track of when to rebuild the PCH add_custom_command(OUTPUT "${CompiledPch}" COMMAND "${CMAKE_CXX_COMPILER}" ${CompilerFlags} -x c++-header -o "${CompiledPch}" "${CopiedPch}" -MD -MF "${PchDepFile}" DEPENDS "${CopiedPch}" "${PchFlagsFile}" DEPFILE "${PchDepFile}" COMMENT "Precompiling header") else() # use CMake's implicit dependency scanner for Makefiles to determine when to rebuild PCH add_custom_command(OUTPUT "${CompiledPch}" COMMAND "${CMAKE_CXX_COMPILER}" ${CompilerFlags} -x c++-header -o "${CompiledPch}" "${CopiedPch}" DEPENDS "${CopiedPch}" "${PchFlagsFile}" IMPLICIT_DEPENDS CXX "${CopiedPch}" COMMENT "Precompiling header") endif() SET_SOURCE_FILES_PROPERTIES(${Sources} PROPERTIES COMPILE_FLAGS "-include \"${CopiedPch}\" -Winvalid-pch" OBJECT_DEPENDS "${CompiledPch}") endif() endmacro() function(export_all_flags Target Filename) set(_include_directories "$") set(_compile_definitions "$") string(TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" _current_build_compile_flags) set(_compile_flags "$" "${CMAKE_CXX_FLAGS}" "${${_current_build_compile_flags}}" "-std=c++14") set(_compile_options "$") set(_include_directories "$<$:-I$\n>") set(_compile_definitions "$<$:-D$\n>") set(_compile_flags "$<$:$\n>") set(_compile_options "$<$:$\n>") file(GENERATE OUTPUT "${Filename}" CONTENT "${_compile_definitions}${_include_directories}${_compile_flags}${_compile_options}\n") endfunction()