ビルドシステム、テストインフラ、コントリビューターワークフロー
前提知識
- ›第1回:アーキテクチャ概要(各コンポーネントの全体像を把握していること)
- ›CMake の基本知識(ターゲット、依存関係、変数)
- ›コマンドラインのビルドツールとテストランナーに慣れていること
ビルドシステム、テストインフラ、コントリビューターワークフロー
アーキテクチャの知識は、実際にプロジェクトをビルドし、テストを走らせ、変更を送れて初めて活きてきます。WebKit のビルドシステムは、プロジェクトの規模からは想像しにくいほど取り組みやすく設計されています。ただし、初心者がつまずきやすいクセもいくつか存在します。デュアルビルドシステム、unified sources による最適化、充実したテストスイート、それぞれの仕組みをしっかり理解しておく必要があります。
この記事は、Part 1〜5 で掘り下げてきたアーキテクチャ解説の実践編です。読み終える頃には、自分のプラットフォームで WebKit をビルドし、変更内容に合ったテストを実行し、git-webkit のワークフローでプルリクエストを送れるようになっているはずです。
デュアルビルドシステム:Xcode と CMake
WebKit は、プラットフォームに応じて 2 つのビルドシステムを並行して維持しています。
| ビルドシステム | 対象プラットフォーム | エントリーポイント |
|---|---|---|
| Xcode | macOS, iOS, tvOS, watchOS, visionOS | WebKit.xcworkspace |
| CMake | GTK, WPE, PlayStation, Windows | CMakeLists.txt |
Apple プラットフォームでは、Xcode ワークスペース(WebKit.xcworkspace)がメインのビルドエントリーポイントです。全コンポーネントのターゲットが含まれており、フレームワークの生成、コード署名、プラットフォーム固有のコンパイルもここで処理されます。Apple のエンジニアや macOS 向けのコントリビューターのほとんどがこの方法を使っています。
Apple 以外のプラットフォームでは CMake を使います。CMakePresets.json には、共通のビルド設定が定義されています。
flowchart TD
subgraph APPLE["Apple Platforms"]
XC["WebKit.xcworkspace"] --> XB["xcodebuild"]
XB --> FW["Frameworks<br/>(WebKit.framework, etc.)"]
end
subgraph OTHER["Non-Apple Platforms"]
CMAKE["CMakeLists.txt"] --> PRESET["CMakePresets.json"]
PRESET --> GTK_P["GTK preset"]
PRESET --> WPE_P["WPE preset"]
PRESET --> PS_P["PlayStation preset"]
GTK_P --> LIBS["Shared Libraries<br/>(libwebkit2gtk.so, etc.)"]
end
BW["build-webkit script"] --> XC
BW --> CMAKE
プリセットには release、debug、dev というベース設定に加え、gtk-release や wpe-debug といったプラットフォーム固有のプリセットも用意されています。dev プリセットを使うと、CMAKE_EXPORT_COMPILE_COMMANDS(IDE 連携用)と DEVELOPER_MODE(テストランナーやパフォーマンスツールのビルドを有効化)が有効になります。
プラットフォーム固有のビルド設定は、各コンポーネント内の PlatformX.cmake ファイルで行います。Part 1 で触れたように、PlatformMac.cmake は macOS 固有のソースとフレームワーク依存関係を追加し、PlatformGTK.cmake は GTK 固有のソースと pkg-config 依存関係を追加します。
Unified Sources:コンパイル時間の最適化
WebCore だけでも 5,270 以上のソースファイルが存在します。これを 1 ファイルずつコンパイルすると数時間かかってしまいます。そこで WebKit では unified sources という仕組みを採用し、複数の .cpp ファイルをひとつの翻訳単位にまとめることでコンパイル時間を大幅に削減しています。
flowchart TD
SOURCES["Sources.txt<br/>5,270 .cpp file paths"] --> GEN["Build system<br/>(CMake or Xcode)"]
GEN --> U1["UnifiedSource1.cpp<br/>#include 'File1.cpp'<br/>#include 'File2.cpp'<br/>...<br/>#include 'File8.cpp'"]
GEN --> U2["UnifiedSource2.cpp<br/>#include 'File9.cpp'<br/>...<br/>#include 'File16.cpp'"]
GEN --> UN["UnifiedSourceN.cpp<br/>..."]
U1 --> COMP["Compiler"]
U2 --> COMP
UN --> COMP
Sources.txt は、.cpp ファイルの相対パスを 1 行に 1 つ並べたシンプルなリストです。ビルドシステムはこのリストを読み込み、約 8 ファイルずつのバンドルに分割して、元のソースファイルを #include する UnifiedSourceN.cpp を生成します。
これによってコンパイル時間が桁違いに短縮される理由は次の 3 つです。
- ヘッダファイルの解析がソースファイルごとではなく、バンドルごとに 1 回で済む。
- テンプレートのインスタンス化がバンドル内のファイル間で共有される。
- コンパイラ全体のパス数が減る。
ただし、同じバンドル内のファイルは翻訳単位を共有するため、次のような点に注意が必要です。
- あるファイルの static シンボルが、同じバンドル内の他のファイルから見えてしまう。
- インクルード順が影響する——ファイル 3 でインクルードしたヘッダは、同じバンドルのファイル 4〜8 でも利用可能になる。
- 同じバンドル内のファイル間でシンボルの衝突が起きる可能性がある。
プラットフォーム固有のファイルは別のリストで管理されます。Apple 向けは SourcesCocoa.txt、GTK 向けは SourcesGTK.txt といった具合です。これらは個別にバンドルされ、対応するプラットフォームでのみコンパイルされます。
ヒント: シンボルの「二重定義」やヘッダの「見つからない」といった謎のビルドエラーが出たときは、unified sources の順序問題を疑ってみてください。自分のファイルがどのバンドルに入っているかを確認し、
Sources.txt内で隣接するファイルと競合していないかチェックしましょう。
条件コンパイル:PLATFORM、ENABLE、USE
Part 1 でも触れましたが、コンパイル対象を制御するマクロファミリーは 3 種類あります。ここではそれぞれの関係をより詳しく見ていきましょう。
flowchart TD
subgraph MACROS["Conditional Compilation Macros"]
PLAT["PLATFORM(X)<br/>Target OS<br/>COCOA, GTK, WPE, WIN"]
EN["ENABLE(X)<br/>Feature Flags<br/>WEBGL, CSS_SELECTOR_JIT, FTL_JIT"]
USE_M["USE(X)<br/>Implementation Choice<br/>CG, CAIRO, SKIA, CF, GLIB"]
end
PLAT --> CONFIG_H["config.h<br/>(auto-generated)"]
EN --> CONFIG_H
USE_M --> CONFIG_H
CONFIG_H --> EVERY_CPP["Every .cpp file<br/>#include 'config.h'"]
PLATFORM(COCOA) はすべての Apple プラットフォーム(macOS、iOS など)で true になります。PLATFORM(GTK) は GTK ポートで true になります。これらは互いに排他的です。
ENABLE(X) フラグはそれぞれ独立して切り替えられます。ENABLE(WEBGL) はプラットフォームに関係なく有効・無効を設定できます。ENABLE(CSS_SELECTOR_JIT) は Part 3 で解説した CSS JIT コンパイラを有効にし、ENABLE(FTL_JIT) は Part 5 で解説した FTL コンパイルティアを有効にします。
USE(X) は実装の選択肢を指定します。USE(CG) は 2D レンダリングに CoreGraphics を使うことを意味します。Apple 以外のプラットフォームでは USE(CAIRO) や USE(SKIA) が代替として使われます。USE(CF) は CoreFoundation、USE(GLIB) は GLib を指します。
WebKit のビルド:build-webkit とその仲間たち
WebKit をビルドする一番シンプルな方法は、Tools/Scripts/ にある build-webkit スクリプトを使うことです。
# Build for macOS (Debug)
Tools/Scripts/build-webkit --debug
# Build for macOS (Release)
Tools/Scripts/build-webkit --release
# Build for GTK
Tools/Scripts/build-webkit --gtk --release
# Build for WPE
Tools/Scripts/build-webkit --wpe --debug
ビルドが完了したら、ローカルビルドで Safari を起動できます。
Tools/Scripts/run-safari --debug
このスクリプトは DYLD_FRAMEWORK_PATH をビルド出力先に向けることで、システムの WebKit フレームワークではなく、自分でビルドしたものを Safari に読み込ませます。
flowchart LR
DEV["Developer"] --> BW["build-webkit<br/>--debug or --release"]
BW -->|macOS| XCODE["xcodebuild<br/>WebKit.xcworkspace"]
BW -->|GTK/WPE| CMAKE_B["cmake --build"]
XCODE --> BUILD_DIR["WebKitBuild/Debug<br/>or Release"]
CMAKE_B --> BUILD_DIR
BUILD_DIR --> RS["run-safari<br/>DYLD_FRAMEWORK_PATH"]
RS --> SAFARI["Safari with local WebKit"]
ルートの Makefile には、主要な設定に対して build-webkit をラップした便利なターゲットが用意されています。
ヒント: 反復作業を速くするには、作業中のコンポーネントだけをビルドしましょう。WebCore を修正しているなら、変更のたびに JSC をゼロからビルドし直す必要はありません。ビルドシステムはインクリメンタルビルドをうまく処理してくれますが、
build-webkit --no-experimental-featuresを使えば不要な機能をスキップすることもできます。
テストインフラ:レイアウトテスト、JSTests、API テスト
WebKit には、それぞれ異なるレイヤーをテストする 4 種類のテストスイートがあります。
| スイート | 場所 | ランナー | テスト対象 |
|---|---|---|---|
| Layout Tests | LayoutTests/ |
run-webkit-tests |
Web プラットフォームへの準拠(HTML、CSS、DOM レンダリング) |
| JSTests | JSTests/ |
run-javascriptcore-tests |
JavaScript エンジンの正確性 |
| API Tests | Tools/TestWebKitAPI/ |
GTest | C++ API の正確性 |
| WebDriver Tests | WebDriverTests/ |
Selenium ベース | ブラウザの自動操作 |
レイアウトテストは WebKit ならではのテスト形式です。各テストは HTML ファイルで、テキストダンプまたはピクセルレンダリングを出力します。テストランナーはその出力を期待値(-expected.txt または -expected.png ファイル)と比較します。パースからレンダリングまでの全パイプラインをテストできるのがこのアプローチの特徴です。実行方法は以下のとおりです。
Tools/Scripts/run-webkit-tests
JSTests は、数千の JavaScript プログラム、ストレステスト、マイクロベンチマークで JavaScriptCore の正確性を検証します。ランナーはすべてのコンパイルティアを通じてテストを実行します。
Tools/Scripts/run-javascriptcore-tests
flowchart TD
subgraph LAYOUT["Layout Tests"]
HTML["test.html"] --> RUNNER["run-webkit-tests<br/>(DumpRenderTree / WebKitTestRunner)"]
RUNNER --> OUTPUT["Actual output"]
EXPECTED["test-expected.txt"] --> DIFF["Compare"]
OUTPUT --> DIFF
DIFF --> RESULT["PASS / FAIL"]
end
subgraph JSC_TESTS["JSTests"]
JS["test.js"] --> JSC_RUN["run-javascriptcore-tests"]
JSC_RUN --> |"All tiers"| JSC_OUT["Test results"]
end
subgraph API["API Tests"]
GTEST["TestWebKitAPI/*.cpp"] --> GTEST_RUN["GTest runner"]
GTEST_RUN --> API_OUT["Test results"]
end
API テストは GTest ベースの C++ テストで、WebKit の公開 API および内部 API を直接検証します。レイアウトテストより高速で、特定の C++ クラスを単独でテストしたい場合に重宝します。
パフォーマンスベンチマーク
WebKit はパフォーマンスのリグレッションを検出するために、3 つの主要なベンチマークを管理しています。
-
JetStream — ハッシュテーブル、暗号処理、物理シミュレーション、パーサーなど、多種多様なワークロードで JavaScript と WebAssembly のパフォーマンスを測定します。
PerformanceTests/JetStream3/にあります。 -
MotionMark — アニメーションする 2D シーンを使って描画・コンポジットパイプラインに負荷をかけ、グラフィックスとレンダリングのパフォーマンスを測定します。
-
Speedometer — React、Angular、Vue などさまざまなフレームワークで構築された TodoMVC スタイルのアプリとのユーザーインタラクションをシミュレートし、Web アプリケーションの応答性を測定します。
これらのベンチマークは PerformanceTests/ に置かれており、WebKit の CI インフラで定期的に実行されます。ベンチマークで検出されたパフォーマンスのリグレッションは、修正すべきバグとして扱われます。
コントリビューターワークフロー:git-webkit とコードスタイル
WebKit はコードレビューに GitHub のプルリクエストを使っています。Tools/Scripts/ からインストールできる git-webkit ツールを使うと、ワークフローをスムーズに進められます。
# Install git-webkit
git webkit setup
# Create a branch and make changes
git checkout -b my-fix
# ... edit files ...
git commit -m "[Component] Fix the thing"
# Submit a pull request
git webkit pr
flowchart LR
BRANCH["Create branch"] --> EDIT["Make changes"]
EDIT --> COMMIT["git commit"]
COMMIT --> PR["git webkit pr"]
PR --> REVIEW["Code review on GitHub"]
REVIEW --> |"approved"| LAND["git webkit land"]
REVIEW --> |"changes requested"| EDIT
コードのフォーマットは .clang-format によって規定されており、WebKit のコーディングスタイルが定義されています。主なルールは以下のとおりです。
- ブレーススタイル:
BreakBeforeBraces: WebKit— 制御構造では同じ行に開き括弧を置き、関数では新しい行に置く。 - インデント:スペース 4 つ、タブは使わない。
- 桁数制限:clang-format では強制しない(WebKit では手動で折り返す)。
- 命名規則:クラスは
PascalCase、メソッドはcamelCase、メンバ変数はm_camelCase。
プルリクエストを送る前に、スタイルチェッカーを実行しておきましょう。
Tools/Scripts/check-webkit-style
ヒント:
Introduction.mdには、開発環境のセットアップ方法、最初に取り組むのに適したバグの見つけ方、レビュープロセスの進め方など、新しいコントリビューター向けの詳細な手順が記載されています。公式のオンボーディングドキュメントとして常に最新の状態に保たれているので、ぜひ参照してください。
シリーズのまとめ
この 6 回にわたるシリーズを通じて、WebKit のコードベースをアーキテクチャの基礎から実践的なコントリビューションワークフローまで幅広く見てきました。
- アーキテクチャ概要 — 階層的な依存関係チェーン、マルチプロセスモデル、リポジトリ構造。
- WTF とメモリ管理 — スマートポインタシステム、
protectedThis、IsoHeap によるセキュリティ強化。 - WebCore — DOM、レンダリングパイプライン、CSS JIT、Web IDL バインディング。
- マルチプロセス IPC —
.messages.inによるコード生成システムとプロキシパターン。 - JavaScriptCore — 4 段階のコンパイルパイプラインと B3 バックエンド。
- ビルドとテスト — デュアルビルドシステム、unified sources、テストスイート、コントリビューターワークフロー。
WebKit は年間数千のコミットが積み重なる現役のプロジェクトです。このシリーズで見てきたアーキテクチャパターン、すなわち階層的な依存関係、プロセス分離、段階的なコンパイル、intrusive な参照カウントは、10 年以上にわたってプロジェクトを支えてきた安定した設計判断です。これらを理解することで、この巨大なコードベースのどの隅へも踏み込んでいける足がかりができます。
理解を深める一番の近道は、興味のある領域を選んでコードを読み、実際にコントリビュートしてみることです。WebKit のコミュニティは新しいコントリビューターを歓迎しており、充実したテストインフラのおかげで安心して試行錯誤できます。ぜひ挑戦してみてください。Happy hacking!