天天看點

不同編譯器對預編譯頭檔案的處理

最近為了給 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

檔案路徑。

它有自己的搜尋規則:

  1. stdafx.h

    所在目錄中,查找

    stdafx.h.pch

    檔案是否存在
  2. -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指令行進行直接編譯,那麼上面的設定足夠了,并且已經對各個編譯器進行支援,但是有些情況下,上面的設定還不能滿足需求:

  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.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/

繼續閱讀