最近為了給 xmake
實作預編譯頭檔案的支援,研究了下各大主流編譯器處理預編譯頭的機制以及之間的一些差異。
現在的大部分c/c++編譯器都是支援預編譯頭的,例如:gcc,clang,msvc等,用于優化c++代碼的編譯速度,畢竟c++的頭檔案如果包含了模闆定義的話,編譯速度是很慢的,
如果能夠吧大部分通用的頭檔案放置在一個
header.h
中,在其他源碼編譯之前預先對其進行編譯,之後的代碼都能重用這部分預編譯頭,就可以極大程度上減少頻繁的頭檔案備援編譯。
但是不同編譯器對它的支援力度和處理方式,還是有很大差異的,并不是非常通用,在xmake中封裝成統一的接口和使用方式,還是費了很大的功夫才搞定。
msvc的預編譯頭處理
預編譯頭在msvc的項目中很常見,經常會看到類似
stdafx.cpp
,
stdafx.h
的檔案,就是用于此目的,而msvc編譯器是通過編譯
stdafx.cpp
來生成預編譯頭檔案
stdafx.pch
的。
建立預編譯頭的指令行如下:
$ cl.exe -c -Yc -Fpstdafx.pch -Fostdafx.obj stdafx.cpp
其中,
-Yc
就是建立預編譯頭
stdafx.pch
的意思,通過
-Fp
來指定
*.pch
的輸出檔案路徑,用
-Fo
指定編譯
stdafx.cpp
生成對象檔案。
其他源碼是如何使用這個
stdafx.pch
的呢,通過将
stdafx.h
傳入
-Yu
來告訴編譯器,編譯目前代碼,忽略
#include "stdafx.h"
,直接使用已經編譯好的
stdafx.pch
檔案。
$ cl.exe -c -Yustdafx.h -Fpstdafx.pch -Fotest.obj test.cpp
最後連結的時候,需要把:
stdafx.obj
test.obj
都連接配接上才行,這個也是和gcc, clang編譯器不同的地方。
$ link.exe -out:test test.obj stdafx.obj
注:一定要吧
stdafx.obj
也連結上哦,雖然
stdafx.cpp
僅用于生成
stdafx.pch
,但是對象檔案也是需要。
還有個跟gcc, clang有差別的地方是,msvc的
-Yu
指定
stdafx.h
必須是
#include "stdafx.h"
中的頭檔案名字,不是檔案路徑哦。
clang的預編譯頭檔案處理
個人感覺clang的預編譯頭檔案支援方式最為友好,也最為簡單。
相比于msvc,不需要
stdafx.cpp
,隻需要一個頭檔案
stdafx.h
就可以生成pch檔案。
相比于gcc,可以靈活控制pch檔案的路徑,更加靈活。
編譯頭檔案生成pch檔案:
$ clang -c -o stdafx.pch stdafx.h
使用預編譯頭檔案:
$ clang -c -include stdafx.h -include-pch stdafx.pch -o test.o test.cpp
其中
-include stdafx.h
用于忽略編譯
test.cpp
中的
#include "stdafx.h"
,通過
-include-pch
使用預先編譯好的
stdafx.pch
。
并且這裡指定的
stdafx.h
和
stdafx.pch
不僅可以是在includedir搜尋路徑下的檔案,也可以指定全路徑檔案名,非常靈活,例如:
$ clang -c -include inc/stdafx.h -include-pch out/stdafx.pch -o test.o test.cpp
gcc的預編譯頭檔案處理
gcc的預編譯頭處理方式基本上跟clang的類似,唯一的差別就是:它不支援
-include-pch
參數,是以不能是以指定使用的
stdafx.pch
檔案路徑。
它有自己的搜尋規則:
- 從
所在目錄中,查找stdafx.h
檔案是否存在stdafx.h.pch
-
的頭檔案搜尋路徑找查找-I
stdafx.h.pch
$ gcc -c -o stdafx.pch stdafx.h
$ gcc -c -include stdafx.h -o test.o test.cpp
為了讓上述代碼正常編譯,
stdafx.h.pch
必須放置在
stdafx.h
的相同目錄下,這樣編譯才能找到,目前我還沒有找到可以是以指定輸出目錄的方法。
其他注意點
gcc、clang對于
*.h
的頭檔案編譯,預設是作為c預編譯頭來使用的,這跟c++的pch是不一樣的,無法給c++的代碼使用,如果要生成c++可用的pch檔案,必須要告訴編譯器,如何去編譯
stdafx.h
這個可以通過
-x c++-header
參數來解決:
$ gcc -c -x c++-header -o stdafx.pch stdafx.h
當然也可以通過修改字尾名來解決:
$ gcc -c -o stdafx.pch stdafx.hpp
xmake對預編譯頭檔案的支援
xmake支援通過預編譯頭檔案去加速
c/c++
程式編譯,目前支援的編譯器有:gcc, clang和msvc。
使用方式如下:
target("test")
set_precompiled_header("header.h")
通常情況下,設定c頭檔案的預編譯,這需要加上這個配置即可,如果是對c++頭檔案的預編譯,改成:
target("test")
set_precompiled_header("header.hpp")
其中的參數指定的是需要預編譯的頭檔案路徑,相對于目前
xmake.lua
所在的目錄。
如果隻是調用xmake指令行進行直接編譯,那麼上面的設定足夠了,并且已經對各個編譯器進行支援,但是有些情況下,上面的設定還不能滿足需求:
- 如果要使用
工程插件生成vs工程檔案,那麼還缺少一個類似xmake project
的檔案(上面的設定在msvc編譯的時候會自動生成一個臨時的,但是對IDE工程不友好)。stdafx.cpp
- 如果gcc/clang下,
想作為c++的預編譯頭檔案就不支援了,除非改成header.h
(預設會當做c頭檔案進行預編譯)。header.hpp
是以為了更加地通用跨平台,可以在工程裡面建立一個類似vc中
stdafx.cpp
的源檔案:
header.cpp
target("test")
set_precompiled_header("header.h", "header.cpp")
header.cpp
的内容如下:
#include "header.h"
上面的設定,就可以很好地處理各種情況下的預編譯處理,追加的
header.cpp
也告訴了xmake:
header.h
是作為c++來預編譯的。
相對于經典的vc工程中的
stdafx.cpp
stdafx.h
,也能完美支援:
target("test")
set_precompiled_header("stdafx.h", "stdafx.cpp")
更多使用說明見:
target.set_precompiled_header參考資料
Speed up C++ compilation, part 1: precompiled headers原文出處:
http://tboox.org/cn/2017/07/31/precompiled-header/