オープンソースSwiftにコントリビュートしよう

Swiftの開発に貢献したいですよね?でもどこから、どうやって手をつけたらいいのかわかりませんか?それでも貢献したい気持ちを抑えることができませんね!try! Swiftで話されたこの講演では、複数のパートから成るSwiftの構造に分け入り、さまざまなSwift関係のプロジェクトをどのように見ていけばいいのか、初めにどのようなスキルが必要なのか、そして最初のパッチが受け入れられるための最善の方法を学ぶことができます。


はじめに (0:00)

この記事ではいくつかの異なる内容についてお話します。Swiftを構成するそれぞれ異なるパートやプロジェクトについて、少しだけLLVMについて、必要とするスキル、そして「どのように/なぜ」Swiftにコントリビュートすべきなのか、ということをお話します。この記事を読み終わったときに、Swiftにコントリビュートすることについて、前よりも身近に感じてもらえれば嬉しいです。

どこから手をつければいいですか? (0:53)

アップルがSwiftをオープンソースとして公開したとき、私たちはとても驚かされました。なぜなら、非常に多くのプロジェクトが合わせて公開されたからです。コンパイラだけではなく、コアライブラリ、標準ライブラリ、そしてSwift evolutionと呼ばれる将来のSwiftの仕様を決定するプロセスまでもオープンになりました。すばらしいことは、誰もがこの恩恵を受けられるということです。あなたがコンパイラのエキスパートでなくても、Swiftにコントリビュートすることができます。

しかし、Swiftのすべてに貢献したいと思っても、どこから手をつければいいか理解することは難しいです。たくさんの異なるパーツはどのように連携しているのでしょうか?コンパイラがどのように動くかの概念を抽象的に捉えることは、あなたがコントリビュートしたい内容がどの領域やプロジェクトであるのかを知るのに役立ちます。

コンパイルする時に何が起こっているでしょうか? (2:18)

SwiftはLLVMの技術を用いてChris Lattner氏によって開発されました。ではどのようにしてネイティブコードや実行バイナリが作られるのでしょうか?

LLVM Compiler Architecture

  1. 最初はフロントエンド、またはある特定の言語から始まります。

  2. フロントエンドはソースコードを構文解析し、LLVM IR(Intermediate Representation)と呼ばれる中間コードを生成します。IRは特定のアーキテクチャに依存しない、高度に抽象化されたアセンブリ言語です。LLVMではコードをIRで表現することになっています。注目すべきは、明確に意味論が定義された第一級言語だということです。

  3. その後、LLVM IRはオプティマイザに渡され、オプティマイザはさまざまな解析と最適化を行います。

  4. 最適化されたIRはバックエンドに送られます。バックエンドはアーキテクチャまたは命令セットに基づいたネイティブコードを生成します。

LLVMは非常によくモジュール化されています。プログラミング言語と構文解析の部分と、バイナリの生成部分とは、LLVM IRという中間レイヤーによって明確に分離されています。LLVMが非常に人気のあるプロジェクトになった理由の一つです。

記事の更新情報を受け取る

Clangパイプライン (4:28)

Swiftの話をする前にClangについて話します。ClangはCとC++、そしてObjective‑CのLLVMフロントエンドです。ClangのパイプラインはCまたはObjective‑Cのソースコードが起点となります。ソースコードを構文解析し、抽象構文木(Abstract Syntax Tree、AST)に変換します。ASTはソースコードを木構造で表現したものです。その後ASTは最適化のために意味解析にかけられます。次のコード生成のフェーズでは最適化されたASTからIRを生成します。IRはLLVMおよびパイプライン処理に渡され、実行バイナリが生成されます。

ここではClangのアーキテクチャについてあまり詳しくは触れませんが、いくつかレガシーな部分があることには触れておきます。実は先ほど私が説明したようにクリーンではありません。構文解析と意味解析は少し関係し合っていて���コードの重複もあります。Clangは偉大なツールですが、同じくらい美しいわけではありません。このことから、Swiftコンパイラが数多くの手法を用いて成り立っていることがわかります。

Swiftパイプライン (6:34)

SwiftパイプラインはClangから非常に多くのことを参考にして、また失敗した部分を避けるようにして作られました。

まずSwiftのソースコードから始まります。ソースコードはClangの時と同様に、構文解析されてASTに変換されます。その後も同様にASTは意味解析に渡されます。そして意味解析のフェーズではASTをwell-formedかつ完全に型検査された形式のASTに変換します。

このフェーズでは、意味解析によって発見されたソースコード上の問題を警告した���エラーとして表示します。そして、Swift中間言語(Swift Intermediate Language、SIL)の生成フェーズに移ります(LLVM IRの場合と同様です。)。SILは基本的にはIRのSwiftにおけるラッパーです。

