-
-
Save larsch/573926 to your computer and use it in GitHub Desktop.
| # Function for setting up precompiled headers. Usage: | |
| # | |
| # add_library/executable(target | |
| # pchheader.c pchheader.cpp pchheader.h) | |
| # | |
| # add_precompiled_header(target pchheader.h | |
| # [FORCEINCLUDE] | |
| # [SOURCE_C pchheader.c] | |
| # [SOURCE_CXX pchheader.cpp]) | |
| # | |
| # Options: | |
| # | |
| # FORCEINCLUDE: Add compiler flags to automatically include the | |
| # pchheader.h from every source file. Works with both GCC and | |
| # MSVC. This is recommended. | |
| # | |
| # SOURCE_C/CXX: Specifies the .c/.cpp source file that includes | |
| # pchheader.h for generating the pre-compiled header | |
| # output. Defaults to pchheader.c. Only required for MSVC. | |
| # | |
| # Caveats: | |
| # | |
| # * Its not currently possible to use the same precompiled-header in | |
| # more than a single target in the same directory (No way to set | |
| # the source file properties differently for each target). | |
| # | |
| # * MSVC: A source file with the same name as the header must exist | |
| # and be included in the target (E.g. header.cpp). Name of file | |
| # can be changed using the SOURCE_CXX/SOURCE_C options. | |
| # | |
| # License: | |
| # | |
| # Copyright (C) 2009-2013 Lars Christensen <[email protected]> | |
| # | |
| # Permission is hereby granted, free of charge, to any person | |
| # obtaining a copy of this software and associated documentation files | |
| # (the 'Software') deal in the Software without restriction, | |
| # including without limitation the rights to use, copy, modify, merge, | |
| # publish, distribute, sublicense, and/or sell copies of the Software, | |
| # and to permit persons to whom the Software is furnished to do so, | |
| # subject to the following conditions: | |
| # | |
| # The above copyright notice and this permission notice shall be | |
| # included in all copies or substantial portions of the Software. | |
| # | |
| # THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, | |
| # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
| # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
| # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS | |
| # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
| # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| # SOFTWARE. | |
| include(CMakeParseArguments) | |
| macro(combine_arguments _variable) | |
| set(_result "") | |
| foreach(_element ${${_variable}}) | |
| set(_result "${_result} \"${_element}\"") | |
| endforeach() | |
| string(STRIP "${_result}" _result) | |
| set(${_variable} "${_result}") | |
| endmacro() | |
| function(export_all_flags _filename) | |
| set(_include_directories "$<TARGET_PROPERTY:${_target},INCLUDE_DIRECTORIES>") | |
| set(_compile_definitions "$<TARGET_PROPERTY:${_target},COMPILE_DEFINITIONS>") | |
| set(_compile_flags "$<TARGET_PROPERTY:${_target},COMPILE_FLAGS>") | |
| set(_compile_options "$<TARGET_PROPERTY:${_target},COMPILE_OPTIONS>") | |
| set(_include_directories "$<$<BOOL:${_include_directories}>:-I$<JOIN:${_include_directories},\n-I>\n>") | |
| set(_compile_definitions "$<$<BOOL:${_compile_definitions}>:-D$<JOIN:${_compile_definitions},\n-D>\n>") | |
| set(_compile_flags "$<$<BOOL:${_compile_flags}>:$<JOIN:${_compile_flags},\n>\n>") | |
| set(_compile_options "$<$<BOOL:${_compile_options}>:$<JOIN:${_compile_options},\n>\n>") | |
| file(GENERATE OUTPUT "${_filename}" CONTENT "${_compile_definitions}${_include_directories}${_compile_flags}${_compile_options}\n") | |
| endfunction() | |
| function(add_precompiled_header _target _input) | |
| cmake_parse_arguments(_PCH "FORCEINCLUDE" "SOURCE_CXX:SOURCE_C" "" ${ARGN}) | |
| get_filename_component(_input_we ${_input} NAME_WE) | |
| if(NOT _PCH_SOURCE_CXX) | |
| set(_PCH_SOURCE_CXX "${_input_we}.cpp") | |
| endif() | |
| if(NOT _PCH_SOURCE_C) | |
| set(_PCH_SOURCE_C "${_input_we}.c") | |
| endif() | |
| if(MSVC) | |
| set(_cxx_path "${CMAKE_CURRENT_BINARY_DIR}/${_target}_cxx_pch") | |
| set(_c_path "${CMAKE_CURRENT_BINARY_DIR}/${_target}_c_pch") | |
| make_directory("${_cxx_path}") | |
| make_directory("${_c_path}") | |
| set(_pch_cxx_header "${_cxx_path}/${_input}") | |
| set(_pch_cxx_pch "${_cxx_path}/${_input_we}.pch") | |
| set(_pch_c_header "${_c_path}/${_input}") | |
| set(_pch_c_pch "${_c_path}/${_input_we}.pch") | |
| get_target_property(sources ${_target} SOURCES) | |
| foreach(_source ${sources}) | |
| set(_pch_compile_flags "") | |
| if(_source MATCHES \\.\(cc|cxx|cpp|c\)$) | |
| if(_source MATCHES \\.\(cpp|cxx|cc\)$) | |
| set(_pch_header "${_input}") | |
| set(_pch "${_pch_cxx_pch}") | |
| else() | |
| set(_pch_header "${_input}") | |
| set(_pch "${_pch_c_pch}") | |
| endif() | |
| if(_source STREQUAL "${_PCH_SOURCE_CXX}") | |
| set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_cxx_pch}\" /Yc${_input}") | |
| set(_pch_source_cxx_found TRUE) | |
| elseif(_source STREQUAL "${_PCH_SOURCE_C}") | |
| set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_c_pch}\" /Yc${_input}") | |
| set(_pch_source_c_found TRUE) | |
| else() | |
| if(_source MATCHES \\.\(cpp|cxx|cc\)$) | |
| set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_cxx_pch}\" /Yu${_input}") | |
| set(_pch_source_cxx_needed TRUE) | |
| else() | |
| set(_pch_compile_flags "${_pch_compile_flags} \"/Fp${_pch_c_pch}\" /Yu${_input}") | |
| set(_pch_source_c_needed TRUE) | |
| endif() | |
| if(_PCH_FORCEINCLUDE) | |
| set(_pch_compile_flags "${_pch_compile_flags} /FI${_input}") | |
| endif(_PCH_FORCEINCLUDE) | |
| endif() | |
| get_source_file_property(_object_depends "${_source}" OBJECT_DEPENDS) | |
| if(NOT _object_depends) | |
| set(_object_depends) | |
| endif() | |
| if(_PCH_FORCEINCLUDE) | |
| if(_source MATCHES \\.\(cc|cxx|cpp\)$) | |
| list(APPEND _object_depends "${_pch_header}") | |
| else() | |
| list(APPEND _object_depends "${_pch_header}") | |
| endif() | |
| endif() | |
| set_source_files_properties(${_source} PROPERTIES | |
| COMPILE_FLAGS "${_pch_compile_flags}" | |
| OBJECT_DEPENDS "${_object_depends}") | |
| endif() | |
| endforeach() | |
| if(_pch_source_cxx_needed AND NOT _pch_source_cxx_found) | |
| message(FATAL_ERROR "A source file ${_PCH_SOURCE_CXX} for ${_input} is required for MSVC builds. Can be set with the SOURCE_CXX option.") | |
| endif() | |
| if(_pch_source_c_needed AND NOT _pch_source_c_found) | |
| message(FATAL_ERROR "A source file ${_PCH_SOURCE_C} for ${_input} is required for MSVC builds. Can be set with the SOURCE_C option.") | |
| endif() | |
| endif(MSVC) | |
| if(CMAKE_COMPILER_IS_GNUCXX) | |
| get_filename_component(_name ${_input} NAME) | |
| set(_pch_header "${CMAKE_CURRENT_SOURCE_DIR}/${_input}") | |
| set(_pch_binary_dir "${CMAKE_CURRENT_BINARY_DIR}/${_target}_pch") | |
| set(_pchfile "${_pch_binary_dir}/${_input}") | |
| set(_outdir "${CMAKE_CURRENT_BINARY_DIR}/${_target}_pch/${_name}.gch") | |
| make_directory(${_outdir}) | |
| set(_output_cxx "${_outdir}/.c++") | |
| set(_output_c "${_outdir}/.c") | |
| set(_pch_flags_file "${_pch_binary_dir}/compile_flags.rsp") | |
| export_all_flags("${_pch_flags_file}") | |
| set(_compiler_FLAGS "@${_pch_flags_file}") | |
| add_custom_command( | |
| OUTPUT "${_pchfile}" | |
| COMMAND "${CMAKE_COMMAND}" -E copy "${_pch_header}" "${_pchfile}" | |
| DEPENDS "${_pch_header}" | |
| COMMENT "Updating ${_name}") | |
| add_custom_command( | |
| OUTPUT "${_output_cxx}" | |
| COMMAND "${CMAKE_CXX_COMPILER}" ${_compiler_FLAGS} -x c++-header -o "${_output_cxx}" "${_pchfile}" | |
| DEPENDS "${_pchfile}" "${_pch_flags_file}" | |
| COMMENT "Precompiling ${_name} for ${_target} (C++)") | |
| add_custom_command( | |
| OUTPUT "${_output_c}" | |
| COMMAND "${CMAKE_C_COMPILER}" ${_compiler_FLAGS} -x c-header -o "${_output_c}" "${_pchfile}" | |
| DEPENDS "${_pchfile}" "${_pch_flags_file}" | |
| COMMENT "Precompiling ${_name} for ${_target} (C)") | |
| get_property(_sources TARGET ${_target} PROPERTY SOURCES) | |
| foreach(_source ${_sources}) | |
| set(_pch_compile_flags "") | |
| if(_source MATCHES \\.\(cc|cxx|cpp|c\)$) | |
| get_source_file_property(_pch_compile_flags "${_source}" COMPILE_FLAGS) | |
| if(NOT _pch_compile_flags) | |
| set(_pch_compile_flags) | |
| endif() | |
| separate_arguments(_pch_compile_flags) | |
| list(APPEND _pch_compile_flags -Winvalid-pch) | |
| if(_PCH_FORCEINCLUDE) | |
| list(APPEND _pch_compile_flags -include "${_pchfile}") | |
| else(_PCH_FORCEINCLUDE) | |
| list(APPEND _pch_compile_flags "-I${_pch_binary_dir}") | |
| endif(_PCH_FORCEINCLUDE) | |
| get_source_file_property(_object_depends "${_source}" OBJECT_DEPENDS) | |
| if(NOT _object_depends) | |
| set(_object_depends) | |
| endif() | |
| list(APPEND _object_depends "${_pchfile}") | |
| if(_source MATCHES \\.\(cc|cxx|cpp\)$) | |
| list(APPEND _object_depends "${_output_cxx}") | |
| else() | |
| list(APPEND _object_depends "${_output_c}") | |
| endif() | |
| combine_arguments(_pch_compile_flags) | |
| message("${_source}" ${_pch_compile_flags}) | |
| set_source_files_properties(${_source} PROPERTIES | |
| COMPILE_FLAGS "${_pch_compile_flags}" | |
| OBJECT_DEPENDS "${_object_depends}") | |
| endif() | |
| endforeach() | |
| endif(CMAKE_COMPILER_IS_GNUCXX) | |
| endfunction() |
I'm not very familiar with cmake, but I keep on getting this error:
$ make -j4 LoopingLibrary
make[3]: *** No rule to make target Includes.h', needed byIncludes.h.gch/.c++'. Stop.
make[2]: *** [CMakeFiles/LoopingLibrary_gch.dir/all] Error 2
make[1]: *** [CMakeFiles/LoopingLibrary.dir/rule] Error 2
make: *** [LoopingLibrary] Error 2
I suggest using this (below) MACRO to check for the forceinclude option.
http://www.cmake.org/Wiki/CMakeMacroParseArguments
I'd suggest using GET_PROPERTY(_target_flags TARGET ${_targetName} PROPERTY INCLUDE_DIRECTORIES) instead of GET_DIRECTORY_PROPERTY. The current code doesn't pick up include directories added by the qt5_use_modules command.
Updated with new version from https://github.com/larsch/cmake-precompiled-header/blob/master/PrecompiledHeader.cmake. Thanks everyone for suggestions!
New version is way more robust and well tested (but still with a couple of caveats).
Please add cmake_minimum_required(VERSION 2.8.12) command.
I had error at https://gist.github.com/larsch/573926#file-precompiledheader-cmake-L75
because we use 2.8.9.
Thank you for the Module!
Nice to have something that finally works for me. Thanks.
I was having trouble with SOURCE_CXX not working until I changed
"SOURCE_CXX:SOURCE_C" to
"SOURCE_CXX;SOURCE_C"
I'm on CMake 3.1.0 (perhaps something changed with cmake_parse_arguments?).
Sadly this breaks for me when my include path contains directories that contain spaces, such as "C:\Program Files (x86)\Microsoft DirectX SDK (June 2010)\Include" :-(
Had the same problem as @eco. Using cmake 3.3.0 rc3.
I discovered another problem where all build configurations share the same generated PCH.
I've fixed that by changing set(_pch_cxx_pch "${_cxx_path}/${_input_we}.pch") to set(_pch_cxx_pch "${_cxx_path}/${_input_we}_${CMAKE_CFG_INTDIR}.pch") (and the same for the _pch_c_pch variable).
I've forked this gist and included this change and the change I made back in December.
Have you ever tried to use https://github.com/sakra/cotire instead?
I added forwarding of CMAKE_CXX_FLAGS / CMAKE_C_FLAGS with respect to the currently selected build type.
The reason is, that it is still quite common to pass flags via these variables instead of setting them via target_compile_options. IDEs may use this to configure different options for release and debug builds, e.g.
Hi,
I've got one suggestion for your macro. Change it into a "FUNCTION". The variable FORCEINCLUDE is a global one and when this macro is used for the 2nd execution it fails. If you used a FUNCTION it will work better.
But anyway it will set props for all the sources in multiple targets.
Ivan