最近爲了給xmake實現預編譯頭文件的支持,研究了下各大主流編譯器處理預編譯頭的機制以及之間的一些差別。c++
如今的大部分c/c++編譯器都是支持預編譯頭的,例如:gcc,clang,msvc等,用於優化c++代碼的編譯速度,畢竟c++的頭文件若是包含了模板定義的話,編譯速度是很慢的,
若是可以吧大部分通用的頭文件放置在一個header.h
中,在其餘源碼編譯以前預先對其進行編譯,以後的代碼都能重用這部分預編譯頭,就能夠極大程度上減小頻繁的頭文件冗餘編譯。bash
可是不一樣編譯器對它的支持力度和處理方式,仍是有很大差別的,並非很是通用,在xmake中封裝成統一的接口和使用方式,仍是費了很大的功夫才搞定。優化
預編譯頭在msvc的項目中很常見,常常會看到相似stdafx.cpp
, stdafx.h
的文件,就是用於此目的,而msvc編譯器是經過編譯stdafx.cpp
來生成預編譯頭文件stdafx.pch
的。lua
建立預編譯頭的命令行以下:插件
$ 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
文件。code
$ 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"
中的頭文件名字,不是文件路徑哦。get
我的感受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的預編譯頭處理方式基本上跟clang的相似,惟一的區別就是:它不支持-include-pch
參數,所以不能因此指定使用的stdafx.pch
文件路徑。
它有本身的搜索規則:
stdafx.h
所在目錄中,查找stdafx.h.pch
文件是否存在-I
的頭文件搜索路徑找查找stdafx.h.pch
編譯頭文件生成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支持經過預編譯頭文件去加速c/c++
程序編譯,目前支持的編譯器有:gcc, clang和msvc。
使用方式以下:
target("test") set_precompiled_header("header.h")
一般狀況下,設置c頭文件的預編譯,這須要加上這個配置便可,若是是對c++頭文件的預編譯,改爲:
target("test") set_precompiled_header("header.hpp")
其中的參數指定的是須要預編譯的頭文件路徑,相對於當前xmake.lua
所在的目錄。
若是隻是調用xmake命令行進行直接編譯,那麼上面的設置足夠了,而且已經對各個編譯器進行支持,可是有些狀況下,上面的設置還不能知足需求:
xmake project
工程插件生成vs工程文件,那麼還缺乏一個相似stdafx.cpp
的文件(上面的設置在msvc編譯的時候會自動生成一個臨時的,可是對IDE工程不友好)。header.h
想做爲c++的預編譯頭文件就不支持了,除非改爲header.hpp
(默認會當作c頭文件進行預編譯)。所以爲了更加地通用跨平臺,能夠在工程裏面建立一個相似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