Carthageの作り方を紐解いてみる~Makefile編~
SwiftがLinuxで動かせるようになりはや数ヶ月、そろそろSwift製のツールを自分で作って使って行きたいなぁと思っています。
そこでSwift製のプロダクトで一番使われているんじゃないか?と思われるCarthage
の作り方について調べてみました。
Makefileを見る
なにはともあれまずはMakefileを読んでみます。
とりあえず $ make install
すると使えるようになりそうな雰囲気を感じるので一旦インストールしてみます。
$ make install rm -f "Carthage.pkg" rm -f "CarthageKit.framework.zip" rm -rf "/tmp/Carthage.dst" xcodebuild -workspace 'Carthage.xcworkspace' -scheme 'carthage' DSTROOT=/tmp/Carthage.dst clean Build settings from command line: DSTROOT = /tmp/Carthage.dst 〜(中略)〜 Sudo installer -pkg Carthage.pkg -target / Password: installer: Package name is Carthage installer: Installing at base path / installer: The install was successful.
xcodebuild
とpkgbuild
が走り、生成物であるCarthage.pkg
をインストールしました。
$ carthage Available commands: archive Archives built frameworks into a zip that Carthage can use bootstrap Check out and build the project's dependencies build Build the project's dependencies checkout Check out the project's dependencies copy-frameworks In a Run Script build phase, copies each framework specified by a SCRIPT_INPUT_FILE environment variable into the built app bundle fetch Clones or fetches a Git repository ahead of time help Display general or command-specific help update Update and rebuild the project's dependencies version Display the current version of Carthage
正しくインストールされていますね。
中で何が起きているか追うためにMakefile
の依存関係を整理してみます。
install
install: package
sudo installer -pkg Carthage.pkg -target /
install
はpackage
に依存していて、最終的にinstaller
を走らせてCarthage.pkg
をインストールするためのターゲットのようです。
package
package: installables pkgbuild \ --component-plist "$(COMPONENTS_PLIST)" \ --identifier "org.carthage.carthage" \ --install-location "/" \ --root "$(TEMPORARY_FOLDER)" \ --version "$(VERSION_STRING)" \ "$(OUTPUT_PACKAGE)" (cd "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)" && zip -q -r --symlinks - "$(OUTPUT_FRAMEWORK)") > "$(OUTPUT_FRAMEWORK_ZIP)"
package
はinstallables
に依存していて、pkgbuild
でCarthage.pkg
を生成するためのターゲットのようです。
引数のTEMPORARY_FOLDER
にはinstallables
でビルドされた生成物が配置されてそうです。
ちなみに最終行のCarthageKit.framework.zip
は何目的で作っているのかよくわかりませんでした。
intallables
installables: clean bootstrap $(BUILD_TOOL) $(XCODEFLAGS) install mkdir -p "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)" mv -f "$(CARTHAGEKIT_BUNDLE)" "$(TEMPORARY_FOLDER)$(FRAMEWORKS_FOLDER)/$(OUTPUT_FRAMEWORK)" mv -f "$(CARTHAGE_EXECUTABLE)" "$(TEMPORARY_FOLDER)$(BINARIES_FOLDER)/carthage" rm -rf "$(BUILT_BUNDLE)"
installables
はclean
とbootstrap
に依存していて、pkgbuild
で利用するコマンドやフレームワークを生成し、必要な箇所に配置するためのターゲットのようです。
実際に叩かれてるコマンドは xcodebuild -workspace 'Carthage.xcworkspace' -scheme 'carthage' DSTROOT=/tmp/Carthage.dst install
で、xcodebuild
のinstall
は指定されたDSTROOT
に対してビルドしたターゲットをインストールするアクションです。
今回で言うと /tmp/Carthage.dst
にCarthage
のビルド結果であるCarthageKit.framework
と、carthage
のバイナリ本体を配置しています。
clean & bootstrap
bootstrap: script/bootstrap clean: rm -f "$(OUTPUT_PACKAGE)" rm -f "$(OUTPUT_FRAMEWORK_ZIP)" rm -rf "$(TEMPORARY_FOLDER)" $(BUILD_TOOL) $(XCODEFLAGS) clean
clean
は過去の生成物の削除、bootstrap
はルート以下のscript/bootstrap
を叩いており、どちらも他のターゲットには依存していませんでした。
script/bootstrap
は、Carthage
プロジェクトが利用している外部ライブラリをgit
のsubmodule
で管理するためのスクリプトでした。
まとめ
フローを整理します。
- 過去の生成物を削除する
Carthage
のビルドに必要なライブラリを取得/更新するxcodebuild
を利用してCarthage
をビルドし、CarthageKit.framework
とcarthage
のバイナリを生成し特定の箇所に配置する- 特定の箇所に配置された生成物を
Carthage.pkg
にパッケージ化する Carthage.pkg
をインストールする
以上のフローで、Carthage
が手元で利用できるようになりました。
次回はCarthage
をビルドする周りを詳しく見てみようと思います。
Objective-Cで書かれたframeworkのSwift対応
先日書いた記事 iOSにおける静的ライブラリ作成技法 - 渋谷ラーメン男道 で、iOSにおける静的ライブラリ・frameworkの作成方法について紹介しましたが、あの内容だけだと Swift のプロジェクトからインポートすることが出来なかったのでその辺の話になります。
Swift からObjective-C のソースを利用する方法
Swift から Objective-Cのソースを利用する方法は2種類あります。
Bridging-Header
を利用する- Clang の
Moduels
を利用する
Bridging-Header
公式のリファレンス Using Swift with Cocoa and Objective-C (Swift 2.1): Swift and Objective-C in the Same Project にもありますが、Bridging-Header.h
ファイルを利用することで利用可能です。
Swift 対応していないサードパーティライブラリを利用する場合はこの方法が主流じゃないでしょうか。
しかし、この方法はライブラリ提供側ではなく利用側の設定になります。
ライブラリを提供する立場では、次に紹介するModules
を利用して Swift 対応したライブラリを提供することで、利用側でBridging-Header
を設定する手間をなくすことが出来ます。
Modules
Modules
は Clang の3.3から提供されている機能です。
iOSの文脈ではXcode5、iOS7から利用可能になっており、Modules
を利用することで、コンパイル周りが最適化され、アプリケーションのコンパイル速度向上が期待できます。
また、Objective-Cで書かれたライブラリを Module として読み込むことで、Swift から利用可能になります。
今回は、前回の記事で作成した framework を例に、 Module を作成する方法を紹介します。
前回同様こちらのリポジトリで作成したプロジェクトを公開しています。
Swiftプロジェクトの作成
まずは Swift プロジェクトを作成して、前回のままの framework ではインポートできないことを確認します。
プロジェクトは特に特別な設定を行っていません。
次に framework のリンクを行います。
アプリのターゲット -> Linked Frameworks and LIbraries -> Add Other から前回作成した framework を選択します。
framework が追加されました。
framewrok のインポートを試してみます
やはり、現状ではエラーがでてしまうので、Module 化を行います。
framework を Module 化する
作業としては framework 内に Modules
ディレクトリを作成し、その下にmodule.modulemap
を追加するだけです。
framework の最終的なディレクトリ構成は、公式ページにある通り下記のようになるはずです。
Name.framework/ Modules/module.modulemap Module map for the framework Headers/ Subdirectory containing framework headers Frameworks/ Subdirectory containing embedded frameworks Resources/ Subdirectory containing additional resources Name Symbolic link to the shared library for the framework
今回はmodule.modulemap
ファイルは下記のようにしました。
framework module HOGEFuga { umbrella header "HOGEFuga.h" export * module * { export * } }
上記記述では、HOGEFuga.h
をアンブレラヘッダとして追加しているので、HOGEFuga.h
内でインポートされているファイルを SubModule
として読み込む記述になっています。
HOGEFuga.h
はHOGEFugaService.h
をインポートしているので、Swift プロジェクト側で Import HOGEFuga
するだけでHOGEFugaService
クラスを利用できるようになるはずです。
#import <Foundation/Foundation.h> #import <HOGEFuga/HOGEFugaService.h>
元のプロジェクトではMakefile
を使って framework を作成しているのでそれも編集します。
最終的には下記のようにしました。
XCODEBUILD?=$(shell which xcodebuild) TARGET_NAME?=HOGEFuga LIB_NAME:=lib${TARGET_NAME}.a PROJECT_NAME:=${TARGET_NAME}.xcodeproj BUILD_DIR:=./ CONFIGURATION?=Release FW_DIR:=${TARGET_NAME}.framework FW_DIR_RES:=${FW_DIR}/Resources FW_DIR_HEAD:=${FW_DIR}/Headers FW_DIR_MOD:=${FW_DIR}/Modules FW_DIRS:=${FW_DIR_RES} ${FW_DIR_HEAD} ${FW_DIR_MOD} all: HOGEFuga.framework HOGEFuga.framework: libHOGEFuga.a module.modulemap mkdir -p ${FW_DIRS} cp ${TARGET_NAME}/*.h ${FW_DIR_HEAD}/. cp ${TARGET_NAME}/Info.plist ${FW_DIR_RES}/. cp module.modulemap ${FW_DIR_MOD}/. cp $< ${FW_DIR}/${TARGET_NAME} libHOGEFuga.a: libHOGEFugaPhoneos.a libHOGEFugaPhonesimulator.a lipo -create -output $@ $^ rm -rf build/ Release-*/ libHOGEFugaPhoneos.a: ${XCODEBUILD} -target ${TARGET_NAME} -project ${PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphoneos -arch armv7 -arch armv7s -arch arm64 clean build ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" mv ${BUILD_DIR}/${CONFIGURATION}-iphoneos/${LIB_NAME} $@ libHOGEFugaPhonesimulator.a: ${XCODEBUILD} -target ${TARGET_NAME} -project ${PROJECT_NAME} -configuration ${CONFIGURATION} -sdk iphonesimulator -arch i386 -arch x86_64 clean build ONLY_ACTIVE_ARCH=NO BUILD_DIR="${BUILD_DIR}" mv ${BUILD_DIR}/${CONFIGURATION}-iphonesimulator/${LIB_NAME} $@ clean: rm *.a rm -rf *.framework
最後に$ make
で framework を作り直します。
Swiftで実装する
Swift の実装は下記のようにしました。
import UIKit import HOGEFuga class ViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. HOGEFugaService.show() } }
ビルドしてみます。今度は成功しました!
実際に処理も成功し、ログが吐かれているのが確認できました。
以上、 Objective-C で書かれた framework のSwift対応でした。
iOSの色系ライブラリ決定版? Chameleon
一人でアプリを作る時に、いわゆるビジュアルのデザインや色使い周りが適当になっちゃう人の救世主的なライブラリを見つけたので紹介。
Chameleonでは何ができるのか?
いい感じの色を簡単にアプリに設定できる
提供する機能
- 100% Flat & Gorgeous
- 厳選された24のフラットなカラーがある
- Flat Color Schemes
- カラースキームを決める時に使える
- Contrasting Text
- 背景色に合わせた文字色を勝手に選んでくれる
- Themes
- 1行のコードでいい感じの色合いをアプリに設定できる
- Colors From Images
- 画像からカラースキームを抽出できる
- Gradient Colors
- グラデーションをカラー
- 方向もある
- グラデーションをカラー
動作環境
- Xcode6.3以降
- ObjC -> iOS7
- Swift -> iOS8
導入
CocoaPodsとCarthageの両方に対応しています。 今回はCarthageでインストール。
$ echo 'github "ViccAlexander/Chameleon"' > Cartfile $ carthage update --platform iOS
これで準備完了。
使ってみる
グローバルテーマを使う
setGlobalThemeUsingPrimaryColor
を利用するとコードを一行書くだけで、コンポーネント群の色を一括指定することができます。
Chameleon.setGlobalThemeUsingPrimaryColor(FlatMint(), withSecondaryColor: FlatBlue(), andContentStyle: UIContentStyle.Contrast) self.view.backgroundColor = FlatYellow()
上記はメインカラーをFlatMint()
、セカンダリカラーをFlatBlue
、ビューの背景色をFlatYellow
に指定しています。
フラット間出てますね!
メインとセカンダリのカラー指定は、特定のコンポーネントに作用して色を変更するようです。
コードを読んだ感じ、下記コンポーネント郡はセカンダリ指定時に色が変更されるようです。
- UIButton
- UIProgressView
- UISlider
- UISwitch
ちなみにContentStyle
をContrast
にすると、テキスト系コンポーネントの色を見やすい色に勝手にしてくれて便利でした。
ただし、よしなにしてくれるのは指定した色がChameleon
が提供しているFlat & Gorgeousな色の場合のみのようです。
補色を使う
Chameleon
には補色を返す機能があります。
上記のグローバルテーマでセカンダリカラー等で補色を返してくれるComplementaryFlatColorOf()
を使うといい感じになりそうでした。
let mainColor = FlatPink() let complementaryColor = ComplementaryFlatColorOf(mainColor) Chameleon.setGlobalThemeUsingPrimaryColor(mainColor, withSecondaryColor: complementaryColor, andContentStyle: .Contrast)
こちらに関しては、下記のようにFlat & Gorgeous以外の色でも利用可能でした。
let mainColor = UIColor.redColor() // 赤色 let complementaryColor = ComplementaryFlatColorOf(mainColor) // 青緑色
色抽出を使う
Chameleon
には下記のように、画像からカラースキーマを抽出する機能もあります。
let averageColor = AverageColorFromImage(UIImage("someImageName")!)
画像コンテンツの色ベースでナビゲーションの色を替えるみたいなアプリを見たことがある気がするので、似たようなことをいくつかやってみました。
ナビゲーションバーの色がいい感じに変わってる気がします、が、素材のか違いがよくわからないですね。
違いを見るために赤系も入れてみました。
Storyboard用アドオン
Storyboard用のパレットがこちらからダウンロードできるので、気に入った人は使ってみると良いかと思われます。
小並感
アプリの色設定をこれで一本化する前提なら結構使えるかなと思います。実際にアプリを作りたいなぁ。
一方、機能が盛りだくさんでただのUIColor
クラス拡張ではないので、色指定をHexでやりたいくらいの思いの方にはオススメできなさそうです。
あくまでも、色やデザイン周りを簡単に設定したかったり、とりあえずモックアップを作る時とかに使える感じかなぁ。
MAC OSXでsedした時の sed: RE error: illegal byte sequence エラー
なぜかよくわからんが、OSXでsedを使っているとたまにタイトルのエラーに遭遇することがちょこちょこあります。
対処方法はロケールをC(POSIX)に指定することらしいけれど、そもそもなぜこれが発生するのかよくわからなかったので調べてみました。
発生した環境
今回このエラーが発生したのは、特定のディレクトリ以下にあるファイル内の文字列を一括置換しようとしたタイミングでした。 コマンドで言うとこんな感じ。
$ find . -type f | xargs sed -i .bak s/hoge/fuga/
エラーとしてはタイトルの通りsed: RE error: illegal byte sequence
となっていました。
改めて対象ファイルを見てみる
実際のものは見せられないので、それっぽい再現ですがこんな感じでした。
$ ls -la total 32 drwxr-xr-x 5 t-sei 2125026295 170 1 13 23:21 . drwx------+ 24 t-sei 2125026295 816 1 13 23:17 .. -rw-r--r-- 1 t-sei 2125026295 6149 1 13 23:19 .DS_Store -rw-r--r-- 1 t-sei 2125026295 5 1 13 23:20 fuga.txt -rw-r--r-- 1 t-sei 2125026295 5 1 13 23:20 hoge.txt
上から三番目に何か怪しい奴が居ますね。彼をsed
に投げてみます
$ sed s/hoge/fuga/ .DS_Store sed: RE error: illegal byte sequence
ビンゴ!さっそく犯人を見つけました。やはりOSXユーザは.DS_Store
とうまく付き合う方法を学ばねばならないようです。
なぜsed
に.DS_Store
を渡すとエラーになるのか?
エラー文言を見る限り、不正なバイト列が渡ってきてるんだよねという話のようです。 しかし一方で、ロケールの設定を変更することでこのエラーは発生しなくなりますので、なぜなのか調べてみました。
とりあえずググってみると、UNIX WARE7のsedページでそれっぽい記述を見つけました。
sed は 環境変数 LC_CTYPE (environ(5) の LANG を参照) に指定されたロケールに従って、script ファイル中の注釈の補助コードセットを処理します。 ただし、後述する y コマンドに関しては例外です。ed(1) で説明しているように、正規表現ではパターン検索をバイト単位ではなく文字単位で行います。
なるほど、LC_ALL
(LC_CTYPE
)の設定を変えることで挙動が変わる理由がわかりました。
つまり、OSXのデフォルトロケール設定のsed
では.DS_Store
のようなバイナリファイルを解釈出来ないということのようです。
ちなみにLC_CTYPE
環境変数はOpenGroupのオンラインヘルプによると、
文字などのテキストデータのバイトシーケンスの解釈や文字の分類をする際に参照されるようです。
(追記)挙動と環境変数に関してはOpenGroupの sedページ見るのが一番よさそうだった。
私の環境では下記のようになっていました。
$ locale LANG="ja_JP.UTF-8" LC_COLLATE="ja_JP.UTF-8" LC_CTYPE="ja_JP.UTF-8" LC_MESSAGES="ja_JP.UTF-8" LC_MONETARY="ja_JP.UTF-8" LC_NUMERIC="ja_JP.UTF-8" LC_TIME="ja_JP.UTF-8" LC_ALL=
ja_JP.UTF-8
を期待しているところにバイナリファイルである.DS_Store
を叩きこまれて解釈出来ずに困ってしまったということでした。
今後、同様のエラーが出た時に参考にしていただければ幸いです。
Server-side Swift, Perfectを使ってHelloWorld ~HTTP Server編~
あけましておめでとうございます。 前回のSwiftのLinux環境を作った記事の続きです。
今回のゴールはSwiftのフレームワークであるところのPerfectを用いてHTTPレスポンスを返すところがゴールになります。
環境等は前回の続きです。
とりあえずログイン。 今回はgitを利用するので、鍵等はssh-agentを利用していきます。
$ ssh-add ~/.ssh/github用key $ vagrant ssh
サーバを立ててみる
Perfectのダウンロード
$ git clone git@github.com:PerfectlySoft/Perfect.git
Perfectを利用する場合、まずはサーバとライブラリのビルドをする必要があります。
$ cd Perfect/ $ make
Makefile
の中身はこちら。
どうやらPerfectLib
のMakefileとPerfectServer
のMakefileを叩いているようです。
ちなみにPerfectServer
のMakefile
は下記です。
# Makefile for Perfect Server TARGET_FCGI = perfectserverfcgi TARGET_HTTP = perfectserverhttp DEBUG = -g -Onone -Xcc -DDEBUG=1 OS = $(shell uname) SWIFTC = swift SWIFTC_FLAGS = -frontend $(DEBUG) -c -module-cache-path $(MODULE_CACHE_PATH) -emit-module -I /usr/local/lib -I ../PerfectLib/linked/LibEvent \ -I ../PerfectLib/linked/OpenSSL -I ../PerfectLib/linked/ICU -I ../PerfectLib/linked/SQLite3 -I ../PerfectLib/linked/LinuxBridge MODULE_CACHE_PATH = /tmp/modulecache Linux_SHLIB_PATH = $(shell dirname $(shell dirname $(shell which swiftc)))/lib/swift/linux SHLIB_PATH = -L$($(OS)_SHLIB_PATH) LFLAGS = $(SHLIB_PATH) -g -lFoundation -lswiftCore -lswiftGlibc /usr/local/lib/PerfectLib.so -Xlinker -rpath -Xlinker $($(OS)_SHLIB_PATH) all: modulecache $(TARGET_FCGI) $(TARGET_HTTP) install: all ln -sf `pwd`/$(TARGET_FCGI) /usr/local/bin/ ln -sf `pwd`/$(TARGET_HTTP) /usr/local/bin/ modulecache: @mkdir -p $(MODULE_CACHE_PATH) $(TARGET_FCGI): $(TARGET_FCGI).o clang++ $(LFLAGS) $@.o -o $@ $(TARGET_HTTP): $(TARGET_HTTP).o clang++ $(LFLAGS) $@.o -o $@ $(TARGET_FCGI).o: main_fcgi.swift $(SWIFTC) $(SWIFTC_FLAGS) main.swift $< -o $@ -module-name $(subst .o,,$@) -emit-module-path $(subst .o,,$@).swiftmodule $(TARGET_HTTP).o: main_http.swift $(SWIFTC) $(SWIFTC_FLAGS) main.swift $< -o $@ -module-name $(subst .o,,$@) -emit-module-path $(subst .o,,$@).swiftmodule clean: @rm *.o
どうやらswift -frontend
でオブジェクトファイルを生成し、clang++
でコンパイルしているようです。
make
すると、perfectserverfcgi
とperfectserverhttp
が生成されて、make install
するとコマンドのパスを通すところまでやってくれます。
上記コマンドをコンパイルするときにPerfectLib
に依存しているため、そちらのビルドが先に必要なようです。ナルホド。
これでサーバを立てる準備が整いましたので、とりあえずHTTPサーバを起動してみます。
下記の通りデフォルトでは8181
番ポートに割り当てられています。
$ Perfectserverhttp Current working directory: /home/vagrant/Perfect/PerfectServer Starting HTTP server on 0.0.0.0:8181
とりあえずリクエストを投げてみます。
curl localhost:8181 The file "/" was not found.
HTTPレスポンスが返ってきました。どうやら動いてるっぽいですね。
次に出力用のHTMLを用意して動作を確認します。
main_http.swiftファイルを見るとわかりますが、デフォルトのドキュメントルートはプロジェクト直下のwebroot/
になっていました。
PerfectServerはHTMLあるいは画像リソース、Mustache
のテンプレートを扱うことができますので、とりあえず適当なHTMLを用意。
$ echo 'Hello World!!' > webroot/index.html
実際にHTMLを解釈してレスポンスが返ってくるか確認しましょう。
$ curl localhost:8181 Hello World!!
正しくレスポンスが返ってきていそうです。非常に簡単ですね。
ルーティング用のスクリプトを書いてみる
PerfectServerのREADMEを読む限り、ハンドラー用のModuleを作成して利用する機能があるようです。
ModuleはPerfectServerModuleInit
という関数をもった共有ライブラリとして作成し、PerfectLibraries
ディレクトリに配置することで、PerfectServerに登録されます。
PerfectServer.swift
ファイルのこの辺りを見るとその様子が伺えます。
また、実際にルーティングを行っているサンプルがこちらにあります。
下記がルーティングの登録部分の抜粋です。
PerfectServerModuleInit()
内でRouting.Handler.Register()
を呼び出して登録し、Routing
のRoutes
(RoutMap構造体
)に対して、パスと振る舞いを表すクラスを登録することで、動作を定義することができます。
public func PerfectServerModuleInit() { // Install the built-in routing handler. // Using this system is optional and you could install your own system if desired. Routing.Handler.registerGlobally() Routing.Routes["GET", ["/", "index.html"] ] = { (_:WebResponse) in return IndexHandler() } Routing.Routes["/foo/*/baz"] = { _ in return EchoHandler() } Routing.Routes["/foo/bar/baz"] = { _ in return EchoHandler() } Routing.Routes["GET", "/user/{id}/baz"] = { _ in return Echo2Handler() } Routing.Routes["POST", "/user/{id}/baz"] = { _ in return Echo3Handler() } // Check the console to see the logical structure of what was installed. print("\(Routing.Routes.description)") }
振る舞いを表すクラスは、RequestHandler
を継承しhandleRequest()
を実装したものとなります。
リクエストを表すWebRequest
のオブジェクトが渡ってくるので、それを用いて特定の処理を行い、WebResponse
のrequestCompletedCallback()
を呼び出すことでレスポンスを返す事ができます。
サンプルではレスポンスボディに文字列を含めて返すものを実装していました。
class EchoHandler: RequestHandler { func handleRequest(request: WebRequest, response: WebResponse) { response.appendBodyString("Echo handler: You accessed path \(request.requestURI()) with variables \(request.urlVariables)") response.requestCompletedCallback() } }
上記サンプルを参考にして、Hello World
を返す処理を実装してみます。
まずはModuleのソースを用意します。今回はこんな感じで実装しました。
import PerfectLib public func PerfectServerModuleInit() { Routing.Handler.registerGlobally() Routing.Routes["GET", "/"] = { _ in return IndexHandler() } print("\(Routing.Routes.description)") } class IndexHandler: RequestHandler { func handleRequest(request: WebRequest, response: WebResponse) { response.appendBodyString("Hello World!! with Request Handler\n") response.requestCompletedCallback() } }
次にソースをコンパイルしていきます。
$ swift -frontend -c -module-cache-path /tmp/modulecache -emit-module -I /usr/local/lib -I /home/vagrant/Perfect/PerfectLib/linked/LibEvent -I \ /home/vagrant/Perfect/PerfectLib/linked/OpenSSL_Linux -I /home/vagrant/Perfect/PerfectLib/linked/ICU \ -I /home/vagrant/Perfect/PerfectLib/linked/SQLite3 -I /home/vagrant/Perfect/PerfectLib/linked/LinuxBridge \ "URLHandlers.swift" -o URLRouting.o -module-name URLRouting -emit-module-path URLRouting.swiftmodule $ clang++ -L/home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/lib/swift/linux \ -lFoundation -lswiftCore -lswiftGlibc /usr/local/lib/PerfectLib.so \ -Xlinker -rpath -Xlinker /home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/lib/swift/linux \ -shared URLRouting.o -o URLRouting.so
結果、下記のファイル群が生成されました。
drwxrwxr-x 2 vagrant vagrant 4096 Jan 8 02:23 ./ drwxrwxr-x 3 vagrant vagrant 4096 Jan 8 01:31 ../ -rw-rw-r-- 1 vagrant vagrant 395 Jan 8 02:23 URLHandlers.swift -rw-rw-r-- 1 vagrant vagrant 9624 Jan 8 02:23 URLRouting.o -rwxrwxr-x 1 vagrant vagrant 20003 Jan 8 02:23 URLRouting.so* -rw-rw-r-- 1 vagrant vagrant 27924 Jan 8 02:23 URLRouting.swiftmodule
このうち、共有ファイルであるところのURLRouting.so
をプロジェクト直下の PerfectLibraries
ディレクトリにつっこんでperfectserverhttp
を起動すれば動作が確認できます。
mkdir -p /home/vagrant/Perfect/PerfectLibraries/ cp URLRouting.so /home/vagrant/Perfect/PerfectLibraries/.
サーバを立てなおして動作確認をします
$ perfectserverhttp
$ curl localhost:8181 Hello World!! with Request Handler
作成したモジュールが読み込まれて、文字列の出力が確認できました。
次回はPerfectが採用しているテンプレートエンジンであるところのMustacheあたりを触ってまとめようかと思っています。
Server-side Swift, Perfectを使ってHelloWorld ~環境構築編~
年の瀬ですね。今年のブログ納め(3回しか書いてないけど)をしたいと思います。
皆さん御存知の通り、iOSネイティブアプリケーション開発に利用できる新言語であるところのSwiftが、OSSとして公開されたタイミングでLinux対応をしてくれたわけですが、さらに、サーバサイドのアプリケーションをSwiftで実現するためのフレームワークの開発が現在いろいろと進んでいます。(Taylor、Swiftra、Swiftyなど
その中でも一番注目度が高いのが、Perfectです。 github.com
その他のフレームワークに比べ、機能の多さやドキュメントの充実の仕方が頭一つ抜けている印象を受けます。
今回はこちらのPerfectを使ってHello Worldをするための環境構築について書いていきます。 方法としてはは、MacOSXの上にVagrantを用いてLinux環境を作り、そこにSwiftをインストールする形です。
開発環境を用意する
今回はUbuntuの14.04を利用したいと思います。
イメージの用意とセットアップ。
$ vagrant box add ubuntu14.04 https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box $ mkdir SwiftSandbox $ cd SwiftSandbox $ vagrant init ubuntu14.04 $ vagrant up
うまいこと作れたらUbuntu環境に接続します。
$ vagrant ssh
Swiftの実行環境を作成する
基本的には公式にある通りに進めれば良いと思います。
まずは必要なライブラリなどのインストール。
$ sudo apt-get update $ sudo apt-get install clang libicu-dev
Swift本体と署名をダウンロード。
$ mkdir swift $ cd swift $ wget https://swift.org/builds/ubuntu1404/swift-2.2-SNAPSHOT-2015-12-21-a/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz $ wget https://swift.org/builds/ubuntu1404/swift-2.2-SNAPSHOT-2015-12-21-a/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz.sig
署名確認周り。
$ wget -q -O - https://swift.org/keys/all-keys.asc | gpg --import - $ gpg --keyserver hkp://pool.sks-keyservers.net --refresh-keys Swift $ gpg --verify swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz.sig
本体を解凍してパスを通す。
$ tar xzf swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04.tar.gz $ export PATH=/home/vagrant/swift/swift-2.2-SNAPSHOT-2015-12-21-a-ubuntu14.04/usr/bin:"${PATH}" //ここはとりえあずなので、お好みの場所に置いてください
動かしてみる。
$ swift Welcome to Swift version 2.2-dev (LLVM 46be9ff861, Clang 4deb154edc, Swift 778f82939c). Type :help for assistance. 1> let hello = "world!" hello: String = "world!" 2> print (hello) world!
これでSwiftの実行環境の構築完了です。 次回はPerfectを利用してのアプリケーション構築を行ってみたいと思います。
Xcodeのグループとフォルダ参照の違い
XocdeにSDKをドラッグドロップしたんだけど、なぜか参照がうまくいかないみたいなエラーで悩まされた。 エラーで言うと
Undefined symbols for architecture arm64: "_OBJC_CLASS_$_******", referenced from: type metadata accessor for __ObjC****** in ******.o
こんな感じ。
何かなーと思ってたけど、結論SDKをフォルダ参照で追加していたのが問題だった・・
↑デフォルトは Create group
なんだと思うけど、変わっていた・・
グループとフォルダ参照の違いについてはこちらのスライドですごくよくまとまっているので割愛。
自分でいつのまにか変えてしまっていたっぽいので、ドラッグドロップした時にフォルダの色が青くなってたらご用心ください。
※茶色いディレクトリアイコンがグループで青いディレクトリアイコンがフォルダ参照