ページ

2016年5月14日土曜日

Swiftの中を見てみる 1 - コンパイルの流れ -


最近Swiftを触っているんですが、中をちゃんと見てなかったんで、
この機会に見てみようかと思います。今回はコンパイルに関してです。

コンパイルの流れ

Xcodeではビルドして実行・・・で簡単に実行できてますが、
その時どのようにコンパイルされ実行されているのかを見てみます。
ざっくりとしたビルド時の流れとしては上から順に、
  • Frontend
    C/C++、Haskell、Swiftなどで書かれたソースを構文解析し
    LLVM IR という中間コードを生成する
  • LLVM Optimizer
    様々な最適化を行う
  • Backend
    特定のマシンに基づいた機械語を生成
という流れになっています。
LLVMとは?
任意のプログラミング言語に対応可能なコンパイラ基盤
JavaとJava VMのように仮想マシンをターゲットにした中間コードを出力し、
特定のマシンの機械語に変換します。

Frontend

C/C++, Objective‑CのFrontendにClangというものがあります。
これはAppleがMacOSやiOS用に作成したものです。
コンパイルの流れとしては、、
  • ソースコード -> 抽象構文木(Abstract Syntax Tree、AST) というものに変換
  • LLVM IRを生成
  • LLVM IR -> 機械語を生成
という流れになってます。
Swift用のFrontend
SwiftではClangをより良くして以下のような流れになってます。
  • Swiftのソースコード -> ASTに変換
  • SIL(Swift intermediate Language)(LLVM IRのSwift版)を生成
  • SILからLLVM IRを生成 -> 最後に機械語が生成
Clangに比べてSILのフェーズが追加されています。

実験

