バージョンの埋め込みと表示

まずは,こんな感じにバージョン情報を表示するコードを考える。

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 による実行でバージョンを気にする人はいないと思うので,これはこれで問題ないだろう。

ブックマーク

参考図書

photo
プログラミング言語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 現在)。復刊を望む!

reviewed by Spiegel on 2016-07-13 (powered by PA-APIv5)

photo
Go言語 100Tips ありがちなミスを把握し、実装を最適化する impress top gearシリーズ
Teiva Harsanyi (著), 柴田 芳樹 (著)
インプレス 2023-08-18 (Release 2023-08-18)
Kindle版
B0CFL1DK8Q (ASIN)
評価     

版元で PDF 版を購入可能。事実上の Effective Go とも言える充実の内容。オリジナルは敢えてタイトルに “tips” という単語を入れるのを避けたのに邦題が「100 Tips」とかなっていて,原作者がお怒りとの噂(あくまで噂)

reviewed by Spiegel on 2023-08-18 (powered by PA-APIv5)

photo
Go言語で学ぶ並行プログラミング 他言語にも適用できる原則とベストプラクティス impress top gearシリーズ
James Cutajar (著), 柴田 芳樹 (著)
インプレス 2024-12-04 (Release 2024-12-04)
Kindle版
B0DNYMMBBQ (ASIN)
評価     

読書会のために購入。インプレス社の本は Kindle 版より版元で PDF 版を買うのがオススメ。「並行処理」について原理的な解説から丁寧に書かれている。 Go で解説されているが Go 以外の言語でも応用できる。

reviewed by Spiegel on 2025-01-25 (powered by PA-APIv5)

thumbs
実用 Go言語 第2版
文法はシンプルで学びやすいという特徴を持つGo言語。システム開発の現場でも使われる機会が増えていますが、複雑な要件を実現するにはプログラミング言語が提供するシンプルな構成要素(文法やライブラリ)を、さまざまに組み合わせます。 本書は「よりGoらしく書くには」「実用的なアプリケーションを書くには」といった観点からGoを使うポイントを紹介します。 構造体やインタフェース、またJSON、CSVファイル、Excel、固定長ファイルの扱い方、ログやテスト、環境構築など、また初版の発行後、Goに追加されたジェネリクスについても紹介。現場に即した幅広いトピックについて、「Goらしいプログラムの書き方」を、その背景と共に教えてくれる先輩のような書籍です。
 
Release: 2025-12-01

Powered by linkcard