Clang モジュールの探検 | try! Swift Tokyo 2018 Day1-3

Swift や Objective-Cフレームワークは Clang モジュールをベースとした同じモジュールシステムを共有しています。ライブラリのインターフェースとやりとりすることでヘッダファイルを置き換えますが、それぞれにエッジケースが存在します。Clang モジュールの #import 文の置き換え方、モジュールを使うことによる影響、Clagnモジュールの設計がSwiftのライブラリにどう影響してきたかを調べてみます。

Clang モジュールの探検

おはようございます

Clang モジュールをみていきます

f:id:niwatako:20180301105043j:plain

f:id:niwatako:20180301105054j:plain

Squareのモバイルエクスペリエンスチームでデベロッパーが楽しく精算的に仕事をできるようにするのが仕事です

モジュールの前はヘッダがありました

f:id:niwatako:20180301105128j:plain

f:id:niwatako:20180301105143j:plain

ヘッダーをインポートすると利用できます

f:id:niwatako:20180301105207j:plain

Cは単一ファイルの言語です Cプロセッサがヘッダサーチパスにあるものを見つけて#includeからコンテンツをペーストする

マクロが関与するとヘッダのincludeの順番で変化が生じる

f:id:niwatako:20180301105259j:plain

相互に矛盾する関数の宣言が起きたらどうなるでしょう

include ガードという仕組みで問題を回避しています

Objective-Cでは状況が良くなります。

importがあるおかげで

2回めにでてきた#imortはincludeしないのでぶつからない

コンパイラはインポートしたものの意味論的理解を深めます ヘッダからテキストでコピーするのではなく、モジュールで意味的なコンテンツにアクセスすることができます。

プリコンパイルヘッダに変わる働きをします

このために頻繁にクリーンビルドする必要がありました

f:id:niwatako:20180301105433j:plain

モジュールのヘッダ、構成、依存性などを教えてくれるのでモジュールにモジュールマップがあれば透過的なモジュールのインポートマップに変換される

f:id:niwatako:20180301105507j:plain

.frameworkをみていきましょう

標準的なモジュールマップでビルドするとXcodeが生成するものです

名前の付いたモジュールを宣言しています

f:id:niwatako:20180301105531j:plain

f:id:niwatako:20180301105545j:plain

モジュールないすべてのヘッダファイルをインポートしてそれを出力します

f:id:niwatako:20180301105628j:plain

f:id:niwatako:20180301105631j:plain

最後にモジュールはUsefulFrameworkをインポートできるようになります ファイルがモジュールのどのパーツに依存しているのか明確になります

UsefulFrameworkがSwiftでもObjCでも利用できるようになりました

f:id:niwatako:20180301105714j:plain

f:id:niwatako:20180301105718j:plain

私のフラストレーションは何か

SwiftStaticLibraryをCocoaPodsライブラリで利用できるようにしたい

モジュールはFrameworkを想定していますがユーザーの作るライブラリは後付でデザインされている

課題です

こういったところが痛みでした

f:id:niwatako:20180301105820j:plain

f:id:niwatako:20180301105831j:plain

ディレクトリの中のヘッダをすべて利用したい

CocoaPodsがパブリックヘッダを直接渡してくれる わざわざカスタムUnbrellaヘッダを作らなくて良い

Explicit Submodules これはGitSubmoduleよりマシです

f:id:niwatako:20180301105918j:plain

セミプライベートファンクショナリティがあるがメインインターフェースには入らない

他のモジュールからはインポートされない CocoaPodsにAddしようとしたがうまくいかなかった。もとに戻すこともできなかった マクロがある場合

f:id:niwatako:20180301110004j:plain

textualでやればいいのかなと考えましたが ファイルにマクロが定義されて、Assertと言うかたちで定義されていればつかえる

いくつかのフィーチャーを指定してモジュールからコンパイラをサポートできるようにします ObjCやC++の幾つかのバージョンで

こうして隠す事ができます

f:id:niwatako:20180301110140j:plain

f:id:niwatako:20180301110118j:plain

互換性がないことも表現できます

カバーするヘッダはモジュールの一部として扱われます

f:id:niwatako:20180301110158j:plain

私が考える一番の文句をとっておきました

f:id:niwatako:20180301110217j:plain

回避する方法が見つかっていません

モジュールマップがストアされたディレクトリに関連するものとして扱われます。モジュールマップでもフラグでも、他のディレクトリを使うように支持できないのです。絶対パスか同じディレクトリに入れておく必要があります

これによってCocoaPodsでフラストレーションが

f:id:niwatako:20180301110312j:plain

任意のディレクトリのヘッダに対応させたい時に、できない。

Clangはフレームワークバンドル内に発見されたヘッダとプライベートのディレクトリに特別な扱いをしている気がします。

ここからみなさん聴きたいことではないでしょうか

f:id:niwatako:20180301110353j:plain

ClangモジュールはSwiftがObjCのヘッダをインポートするのをサポートします

import Foundationが機能するわけです。

他にも方法があります。ブリッジングヘッダなどの方法です。 みなさん、モジュールはヘッダを解決するだけなのかと思うと思います

Swiftのよいところはヘッダがないところだと思います Swiftとモジュールはどのような関係にあるのでしょうか

@objecと書かれたすべてのヘッダ SwiftのXcodeフレームワーク、 Swiftコンパイラに渡してヘッダの生成を矯正することができます。 コンパイラはバイナリSwiftファイルをどう扱えばいいのか分かっています モジュールが同じ名前だったら Objective-CとSwiftの中のファイルなら可能ということです。

Mixed Framework

f:id:niwatako:20180301110609j:plain

f:id:niwatako:20180301110619j:plain

モジュールマップを拡張

そして.mのコンパイル

自動で構築されます

f:id:niwatako:20180301110641j:plain

f:id:niwatako:20180301110720j:plain

f:id:niwatako:20180301110746j:plain

Xcodeのダイナミックフレームワーク構築

Static Libraryを構築するには手作業が必要

Static LibraryでCocoaPodsをサポートするのに必要

f:id:niwatako:20180301110838j:plain

Clangモジュールについて知りたければご参照下さい

f:id:niwatako:20180301111004j:plain

f:id:niwatako:20180301111035j:plain

思い通りに動かない、記述が不十分ならソースを見ます

f:id:niwatako:20180301111101j:plain

f:id:niwatako:20180301111107j:plain