Swiftではコンパイルのオプションで上で見てきた、ASTやSILを
出力してくれるオプションが存在します。
そのオプションを使って色々表示させてみたいと思います。
簡単なサンプルのコンパイル&実行
// hello.swift
print(“hello!!”)
↑をhello.swiftとして保存し、
$ swiftc hello.swift # コンパイル
$ ./hello # -> hello!!
コンパイル&実行します。
AST表示
今度はサンプルのASTを表示させてみたいと思います。
$ swift -dump-ast hello.swift
(source_file
  (top_level_code_decl
    (brace_stmt
      (call_expr type='()' location=hello.swift:1:1 range=[hello.swift:1:1 - line:1:21] nothrow
      ・・・
なにやらずらずらっと表示されたかと思います。
SIL表示
次はSILを表示してみます。
$ swiftc -emit-silgen hello.swift | xcrun swift-demangle
sil_stage raw

import Builtin
import Swift
import SwiftShims
・・・
LLVM IR表示
そしてLLVM IR
$ swiftc -emit-ir hello.swift
; ModuleID = '-'
target datalayout = ・・・
アセンブリ表示
最後にアセンブリ表示
$ swiftc -emit-assembly hello.swift
.section    __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 9
.globl    _main
.align    4, 0x90
_main:
.cfi_startproc
・・・
こうしてみると複数のフェーズを経由している事が分かります。
またSwiftからObjective-Cが使えるのも同じLLVM基盤を採用しているからなんですね。


2016年5月11日水曜日

福岡県篠栗町の南蔵院 - 日本一の涅槃像 -


今回はいつも車で通る度に気になっていた、
福岡県篠栗町の南蔵院に行ってきました^^



外から見たら分からなかったんですが、中に入るとかなり広いです。


中に入ると招き猫の像や、不動明王像などなど沢山の像が。




中にはこんなかわいい像も^^



そして自然たっぷりな場所なので、ゆったりとした時間を楽しめます^^


そして、なんといっても日本一の涅槃像(ねはんぞう)にはびっくりしました!!
全長41メートルということらしいです^^;




有料ですが、涅槃像の中も見学できるみたいです。

今回初めて行ってみましたが、なかなか面白くて癒されるところでした^^

・南蔵院

2016年5月8日日曜日

PhoneGapを使用する 4 - SwiftでPlugin作成 Camera実装 -


前回の続きをやっていきたいと思います。
前回はプラグインを経由してコンソールに文字を出力するだけでしたが、
今回はカメラの撮影画面を表示させてみたいと思います。
ちなみに現時点のプラグインのディレクトリ構成は、
├── custom-plugin-camera
│   ├── plugin.xml
│   ├── src
│   │   └── ios
│   │       └── CustomCamera.swift
│   └── www
│       └── CustomCamera.js
上のようになっています。

カメラ用のカスタムUIViewController作成


カメラ撮影用の画面を表示するカスタムUIViewControllerを作成します。
名前はCustomCameraViewController.swiftとし、
plugins/custom-plugin-camera
├── plugin.xml
├── src
│   └── ios
│       ├── CustomCamera.swift
│       └── CustomCameraViewController.swift ☆
└── www
    └── CustomCamera.js
src/ios配下に作成します。またplugin.xmlに
<source-file src="src/ios/CustomCameraViewController.swift" />
を追加します。
次にCustomCameraViewController.swiftを実装します。
import UIKit
import AVFoundation

class CustomCameraViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        // セッションの作成.
        let session = AVCaptureSession()

        // デバイス一覧の取得.
        let devices = AVCaptureDevice.devices()

        // バックカメラをmyDeviceに格納.
        var device : AVCaptureDevice!
        for d in devices{
            if(d.position == AVCaptureDevicePosition.Back){
                device = d as! AVCaptureDevice
            }
        }

        let videoInput: AVCaptureInput!
        // バックカメラからVideoInputを取得.
        do {
            videoInput = try AVCaptureDeviceInput.init(device: device)
        } catch {
            videoInput = nil
        }

        // セッションに追加.
        session.addInput(videoInput)

        // 出力先を生成.
        let imageOutput = AVCaptureStillImageOutput()

        // セッションに追加.
        session.addOutput(imageOutput)

        // 画像を表示するレイヤーを生成.
        let videoLayer : AVCaptureVideoPreviewLayer = AVCaptureVideoPreviewLayer.init(session: session)
        videoLayer.frame = self.view.bounds
        videoLayer.videoGravity = AVLayerVideoGravityResizeAspectFill

        // Viewに追加.
        self.view.layer.addSublayer(videoLayer)

        // セッション開始.
        session.startRunning()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }
}
UIViewControllerクラスを継承したカスタムクラスを作成し、
画面が表示されたタイミングで実機の背面カメラの撮影画面を表示しています。
次にCustomCamera.swiftから呼ぶようにします。
import Foundation

@objc(CustomCamera) class CustomCamera : CDVPlugin {

    func start(command: CDVInvokedUrlCommand) {        
        let v : CustomCameraViewController = CustomCameraViewController()
        self.viewController.addChildViewController(v)
        self.viewController.view.addSubview(v.view)
    }
}
CDVPluginを継承しているのでself.viewController経由で
ViewControllerの追加、Viewの追加を行っています。

検証


ここまで出来たら早速動かしてみたいと思いますが、、
その前に・・プラグインを更新した場合、phonegap cordova buildしても
プラグインが反映されない場合があるので、
$ phonegap cordova platform rm ios
$ phonegap cordova platform add ios
上のように一度iosのプロジェクトを削除し、再度作成することでキレイに反映されます。
※他にもしかしたらいい方法があるのかもしれませんが・・
今回はカメラを使うので、実機で確認します。
platforms/ios/XXXX.xcodeprojを開き、Deviceに実機を選択し実行します。
※ Xcode7以上でないと実機でデバッグはできません。

また、実行時には設定が必要な場合があります。iphoneの場合、
設定 > 一般 > プロファイルとデバイス管理でデベロッパAPPを選択し認証します。
 
実行して画面全体に背面カメラの撮影画面が表示されていれば成功です^^

2016年5月4日水曜日

PhoneGapを使用する 3 - SwiftでPlugin作成 -


今回はPhoneGapの特徴であるPluginをSwiftで作成してみたいと思います。
とりあえず今回はiOSをターゲットにしたPluginを作成します。
Pluginとは?
PhoneGapのプラグインでは、WebViewからネイティブの機能を使えるようにしたものです。

Goal


プラグインを経由して、ネイティブ側でカメラを起動する所までやってみたいと思います。
今回はPlugin経由でネイティブ内で実行する所を確認し、次回カメラ起動をやってみたいと思います。

ディレクトリとファイルを作成


まずはカスタムプラグイン用のディレクトリを作成します。
$ mkdir plugins/custom-plugin-camera
次にplugin.xml、src/ios/CustomCamera.swift, www/CustomCamera.jsを作成します。
ここまでのディレクトリ構成は
plugins
│
.
.
.
├── custom-plugin-camera
│   ├── plugin.xml
│   ├── src
│   │   └── ios
│   │       └── CustomCamera.swift
│   └── www
│       └── CustomCamera.js
│
├── android.json
├── fetch.json
└── ios.json
こんな感じです。

プラグイン実装


次にplugin.xmlを以下のように修正します。
<?xml version="1.0" encoding="UTF-8"?>
<plugin xmlns="http://apache.org/cordova/ns/plugins/1.0"
    id="custom-plugin-camera"
    version="0.1.0">
    <name>CustomCamera</name>
    <description>Custom Camera Plugin</description>
    <license>Apache 2.0</license>
    <keywords>camera</keywords>

    <!-- ① -->
    <js-module src="www/CustomCamera.js" name="CustomCamera">
        <clobbers target="CustomCamera" />
    </js-module>

    <!-- ② -->
    <platform name="ios">
        <config-file target="config.xml" parent="/*">
            <feature name="CustomCamera">
                <param name="ios-package" value="CustomCamera" />
            </feature>
        </config-file>

        <js-module src="www/CustomCamera.js" name="CustomCamera">
            <clobbers target="CustomCamera" />
        </js-module>

        <source-file src="src/ios/CustomCamera.swift" />
    </platform>
</plugin>
① 
ここではプラグインを使う時に呼ばれるjsのモジュールを定義します。
ちなみに<clobbers target="CustomCamera" />は、プラグインを使う際に
CustomCameraでアクセスができるようにする為のものです。
例えば、プラグインでecho()メソッドが定義されていた場合、
CustomCamera.echo();
でメソッドが実行できます。
② 
ここではiosプラットフォームに関するプラグインの設定になります。
CustomCamera.jsを以下のように実装します。
var exec = require('cordova/exec');

var CustomCamera = {

  start: function() {
    exec(null, null, 'CustomCamera', 'start', []);
  }
};
module.exports = CustomCamera;
次にCustomCamera.swiftを実装します。
import Foundation

@objc(CustomCamera) class CustomCamera : CDVPlugin {

    func start(command: CDVInvokedUrlCommand) {
        print("exec from plugin")
    }
}
今回はただネイティブ側でprintするだけの実装です。
最後にplugins/ios.jsonに作成したプラグインを追加します。
    .
    .
    .
    "cordova-plugin-whitelist": {
        "PACKAGE_NAME": "xxxxxxxxx"
    },
    "custom-plugin-camera": {
        "PACKAGE_NAME": "xxxxxxxxx"
    }
iOSプラットフォームを追加し、作成されたxcodeprojファイルを開きます。
$ phonegap cordova platform add ios
$ open platforms/ios/XXXX.xcodeproj
Xcode上で実行し、コンソールにexec from pluginが表示されていれば成功です。


2016年4月29日金曜日

PhoneGapを使用する 2 - PhoneGap + Grunt で自動化 -


最近仕事でお世話になっているPhoneGap
タイトルがPhoneGapを使用する 2 となってますが、
実は随分前にPhoneGapを触っていた事があります。
その時から全然触ってなかったら、npmのバージョンが
$ npm -v
1.4.28
古い・・・古いすぎる!!
早速バージョンアップ
$ npm update -g npm
$ npm -v
3.8.7
すごいバージョンの差 ^^;
そんなこんなで今回はPhoneGapをタスクツールのGruntと
組み合わせてやってみたいと思います。

ちなみに・・・

PhoneGapと並んでよく出てくるワードでCordovaというのがあります
両者の違いですが、公式のFAQでは
PhoneGap is an open source distribution of Cordova. Think about Cordova’s relationship to PhoneGap like WebKit’s relationship to Safari or Chrome.
となっています。
CordovaをWebkitに例えるならば、PhoneGapはWebKitから派生したSafariやChromeみたいなもので、PhoneGapはCordovaを内包してるようなイメージです。
実際、PhoneGapはCordovaの全ての機能が使えます。

環境セットアップ

※ ここではPhoneGapのコマンドラインツールであるPhoneGap CLIを使用します!!
↑のnpmのアップデートをやりましたが、最初からセットアップする場合、
Node.jsのインストールが必要です。
次に最新のPhoneGap CLIをインストールします。
$ npm install -g phonegap@latest
プロジェクト作成から実行まで

次にプロジェクト作成〜実行まで一気にやりたいと思います。
# プロジェクトの作成
$ phonegap create <path> --id <id> --name <projectname>
# プラットフォームの追加(ios や androidなど)
$ phonegap cordova platform add <ios/android> --save
# ビルド
$ phonegap cordova build <ios/android>
# 実行
$ phonegap cordova run <ios/android>
※ と思ったらphonegapコマンドで以下のようなエラーが・・・
module.js:340
    throw err;
          ^
Error: Cannot find module 'phonegap-build-api'
    at Function.Module._resolveFilename (module.js:338:15)
    at Function.Module._load (module.js:280:25)
    at Module.require (module.js:364:17)
    at require (module.js:380:17)
    at Object.<anonymous> (/usr/local/lib/node_modules/phonegap/node_modules/phonegap-build/lib/phonegap-build/login.js:6:14)
    at Module._compile (module.js:456:26)
    at Object.Module._extensions..js (module.js:474:10)
    at Module.load (module.js:356:32)
    at Function.Module._load (module.js:312:12)
    at Module.require (module.js:364:17)
やっぱりアップデートが極端だったから?
色々試してもダメだったので、phonegapを再インストールしてみ他ところエラーがなくなりました。
$ sudo npm uninstall -g phonegap
$ npm install -g phonegap
エラーがなくなった所でビルドと実行をGruntのタスク化にしてみたいと思います。
タスク化するにあたりgruntとgrunt-execをインストールします。
$ npm install -g grunt-cli
$ npm install grunt --save-dev
$ npm install grunt-exec --save-dev
プロジェクトのビルドと実行をタスク化してみたいと思います。
module.exports = function(grunt) {
  grunt.loadNpmTasks('grunt-exec');

  grunt.initConfig({
   exec: {
      build: {
         command: 'phonegap cordova build'
      },
      run: {
         command: 'phonegap cordova run'
      }
   }
  });

  grunt.registerTask('default', ['exec:build', 'exec:run']);
};
これで
$ grunt
とするとビルドと実行が可能になりました^^。