From e0e1a24eddede59c424df63a8237445196f3f65c Mon Sep 17 00:00:00 2001 From: Roland Kaufmann Date: Wed, 23 Jan 2013 14:28:12 +0100 Subject: [PATCH] Module to precompile headers This CMake module will set up a target for compiling a set of headers which can then be added to compilation modules to speed up compilation. A separate target is created because the function doesn't know all the sources of a target, and to reuse the precompiled header across several targets that share the same characteristics (such as unit tests). --- cmake/Modules/UsePrecompHeaders.cmake | 150 ++++++++++++++++++++++++++ 1 file changed, 150 insertions(+) create mode 100644 cmake/Modules/UsePrecompHeaders.cmake diff --git a/cmake/Modules/UsePrecompHeaders.cmake b/cmake/Modules/UsePrecompHeaders.cmake new file mode 100644 index 00000000..43092ff5 --- /dev/null +++ b/cmake/Modules/UsePrecompHeaders.cmake @@ -0,0 +1,150 @@ +# - Use precompiled headers +# +# precompile_header takes these parameters +# +# language Language in which the header is written; C or CXX. +# +# type Type of target being build, SHARED_LIBRARY, STATIC_LIBRARY +# or EXECUTABLE. +# +# header Relative path within the source tree to the header +# that contains the list of includes to be precompiled. +# This header should not be added to the installation, +# as it will be specific for this project. +# +# target Name of target to be created. All targets that +# use the precompiled header should depend on this target +# so that it is built before them. A variable with this +# name will also be created which contains the file name. +# +# flags_name Name of variable to receive the flags that should be +# added to the command-line. +# +# Example: +# get_target_property (type opmcore TYPE) +# precompile_header (CXX ${type} +# HEADER "opm/core/opm-core-pch.hpp" +# TARGET opmcore_CXX_pch +# FLAGS opmcore_PRECOMP_CXX_FLAGS +# ) +# set_source_files_properties (${opmcore_CXX_SOURCES} PROPERTIES +# OBJECT_DEPENDS "${opmcore_CXX_pch}" +# COMPILE_FLAGS "${opmcore_PRECOMP_CXX_FLAGS}" +# ) + +# reconstruct the compiler command line; this does NOT include the +# DEFINE_SYMBOL that is added for shared libraries. type is the TYPE +# target property. +# see larsch's PrecompiledHeader.cmake: +# and +function (compiler_cmdline language type cmd_name args_name) + # get the compiler for this particular language + set (${cmd_name} "${CMAKE_${language}_COMPILER}" PARENT_SCOPE) + + # in case someone has overridden the compiler (e.g. ccache) + set (_args "${CMAKE_${language}_COMPILER_ARG1}") + + # macro definitions + get_directory_property (_defs DEFINITIONS) + list (APPEND _args "${_defs}") + + # global flags (such as -std=c++11); notice that there are both + # release-dependent and non-release-dependent ones + string (TOUPPER "CMAKE_${language}_FLAGS" _flags) + list (APPEND _args "${${_flags}}") + string (TOUPPER "CMAKE_${language}_FLAGS_${CMAKE_BUILD_TYPE}" _flags) + list (APPEND _args "${${_flags}}") + + # assume that we are always generating position-independent code + # when compiling for a shared library + if (type STREQUAL "SHARED_LIBRARY") + list (APPEND _args "${CMAKE_SHARED_LIBRARY_${language}_FLAGS}") + endif (type STREQUAL "SHARED_LIBRARY") + + # directories included + get_directory_property (_dirs INCLUDE_DIRECTORIES) + foreach (_dir ${_dirs}) + list (APPEND _args "-I${_dir}") + endforeach (_dir) + + # make arguments a real list, and write to output variable + separate_arguments (_args) + set (${args_name} "${_args}" PARENT_SCOPE) +endfunction (compiler_cmdline language type cmd_name args_name) + +# probe the GCC version +function (get_gcc_version language ver_name) + # exec_program is deprecated, but execute_process does't work :-( + exec_program (${CMAKE_${language}_COMPILER} + ARGS ${CMAKE_${language}_COMPILER_ARG1} -dumpversion + OUTPUT_VARIABLE _version + ) + set (${ver_name} ${_version} PARENT_SCOPE) +endfunction (get_gcc_version ver_name) + +function (precompile_header + language type hdr_kw header tgt_kw target flgs_kw flags_name) + + # check "syntax" + if (NOT hdr_kw STREQUAL "HEADER") + message (FATAL "Third token to precompile_header shoulde be \"HEADER\"") + endif (NOT hdr_kw STREQUAL "HEADER") + if (NOT tgt_kw STREQUAL "TARGET") + message (FATAL "Fifth token to precompile_header should be \"TARGET\"") + endif (NOT tgt_kw STREQUAL "TARGET") + if (NOT flgs_kw STREQUAL "FLAGS") + message (FATAL "Seventh token to precompile_header should be \"FLAGS\"") + endif (NOT flgs_kw STREQUAL "FLAGS") + + # only support precompiled headers if the compiler is gcc >= 3.4 + if (CMAKE_COMPILER_IS_GNUCXX) + get_gcc_version (${language} GCC_VERSION) + if (GCC_VERSION VERSION_EQUAL 3.4 OR GCC_VERSION VERSION_GREATER 3.4) + # command-line used to compile modules in this kind of target + compiler_cmdline (${language} ${type} _cmd _args) + + # gcc will include any configurations which are in a directory + # with the same name as the header included + set (_pch_dir "CMakeFiles/pch") + set (_pch_file "${_pch_dir}/${header}.gch/${target}") + + # make sure that output directory exists + get_filename_component (_outdir "${PROJECT_BINARY_DIR}/${_pch_file}" PATH) + file (MAKE_DIRECTORY ${_outdir}) + + # we need to generate the precompiled header in the output tree, but + # at the same time prevent the compiler to pick up the header from the + # source tree. getting the order of the include paths right is fragile + # in CMake. by copying the header, we can put the precompile dump + # right next to it and have the compiler pick it up there + add_custom_command ( + OUTPUT "${_pch_dir}/${header}" + COMMAND ${CMAKE_COMMAND} + ARGS -E copy "${PROJECT_SOURCE_DIR}/${header}" "${_pch_dir}/${header}" + DEPENDS "${PROJECT_SOURCE_DIR}/${header}" + ) + + # add a makefile rule to create the precompiled header + add_custom_command ( + OUTPUT ${PROJECT_BINARY_DIR}/${_pch_file} + COMMAND ${_cmd} + ARGS ${_args} "-o" "${_pch_file}" "${_pch_dir}/${header}" + DEPENDS "${_pch_dir}/${header}" + COMMENT "Precompiling headers ${_pch_file}" + ) + + # create a phony target that is always built, but which only checks + # if the header file is OK (i.e. the header only gets rebuilt if + # necessary) + add_custom_target (${target} ALL + DEPENDS ${PROJECT_BINARY_DIR}/${_pch_file} + ) + + # these flags need to be added to the target + set (${target} "${_pch_file}" PARENT_SCOPE) + set (${flags_name} "-Winvalid-pch -include ${_pch_dir}/${header}" PARENT_SCOPE) + endif (GCC_VERSION VERSION_EQUAL 3.4 OR GCC_VERSION VERSION_GREATER 3.4) + endif (CMAKE_COMPILER_IS_GNUCXX) + +endfunction (precompile_header + language type header tgt_kw target flgs_kw flags_name)