Kitura が 0.3.0 になって、先日に作った Ubuntu 環境が動かなくなったのでその原因を調べて対応しました。

Server Side Swift

数日前に構築した Kitura 環境を使って早速 Web アプリケーションを作ってみようと思ったところ、Kitura がバージョンアップして早くもビルドできなくなってしまいました。

そこで最新の Kitura をちゃんと使えるように環境を作りなおしてみます。


数日前に こちら で整えた Ubuntu + Kitura のサーバーサイド Swift 環境ですけれど、さっそく実際に使ってみようとしたところ、Kitura プロジェクトの準備段階で swift build を実行した時に、次のようなエラーが発生して、先へ進めませんでした。

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:43:26: error: use of unresolved identifier 'DISPATCH_QUEUE_CONCURRENT'

let concurrent = DISPATCH_QUEUE_CONCURRENT

^~~~~~~~~~~~~~~~~~~~~~~~~

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:44:13: warning: constant 'serial' inferred to have type '()', which may be unexpected

let serial = DISPATCH_QUEUE_SERIAL

^

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:44:13: note: add an explicit type annotation to silence this warning

let serial = DISPATCH_QUEUE_SERIAL

^

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:44:22: error: 'DISPATCH_QUEUE_SERIAL' is unavailable: use 'nil' instead of this imported macro

let serial = DISPATCH_QUEUE_SERIAL

^~~~~~~~~~~~~~~~~~~~~

Dispatch.DISPATCH_QUEUE_SERIAL:2:12: note: 'DISPATCH_QUEUE_SERIAL' has been explicitly marked unavailable here

public var DISPATCH_QUEUE_SERIAL: ()

^

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:57:9: error: use of unresolved identifier 'dispatch_async'

dispatch_async(osQueue, block)

^~~~~~~~~~~~~~

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:66:9: error: use of unresolved identifier 'dispatch_sync'

dispatch_sync(osQueue, block)

^~~~~~~~~~~~~

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/Queue.swift:80:13: error: use of unresolved identifier 'dispatch_async'

dispatch_async(dispatch_get_main_queue(), block);

^~~~~~~~~~~~~~

~/Kitura/ESApi/Packages/Kitura-sys-0.3.0/Sources/KituraSys/SysUtils.swift:31:9: error: use of unresolved identifier 'dispatch_once'

dispatch_once(lock, block)

^~~~~~~~~~~~~

<unknown>:0: error: build had 1 command failures

error: exit(1): ["/usr/bin/swift-build-tool", "-f", "/opt/Kitura/ESApi/.build/debug/Kitura-Sys.o/llbuild.yaml"]

もしかして環境作りに失敗していたのかと思ったのですけど、どうやら先日に環境を整えた後に Kitura が 0.3.0 にバージョンアップしていた様子です。それに伴って swift-corelibs-libdispatch 周りの処理でエラーが発生してしまっていた様子です。

環境を Kitura 0.3.0 対応にする

Kitura 0.3.0 を使えるようにするために、先日の Kitura 0.2.0 環境を更新してみることにします。

Swift Development Snapshot (February 25, 2016)

Kitura のインストール手順によると最新の Swift コンパイラを使うようにと記されているので、まずは最近にリリースされた Ubuntu 14.04 Swift Development Snapshot (February 25, 2016) をインストールしておくことにします。

これを こちら に記載したようにして、利用できるように設定します。

swift-corelibs-libdispatch を更新する

Kitura 0.3.0 になって swift-corelibs-libdispatch のインストール方法が変わったようなので、それを更新します。

まず、以前の swift-corelibs-libdispatch 関連ファイルを削除しておきます。

sudo rm /usr/local/lib/libdispatch.*
sudo rm -r /usr/local/include/dispatch
sudo rm /usr/local/include/os/linux_base.h
sudo rm /usr/local/include/os/object.h

sudo rm -rf /toolchains/swift-current/usr/lib/swift/dispatch
sudo rm /usr/lib/swift/linux/x86_64/Dispatch.swiftmodule
sudo rm /usr/lib/swift/linux/x86_64/Dispatch.swiftdoc