SILGenはraw SILを生成します。raw SILは解析と最適化がかけられます。ここがSwiftにおけるもっとも「Swiftらしい」ところです。中間言語のレイヤーがあるからこそ、非常に多くの最適化をこのフェーズで実行することができます。最適化にはSwift特有の最適化や、ARCの最適化、動的なメソッド呼び出しの最適化、その他さまざまな最適化が行われます。そしてcanonical SILが生成されます。canonical SILはIR生成フェーズに渡され、LLVM IRが生成されます。LLVM IRはLLVMに渡され、先にClangで述べたようなバイナリを生成する処理を完了します。

もう一つの役割としてモジュールマップの生成があります。モジュールマップはCまたはObjective‑Cと連携するために必要です。おそらくモジュールマップを見たことがある(少なくとも聞いたことがある)と思います。ClangモジュールをClangインポータが読み込み、ASTが意味解析に渡ります。このことにより、CやObjective‑Cとの連携が可能になっています。

私は上述の説明をよく理解しています。コンパイラはコンピュータサイエンスの中で、もっとも難しい分野の一つです。私はコンパイラの分野についてエキスパートではありませんが、実際には先に述べたすべての分野のエキスパートである、なんて人はいません。コアチームの人間でさえもパイプラインのアーキテクチャのうち、特定のスペシャリストであるというだけなのです。

簡単な例 (10:05)

// hello.swift
print("hello swift!")
>swiftc hello.swift
>./hello

上記は基本的なHello Worldのプログラムです。このプログラムをコマンドラインからコンパイルして実行することができます。また下記のコマンドラインを用いて、ASTを生成し確認することができます。

>swiftc -dump-ast hello.swift

表示されるASTはとても大げさなものです。自分のコードで試そうとしましたか?その場合はemit SILを指定するといいでしょう。シンボル名がデマングルされます。(Swiftはシンボル名をマングリングします。)

>swiftc -emit-silgen hello.swift
 | xcrun swift-demangle

SILを読むことはアセンブリ言語を読むことに似たものがあります。とても冗長ですがそれほど難しいわけではありません。下記のコマンドを使うとLLVM IRやアセンブリをで表示することもできます。

>swiftc -emit-ir hello.swift
>swiftc -emit-assembly hello.swift

もう一度言いますが、オープンソースにコントリビュートするのに、すべての分野のエキスパートである必要はありません。しかし抽象的な概念でパイプライン全体を捉えることは役にたつと考えています。

プロジェクトとリポジトリ構成 (12:20)

先ほど説明したパイプラインは、基本的にはそれぞれ別々のプロジェクトになっています。メインSwiftリポジトリにサブディレクトリがあり、それぞれがパイブラインの1フェーズになっています。

Swift Subdirectory Map

パイプラインにコントリビュートしたり、バグレポートを書く場合に、それがどの部分に属しているかを知るには、上記の図を参考にすることができます。

LLVM、ClangおよびLLDBがパイプラインで役目を負っています。パイプラインの起点には標準ライブラリ、コアライブラリとパッケージマネージャがあります。SwiftとCライブラリはパイプラインを通ってSwiftまたはClangインポータによって解析され、コードで使えるようになり、コンパイル時に使われます。

それぞれのプロジェクトについて難易度をつけて一覧にしました。難易度はやや主観的ではありますが、相対的に評価しました。

Swiftコア (14:46)

コンパイラ 難易度: 難 言語: C++ 開発の活発さ: 高

もしC++を書いたことがない、またはあまり経験がないなら、ここに取り掛かるのはかなりチャレンジングでしょう。ここには未定義の動作のような多くのSwiftが抱える問題があります。

標準ライブラリ 難易度: 普通 言語: Swift* 開発の活発さ: 普通

私たちが普段使っているものです。Swiftの後にアスタリスクを付けたのは、標準のSwiftとは少し違うからです。標準ライブラリからはLLVM組み込みの機能やLLVM IRの型にアクセスすることが可能です。

SourceKit 難易度: 難 言語: C++ 開発の活発さ: 低

最後はSourceKitです。ご存知のように、シンタックスハイライトのためにXcodeが利用していて、絶対にクラッシュしません🙄。C++で書かれているので、よりチャレンジングです。

Swiftインフラストラクチャ (16:03)

LLVM, Clang, and LLDB 難易度: 超難 言語: C++ 開発の活発さ: 低

この3つのプロジェクトはLLVMプロジェクトの一部ですが、Swift特有の変更のためにコピーされています。このプロジェクト群にコントリビュートすることはコンパイラとClang/LLVMの高度な知識が必要になるので、非常に難しいです。私はSwift 3でほぼ完成すると予想しています。

