バージョンの埋め込みと表示
まずは,こんな感じにバージョン情報を表示するコードを考える。
package main
var Version = "v1.2.3"
func main() {
println(Version)
}
これを実行すれば v1.2.3 と表示される。
実際の運用としてバージョンが上がるたびに変数 Version の内容を書き換える必要がある。
また Git でソースコード管理する場合はリリースバージョンに対してバージョンタグを付けることが多いので,それとの整合を保つ必要があり,実はこれが割と煩雑な作業なのである。
そこで Go でビルドしたツールのバージョン表記について,以下の2つの方法を記しておく。
ビルド時にバージョン文字列を埋め込む
これは割と昔からある方法で, go build コマンドの -ldflags オプションを使って変数に値を埋め込む。
$ go build -ldflags="-X main.Version=v9.9.9" -o a.out
$ ./a.out
v9.9.9
Git でバージョンタグを打っている場合は
$ go build -ldflags="-X main.Version=`git describe --tags`" -o a.out
などと Git コマンドの出力を埋め込む手もある(bash シェルの場合)。
もしビルドに GoReleaser を使っているのであれば .goreleaser.yml の中で
builds:
-
ldflags: -X main.Version=v{{ .Version }}
のように書くことで対応できる。
GoReleaser はマルチプラットフォームのコマンドラインツールで,複数プラットフォーム向けの実行バイナリを一度に作ってくれるので重宝している。 また GitHub Actions にも対応していて,ビルドしたバイナリを GitHub Release に自動でアップロードし,リリースノートまで書いてくれる。 ホンマ助かる。
ただし,この方法は go install では使えないし,そもそもユーザに「go build 時に -ldflags でバージョンを埋め込んでね」とお願いするのもおかしな話である。
ビルド情報を表示する
これは Go 1.18 から追加されたらしいのだが runtime/debug パッケージの ReadBuildInfo() 関数を使うと,ビルド時の情報を取得できる。
たとえば,こんな感じ。
package main
import "runtime/debug"
var Version = ""
func replaceVersion(v string) string {
if v != "" {
return v
}
if info, ok := debug.ReadBuildInfo(); ok {
if info.Main.Version != "" {
return info.Main.Version
}
var revision, dirty string
for _, v := range info.Settings {
switch v.Key {
case "vcs.revision":
revision = v.Value
case "vcs.modified":
if v.Value == "true" {
dirty = "+dirty"
}
}
}
if revision != "" {
return revision + dirty
}
}
return "(version not set)"
}
func main() {
println(replaceVersion(Version))
}
debug.ReadBuildInfo() 関数の返り値である BuildInfo 構造体の内容は以下の通り。
type BuildInfo struct {
// GoVersion is the version of the Go toolchain that built the binary
// (for example, "go1.19.2").
GoVersion string `json:",omitempty"`
// Path is the package path of the main package for the binary
// (for example, "golang.org/x/tools/cmd/stringer").
Path string `json:",omitempty"`
// Main describes the module that contains the main package for the binary.
Main Module `json:""`
// Deps describes all the dependency modules, both direct and indirect,
// that contributed packages to the build of this binary.
Deps []*Module `json:",omitempty"`
// Settings describes the build settings used to build the binary.
Settings []BuildSetting `json:",omitempty"`
}
Module 構造体はこんな感じかな。
type Module struct {
Path string `json:",omitempty"` // module path
Version string `json:",omitempty"` // module version
Sum string `json:",omitempty"` // checksum
Replace *Module `json:",omitempty"` // replaced by this module
}
つまり go.mod でモジュールとして定義・管理されている場合は BuildInfo.Main.Version にバージョンが入っている。
ちなみにモジュールとして定義されている場合でもバージョンが(Git 等で管理されず)存在しない場合は (devel) という文字列が返ってくるようだ。
$ go build -o a.out && ./a.out
(devel)
BuildInfo.Main.Version で値を取れない場合は BuildInfo.Settings 配列から取得する。
BuildSetting 構造体は以下の通り key-value 形式になっている。
type BuildSetting struct {
// Key and Value describe the build setting.
// Key must not contain an equals sign, space, tab, or newline.
Key string `json:",omitempty"`
// Value must not contain newlines ('\n').
Value string `json:",omitempty"`
}
このうち Key には以下の値が入ってるらしい。
| Key | 意味 |
|---|---|
-buildmode |
the buildmode flag used (typically "exe") |
-compiler |
the compiler toolchain flag used (typically "gc") |
CGO_ENABLED |
the effective CGO_ENABLED environment variable |
CGO_CFLAGS |
the effective CGO_CFLAGS environment variable |
CGO_CPPFLAGS |
the effective CGO_CPPFLAGS environment variable |
CGO_CXXFLAGS |
the effective CGO_CXXFLAGS environment variable |
CGO_LDFLAGS |
the effective CGO_LDFLAGS environment variable |
DefaultGODEBUG |
the effective GODEBUG settings |
GOARCH |
the architecture target |
GOAMD64/GOARM/GO386/etc |
the architecture feature level for GOARCH |
GOOS |
the operating system target |
GOFIPS140 |
the frozen FIPS 140-3 module version, if any |
vcs |
the version control system for the source tree where the build ran |
vcs.revision |
the revision identifier for the current commit or checkout |
vcs.time |
the modification time associated with vcs.revision, in RFC3339 format |
vcs.modified |
"true" or "false" indicating whether the source tree had local modifications |
先程のコード例では VCS (Version Control System) のリビジョン情報から(vcs.revision および vcs.modified)を使ってバージョンを表示している。
go run ではビルド情報がセットされない
以上は go build あるいは go install でビルドした場合の話だが go run で即時実行した場合はビルド情報がセットされない。
先程のコード例の場合は
$ go run main.go
(version not set)
と表示されビルド情報からバージョンを取得できなかったことが分かる。
まぁ go run による実行でバージョンを気にする人はいないと思うので,これはこれで問題ないだろう。
ブックマーク
参考図書
- プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
- Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
- 丸善出版 2016-06-20
- 単行本(ソフトカバー)
- 4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN)
- 評価
著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。と思ったら絶版状態らしい(2025-01 現在)。復刊を望む!
- Go言語 100Tips ありがちなミスを把握し、実装を最適化する impress top gearシリーズ
- Teiva Harsanyi (著), 柴田 芳樹 (著)
- インプレス 2023-08-18 (Release 2023-08-18)
- Kindle版
- B0CFL1DK8Q (ASIN)
- 評価
版元で PDF 版を購入可能。事実上の Effective Go とも言える充実の内容。オリジナルは敢えてタイトルに “tips” という単語を入れるのを避けたのに邦題が「100 Tips」とかなっていて,原作者がお怒りとの噂(あくまで噂)
- Go言語で学ぶ並行プログラミング 他言語にも適用できる原則とベストプラクティス impress top gearシリーズ
- James Cutajar (著), 柴田 芳樹 (著)
- インプレス 2024-12-04 (Release 2024-12-04)
- Kindle版
- B0DNYMMBBQ (ASIN)
- 評価
読書会のために購入。インプレス社の本は Kindle 版より版元で PDF 版を買うのがオススメ。「並行処理」について原理的な解説から丁寧に書かれている。 Go で解説されているが Go 以外の言語でも応用できる。
- 実用 Go言語 第2版
- 文法はシンプルで学びやすいという特徴を持つGo言語。システム開発の現場でも使われる機会が増えていますが、複雑な要件を実現するにはプログラミング言語が提供するシンプルな構成要素(文法やライブラリ)を、さまざまに組み合わせます。 本書は「よりGoらしく書くには」「実用的なアプリケーションを書くには」といった観点からGoを使うポイントを紹介します。 構造体やインタフェース、またJSON、CSVファイル、Excel、固定長ファイルの扱い方、ログやテスト、環境構築など、また初版の発行後、Goに追加されたジェネリクスについても紹介。現場に即した幅広いトピックについて、「Goらしいプログラムの書き方」を、その背景と共に教えてくれる先輩のような書籍です。
- Release: 2025-12-01
Powered by linkcard


