GitHub ActionsでFlatBuffers compiler（flatcコマンド）を使うための方法

調べてもなかなか情報が出てこず、[使うまでにとても苦労した](https://twitter.com/sounisi5011Prog/status/1382475447179436033)ので、後に続く者が苦労しないよう、備忘録を残す。

## 環境

OSは「Ubuntu 20.04」。現時点では「`runs-on: ubuntu-latest`」または「`runs-on: ubuntu-20.04`」と指定することで選択される。GitHub Actionsでは[他のOSを指定することもできる](https://docs.github.com/en/actions/reference/workflow-syntax-for-github-actions#jobsjob_idruns-on)が、試したのはUbuntu 20.04だけ。

以下の内容は、**Ubuntu 20.04以外のOSでは動作しないかもしれない**。おそらく、WindowsやmacOSでは絶対に使えないだろう。

## 方法その1：`apt-get`でインストール

調べて[一番最初に出てきた](https://stackoverflow.com/a/64741470/4907315)方法。

```yaml
jobs:
  <job_id>:
    runs-on: ubuntu-latest
    steps:
      - name: Install flatbuffers
        run: sudo apt-get install -y flatbuffers-compiler
```

`apt`コマンドでもインストールできるが、「`WARNING: apt does not have a stable CLI interface. Use with caution in scripts.`」という警告文が出る。どうやら、[自動化スクリプトなどでは`apt-get`の使用が推奨されている](https://qiita.com/taro-hida/items/666c1b1cdc851b038215)らしい。

この方法では、**バージョン1.11.0の`flatc`コマンドがインストールされる**（2021年4月15日現在）。[最新版は1.12.0](https://github.com/google/flatbuffers/releases/tag/v1.12.0)だから、少し古い。このせいで、例えば「開発機のMacにHomebrewでFlatBuffersをインストールしたら、バージョンが合わなくてコンパイル結果が違う」なんてことが起こる。実際起きた。おかげで散々苦労させられた。

インストールされるバージョンが古いので、**おすすめしない👎**。

## 方法その2：`snap`でインストール

[「flatbuffers install latest ubuntu」でググって](https://www.google.co.jp/search?q=flatbuffers+install+latest+ubuntu)いたら、検索結果の4番目くらいで[見つけた](https://snapcraft.io/flatbuffers)。**一番オススメ**。

```yaml
jobs:
  <job_id>:
    runs-on: ubuntu-latest
    steps:
      - name: Install flatbuffers
        run: sudo snap install flatbuffers
```

この方法では、**バージョン1.12.0の`flatc`コマンドがインストールされる**（2021年4月15日現在）。まごうことなき最新版。[Snapcraftでのリリースは`apt`よりも簡単らしい](https://qiita.com/dalance/items/c003f93b7f46713091d1)ので、今後も最新版がリリースされ続けると期待できる。

ちなみに、`snap`コマンドは[最初からUbuntuに入っているらしい](https://qiita.com/dalance/items/c003f93b7f46713091d1)。実際、他に何のセットアップもせず当たり前のように使えた。神かよ。筆者はこれを見つけるまでSnapcraftの存在を知らなかったが、これからは存分に使っていきたい。

## 方法その3：ソースからビルドする

上述したように、[最新版の`flatc`コマンドは`snap`コマンドでインストールできる](#%E6%96%B9%E6%B3%95%E3%81%9D%E3%81%AE2snap%E3%81%A7%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB)ものの、最新版以外はインストールできない。特定のバージョンを指定したい場合は、[ソースからビルド](https://google.github.io/flatbuffers/flatbuffers_guide_building.html)する方法が確実。ただし**遅い**。

```yaml
jobs:
  <job_id>:
    runs-on: ubuntu-latest
    steps:
      - name: Build flatbuffers
        shell: bash
        run: |
          readonly FLATBUFFERS_BUILD_PATH='./.flatbuffers'
          git clone --depth 1 https://github.com/google/flatbuffers.git -b v1.12.0 "${FLATBUFFERS_BUILD_PATH}"
          cmake -S "${FLATBUFFERS_BUILD_PATH}" -B "${FLATBUFFERS_BUILD_PATH}" -G 'Unix Makefiles' -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF
          sudo make install -C "${FLATBUFFERS_BUILD_PATH}"
```

この例では、[およそ1分で](https://twitter.com/sounisi5011Prog/status/1382479557026926593)バージョン1.12.0の`flatc`コマンドがビルドされる。他のバージョンが必要な場合は、[GitHub上にあるタグ](https://github.com/google/flatbuffers/tags)の中から好きなものを指定すればいける思われる（未検証）。

なお、ビルドのログは長いので、[ワークフローコマンドの「`::group::`」と「`::endgroup::`」でグループ化](https://docs.github.com/en/actions/reference/workflow-commands-for-github-actions#grouping-log-lines)したほうがいい。

```yaml
jobs:
  <job_id>:
    runs-on: ubuntu-latest
    steps:
      - name: Build flatbuffers
        shell: bash
        run: |
          readonly FLATBUFFERS_BUILD_PATH='./.flatbuffers'

          echo '::group::$' git clone --depth 1 https://github.com/google/flatbuffers.git -b v1.12.0 "${FLATBUFFERS_BUILD_PATH}"
                            git clone --depth 1 https://github.com/google/flatbuffers.git -b v1.12.0 "${FLATBUFFERS_BUILD_PATH}"
          echo '::endgroup::'

          echo '::group::$' cmake -S "${FLATBUFFERS_BUILD_PATH}" -B "${FLATBUFFERS_BUILD_PATH}" -G '"Unix Makefiles"' -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF
                            cmake -S "${FLATBUFFERS_BUILD_PATH}" -B "${FLATBUFFERS_BUILD_PATH}" -G  'Unix Makefiles'  -DFLATBUFFERS_BUILD_TESTS=OFF -DFLATBUFFERS_BUILD_FLATLIB=OFF -DFLATBUFFERS_BUILD_FLATHASH=OFF
          echo '::endgroup::'

          echo '::group::$' sudo make install -C "${FLATBUFFERS_BUILD_PATH}"
                            sudo make install -C "${FLATBUFFERS_BUILD_PATH}"
          echo '::endgroup::'
```

いろいろな試行をしたので、ざっと箇条書きすると：

+ FlatBuffersのソースは`.flatbuffers`ディレクトリ内にダウンロードし、ビルドする。ディレクトリのパスは読み取り専用の`FLATBUFFERS_BUILD_PATH`変数を定義して参照する。本来は、リポジトリ内ではなく、テンポラリディレクトリなどに展開するほうがいい。今回はリポジトリ内に`.flatbuffers`ディレクトリが存在せず、上書きの心配が無かったため使った、
+ `git clone`コマンドの`--depth 1`オプションは、commitを1つだけダウンロードするための指定。デフォルトでは全てのcommitをダウンロードしようとするため遅くなる。ただビルドするだけなので、commitは1つで十分。
+ `git clone`コマンドの`-b`オプションで、ダウンロード対象のタグ、ブランチ、commit hashを選択できる。実は好きなバージョン以外に、好きなcommitも選べてしまう。詳しくは[ググれ](https://www.google.co.jp/search?q=git+clone+b)。
+ `cmake`コマンドでは、`-S`オプションでソース・ディレクトリを、`-B`オプションでビルド結果のディレクトリを指定する。こうすることで`cd`コマンドを使う必要が無くなり、また、`cmake`コマンドが実行時に以下の警告文を表示しなくなる。

    ```
    CMake Warning:
        No source or binary directory provided.  Both will be assumed to be the
        same as the current working directory, but note that this warning will
        become a fatal error in future CMake releases.
    ```

    [ソース・ディレクトリとビルド結果のディレクトリは分けたほうが良い](https://qiita.com/developer-kikikaikai/items/74bc5ad718d7525d1cbc)らしいが、今回は`flatc`コマンドを使うためだけにビルドするので、同じディレクトリでも支障はない。

+ `cmake`コマンドに指定している`-DFLATBUFFERS_ホニャララ=OFF`オプションは、[`CMakeLists.txt`ファイル内で定義されているオプション](https://github.com/google/flatbuffers/blob/v1.12.0/CMakeLists.txt#L9-L40)のうち、不要なのにデフォルト値が`ON`になっているものを無効化している。[書式は`-D{オプション名}={値}`](https://qiita.com/progrhyme/items/c0f21e2a71cfe6fdb7a8#option)。以下のオプションはおそらく必要：

    - `FLATBUFFERS_INSTALL` - 無効化すると、ビルドしたコマンドが`/usr/local/bin`にインストールされない。つまり`flatc`コマンドが使えない。自力で環境変数`PATH`に`flatc`ファイルのパスを追加すれば使えそうだが、わざわざやるメリットはない。
    - `FLATBUFFERS_BUILD_FLATC` - 無効化は試していないが、`CMakeLists.txt`の内容を見るに、これが`flatc`コマンドをビルドするためのフラグ。たぶん、無効化したら`flatc`コマンドのバイナリファイルそのものが作成されない。
    - `FLATBUFFERS_LIBCXX_WITH_CLANG` - 「Force libc++ when using Clang」と書かれている。たぶん、いじらないほうがいいやつ。

    これ以外は全て要らないので、全部無効化で良い気がする。この工夫をしないと、[ビルドに3分はかかる](https://twitter.com/sounisi5011Prog/status/1382479557026926593)。
+ `cmake`コマンドの後に、ビルド用の`make install`コマンドを実行している。実は<ruby>`cmake`<rp> 
─ </rp><rt>Cross platform MAKE</rt><rp></rp></ruby>がやっているのは、`make`コマンドのための`Makefile`ファイルの作成で、ちゃんとビルドするためには[`cmake`のあとに`make`を実行しなければならない](https://stackoverflow.com/a/42881923/4907315)。これを知らなかったせいで、ずいぶん苦労した。
+ `make`コマンドの`install`キーワードは必須。横着して試しに取ったところ、ビルドは成功したが`flatc`コマンドが使えなかった。自力で環境変数`PATH`に`flatc`ファイルのパスを追加すれば使えそうだが、せっかくやってくれるのだから大人しく`install`キーワードを指定しよう。
+ `cmake --install`コマンドは[`make install`コマンド相当のことをやってくれるらしい](https://qiita.com/osamu0329/items/0a72ee32ee6934a7edf7#%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%AB%E5%85%88%E3%81%AE%E5%A4%89%E6%9B%B4---install-%E3%81%A8---prefix-%E3%82%AA%E3%83%97%E3%82%B7%E3%83%A7%E3%83%B3--from-315)が、試したところエラーが発生したので止めた。大人しく`make install`コマンドを使おう。