🐼

ccacheを使ってFlutterのiOSビルドを高速化してみた

2023/12/05に公開
1

はじめに

FirebaseFirestoreを導入したFlutterアプリのiosビルドは非常に時間がかかるため、firestore-ios-sdk-frameworksを使ってビルドの高速化を行っているプロジェクトが多いですよね🙂

先日リリースされたfirebase_core: 2.22.0にてfirebase_ios_sdk: 10.17.0が要求されるようになったんですが、どうやらそのver以降iOSビルドできなくなる不具合があるっぽく、対応にまだしばらく時間がかかりそうな状況😓(2023/12/5時点)

追記:
2024/1/31にリリースされたfirestore-ios-sdk-frameworks: v10.20.0にて、上記の問題は解決したようです!やったね!
 https://github.com/invertase/firestore-ios-sdk-frameworks/issues/79#issuecomment-1919142414

Podの代わりにccacheを使う理由が薄くなったわけですが、この記事は備忘録として残しておきます!

現時点で取れる対応は大きく3つ

  • firestore-ios-sdkのversionをを10.16.0のままにする(firebase_coreのバージョンを2.21.0固定にする)
  • firestore-ios-sdkを使わない(高速化を諦める)
  • ccacheを使う

高速化のためだけにfirebaseのアップデートを我慢し続けるのは嫌だけど、高速化を諦めるのも辛いし・・・。
なんかccacheとか聞いたことないものが提案されてたのでちょっくら試してみよう。という経緯です。

※「周辺知識浅い人間がとりあえずやってみたメモ」みたいな感じなので、間違ったことを書いてる可能性もあります!お気づきの点あればご指摘いただけると幸いです🙇‍♂️🙇‍♂

ccacheとは?

公式ドキュメントはこちら
ccache.dev

Ccache is a compiler cache. It speeds up recompilation by caching the result of previous compilations and detecting when the same compilation is being done again.

Ccache has been carefully written to always produce exactly the same compiler output that you would get without the cache. The only way you should be able to tell that you are using ccache is the speed. Currently known exceptions to this goal are listed under Caveats. If you discover an undocumented case where ccache changes the output of your compiler, please let us know.

Description - ccache.dev

要約すると、

  • ccacheは、前回のコンパイル結果をキャッシュすることで、2回目以降の再コンパイルを高速化する
  • キャッシュを使用しない場合と全く同じコンパイラ出力を常に生成するように作られている

ちなみにfirestore-ios-sdk-frameworksの方は、「事前にプリコンパイルされたデータを取得してPod化する(間違ってたらごめんなさい)」みたいな感じだと思うので、同じ高速化でも実現方法がちょっと違うって感じかな〜👀

導入方法

公式ドキュメントやらGitHubやら記事やらを見ながら設定していきます

※この記事では、ローカル環境の設定のみを記載しています。
CI(GitHub Actions)ではccache-actionを使うことができるようですが、現時点では未検証です(後日検証予定)

①ccacheをインストールする

brew install ccache

ccacheコマンドが通るかテスト。

ukkey@ukimac-book ~ % ccache --version
ccache version 4.8.3
Features: file-storage http-storage redis+unix-storage redis-storage

Copyright (C) 2002-2007 Andrew Tridgell
Copyright (C) 2009-2023 Joel Rosdahl and other contributors

See <https://ccache.dev/credits.html> for a complete list of contributors.

This program is free software; you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation; either version 3 of the License, or (at your option) any later
version.

よさそう!

②シンボリックリンクを作成

ccacheをコンパイラとして動作させるために、/usr/local/bin/ディレクトリ内にいくつかのシンボリックリンクを作成していきます。

sudo ln -s $(which ccache) /usr/local/bin/gcc
sudo ln -s $(which ccache) /usr/local/bin/g++
sudo ln -s $(which ccache) /usr/local/bin/cc
sudo ln -s $(which ccache) /usr/local/bin/c++
sudo ln -s $(which ccache) /usr/local/bin/clang
sudo ln -s $(which ccache) /usr/local/bin/clang++

ちゃんと作成されてるか確認・・・👀

