使用 ldflags 为 Go 应用程序设置版本信息

介绍

当部署应用程序到生产环境中时,构建具有版本信息的二进制和其他元数据将通过添加识别信息来改善您的监控、日志和调试流程,以帮助跟踪您的构建时间。此版本信息通常可以包括高度动态的数据,例如构建时间,机器或用户构建的二进制, 版本控制系统(VCS)委托ID,它是建立的,以及更多。因为这些值不断变化,将这些数据直接编码到源代码中,并在每个新构建之前修改它是无聊的和容易出现错误的:源文件可以移动,而 变量/常数可能会在整个开发过程中切换文件,破坏构建过程。

在Go中解决这一问题的一种方法是使用-ldflags命令go build来在构建时将动态信息插入二进制中,而无需进行源代码修改。在这个旗帜中,ld代表_linker_(https://en.wikipedia.org/wiki/Linker_(computing),该程序将编译的源代码的不同部分连接到最终的二进制中。

在本教程中,您将使用ldflags在构建时更改变量值,并将自己的动态信息输入到二进制中,使用样本应用程序将版本信息打印到屏幕上。

前提条件

要遵循本文中的例子,您将需要:

构建您的样本应用程序

在您可以使用ldflags来输入动态数据之前,您首先需要一个应用程序来输入信息. 在此步骤中,您将创建此应用程序,该应用程序将在此阶段只打印静态版本信息。

在您的src目录中,创建一个名为您的应用程序的目录,本教程将使用应用程序名称app:

1mkdir app

更改您的工作目录到此文件夹:

1cd app

接下来,使用您选择的文本编辑器,创建您的程序的入口点,main.go:

1nano main.go

现在,通过添加以下内容来打印应用程序的版本信息:

 1[label app/main.go]
 2package main
 3
 4import (
 5    "fmt"
 6)
 7
 8var Version = "development"
 9
10func main() {
11    fmt.Println("Version:\t", Version)
12}

main()函数中,您声明了Version变量,然后打印了(https://andsky.com/tech/tutorials/an-introduction-to-working-with-strings-in-go)`Version:`字符串,然后是\t字符串,然后是声明变量。

在此时,变量版本被定义为开发,这将是该应用程序的默认版本。

一旦完成,构建并运行应用程序以确认它打印了正确的版本:

1go build
2./app

您将看到以下结果:

1[secondary_label Output]
2Version:	 development

您现在有一个打印默认版本信息的应用程序,但您还没有方法在构建时传输当前版本信息. 在下一步,您将使用ldflagsgo build来解决此问题。

使用ldflagsgo build

如前所述,ldflags代表 linker flags,用来将旗帜传递到 Go 工具链中的底层链接器上。

1go build -ldflags="-flag"

在本示例中,我们将flag转移到作为go build的一部分运行的底层go tool link命令中,该命令使用了转移到ldflags的内容周围的双重引用,以避免在其中的字符被打破,或者命令行可能解释为我们想要的其他东西。从这里,您可以通过(https://golang.org/cmd/link/)许多不同的link旗帜。为本教程的目的,我们将使用-X旗帜在链接时写入变量,然后是(https://andsky.com/tech/tutorials/importing-packages-in-go)路径到变量及其新值:

1go build -ldflags="-X 'package_path.variable_name=new_value'"

在引用中,现在有 -X 选项和 关键值对,代表要更改的变量及其新值。

若要在示例应用程序中替代版本变量,请使用最后一个命令块中的语法传输一个新的值并构建新的二进制:

1go build -ldflags="-X 'main.Version=v1.0.0'"

在此命令中,mainVersion变量的包路径,因为这个变量位于main.go文件中。

要使用ldflags,您要更改的值必须存在,并且是类型的string的包级变量。 此变量可以导出或导出。 该值不能是const或由函数调用结果设置的值。 幸运的是,Version符合所有这些要求:它已经在main.go文件中被宣称为变量,当前值(development)和所需值(v1.0.0)都是字符串。

一旦你的新的app二进制已建立,运行应用程序:

1./app

您将获得以下输出:

1[secondary_label Output]
2Version:	 v1.0.0

使用ldflags,您已成功将版本变量从开发更改为v1.0.0

您现在在构建时修改了一个简单的应用程序中的字符串变量,使用ldflags,您可以将版本详细信息,许可信息和更多内容嵌入到一个可分发的二进制中,仅使用命令行。

在本示例中,您更改的变量是在程序中,减少了确定路径名称的困难,但有时找到这些变量的路径更为复杂。

目标分包变量

在最后一节中,你操纵了版本变量,这是应用程序的顶级包,但这并不总是如此。 通常,将这些变量放置在另一个包中更为实用,因为不是一个可导入的包。 为了在你的示例应用程序中模拟这一点,你将创建一个新的子包,即app/build,该包将存储有关二进制构建时间和发出构建命令的用户的名字的信息。

若要添加新的子包,请先为您的项目添加一个名为构建的新目录:

1mkdir -p build

然后创建一个名为build.go的新文件,以保持新的变量:

1nano build/build.go

在文本编辑器中,添加时间用户的新变量:

1[label app/build/build.go]
2package build
3
4var Time string
5
6var User string

时间变量将持有二进制构建时期的字符串表示。 用户变量将持有构建二进制的用户的名称。 由于这两个变量将始终具有值,您不需要将这些变量以默认值进行初始化,就像您在版本中所做的那样。

保存和退出文件。

接下来,打开main.go,将这些变量添加到您的应用程序中:

1nano main.go

main.go中,添加以下突出的行:

 1[label main.go]
 2package main
 3
 4import (
 5    "app/build"
 6    "fmt"
 7)
 8
 9var Version = "development"
10
11func main() {
12    fmt.Println("Version:\t", Version)
13    fmt.Println("build.Time:\t", build.Time)
14    fmt.Println("build.User:\t", build.User)
15}

在这些行中,您首先导入了app/build包,然后以同样的方式打印了build.Timebuild.User

保存文件,然后离开文本编辑器。

接下来,用ldflags来瞄准这些变量,你可以使用app/build导入路径,然后是.User.Time,因为你已经知道导入路径。

Go tool nm 命令将输出涉及给定的可执行、对象文件或档案的 _symbols。在这种情况下,一个符号指代码中的一个对象,例如定义或导入的变量或函数。

<$>[注] 注: nm 命令不会帮助您找到变量的路径,如果包名称有任何非 ASCII字符,或一个 "% 字符,因为这是工具本身的局限性。

要使用此命令,首先构建app的二进制:

1go build

现在app已构建,指向它nm工具,并通过输出搜索:

1go tool nm ./app | grep app

在运行时,nm工具会输出大量数据,这就是为什么上一个命令使用``来导出输出到grep命令,然后搜索标题中具有顶级app的术语。

您将收到类似于此的输出:

1[secondary_label Output]
2  55d2c0 D app/build.Time
3  55d2d0 D app/build.User
4  4069a0 T runtime.appendIntStr
5  462580 T strconv.appendEscapedRune
6. . .

在这种情况下,结果集的前两个行包含您正在寻找的两个变量的路径: app/build.Timeapp/build.User

现在你已经知道了路径,再度构建应用程序,这一次在构建时更改版本,用户时间

1go build -v -ldflags="-X 'main.Version=v1.0.0' -X 'app/build.User=$(id -u -n)' -X 'app/build.Time=$(date)'"

在这里,您通过了 id -u -n Bash 命令来列出当前用户,以及 `date' 命令来列出当前日期。

一旦构建了可执行程序,请运行该程序:

1./app

这个命令在Unix系统上运行时,会产生类似于以下的输出:

1[secondary_label Output]
2Version:	 v1.0.0
3build.Time:	 Fri Oct 4 19:49:19 UTC 2019
4build.User:	 sammy

现在你有一个二进制,其中包含版本和构建信息,可在解决问题时在生产中提供关键的帮助。

结论

本教程展示了如何正确应用时,ldflags可以成为在构建时将有价值的信息注入到二进制文件中的一种强大的工具,这样你就可以控制功能旗帜、环境信息、版本信息等等,而不需要对源代码进行更改。

如果您想了解更多关于 Go 编程语言的信息,请查看我们完整的 How To Code in Go 系列

Published At
Categories with 技术
Tagged with
comments powered by Disqus