ひとつ注意しなければならないことは、このプロジェクト群はみなLLVMプロジェクトのポリシーとコーディング規約、ライセンスによって管理されています。このプロジェクト群は真にSwiftブランドのプロジェクトではありません。多くの場合でSwift特有でない修正が入ります。そのため、実際にはより上位のプロジェクトにコントリビュートすることが必要なるでしょう。このプロジェクトは定期的にLLVMプロジェクトに同期されています。

パッケージマネージャ (17:34)

swift package manager 難易度: 普通 言語: Swift 開発の活発さ: 高

パッケージマネージャはすべてSwiftで書かれています。すばらしいですね。その点では障壁は低いと言えます。ですが、多くのコードはCのライブラリをラップしたものです。数多くのCコードとの連携があります。おそらく、ほとんどの人にとってそれはあまり見慣れないものだと思います。

swift-llbuild 難易度: 難 言語: C++ 開発の活発さ: 低

llbuildは低レイヤーのビルドシステムです。ビルドシステムを構築するためのライブラリ群です(メタビルドシステム?)。一般的に言って、このプロジェクトにコントリビュートするのは、よりチャレンジングになります。

コアライブラリ (18:17)

Foundation 難易度: 易 言語: Swift、C 開発の活発さ: 高

XCTest 難易度: 易 言語: Swift 開発の活発さ: 高

libdispatch (GCD) 難易度: 難 言語: C 開発の活発さ: 低

おそらく、コアライブラリは初めに手を付けるものとしてはもっとも簡単だと思います。私たちは長いことObjective‑Cで同じAPIを使ってきたので、とても見慣れています。ほとんどはSwiftで書かれていますが、少しCのコードと連携する部分があります。Objective‑CのFoundationフレームワークがCで書かれたCore foundationの単なるラッパーであったように、Swiftにも同様の部分があります。

Swift evolution (18:49)

最後に、Swift evolutionはSwiftの言語仕様に変更を加える際の正式なプロセスです。コミュニティの誰もがプロポーザルを提出できます。このリポジトリは開発の進捗とリリースのスケジュールも含んでいます。つまり、ここを見ることは各リリースでどんな変更が予定されているのか、予定から外れたものは何かを知るために最適です。

もしプロポーザルを提出しようとしているなら、ぜひとも明白な利点をコミュニティに対して示すべきです。まず深く考え、最初にメーリングリスト上でよく議論すべきです。少しですがこんなことがありました。「私はこういう機能を追加してほしい」と誰かが言いましたが、その機能を追加すべき理由が添えられてないというものです。Swiftに変更したいことがあるなら、それについて良い議論を提示しましょう。そしてコミュニティに協力を求めましょう。

コントリビュートするには (20:14)

コントリビュートに関する議論はメーリングリスト上で行われています。不具合についてはJIRAというバグトラッキングシステムに報告します。いずれも、GitHub上では行われていません。これは次のような理由によるものです。Pull Requestを起点にして議論をすることは非常にやりやすいです。しかし、コアチームは議論の多くをJIRAとメーリングリストに残したいと考えています。

コントリビュートする際のガイドラインをよく読み、従うように心がけましょう。ガイドラインに従うことは変更が受け入れられるためにもっとも良い方法です。

これまでに述べたように、コントリビュートする方法はいくらでもあります。またそれはコードだけに限りません。新しい機能を追加したり不具合を修正する以外にも、ドキュメントを書いたり、翻訳したり、タイポを直したり、テストを追加したり、などです。どれをとってもコミュニティに対して偉大な貢献となります。

タイポの修正は些細なことだと思うかもしれませんが、重要なことです。英語ネイティブでない人びとにとって、キレイな文章は理解しやすいですが、そこにタイポがあると理解することが難しくなります。タイポのないドキュメントは翻訳もしやすいです。タイポを見つかたらすぐにPull Requestを作りましょう。それは非常に大きな貢献になります。

もっというと、意見を出すことは誰にでもできます。20年以上やっているプログラマか、始めてまだ20日しか経っていないプログラマなのかは関係ありません。ただあなたがどのような視点を持っているかだけが問題になります。Chris Lattner氏はもっとわかりやすく述べています。SwiftはObjective-Cの代替の言語としてすばらしいですが、それだけではありません。初心者プログラマにとってもすばらしい言語だということです。

うまくいく秘訣 (23:14)

