2021年に読んで印象に残った本について

2021年の振り返りでも書きましたが、昨年は、隙を見ては本を読んだりコードを読んだりして過ごしていました。印象に残っている本について、ジャンルごとにまとめてみたいと思います。

Java 関連の本

昨年転職してから、仕事で本格的に Java を読み書きするようになりました。勉強のためにいくつか本を読んだのですが、印象に残っているのは以下の2つです。

Effective Java

これは、Java プラットフォームの多くの機能の設計及び実装を経験してきた著者ジョシュア・ブロックさんが、Java の様々な機能についてその使い方や注意点を説明した本です。言語の機能を設計する側だった人が著者なので、記述について信頼性や納得感があるのが特徴です。

自分は Java の文法は把握していたものの、「Java にとっての良いコードとはどういったものか」の知見がなかったので、とても勉強になりました。特に、Java は長い歴史の中で機能が追加されてきた背景もあり、同じことをするのにもいくつか異なる記述が可能となっているので(e.g. 配列 vs List、null vs Optional、for loop vs stream、etc)、どういったメリット・デメリットがあるか、どう使い分けるのかについて知見を得られたのが良かったです。

Java パフォーマンス

Java アプリケーションのパフォーマンス改善に必要な知見をまとめた本です。パフォーマンス計測についての一般的な知識から、Java 特有の知識(Java 用のツールや JVM の挙動、etc.)までをまとめています。

個人的には、JVM の挙動について理解が深められた点が有用でした。JVM には JIT コンパイラGC アルゴリズムとしていくつかの種類があるのですが、その種類や挙動、チューニング方法についてまとめられていて、勉強になりました。

なお、原著が出たのが 2014 年と Java 8 が最新だった時代なので、情報が少し古い点については注意する必要があります。

OS・コンピュータアーキテクチャ関連の本

ゼロからの OS 自作入門

これは、著者が自作した OS のコードが GitHub 上で公開 されていて、動くコードを読み書きしながら、手を動かして OS について学べる本です。あまりに内容が面白かったので、購入してしばらくはずっとこの本を読んでいました。

基本的には「著者が書いた OS のコードについて説明を行う」という内容なのですが、完成形のコードについて説明をするのではなく、とてもシンプルなコードからスタートしてステップバイステップで少しずつ機能を足していく構成となっているのが特徴です。そのため、常に全体像を理解した状態でコードを動かすことができます。

本物の PC で、自分の書いたコードを USB ブートで起動して遊ぶことができるのも素晴らしい点です。自分は昔利用していたノート PC を引っ張り出して動かしてみていたのですが、それだけでもとても楽しかったです。

x86 の仕様や UEFI の機能について、OS を動かす上で必要になる知識を網羅的に学べるので、PC についての「ソフトウェアから見た全体感」を掴むことができるのも良い点でした。「さらなる学び」の出発点となることが出来る本だと感じてます。

構造化コンピュータ構成

「ゼロからの OS 自作入門」を読んで、コンピュータについてもっと詳しくなりたいと感じた時に古本屋で見つけた本です。コンピュータアーキテクチャを「1. デジタルロジックレベル」、「2. マイクロアーキテクチャレベル」、「3. ISA レベル」、「4. オペレーティングシステムマシンレベル」、「5. アセンブリ言語レベル」という5段階の階層からなるものとして捉えた上で、下から順に積み上げる形で説明がなされています。コンピュータアーキテクチャの全体像を下から上まで包括的に理解するための優れた本という印象です。

原著が 1998 年発売なので時代は感じるものの、幅広いトピックについて扱われていてとても勉強になりました。個人的には、バス(特に ISA バスや PCI バス)についての知識が得られた点と、マイクロアーキテクチャを実例を通して学べた点が特に気に入っています。

ディジタル回路設計とコンピュータアーキテクチャ[ARM版]

ARM アーキテクチャを題材に、デジタル回路やコンピュータアーキテクチャについて説明した本です。x86 以外の ISA について知りたくなり、ARM について学ぶために読みました。序盤はデジタルロジックレベルの説明なので ARM は関係ありませんが、説明がかなり丁寧で良い内容でした。中盤以降は ARM の命令セットについて理解を深めることができる内容となっていました。

x86 はよく命令が複雑と呼ばれていますが、実はこれまではあまりピンと来ていませんでした。しかし、後方互換性を保つために増え続けた命令や可変長で複雑なマシン語などを見ていると、確かに ARM(特に AArch64)と比較すると複雑だと感じるようになりました。比較対象が出来たことで理解が深まったように感じています。

Linux デバイスドライバ関連の本

「コンピュータについてもっと詳しくなりたい」と感じた気持ちの延長で、一時期 Linux デバイスドライバ関係の本を読んでいました。

Linuxデバイスドライバの開発

Linuxデバイスドライバについてベーシックな内容を学べる本です。デバイスドライバの書き方自体については、本当に基礎的な部分だけを説明されている印象です。ただし、動かせるコードサンプルがついているので、自分で書き換えながら学べる点が気に入っています。

最初の数ページでいきなり Buildroot を利用した Linux カーネルのビルドを行うことになり、その点については少し驚きました。しかし、そのおかげで Linux カーネルのビルドに対して心理的抵抗が減ったのは結果的には良かったです。

Linux デバイスドライバ 第3版

Linux用のデバイスドライバについて詳細な解説がなされた本です。デバイスドライバについてもう少し詳しい内容が知りたいと思い、買いました。こちらも豊富なサンプルコードをもとに解説されている点がとても良いです。古い本であるため記述やサンプルコードが古いのが難点ですが、以下のレポジトリでは、現在の Linux Kernl で動くサンプルコードが公開されています。(自分の手元の Ubuntu 20.04 でもちゃんと動きました)

https://github.com/martinezjavier/ldd3

Linux デバイスドライバにはいくつか種類があるのですが、基本的には device file としてインターフェースを表現し、必要な callback 関数(e.g. open, read, etc.)を実装することでその機能を実現する、という内容を学べたのが良かったです。

並行プログラミング関連の本

並行プログラミング入門

並行プログラミングというトピック自体が好きなので、発売と同時に買って読んだ本です。最初に並行性、並列性についてきちんと定義を述べた上で、アセンブリ言語のレベルから高級言語のレベルに至るまで並行処理に必要な機能について説明されている点が良かったです。

個人的に、ARM (AArch64) の ldaxr や stlxr などの Load-Link/Store-Conditional 命令については知らなかったので、勉強になりました。x86 のように compare and exchange 命令を持っているものと思い込んでいたので、異なるアプローチをとっていることが驚きでした。

全体的にコードでのサンプルが豊富に掲載されているので、動かしながら学べるのも良い点です。Rust のコードが多数掲載されていて、Rust についての勉強にもなりました。

まとめ

2021年に読んで印象に残った本について、ジャンルごとにまとめてみました。全体的に、コンピュータの基礎について学び直す内容が多かったように思います。すぐに応用には結びつかなくとも大事な部分ではあるので、ゆっくりと学ぶ機会を作れて良かったと考えています。

2021年の振り返り

年が明けて 2022 年になりました。良い機会なので、2021年について簡単に振り返ってみたいと思います。

仕事面の振り返り

仕事面では、転職というとても大きな変化がありました。今の会社で働き始めたのは5月11日なので、8ヶ月弱ほど経ったことになります。初の転職なのでどうなるか少しドキドキしていましたが、とても楽しく仕事が出来ています。

入社以来開発に携わってきたアプリは10月末(日本では11月1日)にリリース出来ました。多くの人に楽しんでもらえて、とても嬉しく思っています。また、身近な友人や知人からも「プレイしているよ」という声を聞くことが出来て、とても幸せなことだと感じています。

自分の開発したものを通じて人々の生活にポジティブな変化を与えられていることが純粋に嬉しいです。今後も、より一層この動きを加速させていければと思っています。

プライベートの振り返り

2021年は結婚を機に引っ越しをして、プライベートの生活も大きく変わりました。新しく住み始めたのは閑静な住宅街で、とても住みやすく気に入っています。

プライベートの時間では、平日・休日問わず、隙を見ては本を読んだりコードを読んだりして過ごしていました。学んだことも多くあり、良かったと思います。読んだ本については、また別エントリでまとめたいと思います。追記: 2021年に読んで印象に残った本についてというエントリを書きました。

まとめ

2021年を簡単に振り返ってみました。見返してみると、仕事とプライベートの両方で変化の多い年だったと感じています。

個人的な考えとして、人間は変化を恐れてコンフォートゾーンにとどまってしまいがちだと考えているので、そこを抜け出す動きを出来たのは良かったです。2022 年も、チャレンジを常に心がけて、コンフォートゾーンを抜け出して日々を過ごしていきたいです。

日本のスタートアップで 6 年間働いたあと、シリコンバレーのスタートアップへ転職する話

2021年4月末最終出社で Wantedly を退職して、5月からは Niantic でソフトウェアエンジニアとして働き始めることになりました。

Wantedly へ 2015 年4月に入社してから、ちょうど6年間働いたことになります。良い機会なので、Wantedly に関する思い出を振り返ってみたいと思います。

また、Niantic で働くことに対しての抱負なども書き残しておこうと思います。全体として長くなってしまったのはご容赦ください。

なお、タイトルは少しキャッチーにしてしまいましたが、実際に働くのは Niantic: Tokyo Studio という東京にある開発拠点です。

Wantedly との出会い

自分が Wantedly という会社を最初に認識したのは 2014 年 2 月初頭でした。まだ、大学院修士1年の学生だった頃です。当時は Wantedly の「会社に話を聞きにいくサービス(= 今の Wantedly Visit)」自体を知らなかったのですが、たまたま何かのきっかけで目にすることになりました。

PC 版の Web サイトを眺めていると、「掲載されているのは知らない企業ばかりなのに、どれも魅力的に見える」のが新鮮な体験だったのを覚えています(注: 当時は WantedlyiOS アプリは開発中で、まだリリースされていませんでした)。しばらく募集を見ていると Wantedly の募集が見つかり、「プラットフォームが自社の募集も出している」というのが面白くて、軽い気持ちで「話を聞きにいきたい」ボタンを押しました。すぐに返事がきて、その日のうちに「話を聞きにいく日程」が決まっていました。

会社に行った際には開発チームのメンバーや開発フローなどを紹介してもらい、さらにエンジニア全員でランチに行きました。当時はまだエンジニアは5名程度、社員全員でも10数名程度だったと記憶しています。色々話をしたのですが、気がつくと3月の春休み中に1週間ほど短期インターンをしようという話になっていました。そして、3月のインターンを経て、面接を受け、内定をもらいました。

当時の自分は(Wantedly 以外では)エンジニアリングのバイトや就業型インターンへの参加経験が無く、そんな自分をスタートアップの Wantedly が採用してくれたことがとても嬉しかったです。少数精鋭のすごいエンジニアが集まる組織に加わることで、自分が成長出来ることへの期待にワクワクしました。

なお、振り返ってみると、当時の経営陣にとって「1年後に入社する学生を採用する」というのはなかなかリスキーな選択肢だったのではないかと思います。リスクをとってくれたお陰で今の自分はあると思うので、感謝しています。

Wantedly での最初の3年間(= プロダクト開発チーム時代)

内定から1年後、2015年に Wantedly に入社しました。同時期に入社したメンバーも含めると、2014年からの1年間でエンジニアは倍以上に増えて、「急成長するスタートアップらしさ」を感じたのを覚えています。

Wantedly に入社してから最初の3年間は、プロダクト開発のエンジニアとして以下の3つのプロダクトのリリースとグロースに携わりました。平均すると毎年1つのサービスを新しくリリースしていた計算になります(注: 実際には最初の2年で3つリリースして、最後の1年はグロースに注力してました)。

2015年に入社後、最初は1人のエンジニアとして「スカウト」のリリースとグロースに携わりました。途中からは、チーム内で技術的なリードを行うテックリード的な役割を担うようになりました。さらに、プロダクトの戦略を考えて機能に落とし込むプロダクトマネージャー的な役割も兼務するようになりました。

そもそも、Wantedly には「エンジニアあるいはデザイナーがプロダクトマネージャーの役割を兼務する」という文化があります。その文化の表れとして、エンジニアとデザイナーからなる開発チーム全体を「個別のプロダクト(or 機能)にオーナーシップを持つ小さなチーム」に分割して、その分割されたチームがそれぞれのプロダクト戦略と数字目標に責任を持つ、という体制で開発を行っていました。それぞれのチームにはリーダーがいて、最終的にはリーダーが数字目標やプロダクト(or 機能)の方向性に責任を持ちます。自分もそういったリーダーの1人でした。

プロダクト開発チームにいた時代はやる事が多くて大変でしたが、一方で「プロダクト開発のライフサイクル」や「様々なトレードオフ」、「ステークホルダーと調整しながら上手くプロジェクトを進める方法」など多くのことを学んだように思います。例えば、書籍「アジャイルサムライ」に出てくる「時間(リリーススケジュール)とスコープ(機能)とクオリティのトレードオフ」などは実感を持って体感出来ました。リリーススケジュールや人的リソースなど様々な制約がある中でどうインパクトを出すかを常に考え、そのために「優先度付け」や「小さなコストでの素早い学び」の重要性を強く意識しました。また、継続的にプロダクトの改善を行うために、「ソフトウェアが変更容易な状態を保つためにどういった設計にするか」ということも考え続けていました。

振り返ってみると、プロダクト開発を行った最初の3年間の経験は、その後の自分の仕事の進め方に強く影響を与えたように思います。

(ちなみにこれは余談ですが、最初に名前をあげた3つのプロダクトのうち、「Wantedly Tools」と「CASE」については昨年4月にサービス終了となりました。様々な事情もありしょうがないのですが、立ち上げに関わっていた1人としては少し寂しくもあります。。。)

Wantedly インフラチームへの異動

