rlua で cjson が使いたい

# はじめに

[rlua を使って Rust に Lua言語 を組み込む](https://qiita.com/kyamamoto9120/items/1120f3f7423fe0fb37d5) をやりたくなったので、開発環境を用意してみます。

Rust のソースコードから Lua スクリプトを実行します。スクリプトの中で cjson が使えるようにセットアップします。

<br>

# セットアップ

## ベースの Docker イメージ

手元にあった、rust:1.65 の Docker イメージを使います。

```sh
$ docker run --rm -it -v $(pwd):/mnt/shared rust:1.65 bash
```

以下のディストリビューションになっています。

```sh
# cat /etc/os-release
PRETTY_NAME="Debian GNU/Linux 11 (bullseye)"
NAME="Debian GNU/Linux"
VERSION_ID="11"
VERSION="11 (bullseye)"
VERSION_CODENAME=bullseye
ID=debian
HOME_URL="https://www.debian.org/"
SUPPORT_URL="https://www.debian.org/support"
BUG_REPORT_URL="https://bugs.debian.org/"
```

<br>

## Lua のインストール

Lua 5.3 を使用します。

この記事では、コマンドは `sudo` なしで書いてしまうことにします。

```sh
# apt update
# apt install -y lua5.3 liblua5.3-dev
```

<br>

## Luarocks のインストール

cjson をインストールするために、luarocks を使用します。

現時点の最新版は 3.9.2 です。

以下のサイトの手順に従ってインストールします。

https://github.com/luarocks/luarocks/wiki/Installation-instructions-for-Unix

```sh
# cd ~
# wget https://luarocks.org/releases/luarocks-3.9.2.tar.gz
# tar zxpf luarocks-3.9.2.tar.gz
# cd luarocks-3.9.2
# ./configure --with-lua-include=/usr/include/lua5.3
# make
# make install
```

`apt` でもインストールできますが、現時点ではバージョンが 2.4.2 と古いようなので、ソースコードから 3 系のバージョンをインストールすることにします。

```sh
# apt search luarocks
Sorting... Done
Full Text Search... Done
luarocks/stable 2.4.2+dfsg-1.1 all
  deployment and management system for Lua modules
```

<br>

## lua-cjson のインストール

インストール先フォルダの内容を確認しておきます。

```sh
# ls /usr/local/share/lua/5.3
luarocks
```

インストールします。luarocks を make したフォルダではない場所で実行します。

```sh
# cd ~
# luarocks install lua-cjson
Installing https://luarocks.org/lua-cjson-2.1.0.10-1.src.rock

lua-cjson 2.1.0.10-1 depends on lua >= 5.1 (5.3-1 provided by VM)
gcc -O2 -fPIC -I/usr/include/lua5.3 -c lua_cjson.c -o lua_cjson.o
gcc -O2 -fPIC -I/usr/include/lua5.3 -c strbuf.c -o strbuf.o
gcc -O2 -fPIC -I/usr/include/lua5.3 -c fpconv.c -o fpconv.o
gcc  -shared -o cjson.so lua_cjson.o strbuf.o fpconv.o
No existing manifest. Attempting to rebuild...
lua-cjson 2.1.0.10-1 is now installed in /usr/local (license: MIT)
```

インストール結果を確認します。

```sh
# ls /usr/local/share/lua/5.3
cjson  json2lua.lua  lua2json.lua  luarocks
```

cjson のフォルダが作成されました。

<br>

## Rust プロジェクトの作成

Rust のプロジェクトを作成します。

```sh
# cd /mnt/shared
# cargo init --bin
# cargo add --no-default-features --features system-lua53 rlua@0.18.0
```

Cargo.toml は以下のようになっています。

```toml:Cargo.toml
[package]
name = "rluacjson"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[dependencies]
rlua = { version = "0.18.0", default-features = false, features = ["system-lua53"] }
```

<br>

## ソースコードを記述

`main.rs` の内容を書きます。

```rust
use rlua::prelude::LuaError;
use rlua::Lua;

fn main() {
    println!("Hello, world!");
    match run_main() {
        Ok(_) => {},
        Err(error) => {
            eprintln!("{:?}", &error);
        }
    }
}

fn run_main() -> Result<(), LuaError> {
    let lua = Lua::new();

    lua.context(|context| {
        context.load(r#"
            local cjson = require "cjson"
            print("Hello, lua.")
            local data = cjson.decode('{"greeting":"Hello, cjson."}')
            print(data["greeting"])
        "#).exec()
    })?;
    Ok(())
}
```

<br>

## ビルド

```
cargo build
```

<br>

## 実行

```sh
# cargo run
   Compiling rluacjson v0.1.0 (/mnt/shared)
    Finished dev [unoptimized + debuginfo] target(s) in 0.48s
     Running `target/debug/rluacjson`
Hello, world!
Hello, lua.
Hello, cjson.
```

実行できました。cjson で JSON 文字列の解析と値の参照ができました。

<br>

# あとで苦しまないために

## luarocks を make したフォルダで lua-cjson をインストールした場合

luarocks のインストールを行い、そのフォルダにいる状態で lua-cjson のインストールをしてみます。

```sh
# luarocks install lua-cjson
Installing https://luarocks.org/lua-cjson-2.1.0.10-1.src.rock

lua-cjson 2.1.0.10-1 depends on lua >= 5.1 (5.3-1 provided by VM)
gcc -O2 -fPIC -I/usr/include/lua5.3 -c lua_cjson.c -o lua_cjson.o
gcc -O2 -fPIC -I/usr/include/lua5.3 -c strbuf.c -o strbuf.o
gcc -O2 -fPIC -I/usr/include/lua5.3 -c fpconv.c -o fpconv.o
gcc  -shared -o cjson.so lua_cjson.o strbuf.o fpconv.o
No existing manifest. Attempting to rebuild...
lua-cjson 2.1.0.10-1 is now installed in /root/luarocks-3.9.2/./lua_modules (license: MIT)
```

よく見るとインストール先が `/root/luarocks-3.9.2/...` となっています。

```sh
# ls /usr/local/share/lua/5.3
luarocks
```

期待するパスには何もインストールされませんでした。luarocks コマンドを実行するときのカレントディレクトリに注意が必要です。

<br>

## `--no-default-features` を指定しなかった場合

人から features に「system-lua53」を指定するのだよと聞いたので、そのようにしてみました。

```sh
# cargo add --features system-lua53 rlua@0.18.0
```

```sh
# cargo build
    Updating crates.io index
  Downloaded memchr v2.5.0
  Downloaded bitflags v1.3.2
  Downloaded num-traits v0.2.15
  Downloaded bstr v0.2.17
  Downloaded pkg-config v0.3.26
  Downloaded cc v1.0.79
  Downloaded rlua v0.18.0
  Downloaded autocfg v1.1.0
  Downloaded libc v0.2.139
  Downloaded 9 crates (1.8 MB) in 11.08s
   Compiling rlua v0.18.0
error: failed to run custom build command for `rlua v0.18.0`

Caused by:
  process didn't exit successfully: `/mnt/shared/target/debug/build/rlua-406b6ab30ac28d1e/build-script-build` (exit status: 101)
  --- stderr
  thread 'main' panicked at 'Cannot enable more than one Lua interpreter feature.', /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.18.0/build.rs:21:9
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```

ビルドに失敗しました。

別の動作しているプロジェクトを見たところ、Cargo.toml に `default-features = false` が記述されていたので、指定してみたところ、ビルドができるようになりました。

<br>

## rlua 0.19 の場合

現時点の rlua の最新版は 0.19.4 です。

https://docs.rs/rlua/latest/rlua/index.html

0.19 系を使ってみます。

```sh
# cargo add --no-default-features --features system-lua53 rlua@0.19.0
```

```sh
# cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.11s
     Running `target/debug/rluacjson`
Hello, world!
RuntimeError("[string \"?\"]:2: module 'cjson' not found:\n\tno field package.preload['cjson']\n\tno file '/usr/local/share/lua/5.3/cjson.lua'\n\tno file '/usr/local/share/lua/5.3/cjson/init.lua'\n\tno file '/usr/local/lib/lua/5.3/cjson.lua'\n\tno file '/usr/local/lib/lua/5.3/cjson/init.lua'\n\tno file '/usr/share/lua/5.3/cjson.lua'\n\tno file '/usr/share/lua/5.3/cjson/init.lua'\n\tno file './cjson.lua'\n\tno file './cjson/init.lua'\nstack traceback:\n\t[C]: in ?\n\t[C]: in function 'require'\n\t[string \"?\"]:2: in main chunk")
```

ビルドはできましたが、cjson を見つけられない、というようなエラーになりました。0.18 系でしか動かすことができませんでした...

<br>

## Lua 5.4 の場合

現時点の Lua の最新版は 5.4.4 です。

https://www.lua.org/

5.4 系を使ってみます。

```sh
# apt update
# apt install -y lua5.4 liblua5.4-dev
```

```sh
# cd ~
# wget https://luarocks.org/releases/luarocks-3.9.2.tar.gz
# tar zxpf luarocks-3.9.2.tar.gz
# cd luarocks-3.9.2
# ./configure --with-lua-include=/usr/include/lua5.4
# make
# make install
```

5.3 系と同じ手順でセットアップを進めます。

```sh
# cargo add --no-default-features --features system-lua54 rlua@0.18.0
```

```sh
# cargo build
  Downloaded bitflags v1.3.2
  Downloaded rlua v0.18.0
  Downloaded memchr v2.5.0
  Downloaded bstr v0.2.17
  Downloaded pkg-config v0.3.26
  Downloaded num-traits v0.2.15
  Downloaded autocfg v1.1.0
  Downloaded libc v0.2.139
  Downloaded 8 crates (1.7 MB) in 10.88s
   Compiling rluacjson v0.1.0 (/mnt/shared)
error: linking with `cc` failed: exit status: 1
  |
  = note: "cc" "-m64" "/tmp/rustcsuQeZP/symbols.o" ...
    : (中略)
  = note: /usr/bin/ld: /mnt/shared/target/debug/deps/librlua-64dafec8882567bf.rlib(rlua-64dafec8882567bf.rlua.f15fbb14-cgu.10.rcgu.o): in function `rlua::lua::create_lua':
          /usr/local/cargo/registry/src/github.com-1ecc6299db9ec823/rlua-0.18.0/src/lua.rs:514: undefined reference to `lua_setcstacklimit'
          collect2: error: ld returned 1 exit status
          
  = help: some `extern` functions couldn't be found; some native libraries may need to be installed or have their path specified
  = note: use the `-l` flag to specify native libraries to link
  = note: use the `cargo:rustc-link-lib` directive to specify the native libraries to link with Cargo (see https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorustc-link-libkindname)

error: could not compile `rluacjson` due to previous error
```

ビルドに失敗しました。`undefined reference to ``lua_setcstacklimit'` と出力されていますが、解決できず...

<br>

# おわりに

Lua 5.3 + rlua 0.18 で cjson を使用するコードを実行することができました。

<br>

# 参考

- [Rust に様々なスクリプト言語を組み込む](https://blog.alprosys.com/2020/08/30/rust-script-binders-exercise/)