NetBSDでもLibXo(JSON/XML出力ライブラリ)を試してみる

[NetBSD Advent Calendar 2021](https://qiita.com/advent-calendar/2021/netbsd) 3日目の記事です。
今日は[FreeBSD Advent Calendar 2021](https://qiita.com/advent-calendar/2021/freebsd)の[2日目の記事](https://qiita.com/furandon_pig/items/28b4d83e24dfd4dda115)で紹介した[LibXo](https://wiki.freebsd.org/LibXo)をNetBSDでも動かしてみようと思います。

## LibXoとは？

[FreeBSD Advent Calendar 2021](https://qiita.com/advent-calendar/2021/freebsd)の[2日目の記事](https://qiita.com/furandon_pig/items/28b4d83e24dfd4dda115)でも解説していますが、LibXoはFreeBSD-11以降でサポートされた、任意のデータをテキスト、XML、JSON、HTMLの各フォーマットで出力するライブラリです。

 * [Juniper/libxo](https://github.com/Juniper/libxo)
 * [Building libxo](https://juniper.github.io/libxo/libxo-manual.html#building-libxo)


## LibXoの出力例

LibXoを組み込んだコマンドは出力をJSON/XMLで行うことが可能となり、実際、FreeBSDのベースコマンドのいくつかはLibXoを使用して出力できる状態になっています。

```sh
$ # FreeBSDでの出力例。
$ freebsd-version -ku
13.0-RELEASE
13.0-RELEASE
$
$ ldd `which ps`
/bin/ps:
        libm.so.5 => /lib/libm.so.5 (0x80024d000)
        libkvm.so.7 => /lib/libkvm.so.7 (0x800280000)
        libjail.so.1 => /lib/libjail.so.1 (0x800295000)
        libxo.so.0 => /lib/libxo.so.0 (0x80029d000) ★★★ LibXoを参照している ★★★
        libc.so.7 => /lib/libc.so.7 (0x8002be000)
        libelf.so.2 => /lib/libelf.so.2 (0x8006cf000)
        libutil.so.9 => /lib/libutil.so.9 (0x8006eb000)
```

単に `ps` と実行すると、通常の出力になります。

```sh
$ # 通常のpsコマンドの出力。
  PID TT  STAT    TIME COMMAND
40356 10  I+J  0:04.92 vim hello_libxo.c
84320 10  IsJ  0:00.21 -bash (bash)
41790 11  I+J  0:00.01 /bin/sh /usr/bin/man xo_emit
41801 11  I+J  0:00.00 less
84440 11  IsJ  0:00.09 -bash (bash)
22328  9  IsJ  0:00.01 -bash (bash)
22368  9  I+J  0:00.00 less
22362 15  IsJ  0:00.01 -bash (bash)
22365 15  I+J  0:00.00 less
20087 19  SsJ  0:00.02 -bash (bash)
20131 19  R+J  0:00.00 ps
```

`ps --libxo json,pretty` とすると、JSON形式で結果が出力されます。

```sh
$ # JSON形式で出力させる。
$ ps --libxo json,pretty
{
  "process-information": {
    "process": [
      {
        "pid": "40356",
        "terminal-name": "10 ",
        "state": "I+J",
        "cpu-time": "0:04.92",
        "command": "vim hello_libxo.c"
      },
      ...
      {
        "pid": "20129",
        "terminal-name": "19 ",
        "state": "D+J",
        "cpu-time": "0:00.00",
        "command": "-bash (bash)"
      }
    ]
  }
}
```

また、 `ps --libxo xml,pretty` とすると、XML形式で結果が出力されます。

```sh
$ ps --libxo xml,pretty
<process-information>
  <process>
    <pid>40356</pid>
    <terminal-name>10 </terminal-name>
    <state>I+J</state>
    <cpu-time>0:04.92</cpu-time>
    <command>vim hello_libxo.c</command>
  </process>
  ...
  <process>
    <pid>20143</pid>
    <terminal-name>19 </terminal-name>
    <state>R+J</state>
    <cpu-time>0:00.00</cpu-time>
    <command>less</command>
  </process>
</process-information>
```

これはなかなか便利そうです。例えばJSON形式で出力させて、 `jq` コマンドとJSONPathで項目を抽出するといった応用も可能です。
## NetBSDでもLibXoを使ってみる

さて、この便利なLibXoをNetBSDでも使ってみたいというのが人情(？)です。さっそくNetBSDでLibXoをビルドする所から試してみます。

### NetBSDでのLibXoビルド手順

まずはビルドに必要なツールやライブラリをパッケージ経由でインストールします。

```sh
$ sudo -i pkg_add -v \
    git \
    ca-certificates \
    autoconf \
    automake \
    autogen \
    libtool
```

次にLibXoのソースコードを入手します。LibXoはGitHubで公開されており、そこから `git clone` します。

```sh
$ git clone https://github.com/Juniper/libxo.git -b develop
$ cd libxo
$ sh ./bin/setup.sh
...
Creating build directory ...
Setup is complete.  To build libslax:
    1) Type 'cd build ; ../configure' to configure libslax gmake
    2) Type 'make' to build libslax
    3) Type 'make install' to install libslax
```

`sh ./bin/setup.sh` を実行後、表示された一連のビルド手順にそって作業を進めます。
ただし、一点注意が必要な個所として、LibXoは `GNU Make` を想定しているため、NetBSD上でビルドする場合は、 `MAKE=gmake ../configure` と指定する必要があります。

```sh
$ # BSD系でのビルドは"MAKE=gmake"指定が必要。
$ cd build ; MAKE=gmake ../configure --prefix=/opt/libxo
...
configure: summary of build options:

  libxo version:    1.6.0 -git-develop
  host type:        x86_64-unknown-netbsd9.1 / netbsd9.1
  install prefix:   /usr/local
  srcdir:           ..
  libdir:           ${exec_prefix}/lib
  bindir:           ${exec_prefix}/bin
  includedir:       ${prefix}/include
  share dir:        /usr/local/share/libxo
  extensions dir:   ${exec_prefix}/lib/libxo/encoder
  oxtradoc dir:

  compiler:         gcc (yes)
  compiler flags:   -g -O2
  library types:    Shared=yes, Static=yes

  warnings:         no
  debug:            no
  printf-like:      no
  libxo-options:    yes
  text-only:        no
  gettext:          no (none)
  isthreaded:       no
  thread-local:     before
  local wcwidth:    yes
  retain size:      default
$
$ # GNU Make(gmake)でビルドする。
$ gmake
$ gmake test
$ sudo gmake install
```

これでLibXoのビルドは完了です。思いのほかサクッとビルドできました。

## LibXoを用いたサンプルプログラム

さっそく簡単なサンプルプログラムを試してみます。先にFreeBSDの `ps` コマンドの出力を示していたので、NetBSDでも `ps` コマンドの出力をJSON/XMLで行ってみます。サンプルはGistに置いてあります。

 * [furandon-pig/libxo_sample.c](https://gist.github.com/furandon-pig/3d226a9b8faca9261726fce0ddad3633)

コンパイルは以下の手順で行います。

```sh
$ gcc \
    -Wall \
    -g \
    -o sample sample.c \
    -I/opt/libxo/include \
    -L/opt/libxo/lib -lxo
```

実行ファイルに `--libxo json,pretty` オプションを指定することで、 `ps` コマンドの結果がJSONで出力されます。
(FreeBSDでの `ps --libxo json,pretty` と同等の出力になります)

```sh
$ LD_LIBRARY_PATH=/opt/libxo/lib ./sample --libxo json,pretty
{
  "process-information": {
    "process": [
      {
        "pid": "0",
        "terminal-name": "?",
        "state": "DKl",
        "cpu-time": "4:10.49",
        "command": "[system]"
      },
      ...
      {
        "pid": "644",
        "terminal-name": "ttyE3",
        "state": "Is+",
        "cpu-time": "0:00.00",
        "command": "/usr/libexec/getty Pc ttyE3 "
      }
    ]
  }
}
```

## まとめ

NetBSDでのLibXoビルドとサンプルプログラムを動かすところまで試してみました。LibXoをアプリケーションに組み込んでおくことで、通常のテキスト出力だけでなく、JSON/XMLでの出力が可能になるため、積極的に利用して行きたいところです。