3年間プロダクト開発を行った後で、2018年にインフラチームへ異動しました。これは、自分の意向と会社の意向が以下のように重なったことで生まれた動きです。

  • 自分の意向: プロダクトの数字目標がある中で、分析や戦略の考案に時間を使う場面も多かった。それは楽しくチャレンジングである一方で、「エンジニアリングに使える時間を増やしたい」という気持ちがあった。また、個人的に学んでいた「スケーラブルなシステムの構築方法(e.g. 分散システムの知識、パフォーマンスチューニングの知識、低レイヤの知識、etc.)」などの知識を活かしたいと思ったこと、2010年代のインフラ領域のテクノロジーの発展に強い興味を持っていたことなども後押しをした。
  • 会社の意向: システムのマイクロサービス化(= チームごとに固有の Repository と Service を持つ状況)が進む中で、アプリケーションレイヤーにガッツリ手を入れてチーム横断での基盤整備が出来る人が欲しかった。

実はこの時は他社も含めて検討したのですが、最終的には「Wantedly のインフラチームで基盤整備をするのが一番面白そう」だと判断しました。

ここで、インフラチームで取り組んだことについて書く前に、「Wantedly のインフラチームがそもそもどういったチームなのか」について軽く説明しておきます。Wantedly のシステムは基本的に全て Kubernetes Cluster の上で動いていて、インフラチームは Kubernetes を中心に基盤(= 開発ツールや開発環境、CI/CD の仕組み、各種ライブラリ、etc.)の整備を行っています。これは、全体像としては以下のような3レイヤーでシステムを捉えられることを意味します。

[Application (= Microservices)]
[Kubernetes]
[Cloud (= AWS, GCP)]

一番下は「Cloud レイヤー」で、これは AWSGCP などのクラウドサービスを表しています。Wantedly は全てのサービスをクラウドに乗せているため、一番下がこのレイヤーになります。下から2つ目は「Kubernetes レイヤー」で、Kubernetes Cluster やその機能拡張を行う各種 Custom Controller、KubernetesAPI サーバーへリクエストしてデプロイなどを行う CLI ツールが該当します。一番上が 「Application レイヤー」で、Kubernetes 上で動く様々なマイクロサービスを表しています。Wantedly のインフラチームは、この3つのレイヤー全てにアプローチするチーム となっています。

強調しておきたいのは、「インフラチームは『依頼を受けて Cloud リソースを用意するチーム』ではない」 ということです。我々は「依頼を受けて何かする」のではなく、「仕組みを作って社内の開発者が自分で必要なものを揃えられるようにする」ということを重要視していました。例えば Cloud リソースは Terraform を利用して Git 管理を行い、「開発者が自分たちに必要なものは自分たちで PR を出して作る」ということを可能にしていました。また、新しくマイクロサービスを追加したい場合も、「開発者が自分たちで Kubernetes 向けの Manifest file を用意して Apply 出来るようにする」ことで、インフラチームがボトルネックにならずに済む様にしていました。マイクロサービス開発が活発になってからは、開発生産性とシステムの信頼性の両方を高めるために、「社内外で利用されるライブラリの開発や、マイクロサービスアーキテクチャの改善」なども行なっていました。

インフラチームは、開発チームのメンバーそれぞれがオーナーシップを持ってマイクロサービス開発の全プロセスに携われる様に「仕組みを整える」ことに注力していました。

なお、この辺りは infrastructure_README.md というドキュメント にもまとめています。これは、Wantedly のインフラチームの活動を社外の方に説明する際に利用していたドキュメントです。CTO や後任のインフラチームリーダーから許可をもらったので、ここに掲載しておきます。詳細が気になる方は、こちらも参照してみてください。

自分がインフラチームで取り組んだプロジェクトのうち、主要なものは以下になります。

上記のプロジェクトを進める際は、「システムのマイクロサービス化が進む中で開発チームの日々の取り組みをいかにスケールさせるか」、「ツールやライブラリやシステムによる基盤をどのように提供するか」、「高い信頼性と高パフォーマンスを両立するシステムをどう実現するか」を常に考えていました。「様々なマイクロサービスから利用されるコアマイクロサービスの開発」や「社内で利用されるライブラリの開発」は、そのためのアプローチの 1 つです。「基盤として提供する事で、開発者が自然と恩恵が受けられる」という体験を意識していました。

その他、OSS 化や取り組みの発信も積極的に行うようにしていました。結果として、取り組み自体を社外の方に知ってもらう機会を増やせたのは良かったなと思っています。

インフラチームで仕事を進める中で、途中からは技術的なリードや People Management にも取り組むようになりました。数人のチームのマネージャーとして、チームのパフォーマンスを最大化させるための取り組みを出来たのは良い経験となったと思います。「改善され続ける組織やチーム」というものに自分は思い入れが強くて、それを意識しつつ、「チームが目指すゴールの設定、チームの日々のワークフロー改善、Member との 1on1、採用活動、etc.」などに取り組みました。

ここまでが、2021年4月までに Wantedly で仕事として取り組んだ内容です。

Wantedly での日々を振り返ってみて

Wantedly での日々を振り返ってみると、ちょうど3年間の区切りで「プロダクト開発チーム」、「インフラチーム」と新しい環境にチャレンジしていたことが分かります。それぞれに異なる難しさがあったように思いますが、どの時代も「学び」はありました。ここまでに書いてきたことと重なりますが、あえて「学び」を書き出すと以下のような内容になると思います。

  • プロダクト開発チーム時代 .. プロダクトをどう成長させるか、様々な制約条件の中でプロジェクトをどう進めるか、ソフトウェアが変更容易な状態を保つためにどういった設計にするか、etc.
  • インフラチーム時代 .. プロダクト開発チームの日々の取り組みをどうスケールさせるか、ツールやライブラリやシステムによる基盤をどのように提供するか、高い信頼性と高パフォーマンスを両立するシステムをどう実現するか、etc.
    • 注: プロダクト開発チーム時代と同様の学びももちろんある

Wantedly には様々なチャレンジの機会を与えてもらい、そのお陰で成長出来たと思っています。感謝しています。一方で、自分が生み出した価値も相当量あると思っていて、十分に恩返しは出来たのかなとも感じています。

Niantic への転職について

自分にとって、 Wantedly という会社は今でも好きな会社です。一方で、以下のようなモチベーションがあり、それはすぐには達成出来ないというもどかしさもありました。

  • 世界中で多数のユーザーに使われるプロダクトの開発に携わりたい
    • 「大量のデータを扱い、多数のトラフィックを捌く」という技術的チャレンジをしたい
    • 「自分の変更が世界中の人々の活動にポジティブな影響を与える」という実感を得たい

そういったモチベーションを持って検討した結果、縁あって Niantic からオファーをいただけることになりました。

Niantic は自分が Pokémon GO ユーザーだったこともあり、憧れていた会社の1つです。そういった会社で働けることを、とても嬉しく思っています。入社後は、3月にアナウンスがあった「ピクミンを起用したアプリ」のサーバーサイド開発を行うことになりそうです。リリースされた際にはぜひ遊んでいただけると嬉しいです。

nianticlabs.com

実は、Niantic からオファーをもらった直後はあまり実感が湧きませんでした。しかしながら、しばらくすると様々なグッズが会社から送られてきて、現実として認識できるようになりました。送られてきたグッズの中でも以下のトートバッグはお気に入りで、日常生活でも使い始めています。

転職とチャレンジ

今回の転職は、様々な面で自分にとってチャレンジだと思っています。例えば、以下のように多くの観点から「自分にとって初めての取り組み」となっています。

  • 初めての転職
  • 初めての米国企業
  • 初めてのゲーム開発
  • 初めての仕事での Java 開発
  • 初めての「世界中で使われるプロダクト(= 大量のデータを扱い、多数のトラフィックを捌く必要のあるプロダクト)」の開発
  • etc.

不安はゼロではないですが、それも含めて「新しいチャレンジ」をとても楽しみにしています。

オフトピック: 選考プロセスについて

具体的には書きませんが、Niantic の選考プロセスは一般的な外資ソフトウェア企業とあまり変わらないんじゃないかと思います。実は、自分は1年以上前に GoogleSWE ポジションで面接を受けて、技術選考は通った(= Phone Interview, Onsite Interview を受けた上で採用委員会から Approve をもらった)ことがあります。2020年はその後色々あり、結局 Google では採用には至りませんでしたが、この経験は役に立ったように思います。

また、これは自分が勝手に思っていることかもしれませんが、選考の中では自分の「スタートアップでの経験」も評価してくれたように感じています。Niantic は(Wantedly よりも大きいとはいえ)まだまだスタートアップなので、経験が活きる部分もあるんじゃないかと期待しています。

まとめ

Wantedly での6年間を振り返ってみました。また、Niantic への転職について、考えをまとめてみました。

自分にとって、この転職は様々な点からチャレンジだと思っています。このチャレンジを楽しみたいと思います。

最後になりますが、今回の転職にあたっては様々な方にお世話になりました。この場を借りてお礼申し上げます。

mold の Build 手順メモ

はじめに

mold と呼ばれる高速なリンカを利用して Chromium を Build してみる という記事の中で、mold と呼ばれる「高速なリンカ」について紹介しました。

https://github.com/rui314/mold

mold は、自分の知る限りでは現時点では特に Binary の配信などは行っていないようです。利用したい場合には repository を git clone して、自分で Build して利用する必要があります。

mold の Build 手順についてメモ程度に記録を残しておこうと思います。

2021年3月20日追記: ちょうど4日ほど前に mold の README に How to build というセクションが追加されたようです。最新のソースコードで Build する場合は、そちらを参照してみてください。cf. https://github.com/rui314/mold#how-to-build

ステップ1. mold のソースコードを取得する

まず、mold の git repository を clone します。

minami@chromium-dev-20210227:~$ git clone https://github.com/rui314/mold.git
minami@chromium-dev-20210227:~$ ls
mold
minami@chromium-dev-20210227:~$ cd mold/

mold は自身が依存する mimalloc と oneTBB を git submodule として利用しているので、git submodule update --init を実行してこれらのソースコードを取得します。

