読者です 読者をやめる 読者になる 読者になる

euphonictechnologies’s diary

Haskell超初心者の日記です。OCamlが好きです。

follow us in feedly

Linux上でSwift Package Managerを使ってビルドするときに躓いたこと

Swift Linux

けっこう茨の道です

なんかまだ「初心者でも鼻くそほじりながら日曜日にさらっとね」って感じではないです。なにせコンパイラ、Xcode、Swift Package Manager(以下SPM)すべてが日進月歩でバージョンアップしているのでウェブに書いてあることそのまま試すと全く動かなかったりします。

このブログの内容は2016年11月6日の内容です。Swiftはversion 3.0.1 (swift-3.0.1-RELEASE)を使いUbuntu 14.04上でやりました。

f:id:euphonictechnologies:20161106185709p:plain

まずは

github.com

公式ドキュメント。必読です。私はここを読まずにたくさんの時間を無駄にしました。アホですね。

CとSwift混ぜるとき

モジュールを分ける

SPMはSources下のディレクトリ単位でモジュール扱いをします。Sources直下にあるディレクトリ一つがモジュールという単位になります。

CとSwiftのコードを同じモジュールに混在させるとバリデーションに引っかかります。素直に分けましょう。私のオセロライブラリGrapheneの場合

(Project root)
├── Graphene.xcodeproj
... xcodeprojの中身がドバ~っと
├── LICENSE
├── Package.swift
├── README.md
├── Sources
│   ├── Graphene
│   │   ├── Board.swift
│   │   ├── BoardBuilder.swift
│   │   ├── BoardMediator.swift
... ファイルがドバ~っと
│   └── Intrinsics
│       ├── Intrinsics.c
│       └── include
│           ├── Graphene-Bridging-Header.h
│           ├── Intrinsics.h
│           └── Swift_Bridging_Header.h
└── Tests
    ├── GrapheneTests
    │   └── GrapheneTests.swift
    └── LinuxMain.swift

こんな感じになっています。Swiftコードは(Project root)/Sources/Grapheneというパッケージ名と同じモジュールにまとめてあります。Cのコードは(Project root)/Sources/Intrinsicsというモジュールにしました。Cモジュールにはinclude(すべて小文字)が必要です。なので(Project root)/Sources/Intrinsics/includeディレクトリがあります。モジュール名と同じヘッダファイルが第一に使われます。他のbridging headerは多分必要ないのですが、念のために残してあります。消して壊れたら嫌なので。

Package.swiftにtargetを追加してdependencyを明示する

オフィシャルのドキュメントに書いてあるのですが、dependencyを明示するために依存しているモジュールをPackage.swiftのtargetをきちんと書く必要があります。私のSources/GrapheneはSources/Intrinsicsに依存しています。つまり

import PackageDescription

let package = Package(
    name: "Graphene",
    targets: [
        Target(name: "Graphene", dependencies: ["Intrinsics"]),
    ]
)

こんな感じです。これはCのコードからなるIntrinsicsモジュールをSwiftモジュールGrapheneより先にビルドするためです。

@_silgen_nameはとりあえず外す

なんかCのシンボルがリンク時に見つからないとか言うし@silgen_nameアトリビュートがあるファイルにimport Intrinsicsと書くと今度はシンボルが曖昧とか言われるしわけわかんないのでとりあえず@silgen_nameは消してimport Intrinsicsを残すことにしました。つまり

#if os(Linux)
import Intrinsics
#else
@_silgen_name("_bitScanForward")
    func _bitScanForward(_: UInt64) -> UInt
    
@_silgen_name("_bitPop")
    func _bitPop(_: UInt64) -> UInt
#endif

こんな感じです。当然macOSでXcode上でビルドするためには_silgen_nameが必要なので、こんなふうに分岐させてみました。Linux向け特殊処理が必要なときは今のところこうやってさっさと回避するのが吉のようです。

