CMake中的target_precompile_headers指令用于添加要預編譯的頭檔案清單,其格式如下:
target_precompile_headers(<target>
<INTERFACE|PUBLIC|PRIVATE> [header1...]
[<INTERFACE|PUBLIC|PRIVATE> [header2...] ...]) # 1
target_precompile_headers(<target> REUSE_FROM <other_target>) # 2
預編譯頭檔案可以通過建立一些頭檔案的部分處理版本(partially processed version),然後在編譯期間使用該版本而不是重複解析原始頭檔案來加快編譯速度。
1. 該指令将頭檔案添加到<target>的PRECOMPILE_HEADERS和/或INTERFACE_PRECOMPILE_HEADERS目标屬性中。命名的<target>必須是由add_executable或add_library等指令建立,并且不能是ALIAS target。
需要INTERFACE, PUBLIC和PRIVATE關鍵字來指定以下參數的範圍:PRIVATE和PUBLIC items将填充<target>的PRECOMPILE_HEADERS屬性。PUBLIC和INTERFACE items将填充<target>的INTERFACE_PRECOMPILE_HEADERS屬性(IMPORTED targets僅支援INTERFACE items)。對相同<target>的重複調用将按所調用的順序追加items。
Projects通常應避免對要導出的targets使用PUBLIC或INTERFACE, 或者至少應使用$<BUILD_INTERFACE:...>生成器表達式(generator expression)來防止預編譯頭(precompile headers)出現在已安裝的導出target中。target的使用者(consumers)通常應該控制他們使用的預編譯頭,而不是被使用的targets強加給他們的預編譯頭(因為預編譯頭通常不是使用要求(usage requirement))。
頭檔案清單用于生成名為cmake_pch.h|xx的頭檔案,該檔案用于生成預編譯頭檔案(.pch、.gch、.pchi)工件(artifact)。cmake_pch.h|xx頭檔案将被強制包含(-include表示GCC,/FI表示MSVC),是以源檔案不需要#include "pch.h"
用尖括号(例如<unordered_map>)或顯式雙引号(例如[["other_header.h"]])指定的頭檔案名将按原樣處理,并且包含目錄必須可供編譯器查找。其它頭檔案名(例如 project_header.h)被解釋為相對于目前源目錄(例如CMAKE_CURRENT_SOURCE_DIR),并将包含在絕對路徑中.
target_precompile_headers指令的參數可以使用文法為$<...>的生成器表達式
add_library(add source/add.cpp)
target_include_directories(add PUBLIC include)
target_precompile_headers(add PRIVATE include/add.hpp)
get_target_property(var add PRECOMPILE_HEADERS)
message("var: ${var}") # var: /home/spring/GitCode/Linux_Code_Test/Samples_CMake/messy_usage/include/add.hpp
執行以上測試代碼,由于有target_precompile_headers指令,将會在build/CMakeFiles/add.dir目錄下,會生成cmake_pch.hxx檔案,其内容如下:
/* generated by CMake */
#pragma GCC system_header
#ifdef __cplusplus
#include "/home/spring/GitCode/Linux_Code_Test/Samples_CMake/messy_usage/include/add.hpp"
#endif // __cplusplus
2. 該簽名可用于指定一個target重用另一個target中的預編譯頭檔案工件(artifact),而不是生成自己的.
此表單(form)将PRECOMPILE_HEADERS_REUSE_FROM屬性設定為<other_target>并添加依賴關系,以便<target>依賴于<other_target>。如果在<target>使用REUSE_FROM表單時已設定PRECOMPILE_HEADERS屬性,則CMake将停止并顯示錯誤。
add_library(add source/add.cpp)
target_include_directories(add PUBLIC include)
target_precompile_headers(add PRIVATE <iostream> <vector>)
add_executable(main samples/sample_add.cpp)
target_include_directories(main PUBLIC include)
target_link_libraries(main add)
target_precompile_headers(main REUSE_FROM add)
# should not cause problems if configured multiple times
target_precompile_headers(main REUSE_FROM add)
get_target_property(var main PRECOMPILE_HEADERS_REUSE_FROM)
message("var: ${var}") # var: add
執行以上測試代碼,在build/CMakeFiles/add.dir目錄下生成的cmake_pch.hxx檔案,其内容如下:
/* generated by CMake */
#pragma GCC system_header
#ifdef __cplusplus
#include <iostream>
#include <vector>
#endif // __cplusplus
在build/CMakeFiles/main.dir目錄下的flags.make檔案内容變為:
# CMAKE generated file: DO NOT EDIT!
# Generated by "Unix Makefiles" Generator, CMake Version 3.22
# compile CXX with /usr/bin/c++
CXX_DEFINES =
CXX_INCLUDES = -I/home/spring/GitCode/Linux_Code_Test/Samples_CMake/messy_usage/include
CXX_FLAGS =
# PCH options: CMakeFiles/main.dir/samples/sample_add.cpp.o_OPTIONS = -Winvalid-pch;-include;/home/spring/GitCode/Linux_Code_Test/Samples_CMake/messy_usage/build/CMakeFiles/add.dir/cmake_pch.hxx
若要禁用特定target的預編譯标頭,參閱DISABLE_PRECOMPILE_HEADERS target屬性。
若要防止在編譯特定源檔案時使用預編譯标頭,參閱SKIP_PRECOMPILE_HEADERS源檔案屬性。
執行測試代碼需要多個檔案:
build.sh内容如下
#! /bin/bash
# supported input parameters(cmake commands)
params=(function macro cmake_parse_arguments \
find_library find_path find_file find_program find_package \
cmake_policy cmake_minimum_required project include \
string list set foreach message option if while return \
math file configure_file \
include_directories add_executable add_library link_libraries target_link_libraries install \
target_sources add_custom_command add_custom_target \
add_subdirectory aux_source_directory \
set_property set_target_properties define_property \
add_definitions target_compile_definitions target_compile_features \
add_compile_options target_include_directories link_directories \
add_link_options target_precompile_headers)
usage()
{
echo "Error: $0 needs to have an input parameter"
echo "supported input parameters:"
for param in ${params[@]}; do
echo " $0 ${param}"
done
exit -1
}
if [ $# != 1 ]; then
usage
fi
flag=0
for param in ${params[@]}; do
if [ $1 == ${param} ]; then
flag=1
break
fi
done
if [ ${flag} == 0 ]; then
echo "Error: parameter \"$1\" is not supported"
usage
exit -1
fi
if [[ ! -d "build" ]]; then
mkdir build
cd build
else
cd build
fi
echo "==== test $1 ===="
# test_set.cmake: cmake -DTEST_CMAKE_FEATURE=$1 --log-level=verbose ..
# test_option.cmake: cmake -DTEST_CMAKE_FEATURE=$1 -DBUILD_PYTORCH=ON ..
cmake -DTEST_CMAKE_FEATURE=$1 ..
# It can be executed directly on the terminal, no need to execute build.sh, for example: cmake -P test_set.cmake
make
# make install # only used in cmake files with install command
主CMakeLists.txt内容如下:
cmake_minimum_required(VERSION 3.22)
project(cmake_feature_usage)
message("#### current cmake version: ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}")
include(test_${TEST_CMAKE_FEATURE}.cmake)
message("==== test finish ====")
test_target_precompile_headers.cmake内容為上面的所有測試代碼段
另外還包括三個目錄:include,source,samples,它們都是非常簡單的實作,僅用于測試,如下:

可能的執行結果如下圖所示:
GitHub: https://github.com/fengbingchun/Linux_Code_Test