Ubuntu Weekly Recipe

第401回オープンソース化されたApple Swiftでこのは弄り

アップルが開発しているプログラミング言語であるSwiftオープンソース版が公開されました。今回はこのSwiftをUbuntu上で利用する方法を紹介します。

アップルさん家のSwift

「アップルさん家の」Swiftは、iOSやOS Xのソフトウェアを開発するために2014年に発表された比較的新しいプログラミング言語です。今年の6月には、その新しいバージョンであるSwift 2をオープンソース化することが発表されていました。オープンソース版の公開に伴ってGitHub上にもプロジェクトページが作成され、さっそくたくさんのPull Requestがマージされているようです。

このオープンソース版Swiftの最大の特徴は「Linuxでも動く」ということです。もともとiOS/OS X用のソフトウェア開発用に作られたこともあって、当初はOS X向けの開発環境のみが提供されていました。今回オープンソース版を開発するにあたって、より多くのプラットフォームで動くようにすべくLinuxへのポーティングが行われたようです。ライセンスはApache Licenseですので、そのうちパッケージリポジトリから簡単にインストールできるようになるかもしれません。

詳しいことはThe Swift Linux Portにも説明がありますが、現時点でその動作にはかなり制約があります。まず最初に、Objective-Cランタイムは使えません。Foundationモジュールを経由して利用できるAPIのうち、Objective-Cに依存するものは使えないということになります。さすがにデータ構造などプリミティブなものはObjective-Cの依存を排除することでLinuxでも使えるようになっているものの、インターネットで公開されているサンプルコードのほとんどはLinux版では動かないと思ったほうがいいでしょう。

また、Linux版のXcodeはありません。XcodeはiOS/OS X向けの統合開発環境であり、Swift/Objective-Cのコード編集はもちろんのこと、UI作成のためのInterface Builderや対話的な実行環境であるPlayground、テストツールなどの機能が搭載されています。SwiftをプログラミングしてコンパイルするだけであればXcodeはなくてもかまわないのですが、さまざまなサードパーティのライブラリやドキュメントが、Xcodeを前提にしたインストール方法を紹介しているため、Linuxでそれらのライブラリを導入したい場合に苦労することになるでしょう。

今のところLinux版のSwiftは、⁠Swiftというプログラミング言語の学習用」以外の用途に使おうとすると、数多くの障害が立ちはだかると思っておきましょう[1]⁠。

Swiftのインストール方法

UbuntuにSwiftをインストールする方法はいくつか存在します。

  • バイナリアーカイブをダウンロードする
  • ソースコードからビルドする
  • Dockerを使う
  • Ubuntu Makeを使う(12月7日時点では未実装)

サポートプラットフォームは、64bit版のUbuntu 14.04 LTSないしUbuntu 15.10となっています。今回はUbuntu 14.04 LTSを対象に、それぞれのインストール方法を個別に紹介しましょう。

バイナリファイルをインストールする

Swiftを試す上で、一番簡単かつ確実に動作する方法です。ただしバイナリファイルは2015年12月6日時点でSwift 2.2の開発版の12月1日のスナップショットになっています。つまりオープンソース版として告知される直前の少し古いバイナリです。今後定期的にバイナリは更新されるものと思いますが、最新版を使いたい場合は次のソースコードからビルドする方法を参照してください。

バイナリファイルはダウンロードページからダウンロードできます。また同じページにLinuxでのインストール手順もありますので、それを元にインストールしましょう。手順としては依存するバイナリパッケージをUbuntuの公式リポジトリからインストールした上で、ダウンロードしたファイルを任意の場所に展開し、その場所を実行ファイルの検索パスに追加するだけです。特にシステムファイルを書き換えるようなことはしませんが、もし不安であればLXCなどの仮想環境上で実行すると良いでしょう。

まずはバイナリアーカイブとその署名ファイルのダウンロードです。ファイル名の日付部分は将来的に変わるかもしれませんので、適宜適切な値をダウンロードページで取得してください。

