この記事は 慶應義塾大学SFC村井&徳田研 Advent Calendar 2015 の15日目の記事です。
Swiftはソースコード公開と共にLinuxへ正式対応しました。
しかし、Mac OS XでSwiftを書くときの定番IDEであるXcodeは提供されていません。
というかそもそも、XcodeはiOSやwatchOSようアプリケーションの開発には便利かもしれませんが、小さなCUIアプリケーションの開発のためにわざわざあの重たいIDEを起動するのは気が引けます。
というわけで、ここではXcodeを使用しないSwiftプログラミングのための各種コマンドの使い方について、REPLやデバッガからライブラリの生成、その他のTipsまでをまとめます。
iOSアプリなどの開発に関わる話は一切出てきません。飽くまでも、C++やJavaのような汎用言語の一員としてSwiftを利用する方法だけを解説しています。
なお、ここでの想定環境はMac OS X El Capitanです。
Linux上でももちろんエディタの設定方法や各コマンドの使い方は変わりませんが、Mac OS X上の方が自分でコマンドをコンパイルしない分その場所などがわかりづらいので、その見つけ方などまで含めて紹介します。
ちなみに、もちろんXcodeとXcode Command Line Toolsはインストール済みであることが前提です。
シェルからSwiftコンパイラを呼び出す
まずはXcodeを開かずに、シェルから直接swiftコンパイラを使ってみましょう。
ソースコードからコンパイルした場合はbuild
とかいうディレクトリに作成されるSwiftコンパイラの実行ファイルですが、Xcodeをダウンロードして使用する場合はXcode.appの中に含まれているコンパイラを呼びださなければなりません。
そのためにべんりなコマンドがxcrun
です。
何も引数を付けずに実行すると使い方が表示されます。
$ xcrun
Usage: xcrun [options] <tool name> ... arguments ...
Find and execute the named command line tool from the active developer
directory.
The active developer directory can be set using `xcode-select`, or via the
DEVELOPER_DIR environment variable. See the xcrun and xcode-select manual
pages for more information.
Options:
-h, --help show this help message and exit
--version show the xcrun version
-v, --verbose show verbose logging output
--sdk <sdk name> find the tool for the given SDK name
--toolchain <name> find the tool for the given toolchain
-l, --log show commands to be executed (with --run)
-f, --find only find and print the tool path
-r, --run find and execute the tool (the default behavior)
-n, --no-cache do not use the lookup cache
-k, --kill-cache invalidate all existing cache entries
--show-sdk-path show selected SDK install path
--show-sdk-version show selected SDK version
--show-sdk-build-version show selected SDK build version
--show-sdk-platform-path show selected SDK platform path
--show-sdk-platform-version show selected SDK platform version
例えば、xcrun swift
とすれば現在アクティブになっているXcodeのSwiftのインタプリタを起動することができますし、xcrun swiftc
とすれば同じようにSwiftのコンパイラを起動できます。
ちなみに、「現在アクティブになっているXcode」は
$ xcode-select -s /Applications/Xcode.app/Contents/Developer/
のようにして指定することができます。現在どこにあるものが使用されているかはxcode-select -p
で表示できます。
では次に、開発で中心的に使用するコマンドを詳しく見ていきましょう。
swift - Swiftインタプリタ
xcrun swift hoge.swift
とするとhoge.swift
を即座に実行できます。
また、xcrun swift
とするとREPLモードとなり、PythonやHaskellなどのようにすぐに結果を確認できる対話環境が開始します。
$ xcrun swift
Welcome to Apple Swift version 2.1.1 (swiftlang-700.1.101.15 clang-700.1.81). Type :help for assistance.
1> print("Hello, World!")
Hello, World!
2>
REPLはLLDBをベースに作られているため、中でデバッガのさまざまなコマンドを利用できます。
今はどうなのかよく知りませんが、β版の頃のPlayGroundはバグが多すぎて使い物にならなかったので、ずっと自分はこのインタプリタで動作の確認などしていました。
ただし、このインタプリタもたまに本来コンパイラを通した時にはエラーになるコードがエラーにならなかったり、エラーにならないはずのコードで止まってしまったりすることがあるので、飽くまでも電卓代わりか、構文の確認程度に使用する形になるかと思います。
swiftc - Swiftコンパイラ
xcrun swiftc
として起動できるのが一番のお目当て、Swiftコンパイラです。
GCCやClangと近いオプションを持っているので、それらでのコンパイルに慣れていれば、特に使い方がわからないということもないかと思います。
コンパイルを行うには、単にコマンドライン引数としてSwiftソースコードのファイル名を与えます。
$ xcrun swiftc hello.swift
生成される実行ファイルはソースコードのファイル名から拡張子を取ったものとなるため、上記の場合はhello
という実行ファイルが生成されます。
これを自分で指定したい場合はGCCなどと同様に-o
オプションを使います。
また、XcodeでiOSアプリなどの開発をしているだけではあまり意識しない点として、複数ファイルをまとめてコンパイルする際にはメイン・エントリ・ポイントを持つかファイルをmain.swift
という名前にする必要があります。
そのため、複数ファイルをコンパイルする場合には、特に指定しなければ出力ファイルはmain
という名前になります。
最適化付きでコンパイルを行うには-O
オプションを指定します。GCCのように-O2
や-O3
といったレベルはありませんが、-Ounchecked
というオプションが別に用意されており、このオプションをつけると実行時に起こった問題のトラップなどの確認用コードまで全て取り去ります。
別にコンパイルされたライブラリをリンクする場合は-I
でライブラリの.swiftmodule
ファイルがあるディレクトリを指定し、動的リンクライブラリがあるディレクトリを-L
で指定した上で、読み込むモジュールの名前を-l
で指定します。
ライブラリの読み込みの具体例やライブラリの生成方法などについては後述します。
lldb - デバッガ
いくらSwiftのコンパイラが強力でコンパイル時にかなり多くのエラーを見つけてくれると言っても、ゆくゆく必ず必要になってくるのがデバッガです。
SwiftはLLVMベースで開発されているため、LLDBというLLVMのデバッガを利用することができ、これもxcrun lldb
とすることで起動できます。
LLDBを利用するためには、コンパイル時に-g
オプションをつけ、実行ファイルにデバッグ用の情報を付加します。
LLDBのコマンドライン引数として実行ファイル名を渡すと、デバッグ用の対話環境が起動します。
$ xcrun swiftc -g hello.swift
$ xcrun lldb hello
(lldb) target create "hello"
Current executable set to 'hello' (x86_64).
(lldb)
LLDBの使い方については他に解説している記事がたくさんありますのでここでは書きませんが、ブレイクポイントの設定やコールスタックのバックトレースなどかなり充実したデバッグ環境となっていますので、複雑なプログラムの挙動を確かめるときや、実行時エラーにあたった時などには是非活用してみてください。
ライブラリの作成
Swiftのライブラリを開発して配布する際や大規模なプログラムをモジュール化する際にはそのためのオプションを知っておく必要があります。
これらを把握するためにはまず、Swiftコンパイラが出力するファイルの形式と、その形式にするためのオプションの対応がわかっていなければなりません。
逆に言えば、それさえわかっていれば使うべきオプションは簡単に判断がつきます。
そこで、まずは普段の開発で使用することになるオプションとファイル形式の対応を表にまとめてみました。
コンパイラオプション | 生成されるファイル | 用途 |
---|---|---|
-emit-ir | LLVM-IR | 生成される低レベルコードを確認するために利用できます |
-emit-assembly (-S) | アセンブリ言語 (.s) | 生成される低レベルコードを確認するために利用できます |
-emit-object (-c) | オブジェクトファイル (.o) | 複数ファイルを別々にコンパイルしてから、後でまとめて1つの実行ファイルにする際などに利用できます |
-emit-library | 動的リンクライブラリ (.dylib) | 動的リンクライブラリとして利用できます |
-emit-module | モジュールファイル (.swiftmodule) | クラスや関数などの定義が書かれたインクルードファイルとして利用できます |
-emit-library -emit-object | ライブラリオブジェクトファイル (.o) | メイン・エントリ・ポイントを含まないオブジェクトファイルとして、他のメイン・エントリ・ポイントを持つオブジェクトファイルとリンクできます |
実際に例を見てみましょう。以下の様なmodule.swift
ファイルとmain.swift
ファイルがある場合を想定します。
// module.swift
public let hello = "Hello, World!"
// main.swift
import Module
print(hello)
複数モジュールから成る1つの実行ファイルを作る場合
まずは、複数のモジュールから成るソフトウェアの開発を行うときのようにmodule.swift
の内容をモジュールModule
としてコンパイルし、その後Main
というモジュール名を持つmain.swift
の内容とリンクして1つの実行ファイルにする場合のコマンドを見てみます。
$ xcrun swiftc -emit-module -module-name Module module.swift
# Module.swiftmodule と Module.swiftdoc が生成される
$ xcrun swiftc -emit-library -emit-object -module-name Module module.swift
# module.o が生成される
$ xcrun swiftc -I . -emit-object main.swift
# main.o が生成される
$ xcrun swiftc module.o main.o
# main が生成される
$ ./main
Hello, World!
ポイントは、module.swift
のオブジェクトファイル生成時に-emit-library -emit-object
と指定するところです。単に-emit-object
だけでコンパイルすると、メイン・エントリ・ポイントが生成されてしまうので、最後のmodule.o
とmain.o
をリンクするタイミングでmain
という名前の関数が二重定義されている旨のエラーとなってしまいます。
また、main.swift
のコンパイル時には-I .
としてカレントディレクトリのModule.swiftmodule
を探し出せるようにしておく必要があります。
しかし、リンク時には全てのオブジェクトファイルを静的にリンクするため、-L
や-l
による指定は必要ありません。
動的リンクライブラリを生成する場合
次に、動的リンクライブラリを生成する場合の手順を見てみます。
$ xcrun swiftc -emit-module -module-name Module module.swift
# Module.swiftmodule と Module.swiftdoc が生成される
$ xcrun swiftc -emit-library -emit-object -module-name Module module.swift
# libModule.dylib が生成される
$ xcrun swiftc -I . -L . -lModule main.swift
# main が生成される
$ ./main
Hello, World!
この場合は動的リンクライブラリを生成するので、module.swift
のコンパイルコマンドから-emit-object
オプションがなくなっており、libModule.dylib
が生成されるようになっています。
また、main.swift
のコンパイル時にはライブラリを検索するパスにカレントディレクトリを追加するよう-L .
と指定されており、その中のModule
というライブラリを使用することが-lModule
オプションによって明示されています。
このように、swiftc
コマンドでは他のコマンドを一切使うこと無く、動的リンクライブラリの生成なども簡単に行うことができてしまうのです。
その他のTips
Swiftならではの機能をコマンドから使うためのオプションを紹介します。
モジュールのテストを書けるようにする
Swift 2.0からはモジュールのインポート時に@testable
という属性を付与することでinternal
なメンバにもアクセスし、テストを書けるようになっています。
しかし、この@testable
を付けてインポートする機能はデフォルトではONになっていません(効率の面を考えればあたり前ですが...)ので、これを有効化するには@testable
を使ってインポートしたいモジュールのコンパイル時に-enable-testing
オプションを付ける必要があります。
$ xcrun swiftc -enable-testing -emit-module -module-name Module module.swift
ただ、なぜかこのオプション、コンパイラのヘルプに書かれていないんですよね。。
自分は自作のテストフレームワークを使ってテストをしているのでよく活用しているのですが、Xcodeなしでは使わないだろうと思われているのか...不思議です。
Objective-CやCをブリッジする
Objective-CやCのブリッジもサードパーティのライブラリが充実していない現状ではよく利用するオプションの1つです。
これも何故かヘルプに書かれていないのですが、import-objc-header
というオプションでブリッジングヘッダファイルを指定することで利用できます。
$ xcrun swiftc -import-objc-header bridging_header.h hello.swift
ちなみに、ブリッジングヘッダファイルの名前は確かXcodeでは特定の名前にしていないと起こられるようになっていたかと思いますが、オプションで指定する際には特にどんな名前でも構いません。
おわりに
Swiftコンパイラをシェルから使用するための最低限のコマンドについてまとめました。
自分はSwift大好きな癖にiOSやMac OS Xなどのアプリケーションは一切開発したことがなく、Xcodeの使い方もよくわからない、という人間なので普段からこれらのコマンドを使い、Makefileを書いてSwiftアプリケーションを作成しています。
みなさんもLinuxへ移植されたこのタイミングを機に、あの異様に重たいIDEから開放されたSwiftプログラミングを楽しんでみてはいかがでしょうか。