それと swift-corelibs-libdispatch をインストールするときにダウンロードしたソースコードも削除しておきます。今回は /usr/local/src/Kitura ディレクトリにダウンロードして作業をしたので、それを削除することにします。

cd /usr/local/src/Kitura
rm -rf swift-corelibs-libdispatch

そうしたら、次のようにして swift-corelibs-libdispatch のダウンロードとインストールを行います。前回の 0.2.0 とは少し手順が変わった様子です。

ちなみに途中の configure では --with-swift-toolchain--prefix オプションで、Swift をインストールしてある /toolchains/swift-current/usr を指定しています。

git clone https://github.com/apple/swift-corelibs-libdispatch.git

cd swift-corelibs-libdispatch

git submodule init
git submodule update

./autogen.sh
./configure --with-swift-toolchain=/toolchains/swift-current/usr --prefix=/toolchains/swift-current/usr

make
sudo make install

これで swift-corelibs-libdispatch のインストールが完了しました。

これまでは /usr/local/include にインストールされてましたけど、今度は関連するライブラリファイルが、上の --prefix で指定したディレクトリの中に、今回であれば /toolchains/swift-current/usr/lib/swift/linux/ にインストールされることになる様子です。

Swift のバージョンを切り替えた時の注意

なお、今回みたいに Swift のバージョンを切り替えた場合、上記の make を実行したタイミングで次のようなエラーが発生することがありました。

/toolchains/swift-current/usr/bin/swiftc -Xcc -fmodule-map-file=/usr/local/src/Kitura/swift-corelibs-libdispatch/dispatch/module.map -I/usr/local/src/Kitura/swift-corelibs-libdispatch -parse-as-library -Xcc -fblocks -c -o /usr/local/src/Kitura/swift-corelibs-libdispatch/src/Dispatch.o /usr/local/src/Kitura/swift-corelibs-libdispatch/src/swift/Dispatch.swift