$ wget https://swift.org/builds/ubuntu1404/swift-2.2-SNAPSHOT-2015-12-01-b/swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu14.04.tar.gz
$ wget https://swift.org/builds/ubuntu1404/swift-2.2-SNAPSHOT-2015-12-01-b/swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu14.04.tar.gz.sig

手順ではこのあと署名ファイルを使って、ファイルを検証することを「強く推奨」しています。そこでGPGを使って署名を検証する方法も説明しておきましょう。まず検証するためには、署名した人の公開鍵(Public Key)を鍵サーバーから取得する必要があります。取得しない状態で検証すると、以下のように「public key not found」と表示されます。

$ gpg --verify swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu14.04.tar.gz.sig
(中略)
gpg: Signature made 2015年12月03日 06時23分10秒 JST using RSA key ID 412B37AD
gpg: Can't check signature: public key not found

この鍵ID「412B837AD」を鍵サーバーで検索すると、次のように署名した人の名前(User ID)などが表示されます。

$ gpg --keyserver hkp://pool.sks-keyservers.net --search-keys 412B37AD
gpg: searching for "412B37AD" from hkp server pool.sks-keyservers.net
(1)     Swift Automatic Signing Key #1 <swift-infrastructure@swift.org>
          4096 bit RSA key 412B37AD, created: 2015-11-19, expires: 2017-11-18
Keys 1-1 of 1 for "412B37AD".  Enter number(s), N)ext, or Q)uit > q

上記のようにこの鍵IDは「Swift Automatic Signing Key」の公開鍵であることがわかります[2]⁠。

今回はこのままインポートするわけではないので、⁠q」を入力して終了してください。手順では、⁠gpg --recv-keys」コマンドか「gpg --import」コマンドで2つの鍵を取り込んでいます。ここでは「gpg --recv-keys」の手順を行ってみましょう。

$ gpg --keyserver hkp://pool.sks-keyservers.net \
      --recv-keys \
      '7463 A81A 4B2E EA1B 551F  FBCF D441 C977 412B 37AD' \
      '1BE1 E29A 084C B305 F397  D62A 9F59 7F4D 21A5 6D5F'
gpg: requesting key 412B37AD from hkp server pool.sks-keyservers.net
gpg: requesting key 21A56D5F from hkp server pool.sks-keyservers.net
gpg: /home/ubuntu/.gnupg/trustdb.gpg: trustdb created
gpg: key 412B37AD: public key "Swift Automatic Signing Key #1 <swift-infrastructure@swift.org>" imported
gpg: key 21A56D5F: public key "Swift 2.2 Release Signing Key <swift-infrastructure@swift.org>" imported
gpg: Total number processed: 2
gpg:               imported: 2  (RSA: 2)

先ほどの「Swift Automatic Signing Key」だけでなく「Swift 2.2 Release Signing Key」の公開鍵も取り込みました。ちなみにコマンドではより確実に特定の鍵を取り込むために、鍵のフィンガープリントを指定していますが、フィンガープリントの末尾である鍵IDを指定してもかまいません。

取り込まれた鍵のリストを表示してみましょう。

$ gpg --list-keys
/home/ubuntu/.gnupg/pubring.gpg
-------------------------------
pub   4096R/412B37AD 2015-11-19 [expires: 2017-11-18]
uid                  Swift Automatic Signing Key #1 <swift-infrastructure@swift.org>

pub   4096R/21A56D5F 2015-11-28 [expires: 2017-11-27]
uid                  Swift 2.2 Release Signing Key <swift-infrastructure@swift.org>

取り込んだ鍵を使って、ファイルの署名を検証しましょう。