こうすることでLinux上のコンパイラはIntrinsics内のシンボルをリンクしようとするし、Xcode上では@_silgen_nameアトリビュートを使ってビルドしてくれます。ここらへんは多分ちゃんとしたやり方があると思うのですが、今はこれが精一杯(ルパン)。

ちゃんとstdヘッダをインクルードする

uint64_tのためにCのコードに#include <stdint.h>が必要です。Xcodeでビルドするときはどっかでインクルードされてるから必要なかったりして、意外にハマりました。他にもimplicitに依存しているstdライブラリはちゃんと明示する必要があります。明示しておくほうがお行儀も良いですしね。

これらがうまくいくと

~/projects/Graphite$ swift build
warning: minimum recommended clang is version 3.6, otherwise you may encounter linker errors.
Compile Intrinsics Intrinsics.c
Linking Intrinsics
Compile Swift Module 'Graphene' (27 sources)
<module-includes>:1:1: warning: umbrella header for module 'Intrinsics' does not include header 'Graphene-Bridging-Header.h'
#include "/home/ubuntu/projects/Graphite/Packages/Graphene-1.9.0/Sources/Intrinsics/include/Intrinsics.h"
^
<module-includes>:1:1: warning: umbrella header for module 'Intrinsics' does not include header 'Swift_Bridging_Header.h'
#include "/home/ubuntu/projects/Graphite/Packages/Graphene-1.9.0/Sources/Intrinsics/include/Intrinsics.h"
^
/home/ubuntu/projects/Graphite/Packages/Graphene-1.9.0/Sources/Graphene/EdaxProtocol.swift:28:13: warning: variable 'bb' was never mutated; consider changing to 'let' constant
        var bb = SimpleBitBoard()
        ~~~ ^
        let
/home/ubuntu/projects/Graphite/Packages/Graphene-1.9.0/Sources/Graphene/NegaAlphaSearch.swift:81:26: warning: result of call to 'put(_:x:y:guides:)' is unused
                newBoard.put(turn, x: px, y: py, guides: false)
                         ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/home/ubuntu/projects/Graphite/Packages/Graphene-1.9.0/Sources/Graphene/BoardMediator.swift:22:9: warning: result of call to 'updateGuides' is unused
        updateGuides(.black)
        ^           ~~~~~~~~
/home/ubuntu/projects/Graphite/Packages/Graphene-1.9.0/Sources/Graphene/SimpleProofSolver.swift:281:33: warning: result of call to 'put(_:x:y:guides:returnChanges:)' is unused
            newBR.boardMediator.put(node.whosTurn, x: px, y: py)
                                ^  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Compile Swift Module 'Graphite' (1 sources)
Linking ./.build/debug/Graphite

と、GraphiteというGrapheneパッケージを利用した実行ファイルがビルドできました(警告出てますね。恥ずかしい)。CモジュールIntrinsicsが先にコンパイルされているのがわかります。

そのほかSwift on Linuxのメモ

Ubuntu 14.04でやる

RHELとかCentOSでもビルドできたという報告がググルと出ますが、どうも更に茨の道のようです。Ubuntu 14.04が一番鉄板です。16も私のところではライブラリ周りでうまくいかなくて諦めました。ヘタレです。

arc4randomはない

知らなかったのですが、arc4randomはFonudationの中ではなくてBSDのライブラリファンクションなんですね。

developer.apple.com

というわけで当然Linuxのglibcにはないので、代替品を用意する必要があります。私はこんな感じにしました。

#if os(Linux)
    import Glibc
    import SwiftShims
#else
    import Darwin
#endif

func cs_arc4random_uniform(upperBound : UInt32 = UINT32_MAX) -> UInt32 {
    #if os(Linux)
        return _swift_stdlib_cxx11_mt19937_uniform(upperBound)
    #else
        return arc4random_uniform(upperBound)
    #endif
}

こういうときもさっさとLinuxだけ別にしちゃいましょう。_swift_stdlib_cxx11_mt19937_uniformはSwiftShimsにあります。CSPRNGの必要あるのかって気はしますが。他のパターンもSwiftShimsで結構カバーできるような気がします。