swift: /home/buildnode/jenkins/workspace/oss-swift-package-linux-ubuntu-14_04/llvm/tools/clang/lib/Serialization/ASTWriter.cpp:2371: unsigned int clang::ASTWriter::getSubmoduleID(clang::Module *): Assertion `(ID || !Mod) && "asked for module ID for non-local, non-imported module"' failed.

0 swift 0x00000000031229b8 llvm::sys::PrintStackTrace(llvm::raw_ostream&) + 40

1 swift 0x0000000003121186 llvm::sys::RunSignalHandlers() + 54

2 swift 0x00000000031234ea

3 libpthread.so.0 0x00002af32ac5c340

4 libc.so.6 0x00002af32c12acc9 gsignal + 57

5 libc.so.6 0x00002af32c12e0d8 abort + 328

6 libc.so.6 0x00002af32c123b86

7 libc.so.6 0x00002af32c123c32

8 swift 0x0000000001578f84 clang::ASTWriter::WritePreprocessor(clang::Preprocessor const&, bool) + 6052

9 swift 0x0000000001593d38 clang::ASTWriter::WriteASTCore(clang::Sema&, llvm::StringRef, std::string const&, clang::Module*) + 12216

10 swift 0x0000000001590d3d clang::ASTWriter::WriteAST(clang::Sema&, std::string const&, clang::Module*, llvm::StringRef, bool) + 589

11 swift 0x00000000015ccbf8 clang::PCHGenerator::HandleTranslationUnit(clang::ASTContext&) + 88

12 swift 0x00000000013d8f0c clang::MultiplexConsumer::HandleTranslationUnit(clang::ASTContext&) + 44

13 swift 0x00000000015d9aa6 clang::ParseAST(clang::Sema&, bool, bool) + 614

14 swift 0x00000000013b5695 clang::FrontendAction::Execute() + 69

15 swift 0x000000000137ed61 clang::CompilerInstance::ExecuteAction(clang::FrontendAction&) + 1153

16 swift 0x00000000030c102a llvm::CrashRecoveryContext::RunSafely(llvm::function_ref<void ()>) + 266

17 swift 0x00000000030c11b4

18 swift 0x000000000312444a

19 libpthread.so.0 0x00002af32ac54182

20 libc.so.6 0x00002af32c1ee47d clone + 109

Stack dump:

0. <eof> parser at end of file

make[2]: *** [/usr/local/src/Kitura/swift-corelibs-libdispatch/src/Dispatch.o] Aborted (core dumped)

これはどうやら、前に swift-corelibs-libdispatch をインストールした時に Swift ツールチェイン内にコピーされた関連ファイルが干渉して発生しているようでした。これからビルドする swift-corelibs-libdispatch で、過去に作ったそれを使おうとしているのかもしれません。

そのため swift-corelibs-libdispatch をビルドするのに使う Swift ツールチェインで過去に swift-corelibs-libdispatch をビルドしてある場合は、次のように以前にインストールしたファイルを削除してからビルドするようにします。

rm -rf /toolchains/swift-current/usr/lib/swift/dispatch

ライブラリのパスを環境変数に登録する

インストールした pcre2 ライブラリを使えるようにするために ~/.bash_profile ファイルの中に次の内容を記載しておきます。

export LD_LIBRARY_PATH="/usr/local/lib":"${LD_LIBRARY_PATH}"
export LD_RUN_PATH="/usr/local/lib":"${LD_RUN_PATH}"

こうしたら、あとは次のようにして .bash_profile の設定内容を反映します。

source ~/.bash_profile

Kitura サンプルを実行してみる

これで Kitura 0.3.0 の実行環境が整ったはずなので、Kitura のサンプルを動かしてみることにします。

以前のビルドファイルを削除

まずは、以前にダウンロードした Kitura リポジトリをアップデートしたり、Kitura 0.2.0 環境でビルドした KituraSample を消去したりしておきます。

/usr/local/src/Kitura/Kitura
git pull

swift build --clean
rm -rf Packages

上記の最後で Packages ディレクトリを手動で削除していますが、これをしないと make の時に次のエラーが発生してしまう様子でした。もっと適切なパッケージの消去方法があるのかもわかりませんけど、わからなかったのでこうしています。

swift build

warning: refname '0.2.0' is ambiguous.

warning: refname '0.2.0' is ambiguous.

Resolved version: 0.2.0

error: rename error: Directory not empty (39): /usr/local/src/Kitura/Kitura/Packages/Kitura-router -> /usr/local/src/Kitura/Kitura/Packages/KituraRouter-0.2.0

Kitura をビルドできるようにする

こうしたら、いよいよ Kitura のビルドに入るわけですけれど、そのままでは KituraSample をビルドすることはできず、次のようなエラーが発生してしまいました。

Compiling Swift Module 'HeliumLogger' (1 sources)

<unknown>:0: error: module 'Dispatch' requires feature 'blocks'

<unknown>:0: error: could not build Objective-C module 'Dispatch'

<unknown>:0: error: module 'Dispatch' requires feature 'blocks'

<unknown>:0: error: could not build Objective-C module 'Dispatch'

<unknown>:0: error: module 'Dispatch' requires feature 'blocks'

<unknown>:0: error: could not build Objective-C module 'Dispatch'

<unknown>:0: error: module 'Dispatch' requires feature 'blocks'

<unknown>:0: error: could not build Objective-C module 'Dispatch'

<unknown>:0: error: build had 1 command failures

error: exit(1): /toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-02-25-a-ubuntu14.04/usr/bin/swift-build-tool -f /usr/local/src/Kitura/Kitura/.build/debug.yaml default

make: [make] Error 1 (ignored)

それともう一つ、次のようなエラーも発生しています。

make -f Packages/Kitura-net*/Makefile

make[1]: Entering directory `/usr/local/src/Kitura/Kitura'

make[1]: Packages/Kitura-net*/Makefile: No such file or directory

これについて、どうしたら良いかと1日ばかり考えあぐねていたのですけど、Twitter で @ki_machoさん にいろいろ教えて頂きました。

わかったこととしては、まず make -f Packages/Kitura-net*/Makefile のところは、Swift パッケージマネージャーが更新されたか何かの都合で、ビルドの際にダウンロードされたパッケージの場所が Packages/KituraNet になったことが原因だった様子でした。

これについては @ki_machoさん の指摘どおり、Kitura の Makefile 内にある make の記載を次のように編集することで対応できる様子でした。