$ gpg --verify swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu14.04.tar.gz.sig
gpg: Signature made 2015年12月03日 06時23分10秒 JST using RSA key ID 412B37AD
gpg: Good signature from "Swift Automatic Signing Key #1 <swift-infrastructure@swift.org>"
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: 7463 A81A 4B2E EA1B 551F  FBCF D441 C977 412B 37AD

「Good signature」と表示されているので、確かにダウンロードしたファイルは「Swift Automatic Signing Key」の秘密鍵を持っている人が署名を行ったファイルのようです。

ちなみに「WARNING」は、公開鍵そのものが署名されていないことを伝えています。本来、取り込んだ鍵が本当に妥当なものであるのならば、公開鍵ファイルに取り込んだあとにユーザー自身がその鍵に署名を行うべきです。今回は一時的な使用なので、このステップをスキップしています。ちなみに公開鍵ファイルから、特定の鍵を削除したい場合は、次のように実行してください。

$ gpg --delete-keys 412B37AD
(中略)
pub  4096R/412B37AD 2015-11-19 Swift Automatic Signing Key #1 <swift-infrastructure@swift.org>

Delete this key from the keyring? (y/N) y
$ gpg --list-keys
/home/ubuntu/.gnupg/pubring.gpg
-------------------------------
pub   4096R/21A56D5F 2015-11-28 [expires: 2017-11-27]
uid                  Swift 2.2 Release Signing Key <swift-infrastructure@swift.org>

さて、GPGによるダウンロードしたファイルの検証が終わったので、次にファイルを展開しましょう。展開先はどこでもかまいません。以下では、展開したあとにディレクトリ名を「swift」に変更しています。

$ tar xvf swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu14.04.tar.gz
$ mv swift-2.2-SNAPSHOT-2015-12-01-b-ubuntu14.04 swift

展開したディレクトリの「usr/bin」を検索パスに追加し、swiftコマンドを実行してみましょう。

$ export PATH=`pwd`/swift/usr/bin:${PATH}
$ which swift
/home/ubuntu/swift/usr/bin/swift
$ swift --version
Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c)
Target: x86_64-unknown-linux-gnu

上記のようにバージョンが表示されればインストール完了です。

ちなみにswiftcコマンドを使う場合、clangとbuild-essentialパッケージが必要です。

$ sudo apt-get install clang build-essential

特に後者をインストールしていない場合、次のようなエラーが表示されます。

$ swiftc foo.swift
clang: error: unable to execute command: Executable "ld" doesn't exist!
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)

ソースコードからビルドする

ソースコードが公開されているので、もちろんビルドして利用できます。特にバイナリ版を公開したあとにマージされたコードの変更を反映したい場合は、今のところソースコードからビルドするしかないでしょう。

ビルド手順はGitHub上のプロジェクトのREADMEに記載されています。けっこう大きなプロジェクトですので、ビルドする際のマシンスペックもそれなりに必要です。たとえばAmazon EC2のm3.medium(Intel Xeon E5-2670 x 1 Core、3.75GiBメモリー、SSD)だと、ビルドとテストでおおよそ5時間ぐらいかかりました。なお、ストレージは最低でもシステム込みで16GBは必要です。

またこの方法では、今のところREPLがうまく動きませんでした。

最初にビルドに必要なパッケージをインストールします。ビルドシステムとしてcmakeとninjaを使っています。

$ sudo apt-get install git cmake ninja-build clang uuid-dev \
    libicu-dev icu-devtools libbsd-dev libedit-dev libxml2-dev \
    libsqlite3-dev swig libpython-dev libncurses5-dev pkg-config

Ubuntu 14.04 LTSの場合、clangは3.4ではなく3.6に置き換える必要があります。clang-3.6パッケージをインストールした上で、/usr/bin/clangがclang-3.6を向くようにalternativesを更新しましょう。ちなみにclang-3.6をインストールすると、clangとclang-3.4パッケージは自動的に削除されます。

$ sudo apt-get install clang-3.6
$ sudo update-alternatives --install /usr/bin/clang clang /usr/bin/clang-3.6 100
$ sudo update-alternatives --install /usr/bin/clang++ clang++ /usr/bin/clang++-3.6 100