まとめ

github.com

とりあえずLinux上でビルドができるようになりました。というわけでLinux上で開発ができるようにEmacsの環境を整えようと思います。

Xcodeで書きたいのはやまやまなのですが、SwiftのコンパイラのバージョンがmacOS用のツールチェーンだと微妙に古いのでうまくいかず、かといってオフィシャルのベータをインストールしたりしてぐちゃぐちゃにするとiOS用のビルド環境がなくなっちゃうのでLinux上で頑張ることにします。

2017.02.05追記

開発はXcodeだけでLinuxと両方サポートできています。なのでXcodeで快適開発しちゃいましょう。Emacsが快適じゃないとは言ってないですよ。Emacsも大好きです。

"iOSアプリとLinuxコマンドラインアプリケーション間でライブラリを共有する"を実践した時のメモ

Swift Xcode

前回は

iOSアプリとLinuxコマンドラインアプリケーションでライブラリを共有するためにSwift Package Managerをつかってライブラリを作ってそれをiOS側ではEmbedded Binariesとして、Linux側では単純にパッケージとしてリンクして使えるようにしてみました。

blog.euphonictech.com

これを自分のオセロプロジェクトに適用した時の躓きを雑多なメモとして残しておきます。

実行できない

The application bundle does not contain a valid identifier

これは単純に私が愚かだからです。当然バンドルとしてコピーするためにはBundle IDが必要です。swift package initしたプロジェクトには当然バンドルIdは設定されていません。設定しましょう。

f:id:euphonictechnologies:20161103134226p:plain

これだけです。

実機で実行時にクラッシュする

dyld: Library not loaded: @rpath/Graphene.framework/Graphene...

これはフレームワークがEmbedded Binariesとして登録されているけれど実際にはコピーされていないために起こっているようです。

私の場合はコード署名の問題でした。この問題の厄介なのはシミュレーターでは大丈夫なのに実機でクラッシュするのです。実機にコピーするにはコード署名が必要だからでしょう。

共有したいライブラリのビルド設定でコード署名を設定しましょう。

f:id:euphonictechnologies:20161103134254p:plain

オートにするとうまくいかないのでマニュアル設定です。

まとめ

私の場合はこの2点だけでした。色々いじくり回した挙句まっとうな設定を抜かしていたというオチです。やっぱり結構straightforwardにできますね。Apple結構えらいぞ。

iOSアプリとLinuxコマンドラインアプリケーション間でライブラリを共有する

Swift Linux Xcode

サーバサイドSwiftって全然盛り上がってないですね

なんかフロントエンド盛り上がってますがサーバサイドっていまいちそういう感じしませんね。悲しいです。フロントエンドアプリばっかり作ってるとフロントエンド脳になってまうで。

というわけで拙作のオセロアプリは着々と進捗があるのですが、ここ1年ほどアップデートをしておりません。iOS側のオセロアプリとサーバーサイドアプリケーションで同じオセロAIエンジンを共有したい、という文脈でどうしてもサーバサイドとフロントエンドでライブラリを共有したいのです。

iOSアプリとLinuxで動くコマンドアプリケーションでコードをシェアする方法…正攻法で挑んでみる

なんとなくググってみたのですが、いまいち決定打みたいなのはまだない・・・ように感じます。おそらくSwiftをヘビーに使っている会社の社内的なノウハウみたいなものはもうあるんでしょうが、あまり公開情報が引っかかりませんでした。なので正攻法で挑んでみようと思いました。

今回のゴールの図

では、iOSのSingleViewApplicationとUbuntuのコマンドラインアプリケーションでライブラリを共有します。こんなアプリです。

f:id:euphonictechnologies:20161024222651g:plain

…アプリってほどのものか?あくまでexampleですから。

ボタンを押すと次々乱数を表示します。それだけです。便利ですね!

んで、その擬似乱数の生成器が共有部分になっていて、コマンドラインアプリケーション側はこんな感じで使います。