コントリビュートがうまくいくためにの秘訣をお教えします。コアチームからの返答はほぼTwitterかGitHub、メーリングリストで行われるので、

  • 人にやさしくしましょう

    メーリングリストで異なる意見を見るかもしれません。狭量にならず、他の人の意見は尊重しましょう。あなたが文章の意図を読み違えていたり、勘違いしている可能性もありますし、それは相手にも同様に言えることです。

  • 協力を求めましょう

    協力を求めることができる場所はすごくたくさんあります。みんなTwitterをやっていますし、聞けば協力してくれます。メーリングリストも良い場所です。

  • ベストプラクティスに従いましょう

    特にプルリクエストを送ろうとするときにはいつでも、

    テストを含める、コードが簡潔で分かりやすいかを確認し、その他の知っていることを全部やりましょう。

    そのように、ベストプラクティスとガイドラインに従っていれば、他の人があなたのコードをレビューしやすくなります。

  • リジェクトされましたか?再度コントリビュートしましょう。

    ある提案やプルリクエストが受け入れられなかったとしても、あなたの能力が否定されたわけではありません。ただタイミングが悪かったか、Swiftにはそれが適さなかったというだけです。コアチームは大きなビジョンを持っているので、プロジェクトの方向を指し示すことができますし、時には何をやらないかを決断します。提案やプルリクエストが受け入れられなかったということは別に世界の終わりではありません。誰にでも起こることです。トップコントリビュータと言われる方々であってもリジェクトされることはあります。

  • CODE_OWNERS.txt

    個々のプロジェクトには「CODE_OWNERS.txt」というものがあります。そこのコードベースに責任を持つコアメンバーが誰か、ということを示すものです。プルリクエストには彼らへのメンションをつけるようにしましょう。そうすることでかなりの手間を減らすことができますし、速やかにレビューしてもらえ、受け入れられやすくなります。

  • なんでも知ってる 人はいません!

    質問をしても大丈夫ですし、すべてが完璧でなくても大丈夫です。 コアチームがあなたの変更を受け入れたいと思っている とばかりは言えませんが、コアチームは全員に関わってほしいと考えています。プルリクエストをできるだけ受け入れたいと考えているので、プルリクエストや提案にフィードバックや変更を求めることもあります。Swiftにとって必要なものだと判断されれば変更は受け入れられるでしょう。

この例に従って始めるのは良いやり方です。すでにコミュニティには、すばらしいコントリビュートをしているアップルの従業員ではない人が、何人かいます。彼らはそれぞれの専門分野を見つけて貢献しています。同じように、Swiftや関連プロジェクトの中で情熱を傾けられる、集中できる分野を見つけてください。そうすれば、コントリビュータとして大きな成功を収めることができます。そして、Swiftの成功を助けることにもなるのです。

始まりはいつでも小さな変更です。そしてその小さな変更が人びとを巻き込んでいきます。コアチームにとってそれは非常に重要です。なぜならSwiftの成功はコミュニティに人びとが参加するかどうかにかかっているのですから。

なぜコントリビュートするのでしょうか? (28:20)

アップルがSwiftに多大な投資をしていることは非常に明白です。私はSwiftがすぐになくなることはありえないと考えています。Swiftは次世代のiOSおよびMac OS、さらにはそれ以外のOSの開発プラットフォームになるでしょう。Swiftはコミュニティが望む何にだってなることができるでしょう。

すべてのプロセスは完全にオープンになっています。コアチームは私たちのSwiftをもっと良くしたいというフィードバックをこころよく受け入れてくれています。個々のデベロッパーにとって、Swiftが成功することは私たちが成功することでもあります。Swiftに移植性があるということは、私たちがどこにでも行けるということでもあります。Swiftが利用できるプラットフォームが増えることは、私たちのスキルを活かせる場所が増えることなのです。

私たちはまだまだSwiftを書き始めて間もないですが、自分たちにこのように問いかけます。

「次の10年でSwiftがどうなってほしいでしょうか?」

これまでの数年間は間違いなくSwiftが何であるかを確かめる期間でした。そして、今や私たちはこの言語の将来を左右することができます。単なるコンパイラ、コード、もしくはライブラリといったものにとどまりません。私たちが どのように コードを書くのか、またSwiftのベストプラクティスは何か、ということです。すべてのコントリビューションにはこういうことが言えます。良いSwiftを書くために一番の方法は何ですか?

Swiftという言葉はプログラミング言語だけを指すのではありません。コミュニティです。それもすばらしい人びと、すばらしいものを作り出す人びとが集まるコミュニティです。どうかコントリビュートすることや人に聞くことを怖がらないでください。あなたができる最高のことは、とにかくやってみること(try!)です。

Jesse Squires

Jesse is an iOS developer at Instagram who writes about Swift and Objective‑C on his blog at jessesquires.com. He’s the curator of the Swift Weekly Brief newsletter and co-host of the Swift Unwrapped podcast. He is fueled primarily by black coffee and black metal.