ukkey@ukimac-book ~ % ls -l /usr/local/bin/                        
total 0
lrwxr-xr-x  1 root  wheel  25 12  1 21:54 c++ -> /opt/homebrew/bin//ccache
lrwxr-xr-x  1 root  wheel  25 12  1 21:54 cc -> /opt/homebrew/bin//ccache
lrwxr-xr-x  1 root  wheel  25 12  1 21:54 clang -> /opt/homebrew/bin//ccache
lrwxr-xr-x  1 root  wheel  25 12  1 21:54 clang++ -> /opt/homebrew/bin//ccache
lrwxr-xr-x  1 root  wheel  25 12  1 21:54 g++ -> /opt/homebrew/bin//ccache
lrwxr-xr-x  1 root  wheel  25 12  1 21:54 gcc -> /opt/homebrew/bin//ccache

PATHが通っているか確認

シンボリックリンクを作成した/usr/local/binディレクトリにPATHが通っているかを確認。

ukkey@ukimac-book ~ % which gcc
/usr/local/bin/gcc

/usr/local/bin/gccと表示されればOK。

もし/usr/bin/gccと表示されてしまうときは、/usr/local/binにPATHが通っていない可能性があるので、以下のコマンドを実行したあと、再度確認。

echo 'export PATH="/usr/local/bin:$PATH"' >> ~/.zshrc
source ~/.zshrc

③iOSビルドのccacheを構成する

どうやらFlutterの場合、デフォルトだとうまくキャッシュしてくれないっぽいので、以下の構成を.zshrcに追記。

export CCACHE_SLOPPINESS=clang_index_store,file_stat_matches,include_file_ctime,include_file_mtime,ivfsoverlay,pch_defines,modules,system_headers,time_macros
export CCACHE_FILECLONE=true
export CCACHE_DEPEND=true
export CCACHE_INODECACHE=true

.zshrcを更新したらsource ~/.zshrcを忘れずに!

③Podfileを編集する

Flutterプロジェクトのios/Podfileに以下の内容を追記します

これは、Xcodeがデフォルトでツール(clang,clang++)への完全指定パスを使用しているため、先程作成したシンボリックリンクを正しく使ってくれるように設定してるみたい。

  post_install do |installer|
    installer.pods_project.targets.each do |target|
      flutter_additional_ios_build_settings(target)

      target.build_configurations.each do |config|

	# --- これを追加 ---
        config.build_settings["CC"] = "clang"
        config.build_settings["LD"] = "clang"
        config.build_settings["CXX"] = "clang++"
        config.build_settings["LDPLUSPLUS"] = "clang++"
	# ----------------

      end
    end
  end

ビルド時間の比較

iOSエミュレータでのデバッグビルド速度を計測して、比較してみました。

※ストップウォッチアプリでのポチポチ計測なので厳密ではないです🙏

- 1回目 2回目 3回目
高速化なし 7m14s78 7m11s63 6m20s43
ios_sdk 1m24s79 1m18s93 1m21s25
ccache 5m43s00 1m06s60 2m09s30

1回目はどうしても初回キャッシュを作成するために1からコンパイルされるので、それなりに時間はかかってますね〜。でも2回目以降のビルドは早くなってる😯😯

所感

まだ本格的に使ってないのでなんとも言えないけど、今の時点だとfirestore-ios-sdk-frameworksが使えるのであればそっちのほうが方が良いかなという印象😓

  • 設定箇所が多い
  • 初回ビルドが遅い

やっぱりこの辺のデメリットが大きく感じました🥲
あと、シンボリックリンクで挙動を上書きしているので他の動作にも影響することも不安要素としてはあるかな〜。

ただ、今回Pod側で起こってる問題のような、内部の依存関係が原因でプリコンパイルしたデータに不具合がある、なんてことには影響されないのでそこはメリットになる・・のかな?(この辺理解浅い🫣)

まだissueの方でも議論続いてるので様子見しつつ、CI設定含め新情報あれば引き続き試してみようと思います〜〜

注意事項

ccacheの設定は全てのコンパイルに影響するため、環境によってはこれが原因で他のソフトウェアのインストールやコンパイルが失敗する可能性もみたい。

そんなときはシンボリックリンクを削除しておきましょう

sudo unlink /usr/local/bin/gcc
sudo unlink /usr/local/bin/g++
sudo unlink /usr/local/bin/cc
sudo unlink /usr/local/bin/c++
sudo unlink /usr/local/bin/clang
sudo unlink /usr/local/bin/clang++

参考

Discussion