f:id:euphonictechnologies:20161024223126g:plain

昔懐かし数あてゲームです。これをiOSで作るのは面倒だったのでiOS側はボタンを押して乱数を表示するだけのアプリになりまして、すみませんすみません。

というわけで、例にしては意外に応用しがいのあるユースケースじゃないかなーと。

今回の流れ

結構ありがちな流れとして(たまたま自分がそうだっていうだけですが)

  • iOSでアプリをもりもり作ってきて、手元に動くアプリがある
  • その一部をライブラリとして切り出したい
  • でもってそのライブラリをサーバサイドで使えるようにしたい

という状況を想定しております。なので今回の説明も

  • まずは完動品のiOSアプリを作ります
  • そしてライブラリを切り出します
  • それをサーバサイドのコマンドラインアプリで使います

という流れで行こうと思います。

まずはiOSアプリ側で乱数表示アプリを完成させる

というわけでMac上で普通にXcodeを使ってSingleViewApplicationを作ります。

f:id:euphonictechnologies:20161024224113p:plain

出来上がったファイルがこんな感じです。非常にシンプルですね。

ストーリーボードにボタンが一つあるだけ、ViewControllerもviewDidLoadで乱数生成器を初期化してボタンの押下に反応するメソッドだけのシンプルな構成です。

gist.github.com

f:id:euphonictechnologies:20161024224715p:plain

こんな感じでどうでしょう。

参考資料:

qiita.com

後々の作業のためにプロジェクトでなくてワークスペース上で作業するようにしましょう。空っぽのワークスペースを作ってそこにプロジェクトファイル(.xcodeprojファイル)をドラッグ・アンド・ドロップするだけです。完成。

ここからが本番です。

ライブラリをSwift Package Manager対応できるように切り出す

Swift Package Manager(SPM)というのが現在ベーシックなパッケージマネジャです。

SPMってなに?

Swift.org - Package Manager

非常にシンプルなパッケージマネジャです。Package.swiftというマニフェストファイルとパッケージを構成するスクリプトやライブラリバイナリ等からなります。

例えば今回作るライブラリは次のような構成になります。

~EFTest/RandomGenerator$ tree
.
├── LICENSE
├── Package.swift
├── README.md
├── RandomGenerator.xcodeproj
│   ├── Configs
...<xcodeprojの中身は省略>...
└── Sources
    └── RandomGeneratorXor128.swift

READMEとか本質的でないものを除くと構成要素は2つだけ、Package.swiftとSourcesディレクトリです。xcodeprojはiOSとXcode上で並行同時開発するためにリポジトリ上にあるだけで、本質的には必要ありません。

Package.swiftには2つの役割があります

つまり、その2つとは

  • あなたは誰?
  • 誰に依存してますか?

カウンセリングみたいになってしまいましたが、要はそのパッケージが何かということと、そのパッケージをビルドするのに他のどんなパッケージが必要なのか(どのバージョンを必要とするのか、どこにあってどうダウンロードするのか)の2つを指定する必要があります。

今回の場合、乱数生成器のライブラリは誰にも依存していないので、パッケージ名だけを指定してあるPackage.swiftを持っており、それを使うコマンドラインアプリケーションのPackage.swiftにはそのコマンドラインアプリケーションの名前と乱数生成器のライブラリのおいてあるgithubのURLとバージョンが収められています。

というわけで、切り出す先のプロジェクトを作ります

座学を終えて実践に移っていきましょう。まずはgithubに新しいリポジトリを作りましょう。使うパッケージはSPMにgithubからダウンロードさせるのが楽です。githubにリポジトリを作りましょう。適当に名前をつけてswift用の.gitignoreを含めておくと楽です。

READMEとか.gitignoreとかLICENSEだけの内容をgit cloneしてローカルのMac上に作業環境を作りましょう。で、そのディレクトリ上で

~/EFTest/RandomGenerator$ swift package init

