Swift Package Manager の実装を追ってみる (`swift package resolve` 編)
↓の続き
次は swift package resolve
を追ってみる。
swift package resolve
は resolve というサブコマンドなので、 SwiftPackageTool の Configuration で対応するサブコマンドがあ確認する。
Resolve
型が定義されていそう。
ファイルは Sources/Commands/PackageTools/Resolve.swift
。
Resolve 型を見ていく。
こちらも Default 型のコマンド同様に SwiftCommand に準拠している。
いくつか引数やオプションを取ることができるようだが、今回はそれらは置いておいて、引数もオプションもなしで呼び出した時の挙動を見てみる。
実際の処理は run(_ swiftTool: SwiftTool)
を見ていけば良い。
早速処理の分岐があるが、 if の処理は引数 <package-name>
を指定しているなので、今回は else の方を見る。
SwiftTool 型に生えている resolve メソッドを呼び出している様子。
SwiftTool.resolve() の定義を確認する。
早速 1行目の let workspace = try getActiveWorkspace()
から分からないので追ってみる。
getActiveWorkspace()
の定義はここ。
_workspace
に値があればこれを返すようになっています。
_workspace
は同じく getActiveWorkspace()
で workspace を取得できた場合にその値を保持しておくもののようです。
今回の swift package resolve
では _workspace
は保持されていない状態で処理を通るので、この早期 return よりも先の処理を見ていく必要がありそう。
getActiveWorkspace()
の続き。
self.workspaceDelegateProvider
がどこからくるか?
追っていくと、どうやら↓にて SwiftTool
の extension で定義されている様子。
ちなみに WorkspaceDelegateProvider
型はいくつかの引数を取って WorkspaceDelegate
型を返す closure の typealias になっている。
ToolWorkspaceDelegate
は WorkspaceDelegate
に準拠しているので、 WorkspaceDelegateProvider
では ToolWorkspaceDelegate
を返している。
次はこの ToolWorkspaceDelegate
を見ていく。
定義はこのファイル。
ざっとメソッドを眺めてみるとパッケージの取得やチェックアウト、binary のダウンロード周りに関するデリゲートになっている。
さらに initializer で渡している outputHandler
, progressHandler
については、それぞれ標準出力のためのハンドラと取得中の進捗状況等を返すハンドラであることが分かる。
ObservabilityScope
はよく分からないのでコードを探してみる。
と、見てみたが、あまり良くはわからなかった。診断レポートに関するもの?くらいに理解しかできなかった。
ここに戻って、 SwiftTool.observabilityHandler
の正体を見ていく。
型は SwiftToolObservabilityHandler
というものらしい。
定義はこのファイル。
print
と progress
が使われていたので、それらを順に見ていく。
print
はここ。
outputHandler をラップしているだけのようなので、 outputHandler.print
を見てみる。
定義はすぐ下にある。
write
を呼び出しているのでそっちも見る。
必要に応じて改行を加えて writer.write
を呼んでいる。
writer.write
も見てみる。
writer
はすぐ下にある InteractorWriter
型のよう。
InteractorWriter.write
はこちら。
term の有無で処理が変わっている。
term は TerminalController
のオプショナル型。
TerminalController
は swift-tools-support-core モジュールで定義されているもので、ターミナルへの出力を扱うクラスになっている様子。
これがオプショナルなのは、 TerminalController
の initializer が failable initializer になっているから。
Swift Package Manager ではこの Initializer の OutputByteStream として、TSCBasic.stderrStream
を渡している。
TSCBasic.stderrStream
はこちら。
これで TerminalController
は nil になっていないようなので、 最終的には term.write が呼ばれてターミナルへの標準出力がされる様子。
次に progress
も見ていく。
これも同じく outputHandler をラップしているだけ。
OutputHander.progress
はこちら
progressAnimation.update
を呼んでいるので、 progressAnimation
を見てみる。
progressAnimation
自体は ProgressAnimationProtocol
という swift-tools-support-core の TSCUtility モジュールで定義されている型になっている。
MultiLineNinjaProgressAnimation
も NinjaProgressAnimation
も同じく swift-tools-support-core のもの。
実際の挙動はこの辺が参考になりそう。
verbose する場合は MultiLineNinjaProgressAnimation
を使って進捗を全て表示しているということらしい。それ以外はよしなに適切な表示をしてくれる様子。
これで SwiftTool.getActiveWorkspace()
に戻って来れるので、続きを見ていく。
次は Workspace のインスタンスを生成するところなので、Workspace 型についてみていく。
Workspace の initializer に渡しているものを一つずつみていく。
fileSystem
self.fileSystem
を渡しており、これは SwiftTool.init
で localFileSystem
を入れていることが分かる
localFileSystem
は swift-tools-support-core で定義されているもの。
次に location
Workspace における諸々のディレクトリ/ファイルの場所を表現している型のように見える。
scratchDirectory
これは initializer でこんな感じで設定されている。
環境変数から取得するか、オプションから取得するか、デフォルトの .build
を使用するかになる様子。
オプションの内容や .build
というディレクトリから見ても、ビルド成果物を格納するディレクトリを示すものであることがわかる。
editsDirectory
これは swift package edit <package-name>
としたときにローカルにコピーされるパッケージの場所を表すものらしい。
getEditsDirectory
を見てみる。
--multiroot-data-file
オプションをつけていればそのディレクトリの下の Packages
ディレクトリ、なければパッケージのルート下の Packages
ディレクトリが設定されるようになっている。
せっかくなので getPackageRoot()
も見ておく。
packageRoot
は initializer で findPackageRoot(fileSystem:)
を通して設定される。
Manifest.filename
は Package.swift
のこと。
Package.swift
が見つかるまで current directory からファイルシステムのルートディレクトリまで親のディレクトリを遡って探している様子。
つまり、 Package.swift
があるディレクトリの子孫ディレクトリにいる場合は swift package
コマンドは機能しそう。