NativeCallを使ったモジュールの作り方: p6-Geo-Hashを例に

こんにちは、20日目の投稿になります。

今日はNativeCallを使ったモジュールの作り方を紹介しようと思います。

# はじめに

私は今年[libgeohash](https://github.com/simplegeo/libgeohash)のPerl6用のバインダを作りました。そのときのノウハウを紹介したいと思います。
ただし、公式のドキュメントがまだまだ不十分というのもあり、[要出典]な部分がたくさんあるかもしれません。

# geohashとは

緯度、経度をひとつのbase32の符号で表現したものです。
base32の符号を01のビット表現に直すと、左から見たときの偶数位置のビットが経度を、奇数位置のビットが緯度を示しています。

# 便利モジュール

## App::Mi6

[mi6](https://github.com/skaji/mi6)を使うと簡単にベースになるファイル群が作れます:

```
$ mi6 new Geo::Hash
$ mv Geo-Hash p6-Geo-Hash
```

+ ちにみに、mi6の他の選択肢としては[Ddt](https://github.com/kalkin/Ddt)とか[miroku](https://github.com/noqisofon/p6-miroku)があります。

## LibraryMake

+ VM(e.g. MoarVM, JVM)をビルドしたときのオプションを流用することで、libgeohashのビルドを行います。
+ 上記のプロセスを簡単に行うためのライブラリが[LibraryMake](https://github.com/retupmoca/P6-LibraryMake)です。
+ `process-makefile`でMakefileの処理を、 `get-vars`でVMのビルド時のオプションを取得することができます。

# 各ファイルについて

## ファイル構成

+ mi6で生成されたファイルとは別に追加で下記のファイルが必要です
  + ルートにBuild.pm
  + src以下にlibgeohash関連ファイル

```
$ tree .
.
├── Build.pm
├── LICENSE
├── META6.json
├── README.md
├── bin
├── lib
│   └── Geo
│       ├── Hash
│       │   └── Coord.pm6
│       └── Hash.pm6
├── src
│   ├── Makefile.in
│   ├── geohash.c
│   ├── geohash.h
│   └── geohash_test.c
└── t
    └── 01-basic.t

6 directories, 11 files
```
と`.travis.yaml`

## META6.json

+ 記述するためのポイントは2点あります
  + build-dependsに忘れないようにLibraryMakeを追加しましょう: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/META6.json#L5-L7 
  + resourcesにビルド後のシェアードオブジェクトやダイナミックライブラリ(e.g. `.so`, `.dylib`, `.dll`なファイル)を入れたいディレクトリへのパスを指定しましょう: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/META6.json#L18

## Build.pm

+ この`Build.pm`はtravis-CIにおける`zef build .`の実行時に呼び出されます: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/.travis.yml#L11

+ 記述するためのポイントは2点あります
  + 先ほどresourcesで指定したディレクトリを生成するようにしましょう: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/Build.pm#L8-L9
  + `%vars<geohash>`変数にどの種類のライブラリ(e.g. `.so`)を生成するのかを代入します: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/Build.pm#L7
  + `process-makefile`の第二引数にこの`%vars`を渡すことで、Makefile.inで使えるようにします(※): https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/Build.pm#L10

## src/Makefile.in

+ 記述するためのポイントは2点あります
  + [LibraryMakeで取得された変数](https://github.com/retupmoca/P6-LibraryMake/blob/master/lib/LibraryMake.pm6#L106-L191)を使うことができます。この変数はVMをビルドしたときのオプションなので自分で環境を判定して分岐を書く(e.g. OSXかLinuxか)必要はありません: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/src/Makefile.in#L6-L7 
  + `%geohash%`でBuild.pmにおいて`%vars`に格納しておいた値を参照できます (リンクは同上)

## .travis.yaml

+ 記述するためのポイントは一つだけです
  + scriptのところに`zef build .`を追加しましょう: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/.travis.yml#L8
     + こうすることでビルド時に`Build.pm`が呼び出され、シェアードオブジェクトやダイナミックライブラリを生成する処理が行われるようになります。

## lib/GeoHash.pm6

+ 記述するためのポイントは一つだけです
  + META6.jsonで記述した`resources`の内容を`%?RESOURCES<libraries/geohash>.Str`を使うことで取得することができます: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/lib/Geo/Hash.pm6#L7 
    + `is native`トレイトに対して上記の値を渡すことで、C言語側の関数を呼び出すことができるようになります: https://github.com/titsuki/p6-Geo-Hash/blob/0.0.1/lib/Geo/Hash.pm6#L9

+ ちなみにNativeCallの文法それ自体については次のページを参照ください: https://docs.perl6.org/language/nativecall

# リリース

[PAUSEアカウント](https://pause.perl.org/pause/query?ACTION=pause_04about)を持っている場合、CPANに対してリリースすることができます。

まず、あなたのホームディレクトリで`.pause`ファイルを作りましょう。
`<USER>`にはPAUSEでのIDを、`<PASSWORD>`にはPAUSEでのパスワードを入れてください:

```
user <USER>
password <PASSWORD>
```

そして、uploadしましょう。uploadコマンドでtar.gzファイルの生成まで行ってくれます。
(※すでにGeo-Hashの0.0.1はアップロード済みなので'N'でAbortしています。実際に上げるときは'y'を押してください):

```
$ mi6 upload
Use of Nil in string context
  in block  at lib/Geo/Hash.pm6 line 7
==> Execute Build.pm
gcc -shared -fPIC geohash.c -o /home/itoyota/Program/p6-Geo-Hash/resources/libraries/libgeohash.so
gcc geohash_test.c geohash.c  
./a.out
rm a.out
Created Geo-Hash-0.0.1.tar.gz
Are you sure to upload Geo-Hash-0.0.1.tar.gz to CPAN? (y/N) N
Abort.
```

以上、20日目の投稿でした。

※ もしかすると`%vars<SO>`に同じ内容が格納されているかもしれません[要出典]、`SO`というネーミングを使ってしまうと可読性の問題もあるのでこの書き方にしています。