次に必要なソースコードをGitHubからcloneしていきます。今回は使わないリポジトリもcloneしています。

$ mkdir swift && cd $_
$ git clone https://github.com/apple/swift.git swift
$ git clone https://github.com/apple/swift-llvm.git llvm
$ git clone https://github.com/apple/swift-clang.git clang
$ git clone https://github.com/apple/swift-lldb.git lldb
$ git clone https://github.com/apple/swift-cmark.git cmark
$ git clone https://github.com/apple/swift-llbuild.git llbuild
$ git clone https://github.com/apple/swift-package-manager.git swiftpm
$ git clone https://github.com/apple/swift-corelibs-xctest.git
$ git clone https://github.com/apple/swift-corelibs-foundation.git

ちなみにバイナリ版のリビジョンには「swift-2.2-SNAPSHOT-2015-12-01-b」というタグがついていますので、バイナリ版と同じコードをビルドしたい場合は、そのタグをcheckoutしておきましょう。

ビルドスクリプトは「swift/utils/build-script」になります。これそのものはPythonのスクリプトです。ヘルプを表示するには次のコマンドを実行します。

$ ./swift/utils/build-script -h

今回はリリースビルド(⁠⁠-R」オプション)を行います[3]⁠。また「-t」オプションがついているとビルド後テストを行います。LLDBもビルドしたい場合は「--lldb」オプションをつけてください[4]⁠。

$ time ./swift/utils/build-script -R -t --lldb
(中略)
Testing Time: 922.64s
  Expected Passes    : 1667
  Expected Failures  : 92
  Unsupported Tests  : 573
-- check-swift-linux-x86_64 finished --
--- Finished tests for swift ---

real    299m19.298s
user    232m14.336s
sys     19m27.081s

ビルドしたバイナリは「./build/Ninja-ReleaseAssert/swift-linux-x86_64/bin/」以下に配置されますので、そこ検索パスに追加した上で実行してみましょう。

$ export PATH=`pwd`/build/Ninja-ReleaseAssert/swift-linux-x86_64/bin:${PATH}
$ which swift
/home/ubuntu/swift/build/Ninja-ReleaseAssert/swift-linux-x86_64/bin
$ swift --version
Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 7245c0729b)
Target: x86_64-unknown-linux-gnu

ちなみに「--version」オプションを指定せずに実行すると、次のようなエラーが出ます。

LLVM ERROR: Compiler-internal integrated REPL unimplemented for this platform; use the LLDB-enhanced REPL instead.

これはswiftコマンド組み込みの対話インターフェースであるREPLが、アップル以外のプラットフォームでは無効化されているためです[5]⁠。Linuxの場合、次のようにlldbコマンドのREPLを使うようです。ただし何か評価させるとエラーとなります※6>⁠⁠。

$ export PATH=`pwd`/build/Ninja-ReleaseAssert/lldb-linux-x86_64/bin:${PATH}
$ lldb --repl