してください。するとそのディレクトリの名前(つまりgithubのリポジトリ名)でパッケージを作ってくれます。マニフェストファイルであるPackage.swiftとか必要なディレクトリ構成とかテストのスクリプトとか一通り全て作ってくれます。

~/EFTest/RandomGenerator$ swift package init
Creating library package: RandomGenerator
Creating Package.swift
Creating .gitignore
Creating Sources/
Creating Sources/RandomGenerator.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/RandomGenerator/
Creating Tests/RandomGenerator/RandomGeneratorTests.swift
~/EFTest/RandomGenerator$ tree
.
├── Package.swift
├── Sources
│   └── RandomGenerator.swift
└── Tests
    ├── RandomGenerator
    │   └── RandomGeneratorTests.swift
    └── LinuxMain.swift

3 directories, 4 files

こんな感じです。で、コマンドラインで作業してもよいのですが、つらいのでXcodeのプロジェクトを作りましょう。

~/EFTest/RandomGenerator$ swift package generate-xcodeproj
~/EFTest/RandomGenerator$ ls -l
total 8
drwxr-xr-x  7 Yoshinori  staff  238 Oct 24 23:12 RandomGenerator.xcodeproj
-rw-r--r--  1 Yoshinori  staff   88 Oct 24 23:08 Package.swift
drwxr-xr-x  3 Yoshinori  staff  102 Oct 24 23:08 Sources
drwxr-xr-x  4 Yoshinori  staff  136 Oct 24 23:08 Tests

ご覧の通り.xcodeprojが出来上がったので先程つくったiOSプロジェクトだけが置かれているワークスペースにこのプロジェクトを追加しましょう。

f:id:euphonictechnologies:20161024231549p:plain

RandomGeneratorがライブラリでEFTestがiOSアプリです。似たような感じになっていれば大丈夫です。TestsがRandomGeneratorにないのですが、私が消しました!気にしないでください!

そして切り出していきます

あとはこの2つのプロジェクトの間をファイルを行ったり来たりさせます。iOSアプリ側ではビルド設定の中でEmbed Frameworkに切り出す先のフレームワークを追加しておきましょう。

f:id:euphonictechnologies:20161024231918p:plain

ビルドエラーが出る度にクリーンするのが面倒なのでCopy only when installingをチェックしておくのがおすすめです。

で、ファイルが移動し終わったら多分やらなくちゃいけないのは

  • Swift 3にアップグレード
  • ビルド(多分クリーンが必要)

です。前者はコンパイラが指示してくれると思いますが、[Edit] -> [Convert] -> [To Current Swift Syntax...]でSwift 3準拠のコードに改めてください。

f:id:euphonictechnologies:20161024232147p:plain

次なんですが、ビルドがうまく行った方はそのままで大丈夫なのですがおそらくそのままビルドが通らないか実行時に怒られるかどっちかだと思います。何か変なことが起きたらクリーンビルドしましょう。

f:id:euphonictechnologies:20161024232343p:plain

ご存知コマンド+シフト+Kです。これで治らない場合はビルドディレクトリを削除しましょう。ここにオルトを加えると[Clean]が[Clean Build Folder ...]に変わると思いますのでそれを実行してください。それで治らない場合は…がんばってください。応援してます。

これで切り出したライブラリパッケージでiOSアプリが動くようになったと思います。動くようになったらライブラリに加えた変更をgithubにコミットしてプッシュしておきましょう。

サーバサイドのコマンドアプリケーションでライブラリパッケージを使う

Vagrantで使えるUbuntu 14.04をセットアップする

色々方法があると思いますが、何故か私がVagrantbox.esを使うと時間がすごくかかるのでいつもApache AuroraのVagrantファイルを改造して使っています。多分正攻法はこちら:

$ vagrant box add Ubuntu14.04 https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-i386-vagrant-disk1.box

私のやり方は:

