Kinx Tips - 実行形式ファイルを作る

# Kinx Tips - 実行形式ファイルを作る

## はじめに

**「見た目は JavaScript、頭脳(中身)は Ruby、（安定感は AC/DC）」** でお届けしているスクリプト言語 [Kinx](https://github.com/Kray-G/kinx)。今回は Tips です。

* 参考
    * 最初の動機 ... [スクリプト言語 KINX（ご紹介）](https://qiita.com/Kray-G/items/ca08b6fb40d15dd0ec76)
        * 個別記事へのリンクは全てここに集約してあります。
    * リポジトリ ... [https://github.com/Kray-G/kinx](https://github.com/Kray-G/kinx)
        * Pull Request 等お待ちしております。  

皆さん、実行ファイルを作る時どうしてますか？ ...そう、普通は C や C++ で書いてコンパイルしますね。もっと簡単に作れるといいですね。

Kinx は独立した exe を作ることはサポートしていませんが、Kinx のワールドの中であれば exe にして単体実行コマンドのように見せかけて実行できるモジュールを作れるようにしています。

その辺を少々。

## コマンド `kxrepl.exe`、`kxtest.exe`

実は v0.13.1 リリースより `kxrepl.exe` と `kxtest.exe` というコマンドが同梱されています。Linux 版では `.exe` はついて無くて `kxrepl` と `kxtest` です。

`kxrepl` を実行してみましょう。

```
$ kxrepl
kinx[  0]> .quit
```

REPL が動作しましたね。

次は `kxtest` です。

```
$ kxtest -v -T declaration.md
Test Cout = 11
Entry: doc/spec/statement/declaration.md
    Suite: Declaration statement
        Case[ 0]: Normal case ................................... successful (  0.10s)
        Case[ 1]: With initializer .............................. successful (  0.09s)
        Case[ 2]: With initializer of expression ................ successful (  0.09s)
        Case[ 3]: Multiple variable declaration ................. successful (  0.10s)
        Case[ 4]: Constant value (1) ............................ successful (  0.07s)
        Case[ 5]: Constant value (2) ............................ successful (  0.10s)
        Case[ 6]: Constant value (3) ............................ successful (  0.07s)
        Case[ 7]: Constant value (4) ............................ successful (  0.07s)
        Case[ 8]: Constant value (5) ............................ successful (  0.09s)
        Case[ 9]: Destructuring assignment (1) .................. successful (  0.09s)
        Case[10]: Destructuring assignment (2) .................. successful (  0.07s)


<Test Result>
    Total Test Cases:       11
        Successful  :       11
        Failed      :        0
        Warning     :        0
```

SpecTest が動作しましたね。

もう一つ、試しにやってみましょう。

```
$ diff -s kxrepl kxtest
Files kxrepl and kxtest are identical
```

**「ファイルは同一です」** というメッセージがでました。というか、そうなんです。この 2 つは **バイナリとして全く同じ** です。

## `--exec` オプションの話

この話の前に、一つ Kinx のオプションに関する情報を書いておきます。`--exec` というオプションです。README にも書いてある通り、このオプションによって以下の 2 つのオプションがサポートされています。

* `--exec:repl` ... REPL を実行する。
* `--exec:specttest` ... SpecTest を実行する。

このメカニズムは、以下の通りになっています。

1. `--exec:xxx` を認識したら、Kinx の実行ファイルのあるフォルダから見て `lib/exec/xxx.kx`、または　`lib/exec/3rdparty/xxx.kx` を探す。
2. そのファイルがあったら、そのファイルがスクリプトファイルとして指定されたとみなして実行する。

なので、REPL と SpecTest はそれぞれ `lib/exec/repl.kx` と `lib/exec/spectest.kx` ファイルを探してそれを実行する、という動作をしているのです。この仕組みによって、REPL と SpecTest の修正はバイナリを修正せずに実施できるようにもなっています。

## kxrepl

さて、kxrepl のバイナリ（＝ kxtest のバイナリ）ですが、どういう作りになっているのでしょう（概ね見当は付くと思いますが）。答えはこうです。

1. 自分自身の実行ファイル名（`name` としましょう）を取得（`argv[0]` から取得できます）。
2. コマンドライン引数の先頭に `--exec:name` を割り込ませて引数リストを更新。
3. Kinx のメインロジックに制御を渡す。

するとどうでしょう。自動的に `lib/exec/name.kx`（または `lib/exec/3rdparty/name.kx`）を参照して実行してくれるのです！

ということで、`kxrepl` は `lib/exec/kxrepl.kx` を、`kxtest` は `lib/exec/kxtest.kx` を自動的に実行してくれるという寸法です。

ん？、そんなファイルあったっけ？はい、追加したんです（＝あります）。中身を見てみましょう。

```javascript:lib/exec/kxrepl.kx
using exec.repl;
```

```javascript:lib/exec/kxtest.kx
using exec.spectest;
```

これだけです。中で `using` するだけのファイルを用意して実現しました。`using` の検索パスに従って `repl.kx` と `spectest.kx` を見つけてくれるので、これで正しく動作します。というわけで、`kxrepl.exe` を `repl.exe` に書き換えても同様に動きます。

ということで、もうお分かりですね。

## オリジナル `exe` を作ろう

オリジナル `exe` ファイルの作り方は以下の通りです。動作には Kinx の dll と各種ライブラリが必要なので、`kinx.exe` と同じ場所に、作成した `exe` を置かなくてはなりませんが、やりたいことをコマンド一発でできるようにはなります。

具体的な例で。

### kxcat.exe

`cat` コマンドみたいなものとして、`kxcat` コマンドを作ってみましょう。指定されたファイルを指定された順に出力します。`cat` コマンドと名前が重ならないように `kxcat` にしておきましょう。一先ずオプションは無しで、ファイルだけ複数受け付けるようにします。

```javascript:lib/exec/3rdparty/kxcat.kx
$$.each {
    // Ignoring the script file name.
    if (_2 > 0) {
        System.print(File.load(_1));
    }
};
```

さて、そうしたら `kxtest.exe` をコピーして名前を変えましょう。Windows でも Linux でもコマンドは違いますが、やりたいことは同じです。

```:Windows
$ copy /y kxtest.exe kxcat.exe
```
```:Linux
$ cp -f kxtest kxcat
```

コピーしたらそのまま `kxcat` コマンドを実行！

```
$ ./kxcat README.md ChangeLog.md
<p align="right">
    <img src="https://github.com/Kray-G/kinx/workflows/Unit%20Test/badge.svg?branch=master"/>
    <img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat"/>
</p>

...(省略)

## V0.1.0 (1st Preview Release)

*   Initial Release.
```

できましたね。

インストールされたもので実行したい場合、Linux では以下の場所に `kinx` コマンドのバイナリがあるので、例えば上記の場合、`kxcat` コマンドは同じ位置に配置してください。`kxrepl`、`kxtest` も同じ場所に配置してあります。

```
$ which kinx kxrepl kxtest
/usr/bin/kinx
/usr/bin/kxrepl
/usr/bin/kxtest
```

## おわりに

C でがっつり書いてコンパイルして実行ファイルを作る、でもいいんですけど、スクリプトでササっと書いたのを実行形式ファイルにしたいですよね。かといって、必要なライブラリとか dll とかを全部含めると結構なサイズになったりするので、ここは割り切って Kinx 自体が存在する前提でコマンド化できるようにしてみました。

これはこれで便利かなー、と思います。

あと、やればすぐできるんですけど、例えば kinx.dll の位置を環境変数で指定したり、オプションで渡したりできると、`.exe` ファイル自体はどこにおいても良くなるのでそのくらいの対応は今後するかも入れません（要望があれば）。`xxx.exe` と同じ場所にある `xxx.kx` を実行するとかね。そのほうが便利かなー。`dll` の場所は何かしらの方法で指定しておく必要はありますが。

ではでは、また次回。