make:
       -swift build
        make -f Packages/KituraNet*/Makefile

その他のエラーについて考える(無視可能)

もうひとつの error: module 'Dispatch' requires feature 'blocks' については別のところでは無視して問題ないという話もあったのですけど、気持ち悪いので調べてみることにしました。

すると、どうやら swift build を実行する時に -Xcc -fblocks オプションを指定しないと、ビルドする Swift コード内で import Dispatch を実行して GCD 関連の機能を使おうとした時に、このエラーが発生する様子でした。

これを改善するには先ほどの Makefile をもう少しだけ調整して、次のようにすることで対応できます。

make:
       -swift build -Xcc -fblocks
        make -f Packages/KituraNet*/Makefile

これでもまだ次のエラーが残るものの、とりあえずこれで KituraSample がビルドされて、動かすことができるようになりました。

Linking KituraSample

/usr/bin/ld: cannot find -lcurlHelpers

/usr/bin/ld: cannot find -lhttpParserHelper

clang: error: linker command failed with exit code 1 (use -v to see invocation)

<unknown>:0: error: link command failed with exit code 1 (use -v to see invocation)

<unknown>:0: error: build had 1 command failures

error: exit(1): /toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-02-25-a-ubuntu14.04/usr/bin/swift-build-tool -f /usr/local/src/Kitura/Kitura/.build/debug.yaml default

ただ、やっぱりこれも残っているのは気持ちが悪いので調べてみたのですけど、これはどうやら curlHelpershttpParserHelper という2つのライブラリが、最初の swift build ではまだ生成されていないため、KituraSample にリンクしようとして失敗した状況みたいです。

ただ、その後に実行される make -f Packages/KituraNet*/Makefile の中でこれら2つのライブラリが生成されて、再び最後に swift build が実行されてリンクされる様子なので、このエラーについてはひとまず無視しておくしかなさそうです。


ともあれここまで調べを進めてみると、最初の error: module 'Dispatch' requires feature 'blocks' のエラーについても、その後のリンクのステップと合わせて、ちゃんと -Xcc -fblocks -Xlinker -L./.build/debug 付きの swift build が改めて実行されるので、確かにそのまま無視しておいても普通に大丈夫そうなことがわかってきました。

そうすると、つまり最終的には Kitura 直下の Makefile を、次のように最後の make のところだけ書き換えておけば大丈夫そうですね。

make:
       -swift build
        make -f Packages/KituraNet*/Makefile

KituraSample をビルドする

ともあれ、このように Makefile を修正することで、次のようにして Kitura サンプルをビルドできるようになりました。

make

初回ビルド時に1つだけ、次のエラーが発生しますけど、上記の通り、無視するしかないエラーな様子です。

Linking KituraSample

/usr/bin/ld: cannot find -lcurlHelpers

/usr/bin/ld: cannot find -lhttpParserHelper

clang: error: linker command failed with exit code 1 (use -v to see invocation)

<unknown>:0: error: link command failed with exit code 1 (use -v to see invocation)

<unknown>:0: error: build had 1 command failures

error: exit(1): /toolchains/swift-DEVELOPMENT-SNAPSHOT-2016-02-25-a-ubuntu14.04/usr/bin/swift-build-tool -f /usr/local/src/Kitura/Kitura/.build/debug.yaml default

Kitura 0.3.0 環境への移行完了

こんな感じで Kitura 0.3.0 環境への移行は無事に完了しました。

複雑な手順になりましたけど、現在の develop ブランチを使えばこういったエラーに悩まされることは無くなるので、しばらくすれば、きっと master ブランチに取り込まれて普通にビルドできるようになりそうです。

まとめ

まとめると、とりあえず今の問題としては、たぶん Swift パッケージマネージャーの仕様変更により Kitura-net パッケージがダウンロードされるディレクトリが KituraNet になったためにエラーが発生、それに手作業で対応しないといけない、ということに集約できそうでした。

その他の Dispatch に関するエラーとリンク失敗に関するエラーは驚くだけで、単に今の Makefile の設計が、敢えて無視して先の手順へ進むようになっている、ということだったみたいでした。