$ wget https://raw.githubusercontent.com/apache/aurora/master/Vagrantfile
$ vi Vagrantfile
# 最後から3行目の"dev.vm.provision "shell", path: "examples/vagrant/provision-dev-cluster.sh""を削除
$ vagrant up

です。たぶん間違ったやり方なので皆さんは素直にUbuntuのbox作ってください。vagrant upしたら早速作ったばかりのUbuntuにログインしましょう。

UbuntuにSwiftがコンパイルできる環境を整える

基本的には公式のインストール方法を追いかければ大丈夫です。

swift.org

sudo apt-get update
sudo apt-get install -y clang libicu-dev

これで下準備は完了です。Swiftをインストールしていきましょう。

wget https://swift.org/builds/swift-3.0-release/ubuntu1404/swift-3.0-RELEASE/swift-3.0-RELEASE-ubuntu14.04.tar.gz
tar xzf swift-3.0-RELEASE-ubuntu14.04.tar.gz

解凍した一式の中にあるswiftにパスを通します。私はきれい好きなズボラなのでホームディレクトリの下にswiftというディレクトリを作ってその中にアーカイブを解凍しました。~/swift/usrがそのアーカイブ内のusrへのリンクになるようにしておいてあとでバージョン切り替えを簡単にする魂胆です。

$ mkdir swift
$ mv swift-3.0-RELEASE-ubuntu14.04.tar.gz swift
$ cd swift/
$ tar xzf swift-3.0-RELEASE-ubuntu14.04.tar.gz
$ ls
swift-3.0-RELEASE-ubuntu14.04  swift-3.0-RELEASE-ubuntu14.04.tar.gz
$ ln -snf /home/vagrant/swift/swift-3.0-RELEASE-ubuntu14.04/usr usr
$ ls -l
total 114632
drwxrwxr-x 3 vagrant vagrant      4096 Oct 30 07:52 swift-3.0-RELEASE-ubuntu14.04
-rw-rw-r-- 1 vagrant vagrant 117377293 Sep 13 23:25 swift-3.0-RELEASE-ubuntu14.04.tar.gz
lrwxrwxrwx 1 vagrant vagrant        53 Oct 30 07:53 usr -> /home/vagrant/swift/swift-3.0-RELEASE-ubuntu14.04/usr

こんな感じ。お好みで。 本当はダウンロードしたファイルをmd5で検証すべきなのだけど面倒なので省略。いつか痛い目見るで。

あとはパスを通しましょう。

$ vi .profile
$ source .profile
$ printenv PATH
/home/vagrant/swift/usr/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games

こんな感じです。

最後にswiftの動作確認をしましょう。

$ swift --version
Swift version 3.0 (swift-3.0-RELEASE)
Target: x86_64-unknown-linux-gnu
$ swift
Welcome to Swift version 3.0 (swift-3.0-RELEASE). Type :help for assistance.
  1> 1 + 2
$R0: Int = 3
  2> let a = 1 + 3
a: Int = 4
  3> let b = 4 + 9
b: Int = 13
  4> let c = a + b
c: Int = 17
  5> ^D

いい感じですね!これでswiftがインストールできました。

数あてゲームをビルドする

それでは最後の工程、数あてゲームをさっきの乱数ライブラリを用いてビルドしてみましょう。

$ mkdir EFTest_CommandLineApp
$ cd EFTest_CommandLineApp/
$ swift package init --type executable

これでプロジェクトの雛形ができました。

vagrant@aurora:~/codes/EFTest_CommandLineApp$ tree
.
|-- Package.swift
|-- Sources
|   `-- main.swift
`-- Tests

2 directories, 2 files

こんな感じです。やらなければいけないことは

  • Package.swiftを修正してこのプロジェクトの設定と使うライブラリの設定を与える
  • swift package updateをして使うライブラリをgithubからダウンロード、インストールする
  • main.swiftを書く
  • ビルドして実行

です。

Package.swiftを修正してこのプロジェクトの設定と使うライブラリの設定を与える

早速パッケージマニフェストファイルPackage.swiftを修正しましょう。

gist.github.com

