TL;DR
Swift Packageをzip -r
で圧縮すると、解凍後にビルドエラーが発生することがあります。
swift package archive-source
コマンドを使うとシンボリックリンクが正しく保持されたり、.buildディレクトリも自動的に除外されるなど、配布に適した形で圧縮されるためおすすめです。
cd swift-package-directory
swift package archive-source --output output/path/package-name.zip
はじめに
Swift Packageの配布や移動のためにZIP圧縮することは珍しくありませんが、通常のzip
コマンドを使うと思わぬビルドエラーに遭遇することがあります。本記事では、realm-swiftを例にその原因と解決法を解説します。
通常のZIP圧縮を使うとどうなる?
例として、realm-swiftをZIP圧縮・解凍してビルドしてみます。
git clone https://github.com/realm/realm-swift.git && cd realm-swift
swift build
...
Build complete! (70.11s)
もちろん、クローン時点ではswift build
は成功します。
では、続けてrealm-swiftを一度ZIP圧縮・解凍してビルドしてみます。
zip -r realm-swift.zip realm-swift # 圧縮
rm -rf realm-swift # 元のディレクトリを削除
unzip realm-swift.zip && cd realm-swift # 解凍
swift build
...
realm-swift/Realm/RLMNetworkTransport.h:24:34: error: redefinition of 'RLMHTTPMethod'
24 | typedef RLM_CLOSED_ENUM(int32_t, RLMHTTPMethod) {
| ^
...
fatal error: too many errors emitted, stopping now [-ferror-limit=]
2 warnings and 20 errors generated.
[34/309] Compiling timestamp_logger.cpp
はい、ビルドに失敗しました。
原因はシンボリックリンクの消失
解凍後にgit statusでリポジトリの状態を確認すると、いつのまにか差分が出ていることがわかります。
git status
On branch master
Your branch is up to date with 'origin/master'.
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git restore <file>..." to discard changes in working directory)
typechange: Realm/ObjectServerTests/include/RLMSyncTestCase.h
typechange: Realm/ObjectServerTests/include/RLMUser+ObjectServerTests.h
typechange: Realm/Tests/Swift/RLMSupport.swift
typechange: RealmSwift/RealmSwift-Info.plist
typechange: RealmSwift/Tests/RealmSwiftTests-Info.plist
...
typechange: include/Realm/RLMNetworkTransport.h
...
no changes added to commit (use "git add" and/or "git commit -a")
typechangeとはなんでしょうか。
こちらのQiita記事によると、ファイルの種類が変更されたことを示しており、通常はシンボリックリンクが通常ファイルに変わったり、その逆の場合に発生します。
今回エラーの原因となったファイル
https://github.com/realm/realm-swift/blob/master/include/Realm/RLMNetworkTransport.h
の中身は、
../../Realm/RLMNetworkTransport.h
となっており、もともとシンボリックリンクだったことがわかります。
realm-swiftのような複雑なライブラリでは、ヘッダーファイルの重複を避けるためにシンボリックリンクが使用されることがあります。zip
コマンドはデフォルトでシンボリックリンクを実ファイルに展開してしまうため:
- 同じ内容のファイルが複数箇所に実体として配置される
- C/C++コンパイラが重複定義エラー(redefinition error)を報告する
- ビルドが失敗する
ような事態が発生してしまったようです。
もちろん、すべてのSwift Packageがシンボリックリンクを使っているわけではないので、ビルドエラーが必ず起きるわけではないです。
ただ、あらかじめ予防できるのであればそれに越したことはないでしょう。
解決策 swift package archive-sourceの使用
そこでタイトルに戻りますが、ZIP圧縮する際は、swift package archive-source
を使うとシンボリックリンクが正しく保持されるため、先ほどのようなビルドエラーを避けることができます。
さらには、.buildディレクトリなどの再配布に不必要なファイルも自動的に除外されます。
swift package archive-source -h
OVERVIEW: Create a source archive for the package
USAGE: swift package archive-source [--output <output>]
OPTIONS:
-o, --output <output> The absolute or relative path for the generated source archive
--version Show the version.
-h, -help, --help Show help information.
では、swift package archive-source
を使ってrealm-swiftをZIP圧縮して解凍してビルドしてみます。
swift package archive-source --output ../realm-swift.zip
cd .. && rm -rf realm-swift # 元のディレクトリを削除
unzip realm-swift.zip && cd realm-swift # 解凍
swift build
...
Build complete! (75.28s)
無事にビルドが成功しました。
ちなみにrealm-swiftの場合、除外されたファイルは以下の通りでした。
- .git
- .build
- .swiftpm
代替策 zip -yオプション
ちなみに、今のディレクトリをありのままの状態でZIP圧縮したい場合は、以下のように-y
オプションを使えばシンボリックリンクを保持したまま圧縮できるため、先の例のようなビルドエラーを避けつつ、zip
コマンドを使うこともできます。
zip -r -y realm-swift.zip realm-swift
# -y オプション: store symbolic links as the link instead of the referenced file
# 訳: シンボリックリンクを参照先のファイルではなく、リンクとして保存する
まとめ
swift package archive-source
を使うことで、シンボリックリンクの問題を回避しつつ、再配布に適した形でSwift PackageをZIP圧縮できることがわかりました。
Swift PackageをZIP圧縮する際は、ぜひswift package archive-source
を使うことをおすすめします。