lldb: /home/ubuntu/swift/swift/lib/AST/Module.cpp:1477:
  void performAutoImport(swift::SourceFile &, SourceFile::ImplicitModuleImportKind):
  Assertion `M && "unable to auto-import module"' failed.
Aborted (core dumped)

swiftのコンパイラそのものは動きます。REPLを使いたい場合のみ、今のところはバイナリ版を使うようにしましょう。

Dockerを使う

Swiftインストール済みのDockerイメージも早速公開されています。これはPhusion Baseimageをベースに、⁠バイナリファイルをインストールする」で紹介した手順でSwiftをインストールしたイメージです。このイメージではルートディレクトリに直接アーカイブの内容を展開していますので、検索パスを変更しなくてもswiftコマンドを実行できます。

まずはDockerのインストールからです。Ubuntu 14.04 LTSにはリポジトリにdocker.ioパッケージが存在しますが、これは1.6止まりです。ここではDockerの公式ドキュメントにしたがって、Docker公式のリポジトリを追加してインストールすることにします[7]⁠。

$ sudo apt-key adv --keyserver hkp://p80.pool.sks-keyservers.net:80 \
    --recv-keys 58118E89F3A912897C070ADBF76221572C52609D
$ echo "deb https://apt.dockerproject.org/repo ubuntu-trusty main" | \
    sudo tee -a /etc/apt/sources.list.d/docker.list
$ sudo apt-get update
$ sudo apt-get install docker-engine
$ sudo service docker status
docker start/running, process 648

インストール後、Dockerサービスが起動していることを確認しておきましょう。

ちなみにdocker-engineパッケージは、新規にdockerグループを作成します。このdockerグループにユーザーを追加すれば、そのユーザーはsudoなしにdockerコマンドを実行できるのです。今回はこの手順はスキップして、常にsudoでdockerを実行することにします。

それではDockerイメージを取得してコンテナを起動しましょう。

$ sudo docker pull swiftdocker/swift
$ sudo docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             VIRTUAL SIZE
swiftdocker/swift   latest              8d736fc221b8        20 hours ago        873.8 MB

$ sudo docker run -i -t --privileged=true --name swiftfun swiftdocker/swift:latest /bin/bash
root@3ad0c66c55e1:/# swift --version
Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c)
Target: x86_64-unknown-linux-gnu
root@3ad0c66c55e1:/# exit

「--privileged=true」は、REPLを使うために指定しています。一度コンテナを作ってしまえば、あとはstartコマンドで起動し、attachコマンドでコンテナ内に入れます。

$ sudo docker start swiftfun
swiftfun
$ sudo docker attach swiftfun
root@54b06cfceebe:/#

Ubuntu Makeを使う

Ubuntuには各種ソフトウェアの開発環境を導入するためのUbuntu Makeというツールが存在します。Ubuntu MakeでAndroid開発環境を構築する方法は、第372回で紹介しました。Androidだけでなく、StencylやVisual Studio Code、Go言語、Dart SDK、IntelliJ IDEA、PyCharm、PhpStormなどなど、いくつものIDEやプログラミング言語に積極的に対応しています。

当然のことながら、Swiftにも対応することになるでしょう。早速、Issueが登録されています。早ければこの記事を公開するころには何らかの実装が行われているかもしれません。

Swiftの基本的な使い方

Swiftは一般的なコンパイラ言語ですが、対話的なREPLRead–eval–print loopとして使うこともできます[8]⁠。Swiftの言語を学ぶ間は、REPLでトライアンドエラーを繰り返すと良いでしょう。

$ swift
Welcome to Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c). Type :help for assistance.
  1> let prog = "swift"
prog: String = "swift"
  2> print("I think the perfection of \(prog) is that it's not perfect.")
I think the perfection of swift is that it's not perfect.
  3> :quit

REPLは対話的なデバッガなので、⁠:quit」のように「:」の後ろにコマンドをつなげることでデバッグ操作もできます。詳しいことは「:help」コマンドを実行してください。

その他のSwift言語の書式を一通り把握したい場合は、Swift Tourが参考になります。

実際のところSwiftのプリミティブな機能だけでは、できることは制約されています。たとえばファイルI/Oやネットワーク通信といったOS固有の操作に関わる部分は手が出せません。ただしLinux版Swiftには「Glibcモジュール」が用意されています。Glibcモジュールをインポートすると、Swiftからlibcの機能を使えるというわけです。

$ swift
Welcome to Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c). Type :help for assistance.
  1> import Glibc
  2> let prog = "swift"
prog: String = "swift"
  3> puts("I think the perfection of \(prog) is that it's not perfect.")
I think the perfection of swift is that it's not perfect.
$R0: Int32 = 58
  4> :quit

上記ではSwiftにないlibcのputs()を用いて文字列を出力しています[9]⁠。

もちろんREPLではなく、ファイルにコードを書いて実行することも可能です。たとえばputs.swiftというファイルを以下の内容で作成します。

import Glibc

let prog = "swift"
puts("I think the perfection of \(prog) is that it's not perfect.")

swiftコマンドにファイル名を渡すと、コンパイルしてその場で実行してくれます。

$ swift puts.swift
I think the perfection of swift is that it's not perfect.

実行バイナリファイルを生成したい場合は、swiftcコマンドを使うといいでしょう。

$ swiftc puts.swift
$ ls
puts  puts.swift
$ ./puts
I think the perfection of swift is that it's not perfect.

このはさん家のSwift

「このはさん家の」Swiftは、OpenStack Swiftベースのオブジェクトストレージサービスです。国産クラウドサービスであるConoHaは、提供しているOpenStackサービスのAPIを公開しているため、このオブジェクトストレージもRESTfulなAPI経由で操作できます。

詳しいことはgihyo.jpの記事でもある個人利用から大規模開発までConoHaで始めるクラウド開発入門第3回 ConoHaオブジェクトストレージを使ってみようを参照してください。ここからはこの記事にもあるcurlによるAPI操作ができる環境を前提に説明を続けます[10]⁠。

さて、せっかく同じ「Swift」同士なので、このはさん家のSwiftを操作するプログラムをアップルさん家のSwiftで作ってみたいところです。上記記事にもあるように、必要なことはHTTPS経由のGET/POSTとJSONのパースだけなので、今風の言語ならそこまで難しくない「ような気がします⁠⁠。

実際のところSwiftでHTTP通信する場合、NSURLSessionNSURLRequestを直接使うか、Alamofireのような外部ライブラリを使うケースがよく紹介されていました。しかしながら実際にLinux版で試してみると、以下のように「not yet implemented」なんてエラーが出ます。

Welcome to Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c). Type :help for assistance.
  1> import Foundation
  2> var url = NSURL(string: "http://gihyo.jp")
  3> var myRequest:NSURLRequest  = NSURLRequest(URL: url!)
fatal error: init(URL:) is not yet implemented: file Foundation/NSObjCRuntime.swift, line 64
myRequest: Foundation.NSURLRequest = <extracting data from value failed>

Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)

どうやら一筋縄ではいかないようです。そこで今回は「いんちき」をします。Linux版のSwiftはGlibcモジュールを使えばlibcのAPIが叩けます[11]⁠。つまり、popen()やらsystem()やらfopen()やらが使えるのです。あとはわかりますね?

というわけで実際に作ってみたコードを、Gistにアップロードしました。

最初の#!/usr/bin/env swiftは、シェルスクリプトのようにconoha.swiftファイルに実行権限を付けて実行するために記述しています。

Glibcのインポートのあとに、各自で設定しなければならない項目をまとめています。設定ファイル名以外はConoHaのコントロールパネルから取得できるはずなので、各自適切な値を設定してください。

let config = [
    "tenantName": "XXXXDDDDDDDD",                   // テナント名
    "tenantId": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", // テナントID
    "username": "XXXXDDDDDDDD",                     // APIユーザー名
    "password": "PPPPPPPPPPPPPPPPP",                // APIパスワード
    "configfile": ".conoha",                        // 設定ファイル名
]

設定ファイルは実行したユーザーのホームディレクトリに保存されます。保存する内容はgetContainer()で取得したトークンとその有効期限です。

d0b24cf7c74346b6b47113a19bd57c29
2015-12-08T12:24:29Z

有効期限以内であればconfigfileのトークンを再利用し、有効期限が過ぎたら自動的に再取得して、configfileを更新します。

では、実際に使ってみましょう。まずはコード内部で使用しているjqとcurlをインストールした上で、ファイルを取得してconfigの内容を更新します。

$ sudo apt-get jq curl
$ wget https://gist.githubusercontent.com/m-shibata/a67eef729aba045bb6eb/raw/e73f457022bdff8c793c01b839e60c5fb9492852/conoha.swift
(conoha.swiftのconfigを更新)
$ swiftc conoha.swift

ファイルをオブジェクトストレージにアップロードするには次のコマンドを実行します。⁠images」がコンテナ名で「taylor.png」がアップロードするファイル名です。

$ ./conoha put images taylor.png

オブジェクトストレージからファイルをダウンロードするには次のコマンドを実行します。ダウンロードする際、ローカルのファイルを上書きしてしまうため、あらかじめ退避しています。

$ mv taylor.png taylor_backup.png
$ ./conoha get images taylor.png

今回のスクリプトでできるのは、本当にこれだけです。

内部ではcurlとjqを使ったコマンド文字列を生成し、それを実行しています。実際に実行しているのはexecToString()で、次のような処理になっています。

func execToString(command: String) -> String {
    let fp = popen(command, "r")

    if fp == nil {
        return ""
    }

    var data = String()
    let buffer = [CChar](count: 1024, repeatedValue: 0)
    while fgets(UnsafeMutablePointer(buffer), Int32(buffer.count), fp) != nil {
        if let read = String.fromCString(buffer) {
            data += read
        }
    }
    if pclose(fp) != 0 {
        return ""
    }

    return data
}

popen()で実行し、その標準出力をfgets()で取得した上で、String型に変更して返しているだけです。この中でpopen()、fgets()、pclose()がGlibcモジュールが提供する関数になります。

popen()の第一引数はconst char *です。これはString型から自動的に変換してくれます。戻り値はFILE *で、Swift的には「UnsafeMutablePointer<FILE>」となります。fgets()の第一引数はchar *で、Swift的には「UnsafeMutablePointer<CChar>」になります。fgets()でbufferに放り込みつつ、その内容をString.fromCString()でString型に変換しています。

トークンの有効期限が、現在時刻を越えているかどうかはisExpired()で確認しています。

func isExpired(expire: String) -> Bool {
    var expireTm = tm()
    var buffer: [Character] = []

    // parse YYYY-mm-ddTHH:MM:SSZ
    for i in 0...3 { buffer.append(expire[expire.startIndex.advancedBy(i)]) }
    expireTm.tm_year = Int32(String(buffer))! - 1900
    buffer = []
    (以下略)

FoundationモジュールにはNSDateがあり、NSFormatterを使えば文字列からNSDateの変換もできることになっています。ところがLinux版のFoundationモジュールは、なぜかNSFormatter.dateFromString()が常に「epoch - (366×24×60×60)」を返してしまい、使うことができませんでした。また、Glibcにはstrftime()はあるもののstrptime()がなかったため、ここでは手作業で時刻情報をパースしています。

「var expireTm = tm()」は、C言語で言うところの「struct tm expireTm;」です。SwiftはC言語の構造体をこのように表現できます。さらに「expireTm.tm_year」でメンバーにアクセスしています。

getToken()では、ホームディレクトリの位置を取得するためにgetpwent()を実行しています。

func getToken() -> String {
    // set config file name
    let pwent = getpwuid(getuid())
    let homeDir = String.fromCString(pwent.memory.pw_dir)

getpwuid()の戻り値はstruct passwd *であり、Swift的には「UnsafeMutablePointer<passwd>」になります。Swiftにはアロー演算子はありませんので、構造体のポインタのメンバーにアクセスするためには、memoryメンバーを経由します。それが上記で言うところの「pwent.memory.pw_dir」です。

このようにSwiftでC APIを使うためには、いくつかのお約束が存在します。アップルの開発者向けドキュメントのInteracting with C APIsにはこれらに関する情報がまとまっていますので、SwiftでGlibcモジュールを活用したい場合に参考になるでしょう。

おすすめ記事

記事・ニュース一覧