Golangビルド時に、サブパッケージ内の変数をいじる

mainパッケージに変数を埋め込みたいとき

Goで作ったアプリで --version 用にバージョン番号とビルド日時を埋め込みたい。

そんな時、-ldflagsを使えばいいのはwell-knownだと思います。

go build -ldflags="
  -X main.compiled 'ビルド日時'
  -X main.version 'バージョン番号'
" .

サブパッケージに変数を埋め込みたいとき

では main以外に変数を埋め込む時は、どうすればよいのでしょうか?

ui/backend/workerの3つのサーバーからなるシステムを作っているとします。 3つのmainパッケージがoptsコマンドラインパーサ)パッケージを共有しています。 バージョン番号はui/backend/workerで同じ番号を使いたいので、optsに埋め込みたいと思います(3つのmainそれぞれではなく)。

この場合は、main.version ではなく opts内のVersionを書き換えなくてはなりません。

 - $GOPATH/src/github.com/my-team/app/
   - cmd/ 
     - ui/      # UIサーバー用の main パッケージ
     - backend/ # backendサーバー用の main パッケージ
     - worker/  # SQSワーカー用の main パッケージ

   - libs       # 共通のライブラリ群
     - dynamodb/
     - pgsql/
     - s3/
     - opts/    # コマンドラインパーサー、ここに共通のVersionを埋め込みたい
// cmd/ui/ui.go
package main

import (
  "http"
  "github.com/my-team/app/libs/opts"
)

func main() {
  o := opts.Parse() # 共通のコマンドラインパーサー

     ...
}
// libs/opts/opts.go
package opts

import (
  "flag"
  "os"
  "fmt"
)

var (
  // この2つをビルド時に書き換えたい

  Version  string // 共通のバージョン番号
  Compiled string // 共通のビルド日時
)

func Parse() Options{
  v := flag.BoolVar('version', false, "print version number")

    ...

  flag.Parse()
  if *v {
    fmt.Printf("version: %s, build: %s", Version, Compiled)
    os.Exit(0)
  }
}

解決

なんのことはない、mainをパッケージのパスに変えれば良いのです。

go build -ldflags="
  -X github.com/my-team/app/libs/opts.Compiled 'ビルド日時'
  -X github.com/my-team/app/libs/opts.Version 'バージョン番号'
" .

"go tool"の1つ"nm"で、実行ファイルの中でどんな変数が使われているのかが見られます。

$ go tool nm bin/ui
bin/ui:   4010d0 T main.init
bin/ui:   b1fc4f D main.initdone.
bin/ui:   400c00 T main.main
bin/ui:   b0b890 D github.com/my-team/app/libs/opts.Compiled
bin/ui:   b1b4e0 D github.com/my-team/app/libs/opts.Version