go1.16 の embed によるファイル埋め込み

Page content

go1.16 から embed が利用可能になりました。

<https://golang.org/pkg/embed/>

embed によって、 プログラムにバイナリデータを埋め込む処理が簡単に行なえるようになります。

LuneScript のコンパイラは、 go でビルドした際に Lua 環境がなくても動作するように、 Lua 用のセルフホストコードをコンパイラ内部に埋め込み、 実行時に埋め込んであるセルフホストコードをロードしています。

以前は、 Lua 用のセルフホストコードを []byte として定義するコードを生成するスクリプトを 自前で実行して、それをビルドしていましたが、 embed を利用することで、その作業が不要になりました。

ここでは、 embed を利用する際に引掛った embed の仕様について説明します。

embed の概要

上記 URL からサンプルを引用します。

import _ "embed"

//go:embed hello.txt
var s string
print(s)

このサンプルは、 hello.txt というファイルに保持されたデータを、 s という string 型の変数で利用できるように設定し、 print(s) で出力するコードです。

重要なのは次です。

  • embed を import する。
  • バイナリデータを格納する変数を トップスコープ に var で宣言する
  • 変数 var の直前に //go:embed path のコメントを記載する

変数宣言

バイナリデータを格納する変数を宣言します。 この時、次が重要です。

  • 変数はファイル内の トップスコープ に宣言する
  • 変数の型は次のいずれか

    • string
    • []byte
    • embed.FS

embed.FS は、次の用途で利用します。

  • 埋め込むのがファイルではなくディレクトリの場合
  • バイナリデータだけでなく、ファイルの更新日時などの情報も必要な場合

上記の条件を満さない場合はビルドエラーします。

変数 var の直前に //go:embed path のコメント

データを格納する変数宣言の直前に //go:embed path のコメントを記載します。

この時、次が重要です。

  • //go の間に スペースが入ってはならない
  • path は go build を実行するディレクトリからの 相対パス

    • ... を含まない。

//go の間にスペースが入ると、単なるコメントとして扱われ、 変数は NULL 値で初期化されます。 この時なんの warning も出力されません。

path は、 ... を含まない相対パスです。 つまり、go build を実行するディレクトリ以降にファイルが存在している必要があります。

この path の仕様は少し不便に感じましたが、 プロジェクトを go get でビルドできるようにするには必要な対応だと思うので、 仕方がないところでしょう。