minami@chromium-dev-20210227:~/mold$ git submodule update --init
Submodule 'mimalloc' (https://github.com/microsoft/mimalloc.git) registered for path 'mimalloc'
Submodule 'oneTBB' (https://github.com/oneapi-src/oneTBB.git) registered for path 'oneTBB'
Cloning into '/home/minami/mold/mimalloc'...
Cloning into '/home/minami/mold/oneTBB'...
Submodule path 'mimalloc': checked out '4cc8bff90d9e081298ca2c1a94024c7ad4a9e478'
Submodule path 'oneTBB': checked out 'eca91f16d7490a8abfdee652dadf457ec820cc37'

これで、必要なソースコードの取得は完了しました。

ステップ2. 必要な package を install して Build する

make, libssl-dev, zlib1g-dev, cmake, build-essential を利用するので、apt で install しておきます(cmake, build-essential は git submodule で取り込んだ mimalloc と oneTBB の Build に利用します)。

minami@chromium-dev-20210227:~/mold$ sudo apt update
minami@chromium-dev-20210227:~/mold$ sudo apt install -y make libssl-dev zlib1g-dev cmake build-essential

$ make submodules で、submodule で取り込んだ oneTBB と mimalloc を Build します。

minami@chromium-dev-20210227:~/mold$ make submodules
make -C oneTBB
make[1]: Entering directory '/home/minami/mold/oneTBB'
Created the ./build/linux_intel64_gcc_cc9.3.0_libc2.31_kernel5.4.0_release directory
make -C "./build/linux_intel64_gcc_cc9.3.0_libc2.31_kernel5.4.0_release"  -r -f ../../build/Makefile.tbb cfg=release
make[2]: Entering directory '/home/minami/mold/oneTBB/build/linux_intel64_gcc_cc9.3.0_libc2.31_kernel5.4.0_release'
.
.
.
make[2]: Leaving directory '/home/minami/mold/oneTBB/build/linux_intel64_gcc_cc9.3.0_libc2.31_kernel5.4.0_release'
make[1]: Leaving directory '/home/minami/mold/oneTBB'
mkdir -p mimalloc/out/release
(cd mimalloc/out/release; cmake ../..)
.
.
.
[100%] Built target mimalloc-test-api
make[2]: Leaving directory '/home/minami/mold/mimalloc/out/release'
make[1]: Leaving directory '/home/minami/mold/mimalloc/out/release'

これで、oneTBB と mimalloc の Build は完了しました。

いよいよ、mold の Build を行いたいと思いいます。ただし、この状態では clang++ が無いのでまだ Build できません。

minami@chromium-dev-20210227:~/mold$ make
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o main.o main.cc
make: clang++: Command not found
make: *** [<builtin>: main.o] Error 127

clang++ の install では、https://apt.llvm.org/ の「Automatic installation script」である bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)" を利用する事にします。

minami@chromium-dev-20210227:~/mold$ sudo bash -c "$(wget -O - https://apt.llvm.org/llvm.sh)"
--2021-02-27 00:50:07--  https://apt.llvm.org/llvm.sh
.
.
.
Processing triggers for install-info (6.7.0.dfsg.2-5) ...
Processing triggers for libc-bin (2.31-0ubuntu9.2) ...

これで、clang++-11 が install されます(注: 2021/02/27 時点の話で、時期によって最新 version は違うかもしれません)。 clang++ として利用できるように、symlink を貼っておきます。

minami@chromium-dev-20210227:~/mold$ ls /usr/bin | grep clang
clang++-11
clang-11
clang-cpp-11
clangd-11

minami@chromium-dev-20210227:~/mold$ sudo ln -s /usr/bin/clang++-11 /usr/bin/clang++

これで clang++ は使えるようになりましたが、まだ「span header が見つからない」というエラーが出る状態です。

minami@chromium-dev-20210227:~/mold$ make
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o main.o main.cc
In file included from main.cc:1:
./mold.h:17:10: fatal error: 'span' file not found
#include <span>
         ^~~~~~
1 error generated.
make: *** [<builtin>: main.o] Error 1

make で実行されているコマンドに -v オプションをつけると詳細が表示されるのですが、この include path の中で span が見つからないのが原因のようです。

minami@chromium-dev-20210227:~/mold$ clang++ -v -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o main.o main.cc
Ubuntu clang version 11.1.0-++20210204121720+1fdec59bffc1-1~exp1~20210203232336.162
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
Candidate multilib: .;@m64
Selected multilib: .;@m64
 (in-process)
 "/usr/lib/llvm-11/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cc -mrelocation-model static -mframe-pointer=none -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debug-info-kind=limited -dwarf-version=4 -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-11/lib/clang/11.1.0 -I oneTBB/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-11/lib/clang/11.1.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-deprecated-volatile -Wno-switch -std=c++20 -fdeprecated-macro -fdebug-compilation-dir /home/minami/mold -ferror-limit 19 -pthread -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -fcolor-diagnostics -vectorize-loops -vectorize-slp -faddrsig -o main.o -x c++ main.cc
clang -cc1 version 11.1.0 based upon LLVM 11.1.0 default target x86_64-pc-linux-gnu
ignoring nonexistent directory "oneTBB/include"
ignoring nonexistent directory "/include"
ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9"
#include "..." search starts here:
#include <...> search starts here:
 /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9
 /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/x86_64-linux-gnu/c++/9
 /usr/bin/../lib/gcc/x86_64-linux-gnu/9/../../../../include/c++/9/backward
 /usr/local/include
 /usr/lib/llvm-11/lib/clang/11.1.0/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.
In file included from main.cc:1:
./mold.h:17:10: fatal error: 'span' file not found
#include <span>
         ^~~~~~
1 error generated.

ここで、おもむろに libstdc++-10-dev package の install をします。

minami@chromium-dev-20210227:~/mold$ sudo apt install -y libstdc++-10-dev

上記 package を install すると、ちゃんと span header を見つけることができます。

minami@chromium-dev-20210227:~/mold$ clang++ -v -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o main.o main.cc
Ubuntu clang version 11.1.0-++20210204121720+1fdec59bffc1-1~exp1~20210203232336.162
Target: x86_64-pc-linux-gnu
Thread model: posix
InstalledDir: /usr/bin
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/9
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/10
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/9
Selected GCC installation: /usr/bin/../lib/gcc/x86_64-linux-gnu/10
Candidate multilib: .;@m64
Selected multilib: .;@m64
 (in-process)
 "/usr/lib/llvm-11/bin/clang" -cc1 -triple x86_64-pc-linux-gnu -emit-obj -disable-free -disable-llvm-verifier -discard-value-names -main-file-name main.cc -mrelocation-model static -mframe-pointer=none -fmath-errno -fno-rounding-math -mconstructor-aliases -munwind-tables -target-cpu x86-64 -fno-split-dwarf-inlining -debug-info-kind=limited -dwarf-version=4 -debugger-tuning=gdb -v -resource-dir /usr/lib/llvm-11/lib/clang/11.1.0 -I oneTBB/include -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10 -internal-isystem /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward -internal-isystem /usr/local/include -internal-isystem /usr/lib/llvm-11/lib/clang/11.1.0/include -internal-externc-isystem /usr/include/x86_64-linux-gnu -internal-externc-isystem /include -internal-externc-isystem /usr/include -O2 -Wno-deprecated-volatile -Wno-switch -std=c++20 -fdeprecated-macro -fdebug-compilation-dir /home/minami/mold -ferror-limit 19 -pthread -fgnuc-version=4.2.1 -fcxx-exceptions -fexceptions -fcolor-diagnostics -vectorize-loops -vectorize-slp -faddrsig -o main.o -x c++ main.cc
clang -cc1 version 11.1.0 based upon LLVM 11.1.0 default target x86_64-pc-linux-gnu
ignoring nonexistent directory "/include"
ignoring duplicate directory "/usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10"
#include "..." search starts here:
#include <...> search starts here:
 oneTBB/include
 /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10
 /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/x86_64-linux-gnu/c++/10
 /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/backward
 /usr/local/include
 /usr/lib/llvm-11/lib/clang/11.1.0/include
 /usr/include/x86_64-linux-gnu
 /usr/include
End of search list.

少し探してみると /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10 の中に span header があるのを見つけました。これが重要だったようです。

minami@chromium-dev-20210227:~/mold$ ls -la /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/span
-rw-r--r-- 1 root root 13251 Aug  8  2020 /usr/bin/../lib/gcc/x86_64-linux-gnu/10/../../../../include/c++/10/span

この状態で make を実行すると、mold の Build が行われて、mold Binary が生成されるはずです。

minami@chromium-dev-20210227:~/mold$ make
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o output_chunks.o output_chunks.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o mapfile.o mapfile.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o perf.o perf.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o linker_script.o linker_script.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o archive_file.o archive_file.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o output_file.o output_file.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o subprocess.o subprocess.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o gc_sections.o gc_sections.cc
clang++  -g -IoneTBB/include -pthread -std=c++20 -Wno-deprecated-volatile -Wno-switch -O2  -c -o icf.o icf.cc
clang++  main.o object_file.o input_sections.o output_chunks.o mapfile.o perf.o linker_script.o archive_file.o output_file.o subprocess.o gc_sections.o icf.o -o mold -L/home/minami/mold/oneTBB/build/linux_intel64_gcc_cc9.3.0_libc2.31_kernel5.4.0_release/ -Wl,-rpath=/home/minami/mold/oneTBB/build/linux_intel64_gcc_cc9.3.0_libc2.31_kernel5.4.0_release/ -L/home/minami/mold/mimalloc/out/release -Wl,-rpath=/home/minami/mold/mimalloc/out/release -lcrypto -pthread -ltbb -lmimalloc

以下のように mold Binary が生成されていれば成功です 🎉

minami@chromium-dev-20210227:~/mold$ ls -la mold
-rwxrwxr-x 1 minami minami 11142376 Feb 27 01:43 mold

まとめ

「高速なリンカである mold」について、Build 手順をまとめました。

今後、mold が広く使われるようになり、package での配信などが行われるようになればここに記載した手順はおそらく不要になると思います。しかしながら、mold はまだ開始したばかりの project であり、開発環境なども未整備の状態です。しばらくは、「自分で Build して動かしてみる」という状態が続くでしょう。

このブログが、「試しに mold を利用してみる」ことへの一助となれば幸いです。

mold と呼ばれる高速なリンカを利用して Chromium を Build してみる

はじめに

現在、広く使われているリンカの中でもっとも高速なものとして有名なのは LLVM project の LLD でしょう。LLD のパフォーマンスについては、公式 document に以下のような benchmark が掲載されていて、GNU ld, GNU gold などと比較して圧倒的に早いという結果が示されています。

Program Output size GNU ld GNU gold w/o threads GNU gold w/threads lld w/o threads lld w/threads
ffmpeg dbg 92 MiB 1.72s 1.16s 1.01s 0.60s 0.35s
mysqld dbg 154 MiB 8.50s 2.96s 2.68s 1.06s 0.68s
clang dbg 1.67 GiB 104.03s 34.18s 23.49s 14.82s 5.28s
chromium dbg 1.14 GiB 209.05s [1] 64.70s 60.82s 27.60s 16.70s

cf. https://lld.llvm.org/#performance

ChromiumChecking out and building Chromium on Linux の手順にしたがって Build する場合、デフォルトで LLD が利用されるようになっています。そのため、何もせずとも「高速なリンク」という恩恵を受けることができるようになっています。

一方、LLD の author である Rui Ueyama さんが最近活発に開発しているのが mold と呼ばれるリンカです。

https://github.com/rui314/mold

こちらは個人 project として開発を進めているようなのですが、既にかなりの完成度のようで、「LLD 以上に高速なリンク」を実現しているようです。

今日は、この「最も高速なリンカである mold」を利用した Chromium の Build を試してみたいと思います。

ステップ1. Linux マシンを用意する

この部分は 前回 と同様です。

GCP の Compute Engine で以下の VM Instance を立ててそこで作業を行うことにします。

  • 8core, 32GiB memory (E2, e2-standard-8)
  • 200GB SSD
  • image: Ubuntu 20.04 LTS
  • zone: asia-northeast1-b

以下のコマンドで ssh して、そこで作業を行います。

$ gcloud beta compute ssh --zone "asia-northeast1-b" <instance 名>

ステップ2. mold を Build する

mold は、自分の知る限りでは現時点では特に Binary の配信などは行っていないようです。利用したい場合には https://github.com/rui314/mold を git clone して、自分で Build して利用する必要があります。

この部分の手順は別途またブログにまとめたいと思います。 追記: この部分の手順は mold の Build 手順メモ に記載しました。そちらを参照してみてください。

mold Binary が生成されて、以下のように利用できるようになっていれば OK です。

minami@chromium-dev-20210227:~$ git clone https://github.com/rui314/mold.git

# ここで、mold を Build

minami@chromium-dev-20210227:~$ ls -l /home/minami/mold/mold
-rwxrwxr-x 1 minami minami 11142376 Feb 27 01:43 /home/minami/mold/mold

ステップ3. Chromium の Build 環境を整える。

Chromium を Build して動かすまでの待ち時間を「7 時間」から「30 分」まで高速化してみる を参照して、Chromium の Build 環境を整えます。30分 もかからずに、chrome Binary を Build できる環境が整うはずです。

ステップ4. chrome Binary の Build に利用されているリンカを確認しておく

ここでは、事前に「$ autoninja -C out/Default chrome で Build をしたときに chrome Binary のリンクに利用されていたリンカは何者なのか」をチェックしてみます。

$ autoninja -C out/Default chrome を実行して、[4/4] LINK ./chrome のタイミングで起動している process を $ ps fax で見てみます。そうすると、以下のように /home/minami/chromium/src/out/Default/../../third_party/llvm-build/Release+Asserts/bin/ld.lld が利用されていることが分かります。

minami@chromium-dev-20210227:~$ ps fax
    PID TTY      STAT   TIME COMMAND
.
.
.
   1343 pts/0    S+     0:00  |           \_ bash /home/minami/depot_tools/autoninja -C out/Default chrome
   1455 pts/0    S+     0:07  |               \_ /home/minami/depot_tools/ninja-linux64 -C out/Default chrome -j 10
   1484 pts/0    S      0:00  |                   \_ /bin/sh -c python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sor
   1485 pts/0    S      0:00  |                       \_ python ../../build/toolchain/gcc_link_wrapper.py --output=./chrome -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -m64 -Wer
   1486 pts/0    S      0:00  |                           \_ ../../third_party/llvm-build/Release+Asserts/bin/clang++ -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -m64 -Werror -Wl,--gdb-index -rdynamic -nostdlib++ --sysroot=../../build/li
   1487 pts/0    D      0:03  |                               \_ /home/minami/chromium/src/out/Default/../../third_party/llvm-build/Release+Asserts/bin/ld.lld @/tmp/response-96ee6b.txt

/home/minami/chromium/src/out/Default/../../third_party/llvm-build/Release+Asserts/bin direcotory は以下のように clang や lld が入っていて、「LLVM project の toolchain が格納された directory」のようです。

minami@chromium-dev-20210227:~$ cd /home/minami/chromium/src/out/Default/../../third_party/llvm-build/Release+Asserts/bin
minami@chromium-dev-20210227:~/chromium/src/third_party/llvm-build/Release+Asserts/bin$ ls
clang  clang++  clang-cl  ld64.lld  ld64.lld.darwinnew  ld.lld  lld  lld-link  llvm-ar  llvm-objcopy  llvm-pdbutil  llvm-symbolizer  llvm-undname

ld.lldlld への symlink が貼られています。これが LLVM project の高速なリンカである LLD です。

minami@chromium-dev-20210227:~/chromium/src/third_party/llvm-build/Release+Asserts/bin$ ls -la ld.lld
lrwxrwxrwx 1 minami minami 3 Dec 12 12:50 ld.lld -> lld
minami@chromium-dev-20210227:~/chromium/src/third_party/llvm-build/Release+Asserts/bin$ ./ld.lld --help
OVERVIEW: lld

USAGE: ./ld.lld [options] file...
.
.
.
./ld.lld: supported targets: elf

LLD が利用されていることは、生成された chrome Binary からも確かめることができます。LLVM project の document である Using LLD - LLVM には以下のように「readelf コマンドで .comment section を読み取ると Linker: LLD という記述があるはず」と記載されています。

LLD leaves its name and version number to a .comment section in an output. If you are in doubt whether you are successfully using LLD or not, run readelf --string-dump .comment <output-file> and examine the output. If the string “Linker: LLD” is included in the output, you are using LLD.

実際に「Build した chrome Binary」に対して readelf を実行してみると、確かに Linker: LLD 12.0.0 という記述を見つけることができます。

minami@chromium-dev-20210227:~/chromium/src$ readelf --string-dump .comment out/Default/chrome

String dump of section '.comment':
  [     0]  GCC: (Debian 7.5.0-3) 7.5.0
  [    1c]  clang version 12.0.0 (https://github.com/llvm/llvm-project/ 6ee22ca6ceb71661e8dbc296b471ace0614c07e5)
  [    82]  Linker: LLD 12.0.0 (https://github.com/llvm/llvm-project/ 6ee22ca6ceb71661e8dbc296b471ace0614c07e5)

ここまで、利用されているリンカが何なのかを確認しました。それ以外に、chrome Binary の Build の際に実行される script もチェックしておきます。これは、autoninja コマンドに -v オプションをつけることで出力することができます。この情報は後々利用します。

minami@chromium-dev-20210227:~/chromium/src$ time autoninja -v -C out/Default chrome
ninja: Entering directory `out/Default'
[1/4] ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/chrome/common/channel_info/channel_info.o.d -DUSE_UDEV -DUSE_AURA=1 -DUSE_GLIB=1 -DUSE_NSS_CERTS=1 -DUSE_OZONE=1 -DUSE_X11=1 -D_FILE_OFFSET_BITS=64 -D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_GNU_SOURCE -DCR_CLANG_REVISION=\"llvmorg-12-init-12923-g6ee22ca6-1\" -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -DCOMPONENT_BUILD -D_LIBCPP_ABI_UNSTABLE -D_LIBCPP_ABI_VERSION=Cr -D_LIBCPP_ENABLE_NODISCARD -D_LIBCPP_DEBUG=0 -DCR_LIBCXX_REVISION=375504 -DCR_SYSROOT_HASH=22f2db7711f7426a364617bb6d78686cce09a8f9 -D_DEBUG -DDYNAMIC_ANNOTATIONS_ENABLED=1 -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_40 -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_40 -DWEBP_EXTERN=extern -DABSL_CONSUME_DLL -DBORINGSSL_SHARED_LIBRARY -I../.. -Igen -I../../third_party/perfetto/include -Igen/third_party/perfetto/build_config -Igen/third_party/perfetto -I../../third_party/libwebp/src -I../../third_party/abseil-cpp -I../../third_party/boringssl/src/include -I../../third_party/protobuf/src -Igen/protoc_out -fno-delete-null-pointer-checks -fno-strict-aliasing --param=ssp-buffer-size=4 -fstack-protector -funwind-tables -fPIC -pthread -fcolor-diagnostics -fmerge-all-constants -fcrash-diagnostics-dir=../../tools/clang/crashreports -mllvm -instcombine-lower-dbg-declare=0 -fcomplete-member-pointers -m64 -march=x86-64 -msse3 -Wno-builtin-macro-redefined -D__DATE__= -D__TIME__= -D__TIMESTAMP__= -Xclang -fdebug-compilation-dir -Xclang . -no-canonical-prefixes -Wall -Werror -Wextra -Wimplicit-fallthrough -Wunreachable-code -Wthread-safety -Wextra-semi -Wno-missing-field-initializers -Wno-unused-parameter -Wno-c++11-narrowing -Wno-unneeded-internal-declaration -Wno-undefined-var-template -Wno-psabi -Wno-ignored-pragma-optimize -Wno-implicit-int-float-conversion -Wno-final-dtor-non-final-class -Wno-builtin-assume-aligned-alignment -Wno-deprecated-copy -Wno-non-c-typedef-for-linkage -Wmax-tokens -O0 -fno-omit-frame-pointer -g2 -Xclang -debug-info-kind=constructor -gsplit-dwarf -ggnu-pubnames -ftrivial-auto-var-init=pattern -fvisibility=hidden -Xclang -add-plugin -Xclang find-bad-constructs -Xclang -plugin-arg-find-bad-constructs -Xclang check-ipc -Wheader-hygiene -Wstring-conversion -Wtautological-overlap-compare -isystem../../build/linux/debian_sid_amd64-sysroot/usr/include/glib-2.0 -isystem../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu/glib-2.0/include -DPROTOBUF_ALLOW_DEPRECATED=1 -Wno-undefined-bool-conversion -Wno-tautological-undefined-compare -std=c++14 -fno-trigraphs -Wno-trigraphs -fno-exceptions -fno-rtti -nostdinc++ -isystem../../buildtools/third_party/libc++/trunk/include -isystem../../buildtools/third_party/libc++abi/trunk/include --sysroot=../../build/linux/debian_sid_amd64-sysroot -fvisibility-inlines-hidden -c ../../chrome/common/channel_info.cc -o obj/chrome/common/channel_info/channel_info.o
[2/4] touch obj/chrome/common/channel_info.stamp
[3/4] python "../../build/toolchain/gcc_solink_wrapper.py" --readelf="readelf" --nm="nm"  --sofile="./libvr_common.so" --tocfile="./libvr_common.so.TOC" --output="./libvr_common.so" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -Wl,-soname="libvr_common.so" -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -m64 -Werror -Wl,--gdb-index -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -Wl,-rpath=\$ORIGIN -o "./libvr_common.so" @"./libvr_common.so.rsp"
[4/4] python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -m64 -Werror -Wl,--gdb-index -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -pie -Wl,--disable-new-dtags -Wl,-rpath=\$ORIGIN -o "./chrome" -Wl,--start-group @"./chrome.rsp" ./libbase.so ./libabsl.so ./libboringssl.so ./libperfetto.so ./libbindings.so ./libbindings_base.so ./libmojo_public_system_cpp.so ./libmojo_public_system.so ./libmojo_cpp_platform.so ./libmessage_support.so ./libmojo_mojom_bindings.so ./libmojo_mojom_bindings_shared.so ./liburl_mojom_traits.so ./libmojo_base_mojom_shared.so ./libmojo_base_shared_typemap_traits.so ./libmojo_base_lib.so ./libbase_i18n.so ./libicui18n.so ./libicuuc.so ./liburl.so ./libui_base.so ./libui_base_features.so ./libui_data_pack.so ./libskia.so ./libgfx.so ./libcolor_space.so ./libcolor_utils.so ./libgeometry.so ./libgeometry_skia.so ./libgfx_switches.so ./libanimation.so ./libcodec.so ./librange.so ./libcc_paint.so ./libcc_base.so ./libcc_debug.so ./libfile_info.so ./libevents_base.so ./libplatform.so ./libkeycodes_x11.so ./libui_base_x.so ./libcontent_public_common_mojo_bindings_shared.so ./libmojom_platform_shared.so ./libandroid_mojo_bindings_shared.so ./libauthenticator_test_mojo_bindings_shared.so ./libcolor_scheme_mojo_bindings_shared.so ./libmojom_mhtml_load_result_shared.so ./libscript_type_mojom_shared.so ./libweb_feature_mojo_bindings_mojom_shared.so ./libservice_manager_mojom_shared.so ./libservice_manager_mojom_constants_shared.so ./libdom_storage_mojom_shared.so ./libframe_mojom_shared.so ./libblink_gpu_mojom_shared.so ./libservice_worker_storage_mojom_shared.so ./libtokens_mojom_shared.so ./libusb_shared.so ./libmojo_base_mojom.so ./libmojo_base_typemap_traits.so ./libcontent_settings_features.so ./libipc.so ./libipc_mojom.so ./libipc_mojom_shared.so ./libprotobuf_lite.so ./libtracing_cpp.so ./libstartup_tracing.so ./libtracing_mojom.so ./libtracing_mojom_shared.so ./libnet.so ./libcrcrypto.so ./libskia_shared_typemap_traits.so ./libcontent.so ./libgpu.so ./libmailbox.so ./libcrash_key_lib.so ./libchrome_zlib.so ./libvulkan_info.so ./libgfx_ipc.so ./libgfx_ipc_geometry.so ./libvulkan_ycbcr_info.so ./liburl_ipc.so ./libviz_common.so ./libviz_resource_format_utils.so ./libviz_vulkan_context_provider.so ./libdisplay.so ./libdisplay_types.so ./libgl_wrapper.so ./libmedia.so ./libshared_memory_support.so ./libleveldb_proto.so ./libkeyed_service_core.so ./libleveldatabase.so ./libgfx_ipc_color.so ./libgfx_ipc_buffer_types.so ./libgfx_ipc_skia.so ./libgfx_native_types_shared_mojom_traits.so ./libgfx_shared_mojom_traits.so ./libgpu_shared_mojom_traits.so ./liblearning_common.so ./libmedia_learning_shared_typemap_traits.so ./libmedia_session_base_cpp.so ./libcookies_mojom_support.so ./libnetwork_cpp_base.so ./libcrash_keys.so ./libcross_origin_embedder_policy.so ./libip_address_mojom_support.so ./libschemeful_site_mojom_support.so ./libwebrtc_component.so ./libservice_manager_mojom.so ./libservice_manager_mojom_constants.so ./libservice_manager_cpp_types.so ./libservice_manager_mojom_traits.so ./libservice_manager_cpp.so ./libmetrics_cpp.so ./libui_base_clipboard_types.so ./libevents.so ./libui_base_cursor_base.so ./libdisplay_shared_mojom_traits.so ./libcc.so ./libvideo_capture_mojom_support.so ./libcapture_base.so ./liblatency_shared_mojom_traits.so ./libprediction.so ./libblink_common.so ./libprivacy_budget.so ./libnetwork_cpp.so ./libweb_feature_mojo_bindings_mojom.so ./libmojom_modules_shared.so ./libmojom_core_shared.so ./libfido.so ./libbluetooth.so ./libscript_type_mojom.so ./libcc_ipc.so ./libcc_shared_mojom_traits.so ./libdom_storage_mojom.so ./libframe_mojom.so ./libblink_gpu_mojom.so ./libservice_worker_storage_mojom.so ./libtokens_traits.so ./libime_shared_mojom_traits.so ./libui_base_ime_types.so ./libui_events_ipc.so ./libweb_bluetooth_mojo_bindings_shared.so ./libax_base.so ./libui_accessibility_ax_mojom.so ./libui_accessibility_ax_mojom_shared.so ./libui_base_ime.so ./libcontent_common_mojo_bindings_shared.so ./libaccessibility.so ./libgfx_x11.so ./libxprotos.so ./libaura.so ./libcompositor.so ./libblink_features.so ./libsurface.so ./libpolicy.so ./libnetwork_service.so ./libmemory_instrumentation.so ./libresource_coordinator_public_mojom.so ./libresource_coordinator_public_mojom_shared.so ./libstorage_common.so ./libpublic.so ./libinterfaces_shared.so ./libstorage_service_filesystem_mojom_shared.so ./libstorage_service_filesystem_mojom.so ./libstorage_service_typemap_traits.so ./libmedia_session_cpp.so ./libstorage_browser.so ./libvr_public_cpp.so ./libdevice_vr_isolated_xr_service_mojo_bindings.so ./libdevice_vr_isolated_xr_service_mojo_bindings_shared.so ./libdevice_vr_test_mojo_bindings_shared.so ./libdevice_vr_service_mojo_bindings_shared.so ./libgamepad_mojom_shared.so ./libdevice_vr_test_mojo_bindings.so ./libdevice_vr_service_mojo_bindings.so ./libgamepad_mojom.so ./libgamepad_shared_typemap_traits.so ./libshared_with_blink.so ./libdevice_vr_public_typemaps.so ./libchrome_features.so ./libprefs.so ./libvariations_features.so ./liburl_matcher.so ./libcapture_lib.so ./libmedia_webrtc.so ./libwtf.so ./libcommon.so ./libnetwork_session_configurator.so ./libsql.so ./libchromium_sqlite3.so ./libwebdata_common.so ./libos_crypt.so ./libomnibox_http_headers.so ./libcloud_policy_proto_generated_compile.so ./libpolicy_component.so ./libpolicy_proto.so ./libgcm.so ./libnative_theme.so ./libservice_provider.so ./libui_message_center_cpp.so ./libppapi_shared.so ./libmojo_core_embedder.so ./libprinting.so ./libsandbox_services.so ./libsuid_sandbox_client.so ./libseccomp_bpf.so ./libsecurity_state_features.so ./libui_base_clipboard.so ./libui_base_data_transfer_policy.so ./libkeyed_service_content.so ./libuser_prefs.so ./libextras.so ./libsessions.so ./libcaptive_portal_core.so ./libdevice_features.so ./libweb_modal.so ./libdevice_event_log.so ./libshell_dialogs.so ./libui_base_idle.so ./libdbus.so ./libonc.so ./libhost.so ./libukm_recorder.so ./libcrdtp.so ./libuser_manager.so ./libperformance_manager_public_mojom.so ./libperformance_manager_public_mojom_shared.so ./libviews.so ./libui_base_ime_init.so ./libui_base_cursor_theme_manager.so ./libui_base_cursor.so ./libx11_window.so ./libui_touch_selection.so ./libproxy_config.so ./libtab_groups.so ./libmanager.so ./libmessage_center.so ./libfontconfig.so ./libx11_events_platform.so ./libdevices.so ./libevents_devices_x11.so ./libevents_x.so ./libffmpeg.so ./libwebview.so ./libdomain_reliability.so ./liblookalikes_features.so ./libui_devtools.so ./libdata_exchange.so ./libgesture_detection.so ./libsnapshot.so ./libweb_dialogs.so ./libcolor.so ./libmixers.so ./libdiscardable_memory_service.so ./libAPP_UPDATE.so ./libozone.so ./libozone_base.so ./libdisplay_util.so ./libvulkan_wrapper.so ./libplatform_window.so ./libui_base_ime_linux.so ./libfreetype_harfbuzz.so ./libmenu.so ./libproperties.so ./libthread_linux.so ./libgtk.so ./libgtk_ui_delegate.so ./libbrowser_ui_views.so ./libwm.so ./libmedia_message_center.so ./libtab_count_metrics.so ./libui_gtk_x.so ./libwm_public.so ./libppapi_host.so ./libppapi_proxy.so ./libcertificate_matching.so ./libdevice_base.so ./libswitches.so ./libcapture_switches.so ./libmidi.so ./libmedia_mojo_services.so ./libmedia_gpu.so ./libgles2_utils.so ./libgles2.so ./libgpu_ipc_service.so ./libgl_init.so ./libcert_net_url_loader.so ./liberror_reporting.so ./libevents_ozone.so ./libschema_org_common.so ./libmirroring_service.so ./libvr_common.so ./libvr_base.so ./libdevice_vr.so ./libblink_controller.so ./libblink_core.so ./libblink_mojom_broadcastchannel_bindings_shared.so ./libwtf_support.so ./libweb_feature_mojo_bindings_mojom_blink.so ./libmojo_base_mojom_blink.so ./libservice_manager_mojom_blink.so ./libservice_manager_mojom_constants_blink.so ./libblink_platform.so ./libcc_animation.so ./libresource_coordinator_public_mojom_blink.so ./libv8.so ./libblink_embedded_frame_sink_mojo_bindings_shared.so ./libperformance_manager_public_mojom_blink.so ./libui_accessibility_ax_mojom_blink.so ./libgin.so ./libblink_modules.so ./libgamepad_mojom_blink.so ./liburlpattern.so ./libdevice_vr_service_mojo_bindings_blink.so ./libdevice_vr_test_mojo_bindings_blink.so ./libdiscardable_memory_client.so ./libcbor.so ./libpdfium.so ./libheadless_non_renderer.so ./libc++.so -Wl,--end-group  -ldl -lpthread -lrt -lgmodule-2.0 -lgobject-2.0 -lgthread-2.0 -lglib-2.0 -lnss3 -lnssutil3 -lsmime3 -lplds4 -lplc4 -lnspr4 -latk-1.0 -latk-bridge-2.0 -lcups -ldbus-1 -lgio-2.0 -lexpat

real    0m15.202s
user    0m19.157s
sys     0m4.398s

ステップ5. mold を利用して chrome Binary をリンクしてみる

さて、ここまでで「chrome のリンクに LLD が利用されていること」、「コマンドとしては /home/minami/chromium/src/out/Default/../../third_party/llvm-build/Release+Asserts/bin/ld.lld が利用されていること」が確認できました。

次は、LLD の代わりに mold を利用してみたいと思います。ここでは、「ld.lld の symlink の向き先を lld から mold に切り替えて、Build する」というアプローチをとってみます。 以下のように ld.lld を消して、symlink の向き先を /home/minami/mold/mold に変えてみます。

minami@chromium-dev-20210227:~$ cd /home/minami/chromium/src/out/Default/../../third_party/llvm-build/Release+Asserts/bin
minami@chromium-dev-20210227:~/chromium/src/third_party/llvm-build/Release+Asserts/bin$ rm ld.lld
minami@chromium-dev-20210227:~/chromium/src/third_party/llvm-build/Release+Asserts/bin$ ln -s /home/minami/mold/mold ld.lld
minami@chromium-dev-20210227:~/chromium/src/third_party/llvm-build/Release+Asserts/bin$ ls -la ld.lld
lrwxrwxrwx 1 minami minami 22 Feb 28 03:06 ld.lld -> /home/minami/mold/mold

これで、mold が利用されるようになるはずです。この状態で再度 chrome の Build をしてみます。

ただ、この状態で $ autoninja -C out/Default chrome を実行すると、以下のように [3/4] SOLINK ./libvr_common.so のステップでリンクに失敗してしまします。

minami@chromium-dev-20210227:~/chromium/src$ time autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
[3/4] SOLINK ./libvr_common.so
FAILED: libvr_common.so libvr_common.so.TOC
python "../../build/toolchain/gcc_solink_wrapper.py" --readelf="readelf" --nm="nm"  --sofile="./libvr_common.so" --tocfile="./libvr_common.so.TOC" --output="./libvr_common.so" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -Wl,-soname="libvr_common.so" -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -Wl,--color-diagnostics -Wl,--no-call-graph-profile-sort -m64 -Werror -Wl,--gdb-index -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -Wl,-rpath=\$ORIGIN -o "./libvr_common.so" @"./libvr_common.so.rsp"
mold: unknown command line option: -soname=libvr_common.so
clang: error: linker command failed with exit code 1 (use -v to see invocation)
ninja: build stopped: subcommand failed.

real    0m5.480s
user    0m4.577s
sys     0m0.746s

mold が -soname オプションをサポートしてないために、エラーが出ているようです。

上記の python "../../build/toolchain/gcc_solink_wrapper.py" ... という部分が [3/4] SOLINK ./libvr_common.so のステップとして実際に実行されているコマンドです。ここから、「mold がサポートしていないオプション」を消して、同じコマンドを手動で実行してみます。具体的には、-soname , --color-diagnostics, --no-call-graph-profile-sort, --gdb-index オプションの指定を消して、以下のように実行します。こうすると、ちゃんと mold によるリンクに成功して、 libvr_common.so という Shared Object File が生成されます。

minami@chromium-dev-20210227:~/chromium/src/out/Default$ time python "../../build/toolchain/gcc_solink_wrapper.py" --readelf="readelf" --nm="nm"  --sofile="./libvr_common.so" --tocfile="./libvr_common.so.TOC" --output="./libvr_common.so" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -m64 -Werror -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -Wl,-rpath=\$ORIGIN -o "./libvr_common.so" @"./libvr_common.so.rsp"

real    0m0.563s
user    0m0.033s
sys     0m0.029s

minami@chromium-dev-20210227:~/chromium/src/out/Default$ ls -la libvr_common.so
-rwxrwxr-x 1 minami minami 192508187 Feb 28 03:14 libvr_common.so

上記の python script の実行が、[3/4] SOLINK ./libvr_common.so に相当する処理でした。さらに、 [4/4] LINK ./chrome に相当する処理も、直接 python script の実行を行う事にします。これは、ステップ4の最後に $ autoninja -v -C out/Default chrome コマンドで出力したコマンドの、[4/4] python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ...(長いので省略) が該当します。

ただ、この状態で [4/4] LINK ./chromeに相当する python script を実行しても、以下のように chrome.rsp という File が無いことで失敗してしまいます。

minami@chromium-dev-20210227:~/chromium/src/out/Default$ python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ...(長いので省略)
clang: error: no such file or directory: '@./chrome.rsp'

そこで、一度 ld.lld の symlink 先を lld に戻してから、$ autoninja -C out/Default chrome の実行中に [4/4] LINK ./chrome のタイミングで Ctrl-C で python script を強制 exit してみます。 こうすることで、「libvr_common.sochrome.rsp が存在する状態(ちょうど [4/4] LINK ./chrome の開始前の状態」を再現することが出来ます。

minami@chromium-dev-20210227:~/chromium/src$ ls out/Default/libvr_common.so
out/Default/libvr_common.so
minami@chromium-dev-20210227:~/chromium/src$ ls out/Default/chrome.rsp
out/Default/chrome.rsp

この状態で、再度 ld.lld の symlink 先を mold にしてから、[4/4] LINK ./chrome に相当する python script を手動で実行します。mold でサポートされてない --color-diagnostics, --no-call-graph-profile-sort, --gdb-indexオプションの指定は消しておきます。 こうすると、以下のようにちゃんとリンクに成功します。chrome Binary が生成されたことも確認できます。

minami@chromium-dev-20210227:~/chromium/src/out/Default$ time python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -m64 -Werror -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -pie -Wl,--disable-new-dtags -Wl,-rpath=\$ORIGIN -o "./chrome" -Wl,--start-group @"./chrome.rsp" ./libbase.so ./libabsl.so ./libboringssl.so ./libperfetto.so ./libbindings.so ./libbindings_base.so ./libmojo_public_system_cpp.so ./libmojo_public_system.so ./libmojo_cpp_platform.so ./libmessage_support.so ./libmojo_mojom_bindings.so ./libmojo_mojom_bindings_shared.so ./liburl_mojom_traits.so ./libmojo_base_mojom_shared.so ./libmojo_base_shared_typemap_traits.so ./libmojo_base_lib.so ./libbase_i18n.so ./libicui18n.so ./libicuuc.so ./liburl.so ./libui_base.so ./libui_base_features.so ./libui_data_pack.so ./libskia.so ./libgfx.so ./libcolor_space.so ./libcolor_utils.so ./libgeometry.so ./libgeometry_skia.so ./libgfx_switches.so ./libanimation.so ./libcodec.so ./librange.so ./libcc_paint.so ./libcc_base.so ./libcc_debug.so ./libfile_info.so ./libevents_base.so ./libplatform.so ./libkeycodes_x11.so ./libui_base_x.so ./libcontent_public_common_mojo_bindings_shared.so ./libmojom_platform_shared.so ./libandroid_mojo_bindings_shared.so ./libauthenticator_test_mojo_bindings_shared.so ./libcolor_scheme_mojo_bindings_shared.so ./libmojom_mhtml_load_result_shared.so ./libscript_type_mojom_shared.so ./libweb_feature_mojo_bindings_mojom_shared.so ./libservice_manager_mojom_shared.so ./libservice_manager_mojom_constants_shared.so ./libdom_storage_mojom_shared.so ./libframe_mojom_shared.so ./libblink_gpu_mojom_shared.so ./libservice_worker_storage_mojom_shared.so ./libtokens_mojom_shared.so ./libusb_shared.so ./libmojo_base_mojom.so ./libmojo_base_typemap_traits.so ./libcontent_settings_features.so ./libipc.so ./libipc_mojom.so ./libipc_mojom_shared.so ./libprotobuf_lite.so ./libtracing_cpp.so ./libstartup_tracing.so ./libtracing_mojom.so ./libtracing_mojom_shared.so ./libnet.so ./libcrcrypto.so ./libskia_shared_typemap_traits.so ./libcontent.so ./libgpu.so ./libmailbox.so ./libcrash_key_lib.so ./libchrome_zlib.so ./libvulkan_info.so ./libgfx_ipc.so ./libgfx_ipc_geometry.so ./libvulkan_ycbcr_info.so ./liburl_ipc.so ./libviz_common.so ./libviz_resource_format_utils.so ./libviz_vulkan_context_provider.so ./libdisplay.so ./libdisplay_types.so ./libgl_wrapper.so ./libmedia.so ./libshared_memory_support.so ./libleveldb_proto.so ./libkeyed_service_core.so ./libleveldatabase.so ./libgfx_ipc_color.so ./libgfx_ipc_buffer_types.so ./libgfx_ipc_skia.so ./libgfx_native_types_shared_mojom_traits.so ./libgfx_shared_mojom_traits.so ./libgpu_shared_mojom_traits.so ./liblearning_common.so ./libmedia_learning_shared_typemap_traits.so ./libmedia_session_base_cpp.so ./libcookies_mojom_support.so ./libnetwork_cpp_base.so ./libcrash_keys.so ./libcross_origin_embedder_policy.so ./libip_address_mojom_support.so ./libschemeful_site_mojom_support.so ./libwebrtc_component.so ./libservice_manager_mojom.so ./libservice_manager_mojom_constants.so ./libservice_manager_cpp_types.so ./libservice_manager_mojom_traits.so ./libservice_manager_cpp.so ./libmetrics_cpp.so ./libui_base_clipboard_types.so ./libevents.so ./libui_base_cursor_base.so ./libdisplay_shared_mojom_traits.so ./libcc.so ./libvideo_capture_mojom_support.so ./libcapture_base.so ./liblatency_shared_mojom_traits.so ./libprediction.so ./libblink_common.so ./libprivacy_budget.so ./libnetwork_cpp.so ./libweb_feature_mojo_bindings_mojom.so ./libmojom_modules_shared.so ./libmojom_core_shared.so ./libfido.so ./libbluetooth.so ./libscript_type_mojom.so ./libcc_ipc.so ./libcc_shared_mojom_traits.so ./libdom_storage_mojom.so ./libframe_mojom.so ./libblink_gpu_mojom.so ./libservice_worker_storage_mojom.so ./libtokens_traits.so ./libime_shared_mojom_traits.so ./libui_base_ime_types.so ./libui_events_ipc.so ./libweb_bluetooth_mojo_bindings_shared.so ./libax_base.so ./libui_accessibility_ax_mojom.so ./libui_accessibility_ax_mojom_shared.so ./libui_base_ime.so ./libcontent_common_mojo_bindings_shared.so ./libaccessibility.so ./libgfx_x11.so ./libxprotos.so ./libaura.so ./libcompositor.so ./libblink_features.so ./libsurface.so ./libpolicy.so ./libnetwork_service.so ./libmemory_instrumentation.so ./libresource_coordinator_public_mojom.so ./libresource_coordinator_public_mojom_shared.so ./libstorage_common.so ./libpublic.so ./libinterfaces_shared.so ./libstorage_service_filesystem_mojom_shared.so ./libstorage_service_filesystem_mojom.so ./libstorage_service_typemap_traits.so ./libmedia_session_cpp.so ./libstorage_browser.so ./libvr_public_cpp.so ./libdevice_vr_isolated_xr_service_mojo_bindings.so ./libdevice_vr_isolated_xr_service_mojo_bindings_shared.so ./libdevice_vr_test_mojo_bindings_shared.so ./libdevice_vr_service_mojo_bindings_shared.so ./libgamepad_mojom_shared.so ./libdevice_vr_test_mojo_bindings.so ./libdevice_vr_service_mojo_bindings.so ./libgamepad_mojom.so ./libgamepad_shared_typemap_traits.so ./libshared_with_blink.so ./libdevice_vr_public_typemaps.so ./libchrome_features.so ./libprefs.so ./libvariations_features.so ./liburl_matcher.so ./libcapture_lib.so ./libmedia_webrtc.so ./libwtf.so ./libcommon.so ./libnetwork_session_configurator.so ./libsql.so ./libchromium_sqlite3.so ./libwebdata_common.so ./libos_crypt.so ./libomnibox_http_headers.so ./libcloud_policy_proto_generated_compile.so ./libpolicy_component.so ./libpolicy_proto.so ./libgcm.so ./libnative_theme.so ./libservice_provider.so ./libui_message_center_cpp.so ./libppapi_shared.so ./libmojo_core_embedder.so ./libprinting.so ./libsandbox_services.so ./libsuid_sandbox_client.so ./libseccomp_bpf.so ./libsecurity_state_features.so ./libui_base_clipboard.so ./libui_base_data_transfer_policy.so ./libkeyed_service_content.so ./libuser_prefs.so ./libextras.so ./libsessions.so ./libcaptive_portal_core.so ./libdevice_features.so ./libweb_modal.so ./libdevice_event_log.so ./libshell_dialogs.so ./libui_base_idle.so ./libdbus.so ./libonc.so ./libhost.so ./libukm_recorder.so ./libcrdtp.so ./libuser_manager.so ./libperformance_manager_public_mojom.so ./libperformance_manager_public_mojom_shared.so ./libviews.so ./libui_base_ime_init.so ./libui_base_cursor_theme_manager.so ./libui_base_cursor.so ./libx11_window.so ./libui_touch_selection.so ./libproxy_config.so ./libtab_groups.so ./libmanager.so ./libmessage_center.so ./libfontconfig.so ./libx11_events_platform.so ./libdevices.so ./libevents_devices_x11.so ./libevents_x.so ./libffmpeg.so ./libwebview.so ./libdomain_reliability.so ./liblookalikes_features.so ./libui_devtools.so ./libdata_exchange.so ./libgesture_detection.so ./libsnapshot.so ./libweb_dialogs.so ./libcolor.so ./libmixers.so ./libdiscardable_memory_service.so ./libAPP_UPDATE.so ./libozone.so ./libozone_base.so ./libdisplay_util.so ./libvulkan_wrapper.so ./libplatform_window.so ./libui_base_ime_linux.so ./libfreetype_harfbuzz.so ./libmenu.so ./libproperties.so ./libthread_linux.so ./libgtk.so ./libgtk_ui_delegate.so ./libbrowser_ui_views.so ./libwm.so ./libmedia_message_center.so ./libtab_count_metrics.so ./libui_gtk_x.so ./libwm_public.so ./libppapi_host.so ./libppapi_proxy.so ./libcertificate_matching.so ./libdevice_base.so ./libswitches.so ./libcapture_switches.so ./libmidi.so ./libmedia_mojo_services.so ./libmedia_gpu.so ./libgles2_utils.so ./libgles2.so ./libgpu_ipc_service.so ./libgl_init.so ./libcert_net_url_loader.so ./liberror_reporting.so ./libevents_ozone.so ./libschema_org_common.so ./libmirroring_service.so ./libvr_common.so ./libvr_base.so ./libdevice_vr.so ./libblink_controller.so ./libblink_core.so ./libblink_mojom_broadcastchannel_bindings_shared.so ./libwtf_support.so ./libweb_feature_mojo_bindings_mojom_blink.so ./libmojo_base_mojom_blink.so ./libservice_manager_mojom_blink.so ./libservice_manager_mojom_constants_blink.so ./libblink_platform.so ./libcc_animation.so ./libresource_coordinator_public_mojom_blink.so ./libv8.so ./libblink_embedded_frame_sink_mojo_bindings_shared.so ./libperformance_manager_public_mojom_blink.so ./libui_accessibility_ax_mojom_blink.so ./libgin.so ./libblink_modules.so ./libgamepad_mojom_blink.so ./liburlpattern.so ./libdevice_vr_service_mojo_bindings_blink.so ./libdevice_vr_test_mojo_bindings_blink.so ./libdiscardable_memory_client.so ./libcbor.so ./libpdfium.so ./libheadless_non_renderer.so ./libc++.so -Wl,--end-group  -ldl -lpthread -lrt -lgmodule-2.0 -lgobject-2.0 -lgthread-2.0 -lglib-2.0 -lnss3 -lnssutil3 -lsmime3 -lplds4 -lplc4 -lnspr4 -latk-1.0 -latk-bridge-2.0 -lcups -ldbus-1 -lgio-2.0 -lexpat

real    0m3.312s
user    0m0.064s
sys     0m0.027s

minami@chromium-dev-20210227:~/chromium/src/out/Default$ ls -la chrome
-rwxrwxr-x 1 minami minami 1296141738 Feb 28 03:46 chrome

この chrome.comment section を見て、出自を確認してみましょう。LLD でリンクした時は Linker: LLD 12.0.0 という記述があったのに対して、この chrome Binary にはその記述がありません。逆説的に、「LLD 以外のリンカ(= mold)でリンクしたこと」が確認できたと言えそうです。

minami@chromium-dev-20210227:~/chromium/src$ readelf --string-dump .comment out/Default/chrome

String dump of section '.comment':
  [     1]  GCC: (Debian 7.5.0-3) 7.5.0
  [    1d]  clang version 12.0.0 (https://github.com/llvm/llvm-project/ 6ee22ca6ceb71661e8dbc296b471ace0614c07e5)

2021年3月27日追記: mold の README の How to use を見ると、今では .comment section に mold という文字列が commit hash つきで記載されるようになったようです(ただし、自分は動作未検証です)。

生成した chrome Binary の挙動も確認してみましょう。以前のブログ記事 のように、「headless mode での実行」を試してみます。以下のようにちゃんと動作をすることが確認できました 🎉

minami@chromium-dev-20210227:~/chromium/src$ ./out/Default/chrome --headless --disable-gpu --dump-dom https://example.com
[0228/035317.179292:WARNING:headless_content_main_delegate.cc(530)] Cannot create Pref Service with no user data dir.
[0228/035317.194952:INFO:content_main_runner_impl.cc(1027)] Chrome is running in full browser mode.
<!DOCTYPE html>
.
.
.

ということで、無事「mold による chrome Binary のリンク」に成功しました。

既存の Build の仕組みに組み込もうと思うと「mold ではサポートされてない option の扱い」などいくつか考える必要がありそうですが、試しにリンクをするだけであれば比較的簡単に実行ができることが分かりました。

LLD と mold の速度比較

mold は「LLD よりも高速なリンカ」として開発されています。実際に、リンクにかかる時間がどれだけ変わったのか、比較してみましょう。ここでは、上記の「リンカに渡す option を減らした状態でのリンクの実行」を mold と LLD の両方で行って、time で計測した結果を載せています。

LLD

  • [3/4] SOLINK ./libvr_common.so 相当の処理
minami@chromium-dev-20210227:~/chromium/src/out/Default$ time python "../../build/toolchain/gcc_solink_wrapper.py" --readelf="readelf" --nm="nm"  --sofile="./libvr_common.so" --tocfile="./libvr_common.so.TOC" --output="./libvr_common.so" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -m64 -Werror -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -Wl,-rpath=\$ORIGIN -o "./libvr_common.so" @"./libvr_common.so.rsp"

real    0m1.265s
user    0m1.391s
sys     0m0.837s
  • [4/4] LINK ./chrome 相当の処理
minami@chromium-dev-20210227:~/chromium/src/out/Default$ time python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ...(長いので省略)

real    0m7.777s
user    0m8.309s
sys     0m3.664s

mold

  • [3/4] SOLINK ./libvr_common.so 相当の処理
minami@chromium-dev-20210227:~/chromium/src/out/Default$ time python "../../build/toolchain/gcc_solink_wrapper.py" --readelf="readelf" --nm="nm"  --sofile="./libvr_common.so" --tocfile="./libvr_common.so.TOC" --output="./libvr_common.so" -- ../../third_party/llvm-build/Release+Asserts/bin/clang++ -shared -fuse-ld=lld -Wl,--fatal-warnings -Wl,--build-id -fPIC -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,defs -Wl,--as-needed -m64 -Werror -rdynamic -nostdlib++ --sysroot=../../build/linux/debian_sid_amd64-sysroot -L../../build/linux/debian_sid_amd64-sysroot/usr/local/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/lib/x86_64-linux-gnu -L../../build/linux/debian_sid_amd64-sysroot/usr/lib/x86_64-linux-gnu -Wl,-rpath=\$ORIGIN -o "./libvr_common.so" @"./libvr_common.so.rsp"

real    0m0.563s
user    0m0.033s
sys     0m0.029s
  • [4/4] LINK ./chrome 相当の処理
minami@chromium-dev-20210227:~/chromium/src/out/Default$ time python "../../build/toolchain/gcc_link_wrapper.py" --output="./chrome" -- ...(長いので省略)

real    0m3.312s
user    0m0.064s
sys     0m0.027s

mold と LLD の比較

[3/4] SOLINK ./libvr_common.so 相当の処理については 1.265s -> 0.563s、[4/4] LINK ./chrome 相当の処理については 7.777s -> 3.312s とそれぞれ 2倍以上の高速化が達成できています 🎉

なお、上記の比較を見ると十分に早いですが、それでも「はじめにで掲載した Rui Ueyama さんの Tweet」の 2.5秒という記述に比べると遅いです。これはおそらく、今回利用した GCP VM instance の「8core」という構成に起因するのではないかと考えています。mold は複数 CPU core をうまく活用する作りになっているようなので、CPU core 数が増えることで近いパフォーマンスが出るのだと思われます。

まとめ

Rui Ueyama さんが開発してる mold を利用して、Chromium の Build をしてみました。

実験の結果、試しにリンクをするだけであれば比較的簡単に実行ができることが分かりました。また、CPU core 数が 8 とそれほど多くない構成であっても、 LLD に比べてリンクが2倍以上高速化する という改善が見られることが分かりました。

Rui Ueyama さんは自分が尊敬するエンジニアの1人なのですが、彼のエンジニアリングによって世界がより良くなっている事を体感できたように思います。

Chromium を Build して動かすまでの待ち時間を「7 時間」から「30 分」まで高速化してみる

Chromium をゼロから Build して動かしてみる という前回のブログでは、Chromium を Build して動かすという一連のフローを試してみました。

この時は、Checking out and building Chromium on Linux の手順に従って作業をしました。これは、「最新の Chromium」を Linux 上で Build する事が出来るしっかりした公式手順です。しかしながら、8core, 32GiB memory の GCP VM instance でも Build に 6-7 時間程度かかってしまうのがネックでした。

そこで今日は、Chromium を Build して動かすまでのサイクルを 30 分で試せるようにすること を目指します。7時間と比較すると約15倍の高速化です!

なお、具体的には 事前に Build 済みの Object File を GCS に置いておき、ダウンロードして再利用することで再ビルドの手間を避ける というアプローチをとります。そのため、「最新の Chromium ではなく事前に Build 済みの古い Version しか試せない」ことが欠点となります。「最新の Chromium へのコミット」などを行いたい場合には適さないアプローチでしょう。

しかしながら、「過去の Version の Chromium でも良いから、試しに Build して動かしてみたい」という場合には役立つのではないかと期待しています。

以下、ステップ 0 から順番に作業を進めてみます。

ステップ0. 事前に Chromium の Build 成果物を用意して、GCS へアップロードしておく

Chromium はまともにゼロから Build すると、とてつもなく長い時間がかかってしまいます。そこで、「事前に Chromium を Build して生成した一連の Object File 及び Binary を tar.gz にまとめたもの」を用意しておきます。これをダウンロードして利用することで、「再ビルドの手間」を省くことができるはずです。

対象の File は、chromium-dev-south37 GCS Bucket の中で chromium20201213.tar.gz という名前で配置しています(suffix は日付で、2020/12/13 時点の Chromium のコードを Build したことを示しています)。

$ gsutil ls -l gs://chromium-dev-south37/chromium20201213.tar.gz
18900879483  2020-12-13T15:56:30Z  gs://chromium-dev-south37/chromium20201213.tar.gz
TOTAL: 1 objects, 18900879483 bytes (17.6 GiB)

Public にダウンロード可能な形で公開しているので、誰でも試していただくことが可能です。ソースコードも含んでいるので、17.6GiB というかなり巨大なサイズになっています。

この後のステップでは、上記の「ソースコードと Build 成果物を tar.gz でまとめたもの」を利用して作業を進めます。

ステップ1. Linux マシンを用意する

この部分は 前回 と同様です。

GCP の Compute Engine で以下の VM Instance を立ててそこで作業を行うことにします。

  • 8core, 32GiB memory (E2, e2-standard-8)
  • 200GB SSD
  • image: Ubuntu 20.04 LTS
  • zone: asia-northeast1-b

以下のコマンドで ssh して、そこで作業を行います。

$ gcloud beta compute ssh --zone "asia-northeast1-b" <instance 名>

ステップ2. 事前に用意しておいた Chromium の一連の Build 成果物をダウンロードする

早速、ステップ0 として事前に用意しておいた「Chromium の一連の Build 成果物」をダウンロードしましょう。以下のように $ gsutil cp コマンド利用して GCS からダウンロードをします。

$ gsutil -o 'GSUtil:parallel_thread_count=1' \
   -o 'GSUtil:sliced_object_download_max_components=8' \
   cp gs://chromium-dev-south37/chromium20201213.tar.gz ./chromium20201213.tar.gz

なお、ここでは Cloud Storage のパフォーマンスを最適化する - Google Cloud Platform の推奨に従って、GSUtil:parallel_thread_count オプションや GSUtil:sliced_object_download_max_components=8 オプションを利用しています。これは、 「1つの巨大なファイルの転送」をしたい場合に推奨されるオプション として紹介されています。

上記のコマンドを実行すると、環境にもよりますが大体 3 分ほどでダウンロードが完了するはずです。

minami@chromium-dev-20210226:~$ gsutil -o 'GSUtil:parallel_thread_count=1' \
>    -o 'GSUtil:sliced_object_download_max_components=8' \
>    cp gs://chromium-dev-south37/chromium20201213.tar.gz ./chromium20201213.tar.gz
Copying gs://chromium-dev-south37/chromium20201213.tar.gz...
- [1 files][ 17.6 GiB/ 17.6 GiB]   25.7 MiB/s
Operation completed over 1 objects/17.6 GiB.

ダウンロードが終わると、 17.6 GiB の chromium20201213.tar.gz が作られているはずです。

minami@chromium-dev-20210226:~$ ls -l | grep chromium
total 18457900
-rw-rw-r-- 1 minami minami 18900879483 Feb 26 12:34 chromium20201213.tar.gz

今度は、この File を $ tar xvzf chromium20201213.tar.gz で展開します。これは大体 10-15 分程度で完了するはずです。

minami@chromium-dev-20210226:~$ time tar xvzf chromium20201213.tar.gz
chromium/
.
.
.
chromium/src/cloud_print/OWNERS
chromium/.gclient
chromium/.gclient_entries

real    15m45.171s
user    5m57.618s
sys     2m49.498s

展開が終わると、以下のように chromium directory が作られているはずです。chromium directory の中には chromium/src/out/Default directory が入っています。その中には Build 済みの Object File や chrome Binary が存在する事も確認できます。

minami@chromium-dev-20210226:~$ ls -la | grep chromium
drwxrwxr-x  4 minami minami        4096 Dec 12 12:33 chromium
-rw-rw-r--  1 minami minami 18900879483 Feb 26 12:34 chromium20201213.tar.gz

minami@chromium-dev-20210226:~$ ls chromium/src/out/
Default

minami@chromium-dev-20210226:~$ ls chromium/src/out/Default/ | grep '\.so$' | wc -l
375

minami@chromium-dev-20210226:~$ ls -la chromium/src/out/Default/chrome
-rwxrwxr-x 1 minami minami 1058842464 Dec 13 14:43 chromium/src/out/Default/chrome

ただし、この状態ではまだ chrome Binary は実行できません。実行しようとすると、以下のようにエラーが出てしまいます。

minami@chromium-dev-20210226:~$ ./chromium/src/out/Default/chrome
./chromium/src/out/Default/chrome: error while loading shared libraries: libnss3.so: cannot open shared object file: No such file or directory

ステップ3. 必要な package を install

chrome Binary 実行にはいくつか package が必要となります。

まず、python2 が必要になるので、install しておきます。

minami@chromium-dev-20210226:~$ sudo apt update -y
minami@chromium-dev-20210226:~$ sudo apt install -y python2
minami@chromium-dev-20210226:~$ which python2
/usr/bin/python2
minami@chromium-dev-20210226:~$ python2 -V
Python 2.7.18
minami@chromium-dev-20210226:~$ sudo ln -s /usr/bin/python2 /usr/local/bin/python
minami@chromium-dev-20210226:~$ which python
/usr/local/bin/python
minami@chromium-dev-20210226:~$ python -V
Python 2.7.18

次に、./build/install-build-deps.sh を実行します。これは 6 分程度で完了するはずです。

minami@chromium-dev-20210226:~/chromium/src$ time ./build/install-build-deps.sh
.
.
.
Installing locales.
Generating locales (this might take a while)...
  da_DK.UTF-8... done
  en_US.UTF-8... done
  fr_FR.UTF-8... done
  he_IL.UTF-8... done
  zh_TW.UTF-8... done
Generation complete.

real    6m10.812s
user    2m35.472s
sys     0m58.130s

これで、必要な package の install が完了しました。この状態で、試しに chrome Binary を実行してみましょう。前回のように、「headless mode での実行」を試してみます。以下のようにちゃんと動作をすることが確認できました 🎉

minami@chromium-dev-20210226:~/chromium/src$ ./out/Default/chrome --headless --disable-gpu --dump-dom https://example.com
<!DOCTYPE html>
.
.
.

ここまで、 GCP VM instance への ssh から大体 20-25 分 ほどで「chrome Binary を動かすこと」が出来ました

ステップ4. Build に必要な環境をセットアップ

さて、「ダウンロードした chrome Binary」を動かせただけでは、あまり嬉しくありません。「コードを書き換えながら、chrome を Build して実行することができる」という状態を目指します。

まず、ninja など Build に必要なツールを利用するために、depot_tools をインストールします。

minami@chromium-dev-20210226:~/chromium/src$ cd $HOME
minami@chromium-dev-20210226:~$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
# PATH を通しておく
minami@chromium-dev-20210226:~$ export PATH="$PATH:${PWD}/depot_tools"

これで、gn などの Build に必要なコマンドが使えるようになります。

minami@chromium-dev-20210226:~$ which gn
/home/minami/depot_tools/gn

次に、この状態で chrome Binary の Build をしてみます。autoninja -C out/Default chrome を実行すると、既に chrome Binary は存在するため、no work to do という表示が出て 5 秒程度で終了します。

minami@chromium-dev-20210226:~$ cd chromium/src/
minami@chromium-dev-20210226:~/chromium/src$ autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
ninja: no work to do.

前回と同様に、chrome/common/channel_info.cc という File を touch して timestamp を更新してみます。こうすると、更新した File (及びその File に依存した File)だけを対象に Build が行われるので、20 秒程度で chrome Binary の Build が終了します。

minami@chromium-dev-20210226:~/chromium/src$ touch chrome/common/channel_info.cc
minami@chromium-dev-20210226:~/chromium/src$ time autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
[4/4] LINK ./chrome

real    0m21.574s
user    0m20.022s
sys     0m5.478s

ということで、 「コードを書き換えながら、chrome をBuild して実行することができる」という状態も実現できました!🎉

まとめ

事前に Build 済みの Object File を GCS からダウンロードすることで、再ビルドの手間を避けるアプローチを試しました。その結果、前回の「ゼロからの Build」ではトータルで 7 時間かかったのに対して、今回は 30 分程度で「Chromium を Build して動かすまでのサイクルを試す」ことが出来るようになりました。

このように、「事前に用意した Build 成果物」をうまく活用することで、再ビルドの時間的・マシン的コストは劇的に下げることができます。この記事が、Chromium を試しに触ってみることへの障壁を下げることに少しでも貢献していれば幸いです。

Chromium をゼロから Build して動かしてみる

はじめに

ソフトウェアに対しての理解を深めたい場合、どうするでしょうか?

1つの方法は、ソースコードを読むことです。よく出来たソフトウェアのソースコードを読むことで、様々な学びを得る事が出来ます。

しかし、ソースコードだけでは挙動が掴みづらかったり、自分の理解に自信が持てない時もあります。そういった時は、動かしながら試すというアプローチも選択肢になるでしょう。「少し書き換えて動かしてみる」というサイクルを繰り返す事で、自分の中のメンタルモデルの正しさを確認しながらソースコードを読み進める事が出来ます。

今日は、巨大な OSS として有名な Chromium を対象に、「ソースコードから Build して動かしてみる」ことにします。Build だけでも大変なので、いったんは「Build して動かす」ところまでです。これをきっかけに Chromium に対して理解を深めたいと思います。

TL; DR

  • Checking out and building Chromium on Linux に従って作業を進めれば、特にハマることなく Linux 上で Chromium の Build が可能。ただし、何も高速化の工夫をしない場合は 8core, 32GiB memory の GCP VM instance で 6-7 時間程度かかる。
  • 一度 Build すると、それ以降は変更した file に依存するものだけが Build 対象になるので、Build は十数秒程度に高速化する。
  • Build した Chromium はページの取得やレンダーが可能。「自分が Build した Chromium」がちゃんと動くことを確認できた。

参照するもの: Chromium の Document

Chromium のページを見ていると、様々な Document が見つかります。 その中で、「LinuxChromium を Check out して Build する手順」がまとまっているのが以下の Document です。

今日は、このドキュメントを元に作業を進めてみます。

ステップ1. Linux マシンを用意する

まず始めに、作業を行うための Linux マシンを用意する必要があります。 再現性を持たせるために、今回は GCP の Compute Engine で以下の VM Instance を立ててそこで作業を行うことにします。

  • 8core, 32GiB memory (E2, e2-standard-8)
  • 200GB SSD
  • image: Ubuntu 20.04 LTS
  • zone: asia-northeast1-b

以下のコマンドで ssh して、そこで作業を行います。

$ gcloud beta compute ssh --zone "asia-northeast1-b" <instance 名>

ステップ2. 環境をセットアップ

まずは、Build に必要な様々な tool 群である depot_tools を install します。これで、fetch などのコマンドが使えるようになります。

minami@chromium-experiment:~$ git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
minami@chromium-experiment:~$ export PATH="$PATH:${PWD}/depot_tools"
minami@chromium-experiment:~$ which fetch
/home/minami/depot_tools/fetch

次に、作業用 dicretory として chromium directory を作ります。

minami@chromium-experiment:~$ mkdir ~/chromium && cd ~/chromium

depot_toolsfetchソースコードを取得します。

minami@chromium-experiment:~/chromium$ fetch --no-history --nohooks chromium
Running: gclient root
WARNING: Your metrics.cfg file was invalid or nonexistent. A new one will be created.
Running: gclient config --spec 'solutions = [
{
"name": "src",
"url": "https://chromium.googlesource.com/chromium/src.git",
"managed": False,
"custom_deps": {},
"custom_vars": {},
},
]
'
Running: gclient sync --nohooks --no-history

________ running 'git -c core.deltaBaseCacheLimit=2g clone --no-checkout --progress https://chromium.googlesource.com/chromium/src.git --depth=1 /home/minami/chromium/_gclient_src_p28570xw' in '/home/minami/chromium'
Cloning into '/home/minami/chromium/_gclient_src_p28570xw'...
remote: Counting objects: 352926, done
remote: Finding sources: 100% (352926/352926)
remote: Total 352926 (delta 78472), reused 232647 (delta 78472)
Receiving objects: 100% (352926/352926), 1.10 GiB | 15.97 MiB/s, done.
Resolving deltas: 100% (78472/78472), done.
Syncing projects: 98% (122/124) src/third_party/tflite/src
[0:07:56] Still working on:
[0:07:56] src/v8
[0:07:56] src/third_party/angle/third_party/VK-GL-CTS/src
.
.
.
Syncing projects: 99% (123/124) src/v8
.
.
.
Syncing projects: 100% (124/124), done.
Running: git submodule foreach 'git config -f $toplevel/.git/config submodule.$name.ignore all'
Running: git config diff.ignoreSubmodules all

これで、Chromiumソースコード取得が完了しました。

次に、dependency の install を行います。まずは、python2 を install します。

minami@chromium-experiment:~/chromium/src$ sudo apt update
minami@chromium-experiment:~/chromium/src$ sudo apt install -y python2
minami@chromium-experiment:~/chromium/src$ which python2
/usr/bin/python2
minami@chromium-experiment:~/chromium/src$ python2 -V
Python 2.7.18

minami@chromium-experiment:~/chromium/src$ sudo ln -s /usr/bin/python2 /usr/local/bin/python
minami@chromium-experiment:~/chromium/src$ which python
/usr/local/bin/python
minami@chromium-experiment:~/chromium/src$ python -V
Python 2.7.18

さらに、./build/install-build-deps.sh を実行します。これで、Build に必要な package などの install が行われます。これはしばらく時間がかかります。

minami@chromium-experiment:~/chromium/src$ ./build/install-build-deps.sh
Running as non-root user.
You might have to enter your password one or more times for 'sudo'.
.
.
.
Installing locales.
Generating locales (this might take a while)...
da_DK.UTF-8... done
en_US.UTF-8... done
fr_FR.UTF-8... done
he_IL.UTF-8... done
zh_TW.UTF-8... done
Generation complete.

さらに、$ gclient runhooks を実行します。

minami@chromium-experiment:~/chromium/src$ gclient runhooks
Hook 'vpython src/build/landmines.py' took 45.92 secs
Running hooks: 6% ( 6/97) nacltools
________ running 'vpython src/build/download_nacl_toolchains.py --mode nacl_core_sdk sync --extract' in '/home/minami/chromium'
INFO: --Syncing arm_trusted to revision 2--
.
.
.
Running hooks: 61% (60/97) subresource-filter-ruleset
________ running 'vpython src/third_party/depot_tools/download_from_google_storage.py --no_resume --no_auth --bucket chromium-ads-detection -s src/third_party/subresource-filter-ruleset/data/UnindexedRules.sha1' in '/home/minami/chromium'
0> Downloading src/third_party/subresource-filter-ruleset/data/UnindexedRules@83bced6c2676ed8d7c57a84c9a8d4f76c08f79e2...
Downloading 1 files took 5.270337 second(s)
Running hooks: 100% (97/97), done.

これで、Chromiumソースコード全体と、Chromium の build に必要な dependency が揃いました。

ステップ3. Chromium の Build を行う

いよいよ Chromium の Build を行います。ChromiumNinja をメインの Build tool として利用するのですが、そのための Build directory および Configuration を GN というツールを利用して生成します。今回は out/Default という Build directory を生成します。

minami@chromium-experiment:~/chromium/src$ gn gen out/Default
Done. Made 17175 targets from 2693 files in 6820ms

Checking out and building Chromium on Linux を見る限りでは、 $ gn args out/Default を実行して、Build を高速化するための様々な設定を追加で行うことが出来るようですが、一旦何も追加設定はせずに進みます。

Ninja を利用した Chromium の Build は、$ autoninja -C out/Default chrome で行うことが出来るようです。このコマンドを実行して Build を開始します。

minami@chromium-experiment:~/chromium/src$ autoninja -C out/Default chrome

これで、Build が開始しました。Chromium の Build はとても時間がかかります(自分の環境だと Build が終わるまでに 5時間半 かかりました。長いです)。

待っている間に別の shell で $ ps fax を実行して Process tree を見てみると、以下のような結果になっていることが分かります。autoninja は内部で ninja-linux64 を実行しているようです。-j 10 がオプションとして渡されてるので、10 並列で Build が行われているように見えます。

 1783 ?        Ss     0:00  \_ sshd: minami [priv]
 1853 ?        S      0:01      \_ sshd: minami@pts/1
 1854 pts/1    Ss     0:00          \_ -bash
56626 pts/1    S+     0:00              \_ bash /home/minami/depot_tools/autoninja -C out/Default chrome
56760 pts/1    S+     0:03                  \_ /home/minami/depot_tools/ninja-linux64 -C out/Default chrome -j 10
61079 pts/1    S      0:00                      \_ /bin/sh -c ../../third_party/llvm-build/Release+Asserts/bin/clang -MMD -MF obj/native_client/src/t
61084 pts/1    R      0:02                      |   \_ ../../third_party/llvm-build/Release+Asserts/bin/clang -MMD -MF obj/native_client/src/trusted/
61090 pts/1    S      0:00                      \_ /bin/sh -c ../../third_party/llvm-build/Release+Asserts/bin/clang -MMD -MF obj/native_client/src/t
61091 pts/1    R      0:02                      |   \_ ../../third_party/llvm-build/Release+Asserts/bin/clang -MMD -MF obj/native_client/src/trusted/
61476 pts/1    S      0:00                      \_ /bin/sh -c python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylib
61479 pts/1    R      0:00                      |   \_ python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylibs -o ge
61482 pts/1    S      0:00                      \_ /bin/sh -c python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylib
61483 pts/1    R      0:00                      |   \_ python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylibs -o ge
61486 pts/1    S      0:00                      \_ /bin/sh -c ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/ppapi/cpp/objects
61488 pts/1    R      0:00                      |   \_ ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/ppapi/cpp/objects/var.o.
61492 pts/1    S      0:00                      \_ /bin/sh -c python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylib
61495 pts/1    S      0:00                      |   \_ python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylibs -o ge
61506 pts/1    R      0:00                      |       \_ /bin/sh /sbin/ldconfig -p
61497 pts/1    S      0:00                      \_ /bin/sh -c python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylib
61498 pts/1    S      0:00                      |   \_ python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylibs -o ge
61501 pts/1    S      0:00                      \_ /bin/sh -c ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/ppapi/cpp/objects
61502 pts/1    R      0:00                      |   \_ ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/ppapi/cpp/objects/var_ar
61503 pts/1    S      0:00                      \_ /bin/sh -c ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/ppapi/cpp/objects
61505 pts/1    R      0:00                      |   \_ ../../third_party/llvm-build/Release+Asserts/bin/clang++ -MMD -MF obj/ppapi/cpp/objects/var_ar
61504 pts/1    S      0:00                      \_ /bin/sh -c python ../../mojo/public/tools/bindings/mojom_bindings_generator.py --use_bundled_pylib

待ち続ければ、Build が終わります。終了時には以下のような表示になっています。

minami@chromium-experiment:~/chromium/src$ autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
[52205/52205] LINK ./chrome

Build が終わると、Build directory である out/Default の中に様々な成果物が作られています。

minami@chromium-experiment:~/chromium/src$ ls -1 out/Default/
absl_hardening_tests.runtime_deps
accessibility_perftests.runtime_deps
accessibility_unittests.runtime_deps
.
.
.
zucchini_integration_test.runtime_deps
zucchini_unittests.runtime_deps
zxcvbn_unittests.runtime_deps

その中に chrome という名前の binary があり、それが「chrome browser」になっています。

minami@chromium-experiment:~/chromium/src$ ls -la out/Default/chrome
-rwxrwxr-x 1 minami minami 1058842464 Dec 12 18:22 out/Default/chrome

以下のように version を確認する事もできます。

minami@chromium-experiment:~/chromium/src$ ./out/Default/chrome --version
Chromium 89.0.4354.0

無事、Chromium を Build することが出来ました 🎉

ステップ4. Build した chrome binary を実行してみる

せっかくなので、Build した chrome binary を実行してみましょう。

まず、何も考えずに $ ./out/Default/chrome で実行してみます。すると、以下のようにエラーになります。Unable to open X display. と出ているので、display 周りの設定が必要なようです。

minami@chromium-experiment:~/chromium/src$ ./out/Default/chrome
[203604:203604:1212/184031.352086:INFO:content_main_runner_impl.cc(1027)] Chrome is running in full browser mode.
[203604:203604:1212/184031.944147:ERROR:browser_main_loop.cc(1429)] Unable to open X display.
[203604:203604:1212/184031.944774:FATAL:tracing_controller_impl.cc(390)] Check failed: g_tracing_controller.
.
.
.
Calling _exit(1). Core file will not be generated.

minami@chromium-experiment:~/chromium/src$ ./out/Default/chrome
[203604:203604:1212/184031.352086:INFO:content_main_runner_impl.cc(1027)] Chrome is running in full browser mode.
[203604:203604:1212/184031.944147:ERROR:browser_main_loop.cc(1429)] Unable to open X display.
[203604:203604:1212/184031.944774:FATAL:tracing_controller_impl.cc(390)] Check failed: g_tracing_controller.
#0 0x7fb4a0717aff base::debug::CollectStackTrace()
#1 0x7fb4a04a12fa base::debug::StackTrace::StackTrace()
#2 0x7fb4a04a12b5 base::debug::StackTrace::StackTrace()
#3 0x7fb4a04ed039 logging::LogMessage::~LogMessage()
#4 0x7fb4a04ed779 logging::LogMessage::~LogMessage()
#5 0x7fb4a0460b4b logging::CheckError::~CheckError()
#6 0x7fb498aa543f content::TracingControllerImpl::GetInstance()
#7 0x7fb49780d847 content::BrowserMainRunnerImpl::Shutdown()
#8 0x7fb49780cd14 content::BrowserMainRunnerImpl::~BrowserMainRunnerImpl()
#9 0x7fb49780cd69 content::BrowserMainRunnerImpl::~BrowserMainRunnerImpl()
#10 0x7fb4977fb75c std::__Cr::default_delete<>::operator()()
#11 0x7fb4977fb6ea std::__Cr::unique_ptr<>::reset()
#12 0x7fb4977fb649 std::__Cr::unique_ptr<>::~unique_ptr()
#13 0x7fb4977fb280 content::BrowserMain()
#14 0x7fb4999c16d6 content::RunBrowserProcessMain()
#15 0x7fb4999c2cf9 content::ContentMainRunnerImpl::RunBrowser()
#16 0x7fb4999c25e7 content::ContentMainRunnerImpl::Run()
#17 0x7fb4999bf915 content::RunContentProcess()
#18 0x7fb4999c02ad content::ContentMain()
#19 0x55dd5fee70cb ChromeMain
#20 0x55dd5fee6f82 main
#21 0x7fb46988f0b3 __libc_start_main
#22 0x55dd5fee6e7a _start

Received signal 6
#0 0x7fb4a0717aff base::debug::CollectStackTrace()
#1 0x7fb4a04a12fa base::debug::StackTrace::StackTrace()
#2 0x7fb4a04a12b5 base::debug::StackTrace::StackTrace()
#3 0x7fb4a07175cb base::debug::(anonymous namespace)::StackDumpSignalHandler()
#4 0x7fb46a1f23c0 (/usr/lib/x86_64-linux-gnu/libpthread-2.31.so+0x153bf)
#5 0x7fb4698ae18b gsignal
#6 0x7fb46988d859 abort
#7 0x7fb4a0716c06 base::debug::(anonymous namespace)::DebugBreak()
#8 0x7fb4a0716be5 base::debug::BreakDebugger()
#9 0x7fb4a04ed639 logging::LogMessage::~LogMessage()
#10 0x7fb4a04ed779 logging::LogMessage::~LogMessage()
#11 0x7fb4a0460b4b logging::CheckError::~CheckError()
#12 0x7fb498aa543f content::TracingControllerImpl::GetInstance()
#13 0x7fb49780d847 content::BrowserMainRunnerImpl::Shutdown()
#14 0x7fb49780cd14 content::BrowserMainRunnerImpl::~BrowserMainRunnerImpl()
#15 0x7fb49780cd69 content::BrowserMainRunnerImpl::~BrowserMainRunnerImpl()
#16 0x7fb4977fb75c std::__Cr::default_delete<>::operator()()
#17 0x7fb4977fb6ea std::__Cr::unique_ptr<>::reset()
#18 0x7fb4977fb649 std::__Cr::unique_ptr<>::~unique_ptr()
#19 0x7fb4977fb280 content::BrowserMain()
#20 0x7fb4999c16d6 content::RunBrowserProcessMain()
#21 0x7fb4999c2cf9 content::ContentMainRunnerImpl::RunBrowser()
#22 0x7fb4999c25e7 content::ContentMainRunnerImpl::Run()
#23 0x7fb4999bf915 content::RunContentProcess()
#24 0x7fb4999c02ad content::ContentMain()
#25 0x55dd5fee70cb ChromeMain
#26 0x55dd5fee6f82 main
#27 0x7fb46988f0b3 __libc_start_main
#28 0x55dd5fee6e7a _start
r8: 0000000000000000 r9: 00007ffd3f1f1e30 r10: 0000000000000008 r11: 0000000000000246
r12: 000055dd5fee6e50 r13: 00007ffd3f1f4180 r14: 0000000000000000 r15: 0000000000000000
di: 0000000000000002 si: 00007ffd3f1f1e30 bp: 00007ffd3f1f2080 bx: 00007fb45b9f0340
dx: 0000000000000000 ax: 0000000000000000 cx: 00007fb4698ae18b sp: 00007ffd3f1f1e30
ip: 00007fb4698ae18b efl: 0000000000000246 cgf: 002b000000000033 erf: 0000000000000000
trp: 0000000000000000 msk: 0000000000000000 cr2: 0000000000000000
[end of stack trace]
Calling _exit(1). Core file will not be generated.

そこで、Getting Started with Headless Chrome を参考に、headless mode で Chromium を起動してみることにします。以下のように --headless オプションを渡して chrome binary を実行することで、ちゃんと起動することが確認できました 🎉

minami@chromium-experiment:~/chromium/src$ ./chromium/src/out/Default/chrome --headless --disable-gpu --remote-debugging-port=9222 https://www.chromestatus.com
[0124/153340.940562:WARNING:headless_content_main_delegate.cc(530)] Cannot create Pref Service with no user data dir.
[0124/153340.942433:INFO:content_main_runner_impl.cc(1027)] Chrome is running in full browser mode.

DevTools listening on ws://127.0.0.1:9222/devtools/browser/bab01154-70d7-4b82-8bcb-b8beec05a94b
[0124/153345.275372:WARNING:http_cache_transaction.cc(1192)] Unable to open or create cache entry
[0124/153345.293075:WARNING:http_cache_transaction.cc(1192)] Unable to open or create cache entry

もう少し遊んでみましょう。Chromium は、--dump-dom オプションを渡すことで、指定した URL のページを取得して document.body.innerHTML の内容を標準出力に print してくれます。試しに実行してみると、以下のようにちゃんと https://example.com のページからコンテンツを取得した上で、その内容を print してくれます。自分で Build した Chromium が、ちゃんとページ取得の動作をすることが確認できました 🎉

minami@chromium-experiment:~/chromium/src$ ./out/Default/chrome --headless --disable-gpu --dump-dom https://example.com
[0124/155618.269750:WARNING:headless_content_main_delegate.cc(530)] Cannot create Pref Service with no user data dir.
[0124/155618.271645:INFO:content_main_runner_impl.cc(1027)] Chrome is running in full browser mode.
<!DOCTYPE html>
<html><head>
    <title>Example Domain</title>

    <meta charset="utf-8">
    <meta http-equiv="Content-type" content="text/html; charset=utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <style type="text/css">
    body {
        background-color: #f0f0f2;
        margin: 0;
        padding: 0;
        font-family: -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", "Open Sans", "Helvetica Neue", Helvetica, Arial, sans-serif;

    }
    div {
        width: 600px;
        margin: 5em auto;
        padding: 2em;
        background-color: #fdfdff;
        border-radius: 0.5em;
        box-shadow: 2px 3px 7px 2px rgba(0,0,0,0.02);
    }
    a:link, a:visited {
        color: #38488f;
        text-decoration: none;
    }
    @media (max-width: 700px) {
        div {
            margin: 0 auto;
            width: auto;
        }
    }
    </style>
</head>

<body>
<div>
    <h1>Example Domain</h1>
    <p>This domain is for use in illustrative examples in documents. You may use this
    domain in literature without prior coordination or asking for permission.</p>
    <p><a href="https://www.iana.org/domains/example">More information...</a></p>
</div>


</body></html>
[0124/155619.646701:ERROR:process_posix.cc(335)] Unable to terminate process 2345: No such process (3)
[0124/155619.646976:WARNING:internal_linux.cc(66)] Failed to read /proc/2345/stat
[0124/155619.650882:WARNING:discardable_shared_memory_manager.cc(432)] Some MojoDiscardableSharedMemoryManagerImpls are still alive. They will be leaked.

さらに、--screenshot オプションを渡すことで、スクリーンショットを撮ることもできます。試しに https://example.com に対して実行してみると、以下のように screenshot.png という画像ファイルが生成されます。

minami@chromium-experiment:~/chromium/src$ ./out/Default/chrome --headless --disable-gpu --screenshot https://example.com
[0124/162636.934902:WARNING:headless_content_main_delegate.cc(530)] Cannot create Pref Service with no user data dir.
[0124/162636.936935:INFO:content_main_runner_impl.cc(1027)] Chrome is running in full browser mode.
[0124/162638.402065:INFO:headless_shell.cc(616)] Written to file screenshot.png.
[0124/162638.466196:ERROR:process_posix.cc(335)] Unable to terminate process 3074: No such process (3)
[0124/162638.466571:WARNING:internal_linux.cc(66)] Failed to read /proc/3074/stat
[0124/162638.469149:WARNING:discardable_shared_memory_manager.cc(432)] Some MojoDiscardableSharedMemoryManagerImpls are still alive. They will be leaked.
minami@chromium-dev-20210123:~/chromium/src$ gsutil cp ./screenshot.png gs://chromium-dev-south37/screenshot.png
Copying file://./screenshot.png [Content-Type=image/png]...
/ \[1 files\][ 26.4 KiB/ 26.4 KiB]
Operation completed over 1 objects/26.4 KiB.

minami@chromium-dev-20210123:~/chromium/src$ ls -la screenshot.png
-rw------- 1 minami minami 27069 Jan 24 16:26 screenshot.png

screenshot.png をダウンロードして内容を見てみると、以下のような内容になっています。これは https://example.com を手元の Chrome で開いた時と同一の内容になっています。自分で Build した Chromium が、ちゃんとページを Render し、さらにスクリーンショットまで撮れることが確認できました 🎉

ステップ5. 高速な Build を体感する

Chromium をゼロから Build する」というステップ3では、かなりの時間がかかりました。一方、一度 Build した状態からでは、高速な Build が行えるようになります。これは、Ninja が最終成果物からの依存関係を check していて、「依存先に変更があった場合のみ、再 Build を行う」ようになっているためです(注: Ninja だけでなく、Make も同様の挙動をします)。

例えば、何も書き換えない状態で $ autoninja -C out/Default chrome を実行した場合は、Build は 5 秒程度で終了します。この場合、ソースコードに変更はないので chrome binary にも変更は行われません。

minami@chromium-experiment:~/chromium/src$ time autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
ninja: no work to do.

real    0m5.676s
user    0m4.074s
sys     0m0.919s

次に、chrome/common/channel_info.cc という file を touch して timestamp を更新してみます。以下の document によれば Ninja は Make と同様に file timestamp を利用して更新を check しているらしいので、これで依存する file は Build が行われるはずです。

Ninja is closest in spirit and functionality to Make, relying on simple dependencies between file timestamps.

cf. https://ninja-build.org/manual.html

今度は、以下のように obj/chrome/common/channel_info/channel_info.o の Build が行われる様子が観測できました。さらに、時間経過とともに LINK などの step に進み、15 秒程度で chrome binary の Build が終了します。

minami@chromium-experiment:~/chromium/src$ touch chrome/common/channel_info.cc
minami@chromium-experiment:~/chromium/src$ time autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
[0/4] CXX obj/chrome/common/channel_info/channel_info.o
minami@chromium-experiment:~/chromium/src$ time autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
[2/4] SOLINK ./libvr_common.so
minami@chromium-experiment:~/chromium/src$ time autoninja -C out/Default chrome
ninja: Entering directory `out/Default'
[4/4] LINK ./chrome

real    0m15.238s
user    0m18.995s
sys     0m4.492s

何かしら file を変更した場合でも、依存した file だけが Build 対象となるため、「Chromium をゼロから Build する」というステップ3に比べると 圧倒的に高速に Build が出来る ことが分かります。

また、chrome binary の Build の様子を見ていると、LINK ステップにかかる時間が長いことに気づきます。 lld の作者の rui さんが「Link にかかる時間を減らすことが開発生産性に繋がる」という話をしているのを聞いた記憶がありますが、それが実感できる機会となりました。

まとめ

ChromiumLinux 上でゼロから Build して動かしてみました。トータルでかかった時間は6-7時間程度とかなり長いので気軽に試すのは難しいですが、「巨大なソフトウェアであっても Build して動かすことが出来る」ということが確かめられたと思います。

また、一度動くところまで持っていけば、「少し変えて動かして試す」ということが簡単に出来るようになるので、ソフトウェアに対しての理解を深めやすくなります。これをきっかけに Chromium に対して理解を深めたいと思います。