dependenciesには前のセクションであなたが作ったライブラリのgithubのurlを指定してください。 ここでmajorVersion: 1, minor: 2という部分が不思議だと思うのですが、そうなんです、Swift Package Managerはgit上のタグ付けされた内容しか使えないのです。バージョニングせよということなんでしょう。なのでGithub上でタグ付け、つまりリリースをしましょう。

f:id:euphonictechnologies:20161030171645p:plain

Githubのリリースの項目から

f:id:euphonictechnologies:20161030171656p:plain

新しいドラフトを編集する画面に移り

f:id:euphonictechnologies:20161030171807p:plain

という感じでバージョンをつけます。画面では1.3.0になっていますが、普通は1.0.0ですね。もしくはα版扱いにして0.1.0としてもよいと思います。お好きにどうぞ。で、ここでつけた最初と2番めの数字がそれぞれmajor/minorバージョンとしてPackage.swiftに指定する番号になります。

dependencies: [
        .Package(url: "https://github.com/ysnrkdm/EFTest_RandomGenerator.git", majorVersion: 1, minor: 2)
    ]

ここですね。

swift package updateをして使うライブラリをgithubからダウンロード、インストールする

Package.swiftの設定が終わったらライブラリをダウンロードしましょう。

swift package update

です。そうすると…

Cloning https://github.com/ysnrkdm/EFTest_RandomGenerator.git
HEAD is now at e8da832 Commit xcodeproj
Resolved version: 1.2.0
vagrant@aurora:~/codes/EFTest_CommandLineApp$ tree
.
|-- Packages
|   `-- RandomGenerator-1.2.0
|       |-- LICENSE
|       |-- Package.swift
|       |-- RandomGenerator.xcodeproj
|       |   |-- Configs
|       |   |   `-- Project.xcconfig
|       |   |-- project.pbxproj
|       |   |-- RandomGenerator_Info.plist
|       |   `-- xcshareddata
|       |       `-- xcschemes
|       |           |-- RandomGenerator.xcscheme
|       |           `-- xcschememanagement.plist
|       |-- README.md
|       `-- Sources
|           `-- RandomGeneratorXor128.swift
|-- Package.swift
|-- Sources
|   `-- main.swift
`-- Tests

9 directories, 11 files

という感じでさっきプッシュしたファイルがズラッとダウンロードされているのがわかります。もしライブラリに変更を加えたらそれを再びリリースして、Package.swiftのバージョンを修正してswift package updateすると変更がきちんと反映されます。

main.swiftを書く

今回は簡単にこんな感じにしてみました。

gist.github.com

乱数を4回空打ちしているのが最高にアホですね。すみません。

ビルドして実行

$ TOOLCHAINS=swift swift build
Compile Swift Module 'RandomGenerator' (1 sources)
Compile Swift Module 'EFTest_CommandLineApp' (1 sources)
Linking ./.build/debug/EFTest_CommandLineApp

これでビルドできました。遊びましょう!

ビルドされたファイルはLinking ...が言うとおりプロジェクトルート下の.buildの中にあります。./.build/debug/EFTest_CommandLineAppですね。

$ .build/debug/EFTest_CommandLineApp
Seed set: -3618550839602295192
Your guess? [0-99, >=100 to exit]
50
Less!
Your guess? [0-99, >=100 to exit]
25
More!
Your guess? [0-99, >=100 to exit]
32
More!
Your guess? [0-99, >=100 to exit]
37
Less!
Your guess? [0-99, >=100 to exit]
34
Bingo!
Generating next number...
Your guess? [0-99, >=100 to exit]
100
Bye!

楽しかったですね。

まとめ

iOSとLinuxのコマンドラインアプリケーションとの間でコードを共有するのは簡単。

今回紹介したコードはすべて下のGithubから利用可能です。自分でライブラリ作ったりコード書くの面倒だけどswift on linux使ってみたい方や私のアレな説明でわからなかった方は是非どうぞ。

github.com

github.com