rakyll/statik でシングルバイナリにまとめる
//go:embed
ディレクティブが追加されたため,この記事は不要になった。
一応,過去の記録として残しておく。以前紹介した『改訂2版 みんなのGo言語』を読んで知ったのだが,これまたずいぶん前に紹介した jessevdk/go-assets はもうメンテされてないらしい。 確かにリポジトリを見ると3年前(2016年)から更新されてないな。
というわけで『改訂2版 みんなのGo言語』を見ながら rakyll/statik を試してみるとしよう。
rakyll/statik のダウンロードとビルド
rakyll/statik のダウンロードとビルドは以下の通り。
$ go get github.com/rakyll/statik@latest
go: finding github.com/rakyll/statik v0.1.6
go: downloading github.com/rakyll/statik v0.1.6
go: extracting github.com/rakyll/statik v0.1.6
おー。 外部パッケージは使わないのか。 善き哉。
ビルドが成功すれば $GOPATH/bin
ディレクトリに statik
コマンドがインストールされる。
$ statik -h
Usage of statik:
-Z Do not use compression to shrink the files.
-c string
The package comment. An empty value disables this comment.
(default "Package statik contains static assets.")
-dest string
The destination path of the generated package. (default ".")
-f Overwrite destination file if it already exists.
-m Ignore modification times on files.
-p string
Name of the generated package (default "statik")
-src string
The path of the source directory. (default "public")
-tags string
Write build constraint tags
rakyll/statik によるソース・コードの生成と組み込み
今回の構成は以下の通り(ちなみにモジュール名は sample
で)。
$ tree .
.
├── go.mod
├── html
│ └── index.html
└── sample.go
このうち html
ディレクトリ以下のファイルを実行バイナリに組み込む。
$ statik -src html
これで statik/statik.go
が生成される。
中身はこんな感じ。
// Code generated by statik. DO NOT EDIT.
// Package statik contains static assets.
package statik
import (
"github.com/rakyll/statik/fs"
)
func init() {
data := "PK\x03\x04\x14\x00\x08\x00\x08\x00\x92=/O\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\n\x00 \x00index.htmlUT\x05\x00\x01\xe5\xeb}]\\\x8e1\xcb\xc2@\x0c@\xe7\xaf\xbf\xe2z\xf3W\x8a\x9bC\xae\x8b\nn:\x08\xe2\x18\xef\"\x0d\xa4w\xa5\x8d-\xfe{i\x0f\x1d\x9cB^\x1e\xe1A\xb9?\xed.\xb7\xf3\xc1\xb4\xdaIS\xc0g\x10\x86\xa6\xf8\x83\x8e\x14\x8doq\x18I\x9d}\xea\xa3\xda\xda/\x8f\xd8\x91\xb3\x13\xd3\xdc\xa7A\xad\xf1)*Euv\xe6\xa0\xad\x0b4\xb1\xa7j]\xfe\x0dGVF\xa9F\x8fBn\xb3~QV\xa1\xe6H\"\xc9\\\xd3 \xa1\x84:\xb3\x02\xea\x9c\x00\xf7\x14^\x8b\xdb\xffx\xfd\xe2\xe4#\xd4k\xf5;\x00\x00\xff\xffPK\x07\x08\x13\xf25U\x97\x00\x00\x00\xcc\x00\x00\x00PK\x01\x02\x14\x03\x14\x00\x08\x00\x08\x00\x92=/O\x13\xf25U\x97\x00\x00\x00\xcc\x00\x00\x00\n\x00 \x00\x00\x00\x00\x00\x00\x00\x00\x00\xa4\x81\x00\x00\x00\x00index.htmlUT\x05\x00\x01\xe5\xeb}]PK\x05\x06\x00\x00\x00\x00\x01\x00\x01\x00A\x00\x00\x00\xd8\x00\x00\x00\x00\x00"
fs.Register(data)
}
html/index.html
ファイルの中身は圧縮されて格納されるようだ。
-Z
オプションで圧縮を解除できるが,まぁ普通は圧縮するだろう(笑)
生成された statik
サブパッケージを使って以下のように HTTP サーバを構成してみる。
package main
import (
"fmt"
"net/http"
"os"
_ "sample/statik"
"github.com/rakyll/statik/fs"
)
func main() {
fmt.Println("Open http://localhost:3000/")
fmt.Println("Press ctrl+c to stop")
statikFs, err := fs.New()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
http.Handle("/", http.FileServer(statikFs))
if err := http.ListenAndServe(":3000", nil); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
これで
$ go run sample.go
Open http://localhost:3000/
Press ctrl+c to stop
としてサーバを起動しブラウザで http://localhost:3000/
にアクセスすれば html/index.html
ファイルの内容が表示される。
statik
/fs.New()
関数は http
.FileSystem
インスタンスを返す。
http
.FileSystem
は interface 型で,以下のように定義されている。
// A FileSystem implements access to a collection of named files.
// The elements in a file path are separated by slash ('/', U+002F)
// characters, regardless of host operating system convention.
type FileSystem interface {
Open(name string) (File, error)
}
http
.File
も interface 型で,以下のように定義されている。
// A File is returned by a FileSystem's Open method and can be
// served by the FileServer implementation.
//
// The methods should behave the same as those on an *os.File.
type File interface {
io.Closer
io.Reader
io.Seeker
Readdir(count int) ([]os.FileInfo, error)
Stat() (os.FileInfo, error)
}
つまり rakyll/statik パッケージで生成されるファイルシステムを使えばこれだけの操作ができるということだ。 上手く使っていきたい。
go generate コマンドによるソース・コードの生成
//
コメントに go:generate
マーカを使うことにより go generate
コマンドで statik
コマンドを呼び出せる。
package main
import (
"fmt"
"net/http"
"os"
_ "sample/statik"
"github.com/rakyll/statik/fs"
)
//go:generate statik -src html
func main() {
fmt.Println("Open http://localhost:3000/")
fmt.Println("Press ctrl+c to stop")
statikFs, err := fs.New()
if err != nil {
fmt.Fprintln(os.Stderr, err)
return
}
http.Handle("/", http.FileServer(statikFs))
if err := http.ListenAndServe(":3000", nil); err != nil {
fmt.Fprintln(os.Stderr, err)
}
}
$ go generate
$ go run sample.go
Open http://localhost:3000/
Press ctrl+c to stop
go generate
コマンドは明示的に行う必要があるので注意。
ブックマーク
- Goのバイナリに静的ファイルを埋め込むツール assets-life を書いた : 自コードを Quine として埋め込むらしい。面白い!
参考図書
- 改訂2版 みんなのGo言語
- 松木 雅幸 (著), mattn (著), 藤原 俊一郎 (著), 中島 大一 (著), 上田 拓也 (著), 牧 大輔 (著), 鈴木 健太 (著)
- 技術評論社 2019-08-01 (Release 2019-08-01)
- Kindle版
- B07VPSXF6N (ASIN)
- 評価
改訂2版の目玉は7章の「データベースの扱い方」が追加されたことだろう。他の章では,大まかな構成は1版と同じだが細かい部分が変わっていて Go 1.12 への言及まであるのには驚いた。
- プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
- 丸善出版 2016-06-20
- 単行本(ソフトカバー)
- 4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN), 9784621300251 (ISBN)
- 評価
著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。