不一樣編譯器對預編譯頭文件的處理

最近爲了給xmake實現預編譯頭文件的支持,研究了下各大主流編譯器處理預編譯頭的機制以及之間的一些差別。c++

如今的大部分c/c++編譯器都是支持預編譯頭的,例如:gcc,clang,msvc等,用於優化c++代碼的編譯速度,畢竟c++的頭文件若是包含了模板定義的話,編譯速度是很慢的,
若是可以吧大部分通用的頭文件放置在一個header.h中,在其餘源碼編譯以前預先對其進行編譯,以後的代碼都能重用這部分預編譯頭,就能夠極大程度上減小頻繁的頭文件冗餘編譯。bash

可是不一樣編譯器對它的支持力度和處理方式,仍是有很大差別的,並非很是通用,在xmake中封裝成統一的接口和使用方式,仍是費了很大的功夫才搞定。優化

msvc的預編譯頭處理

預編譯頭在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的預編譯頭文件處理

我的感受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.hstdafx.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文件路徑。

它有本身的搜索規則:

  1. stdafx.h所在目錄中,查找stdafx.h.pch文件是否存在
  2. -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對預編譯頭文件的支持

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命令行進行直接編譯,那麼上面的設置足夠了,而且已經對各個編譯器進行支持,可是有些狀況下,上面的設置還不能知足需求:

  1. 若是要使用xmake project工程插件生成vs工程文件,那麼還缺乏一個相似stdafx.cpp的文件(上面的設置在msvc編譯的時候會自動生成一個臨時的,可是對IDE工程不友好)。
  2. 若是gcc/clang下,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.cppstdafx.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/

相關文章
相關標籤/搜索