SoFunction
Updated on 2025-03-05

Detailed explanation of how Golang injects version information at compile time

question

Generally speaking, open source software that does slightly better have version information on its binary files, such as Docker, you can view it through the following command:

$ docker --version
Docker version 20.10.17, build 100c701
$ docker version
...
//The output is too long,I won't show it here

Some web applications also provide a /version interface, which you can obtain through:

$ curl http://127.0.0.1:8080/version | jq
{
  "Build Time": "Fri, 16 Jun 2023 16:19:23 +0800",
  "Git Commit": "b554659",
  "Go Version": "go1.19",
  "OS/Arch": "darwin/amd64",
  "version": "0.9.2"
}

However, many companies do not have mandatory norms in this regard, and most developers do not have such awareness or habits, so most projects do not implement this function. If the binary file does not contain version information, you may have to use other systems to query or trace it. This is undoubtedly a discomfort and is not friendly to code debugging and problem positioning.

So, how to implement this function in Golang?

Ideas

There are many version information, such as code version number, Git submission number, compilation time, Go version number, etc. How to get the binary file to bring this information?

Hard code is definitely not possible in code. Because the Git commit number is generated when the code is submitted to the code repository, the Go version number and compilation time can only be obtained at compile time.

A more reasonable approach is to inject it at compile time. The Go compilation tool provides the -ldflags option, which can inject the value of the package variable through the -X parameter. We only need to define the version information as the package variable in the code, and then complete the injection at compile time. This process can be implemented as automated, and the following is an example of the make tool.

Implementation steps

1. Define the variables for version information in the code

Define the code version number, Git commit number, and compile time as package variables:

package config
var (
	Version   string  //Code version number	GitCommit string  //Git submission number	BuildTime string  //Compilation time)

Go version number and other information can be obtained with the help of the runtime package:

()  //Go version       //operating system     //Platform architecture

2. Use these variables in your code

1) Implement --version parameters or version subcommand

byCobraAs an example, the command line framework implements the --version parameter for rootCmd:

 = 
((`{{with .Name}}{{printf "%%s version information: " .}}{{end}}
    {{printf "Version:    %%s" .Version}}
    Git Commit: %s
    Build Time: %s
    Go version: %s
    OS/Arch:    %s/%s
`, , , (), , ))

2) Implement/version interface

byGinAs an example, route /version to the following Version function:

func Version(ctx *) {
    ().Set("Content-Type", "application/json")
    ()
    ().Encode(map[string]string{
        "version":    ,
        "Git Commit": ,
        "Build Time": ,
        "Go Version": (),
        "OS/Arch":     + "/" + ,
    })
}

3. Get version information before compilation

In Makefile, use the git command to get the code version number and Git submission number, and use the date command to get the current time

VERSION    = $(shell git describe --tags --always)
GIT_COMMIT = $(shell git rev-parse --short HEAD)
BUILD_TIME = $(shell date -R)

4. Inject version information at compile time

In Makefile, use the -ldflags option to pass in the compile parameters to the go build command to implement the injection of version information

define LDFLAGS
"-X '/myname/myapp/=${VERSION}' \
-X '/myname/myapp/=${GIT_COMMIT}' \
-X '/myname/myapp/=${BUILD_TIME}'"
endef
build:
    go build -ldflags ${LDFLAGS} -o myapp_${VERSION} .

5. Verify the effect

Compile the binary and verify the --version parameter; start the service process with binary and verify the /version interface.

$ make build
...
//Compilation process, omit output$ myapp --version
myapp version information: 
    Version:    0.9.2
    Git Commit: b554659
    Build Time: Sat, 17 Jun 2023 11:30:44 +0800
    Go version: go1.20.5
    OS/Arch:    darwin/arm64
$ myapp
...
//Start the process and omit the output$ curl http://127.0.0.1:8080/version | jq
{
  "Build Time": "Sat, 17 Jun 2023 11:30:44 +0800",
  "Git Commit": "b554659",
  "Go Version": "go1.20.5",
  "OS/Arch": "darwin/arm64",
  "version": "0.9.2"
}

Summarize

The binary file is directly equipped with version information, which can bring great convenience to code debugging and problem positioning. This article introduces the specific practices of injecting version information at compile time through the -ldflags option.

This is a relatively general method, many open source software such asDockerThis is also done.

This is the end of this article about how Golang injects version information during compilation. For more information about Golang compilation and injection, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!