フューチャー技術ブログ

Go 1.18 集中連載 実行ファイルのメタデータに関するアップデート(コミットID追加等)

TIG DX ユニットの多賀です。 Go 1.18連載 の最後6本目です。
最近業務で久々に Go を触ることになりそうで、少し思い出しながらコードを実装してみたりしてます。

TL;DR

  • Go 1.18 から build 時に含まれるメタデータに VCS, ビルド情報が追加
  • メタデータを参照可能な実装が debug/buildinfo パッケージとして公開

Go 1.18 アップデート概要

Go 1.18 ではビルドして生成される実行ファイル中のメタデータに関連して、2点アップデートが入っています。

1点目は、メタデータの追加です。Go のビルドした実行ファイルには、Go 1.17 以前からメタデータとしてコンパイルした Go のバージョンや、依存 module の情報が含まれていました。 Go 1.18 からは、元々の情報に加えて Git の commit id や commit した時刻等の VCS 情報と、build したアーキテクチャやOS等のビルド情報が追加されました。

2 点目は、実行ファイルのメタデータをパッケージを通して参照できるようになったことです。
Go 1.17 以前は、実行ファイルのメタデータを外部から取得するためには、 go version -m ${実行ファイル} コマンドを実行するしか方法がありませんでした(※1)。 go version コマンドの実装を利用しようにも、 internal パッケージ下に含まれてしまい、外部から利用することはできず、コードをコピーして実装したりする状況が生まれていました。

そのため、Go 1.18 にて go version の実装を移植した外部利用可能な debug/buildinfo パッケージが追加されました。

※1: 正確には runtime/debug.BuildInfo を利用すれば、実装したコード自身のメタデータへアクセスすることはできました。 BuildInfo をコンパイルコード内で取得、Print しておけば実行時に出力できたりします。

アップデート詳細

Go 1.18 はまだリリース前なので、ベータバージョン(go1.18beta2)で動作確認してます。
正式リリース時に挙動が変更されている可能性がありますので、ご注意ください。

メタデータ追加

追加されるメタデータを、各バージョンの出力を参考に比較してみます。
build 項目以下が、 Go 1.18 で追加予定です。

Go 1.17.6

❯ go version -m 1.17.6
1.17.6: go1.17.6
path poc/go-buildinfo
mod poc/go-buildinfo (devel)

Go 1.18beta2

❯ go1.18beta2 version -m 1.18beta2
1.18beta2: go1.18beta2
path poc/go-buildinfo
mod poc/go-buildinfo (devel)
build -compiler=gc
build CGO_ENABLED=1
build CGO_CFLAGS=
build CGO_CPPFLAGS=
build CGO_CXXFLAGS=
build CGO_LDFLAGS=
build GOARCH=amd64
build GOOS=darwin
build GOAMD64=v1
build vcs=git
build vcs.revision=ae89406296a8de9e9ccfecf06659c4e9c8c5f0b3
build vcs.time=2022-02-09T08:47:00Z
build vcs.modified=true

よく使いそうな項目について補足します。

  • GOARCH/GOOS
    • コンパイル後にbuild した アーキテクチャと OS を見ることができるようになります
  • vcs.revision/vcs.time
    • (Gitの場合) build したタイミングの commit id と時刻を保持します
  • vcs.modified
    • (Gitの場合) build したタイミングに、commit していない変更が残されている場合に true となり、変更漏れがないか確認できます

debug/buildinfo

実行ファイルからメタデータを読み取れる、debug/buildinfo パッケージが追加されました。
ドキュメントを見ると、API としては以下が定義されています。実行ファイルを io.ReaderAt か ファイルから読み込む実装だけですね。

type BuildInfo
func Read(r io.ReaderAt) (*BuildInfo, error)
func ReadFile(name string) (info *BuildInfo, err error)

読み込んだ返却値である BuildInfo 型は、 runtime/debug.BuildInfo の型エイリアスになっています。 Go 1.18 でメタデータが追加されているため、 runtime/debug.BuildInfo の定義も以下の通りアップデートされています。

Go 1.17.6

type BuildInfo struct {
Path string // The main package path
Main Module // The module containing the main package
Deps []*Module // Module dependencies
}

Go 1.18beta2

type BuildInfo struct {
GoVersion string // Version of Go that produced this binary.
Path string // The main package path
Main Module // The module containing the main package
Deps []*Module // Module dependencies
Settings []BuildSetting // Other information about the build.
}

試しに実行ファイルを読み込んだコードを動かしてみます。 BuildInfo を JSON にして中身も確認してみます。

package main

import (
"debug/buildinfo"
"encoding/json"
"flag"
"fmt"
"log"
)

func main() {
e := flag.String("e", "", "execution file")
if e == nil {
log.Fatalln("-e required")
}
flag.Parse()

// 実行ファイル読込
b, err := buildinfo.ReadFile(*e)
if err != nil {
log.Fatalf("read file failed: %v", err)
}

// 出力
j, err := json.MarshalIndent(*b, "", " ")
if err != nil {
log.Fatalf("marshal json failed: %v", err)
}
fmt.Println(string(j))
}

出力はこちらになりました。 Go 1.18 で追加予定の項目は Settings へ詰め込まれるようです。

{
"GoVersion": "go1.18beta2",
"Path": "poc/go-buildinfo",
"Main": {
"Path": "poc/go-buildinfo",
"Version": "(devel)",
"Sum": "",
"Replace": null
},
"Deps": null,
"Settings": [
{
"Key": "-compiler",
"Value": "gc"
},
{
"Key": "CGO_ENABLED",
"Value": "1"
},
{
"Key": "CGO_CFLAGS",
"Value": ""
},
{
"Key": "CGO_CPPFLAGS",
"Value": ""
},
{
"Key": "CGO_CXXFLAGS",
"Value": ""
},
{
"Key": "CGO_LDFLAGS",
"Value": ""
},
{
"Key": "GOARCH",
"Value": "amd64"
},
{
"Key": "GOOS",
"Value": "darwin"
},
{
"Key": "GOAMD64",
"Value": "v1"
},
{
"Key": "vcs",
"Value": "git"
},
{
"Key": "vcs.revision",
"Value": "ae89406296a8de9e9ccfecf06659c4e9c8c5f0b3"
},
{
"Key": "vcs.time",
"Value": "2022-02-09T08:47:00Z"
},
{
"Key": "vcs.modified",
"Value": "true"
}
]
}

関連 issue

関連 パッチ

所感

Go 1.18 で変更される実行ファイルに含まれるメタデータ関連について、整理してみました。

Git の commit id が参照できるようになるのも良いですが、 GOOS / GOARCH を見ることができるようになったのも良さそうです。 複数環境向けの build 後に、どの実行ファイルがどの環境向けかわからなくなっても確認するのに使えそうです。 (わからなくなったときは、とりあえず実行してみて確認したりしてました)debug/buildinfo パッケージの登場により、実行されているバイナリをスキャンしてツールへ連携する実装がやりやすくなったのも良さそうですね。

Go 1.18 連載は以上で終了です。

一般的に待望されていたジェネリクスや、コードの安全性を高める fuzzing にサポートしたりと盛り沢山なリリースだったかと思います。リリースは 3 月にずれ込みそうとのことですが、それまでのつなぎとして楽しんでいただけていたら幸いです。

参考