[iOS]CCache 讓你的編譯時間飛起來

NewPan發表於2017-12-21

[iOS]CCache 讓你的編譯時間飛起來

原文地址:Using ccache for Fun and Profit 作者 Peter Steinberger

我們的 PSPDFKit 專案超過 60 萬行程式碼,並且程式碼量還在增長。儘管我們致力於寫簡潔而高效的程式碼,但是這個專案很大,而且有許多邊界情況需要尤其注意。在 PSPDFKit 5 for iOS 專案上,編譯時間尤其成為一個令人頭痛的問題:每次編譯都很慢。

我們的安卓 SDK 也有同樣的問題,幾個月前我們的安卓負責人在技術棧中引入了 ccache 來處理冗長的 C++ NDK 編譯時間,我也是從那個時候開始接觸 ccache。

ccache 是個啥?

ccache 是一個編譯快取器,它會在實際編譯之前先檢查快取。它有直接和預處理模式,而且由於在 Clang 3.2 版本之前是不支援 ccache 外掛,所以在 Clang 3.2 之前會有一些問題,但是現在 Clang 的版本是 3.2.3,所以沒有 Clang 不支援的問題。ccache 是一個具有悠久歷史的專案,其主要焦點是快速正確。

網上搜到“ccache xcode”的資訊都是過時無效的資訊,經過我快速的嘗試網上的方法,都無法配置好使其正常工作。隨著我們的程式碼庫越來越複雜,同時我們的 Jenkins 工作叢集數也有 10 臺 Mac,現在測試時間從幾乎無法忍受變成了正真無法忍受。在 Twitter 抱怨現在每天的工作就是管理 Jenkins 工作叢集之後,Facebook 的 Christian Legnitto(他之前在 Apple 負責 OS X 版本管理工作)建議我們嘗試 ccache

Let’s get started

使用以下命令安裝 ccache

 brew install ccache
複製程式碼

如果你沒安裝 Homebrew,請移步這裡,先去安裝 Homebrew,如果你不想移步,就直接使用以下命令安裝 Homebrew

/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
複製程式碼

為了讓 Xcode 呼叫 ccache,我們需要一個小指令碼來配置一些環境變數,然後再呼叫 ccache。將這個指令碼儲存到您專案的某個地方,並將其命名為 ccache-clang

#!/bin/sh
if type -p ccache >/dev/null 2>&1; then
export CCACHE_MAXSIZE=10G
export CCACHE_CPP2=true
export CCACHE_HARDLINK=true
export 
CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches  
exec ccache /usr/bin/clang "$@"
else
exec clang "$@" 
fi
複製程式碼

根據你的具體情況,如果你的專案中有 C++的檔案,你可能還需要一個命名為 ccache-clang++ 的指令碼,並在這個指令碼里這麼寫:

#!/bin/sh
if type -p ccache >/dev/null 2>&1; then
export CCACHE_MAXSIZE=10G
export CCACHE_CPP2=true
export CCACHE_HARDLINK=true
export 
CCACHE_SLOPPINESS=file_macro,time_macros,include_file_mtime,include_file_ctime,file_stat_matches  
exec ccache /usr/bin/clang "$@"
else
exec clang++ "$@" 
fi
複製程式碼

這樣看起來是不是有點複雜,如果沒有命中快取,那麼將會按照之前的編譯方式一樣編譯,而不是報 ccache not found(找不到快取)的錯誤(ccache 內建 shell 指令碼,所以檢查快取很迅速)。

建立 shell 指令碼方法:

建立           touch ccache-clang
開啟指令碼        open -a xcode ccache-clang
貼上指令碼內容
執行指令碼        chmod 755 ccache-clang
複製程式碼

如果去學習 ccache 的配置,你會發現有很多選項可選。上面我們使用的是一種相當激進的快取策略,同時執行良好。對於你自己的專案,你可能在沒有 CCACHE_SLOPPINESS 的情況下開始,然後在一切執行良好的情況下一次性新增快取。

這裡最重要的引數是 CCACHE_CPP2,這個引數用於解決 Clang 將處理前處理器的檔案輸出,並可能會發現許多你沒有注意到的潛在問題,例如由於巨集擴充套件導致的不必要的括號。使用此選項會稍微減慢編譯時間,但是要比完全沒有使用 ccache 要快得多。Peter Eisentraut 寫了一篇關於這個問題的好文章

您還需要在 Xcode 中定義 CC 變數。在 PSPDFKit 中,我們在 .xcconfig 檔案中執行此操作,這個檔案在我們所有專案中共享(這是一個很好的統一的專案配置,和易於閱讀和查詢)。同時,您可以直接在 Xcode 專案設定內配置:

CC = "$(SRCROOT)/../Resources/ccache-clang"
複製程式碼

[iOS]CCache 讓你的編譯時間飛起來

就這麼多了!下次編譯的時候會比正常慢一點,你可以在終端中使用 ccache -s 來檢視 ccache 是否正常工作。剛開始時應該有很多快取沒有命中,但是當快取開始漸漸替代之後的編譯時,編譯速度將會變得快起來。

坑來了

路不平的地方就有坑:ccache 有一些缺點。

不支援 Clangmodules,如果檢測到 -fmodulesccache 就會失效。因此,為了相容 ccache,你需要用老舊的 # import <UIKit/UIKit.h> 替換你專案中所有優雅的 @import UIKit,以及所有使用 ccache 帶來的問題,比方說巨集的問題。在 PSPDFKit 專案中我們採用了 Objective-C++ 的形式,當我們使用很多 C++ 程式碼時,就無法使用 modules 了,所以這一點(ccache 不支援 modules)並沒有影響到我們。 modules 會自動連結用到的 framework,但是在禁用了 modules 以後,你需要手動新增用到的 framework,這個工作很無趣,但是也很快就做完。

還需要停止使用 .pch。蘋果不推薦使用 .pch,而且一般認為使用 .pch 是不好的程式設計風格,哪裡用到就在哪裡匯入會比 .pch 要好。對我們而言,刪除那些 .pch 還是很容易的。當然,ccache 沒法幫你快取 Swift 檔案。雖然 Swift 也使用 Clang,但是ccacheSwift 檔案束手無策。也許 ccache 最終會支援 Swift,但我指望不上。因為 Swift 至今沒有穩定,甚至我們要在 Swift 的兩個版本之間做二進位制相容,我們沒法用 Swift 來編寫我們的 SDK,所以 ccache 不支援 Swift 的問題,對我們不是問題。

在編譯期間,我們應該隨時監視專案是否丟擲不相容的警告。請參閱“不支援的編譯器”選項。我花了相當一部分時間去處理這些不相容的問題。設定 CCACHE_LOGFILE 臨時環境變數將有助於我們精確定位錯誤:ccache 將會提示那些標識是有問題的,以及快取命中和未命中的具體情況。

steipete@steipete-rmbp ~ $ ccache -s
cache directory                     /Users/steipete/.ccache
primary config                      /Users/steipete/.ccache/ccache.conf
secondary config      (readonly)    /usr/local/Cellar/ccache/3.2.3/etc/ccache.conf
cache hit (direct)                 42530
cache hit (preprocessed)           18147
cache miss                         28379
called for link                     1344
called for preprocessing             645
compile failed                         1
preprocessor error                     2
can't use precompiled header        2567
unsupported source language           12
unsupported compiler option        11564
no input file                          2
files in cache                    124223
cache size                           8.7 GB
max cache size                      15.0 GB
複製程式碼

搞這個值不值?

給你說一下我們使用的情況,使用了 ccache 以後,我們的編譯執行時間平均為 8 分鐘,之前我們沒有用 ccache 的時候是 14 分鐘。使用 ccache 之前在最快的 MacBook Pro 上編譯打包整個 PSPDFKit 需要 50 分鐘,使用了之後,時間為 15 分鐘。新增 ccache 到我們的技術棧是一個巨大的進步,真後悔我沒有早點知道這個那麼棒的工具!

Precompiled Header 問題

Anton Bukov 說通過禁用 GCC_PRECOMPILE_PREFIX_HEADER,開啟 GCC_PREFIX_HEADER 的方式來處理這個問題。

NewPan 的文章集合

下面這個連結是我所有文章的一個集合目錄。這些文章凡是涉及實現的,每篇文章中都有 Github 地址,Github 上都有原始碼。

NewPan 的文章集合索引

如果你有問題,除了在文章最後留言,還可以在微博 @盼盼_HKbuy 上給我留言,以及訪問我的 Github

相關文章