ldflags
介绍 (Introduction)
When deploying applications into a production environment, building binaries with version information and other metadata will improve your monitoring, logging, and debugging processes by adding identifying information to help track your builds over time. This version information can often include highly dynamic data, such as build time, the machine or user building the binary, the Version Control System (VCS) commit ID it was built against, and more. Because these values are constantly changing, coding this data directly into the source code and modifying it before every new build is tedious and prone to error: Source files can move around and variables/constants may switch files throughout development, breaking the build process.
在将应用程序部署到生产环境中时,使用版本信息和其他元数据构建二进制文件会通过添加标识信息来帮助您跟踪构建情况,从而改善您的监视,日志记录和调试过程。 此版本信息通常可以包括高度动态的数据,例如构建时间,构建二进制文件的机器或用户,针对其构建的版本控制系统(VCS)提交ID等。 由于这些值一直在变化,因此在每个新版本繁琐且容易出错之前,将这些数据直接编码为源代码并进行修改:源文件会四处移动, 变量/常量可能会在整个开发过程中切换文件,从而破坏了构建过程。
One way to solve this in Go is to use -ldflags
with the go build
command to insert dynamic information into the binary at build time, without the need for source code modification. In this flag, ld
stands for linker, the program that links together the different pieces of the compiled source code into the final binary. ldflags
, then, stands for linker flags. It is called this because it passes a flag to the underlying Go toolchain linker, cmd/link
, that allows you to change the values of imported packages at build time from the command line.
在Go中解决此问题的一种方法是在构建时将-ldflags
与go build
命令一起使用,以将动态信息插入二进制文件中,而无需修改源代码。 在此标志中, ld
代表linker ,该程序将已编译的源代码的不同部分链接到最终的二进制文件中。 ldflags
代表链接器标志 。 之所以这样称呼,是因为它向基础Go工具链链接器cmd/link
传递了一个标志,该标志使您可以在构建时从命令行更改导入包的值。
In this tutorial, you will use -ldflags
to change the value of variables at build time and introduce your own dynamic information into a binary, using a sample application that prints version information to the screen.
在本教程中,您将使用-ldflags
在构建时更改变量的值,并使用将版本信息打印到屏幕的示例应用程序将自己的动态信息引入二进制文件。
先决条件 (Prerequisites)
To follow the example in this article, you will need:
要遵循本文中的示例,您将需要:
A Go workspace set up by following How To Install Go and Set Up a Local Programming Environment.
通过遵循如何安装Go和设置本地编程环境来设置 Go工作区。
构建示例应用程序 (Building Your Sample Application)
Before you can use ldflags
to introduce dynamic data, you first need an application to insert the information into. In this step, you will make this application, which will at this stage only print static versioning information. Let’s create that application now.
在使用ldflags
引入动态数据之前,首先需要一个应用程序来将信息插入其中。 在此步骤中,您将创建此应用程序,该应用程序在此阶段将仅打印静态版本控制信息。 现在创建该应用程序。
In your src
directory, make a directory named after your application. This tutorial will use the application name app
:
在src
目录中,建立一个以应用程序命名的目录。 本教程将使用应用程序名称app
:
mkdir app
mkdir 应用
Change your working directory to this folder:
将工作目录更改为此文件夹:
cd app
光盘应用
Next, using the text editor of your choice, create the entry point of your program, main.go
:
接下来,使用您选择的文本编辑器,创建程序的入口点main.go
:
- nano main.go 纳米main.go
Now, make your application print out version information by adding the following contents:
现在,通过添加以下内容使您的应用程序打印出版本信息:
package main
import (
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
}
Inside of the main()
function, you declared the Version
variable, then printed the string Version:
, followed by a tab character, \t
, and then the declared variable.
在main()
函数内部,您声明了Version
变量,然后打印了字符串 Version:
:,后跟一个制表符\t
,然后声明了变量。
At this point, the variable Version
is defined as development
, which will be the default version for this app. Later on, you will change this value to be an official version number, arranged according to semantic versioning format.
此时,变量Version
定义为development
,它将是此应用程序的默认版本。 稍后,您将把该值更改为正式版本号,根据语义版本格式进行排列。
Save and exit the file. Once this is done, build and run the application to confirm that it prints the correct version:
保存并退出文件。 完成此操作后,构建并运行该应用程序以确认其打印正确的版本:
- go build 去建造
./app
./ 应用
You will see the following output:
您将看到以下输出:
- Version: development 版本:开发
You now have an application that prints default version information, but you do not yet have a way to pass in current version information at build time. In the next step, you will use -ldflags
and go build
to solve this problem.
现在,您有了一个可以打印默认版本信息的应用程序,但是您尚无法在构建时传递当前版本信息。 在下一步中,您将使用-ldflags
并go build
以解决此问题。
go build
使用ldflags
(Using ldflags
with go build
)
As mentioned before, ldflags
stands for linker flags, and is used to pass in flags to the underlying linker in the Go toolchain. This works according to the following syntax:
如前所述, ldflags
代表链接器标志 ,用于将标志传递到Go工具链中的基础链接器。 这根据以下语法工作:
go build -ldflags="-flag"
转到build -ldflags =“- flag ”
In this example, we passed in flag
to the underlying go tool link
command that runs as a part of go build
. This command uses double quotes around the contents passed to ldflags
to avoid breaking characters in it, or characters that the command line might interpret as something other than what we want. From here, you could pass in many different link
flags. For the purposes of this tutorial, we will use the -X
flag to write information into the variable at link time, followed by the package path to the variable and its new value:
在此示例中,我们将flag
传递给了底层go tool link
命令,该命令作为go build
的一部分运行。 此命令在传递给ldflags
的内容周围使用双引号,以避免破坏其中的字符或命令行可能解释为我们想要的字符以外的字符。 从这里,您可以传入许多不同的link
标志 。 在本教程中,我们将使用-X
标志在链接时将信息写入变量,然后是变量的包路径及其新值:
go build -ldflags="-X 'package_path.variable_name=new_value'"
去构建-ldflags = “ - X 'package_path 变量名 = NEW_VALUE。'”
Inside the quotes, there is now the -X
option and a key-value pair that represents the variable to be changed and its new value. The .
character separates the package path and the variable name, and single quotes are used to avoid breaking characters in the key-value pair.
在引号内,现在有-X
选项和一个键值对 ,代表要更改的变量及其新值。 的.
字符分隔包路径和变量名,并且单引号用于避免在键值对中破坏字符。
To replace the Version
variable in your example application, use the syntax in the last command block to pass in a new value and build the new binary:
要在示例应用程序中替换Version
变量,请使用最后一个命令块中的语法传递新值并构建新的二进制文件:
- go build -ldflags="-X 'main.Version=v1.0.0'" go build -ldflags =“-X'main.Version = v1.0.0'”
In this command, main
is the package path of the Version
variable, since this variable is in the main.go
file. Version
is the variable that you are writing to, and v1.0.0
is the new value.
在此命令中, main
是Version
变量的程序包路径,因为此变量位于main.go
文件中。 Version
是您要写入的变量,而v1.0.0
是新值。
In order to use ldflags
, the value you want to change must exist and be a package level variable of type string
. This variable can be either exported or unexported. The value cannot be a const
or have its value set by the result of a function call. Fortunately, Version
fits all of these requirements: It was already declared as a variable in the main.go
file, and the current value (development
) and the desired value (v1.0.0
) are both strings.
为了使用ldflags
,要更改的值必须存在并且必须是string
类型的程序包级变量。 此变量可以导出或不导出。 该值不能是const
或通过函数调用的结果来设置其值。 幸运的是, Version
满足了所有这些要求:在main.go
文件中已经将它声明为变量,并且当前值( development
)和所需值( v1.0.0
)都是字符串。
Once your new app
binary is built, run the application:
构建新的app
二进制文件后,运行该应用程序:
./app
./ 应用
You will receive the following output:
您将收到以下输出:
Version: v1.0.0
版本: v1.0.0
Using -ldflags
, you have succesfully changed the Version
variable from development
to v1.0.0
.
使用-ldflags
成功地将Version
变量从development
更改为v1.0.0
。
You have now modified a string
variable inside of a simple application at build time. Using ldflags
, you can embed version details, licensing information, and more into a binary ready for distribution, using only the command line.
现在,您已在构建时在简单应用程序内部修改了string
变量。 使用ldflags
,您可以仅使用命令行将版本详细信息,许可信息以及其他内容嵌入准备分发的二进制文件中。
In this example, the variable you changed was in the main
program, reducing the difficulty of determining the path name. But sometimes the path to these variables is more complicated to find. In the next step, you will write values to variables in sub-packages to demonstrate the best way to determine more complex package paths.
在这个例子中,你改变了变量是在main
程序,减少确定路径名称的难度。 但是有时候找到这些变量的路径更加复杂。 在下一步中,您将把值写入子包中的变量,以演示确定更复杂的包路径的最佳方法。
定位子包变量 (Targeting Sub-Package Variables)
In the last section, you manipulated the Version
variable, which was at the top-level package of the application. But this is not always the case. Often it is more practical to place these variables in another package, since main
is not an importable package. To simulate this in your example application, you will create a new sub-package, app/build
, that will store information about the time the binary was built and the name of the user that issued the build command.
在上一节中,您操纵了Version
变量,该变量位于应用程序的顶级程序包中。 但这并非总是如此。 通常将这些变量放在另一个包中更为实用,因为main
并不是可导入的包。 为了在示例应用程序中对此进行模拟,您将创建一个新的子包app /build
,该子包将存储有关生成二进制文件的时间和发出build命令的用户名的信息。
To add a new sub-package, first add a new directory to your project named build
:
要添加一个新的子包,首先向您的项目中添加一个名为build
的新目录:
- mkdir -p build mkdir -p构建
Then create a new file named build.go
to hold the new variables:
然后创建一个名为build.go
的新文件来保存新变量:
- nano build/build.go 纳米build / build.go
In your text editor, add new variables for Time
and User
:
在您的文本编辑器中,为Time
和User
添加新变量:
package build
var Time string
var User string
The Time
variable will hold a string representation of the time when the binary was built. The User
variable will hold the name of the user who built the binary. Since these two variables will always have values, you don’t need to initialize these variables with default values like you did for Version
.
Time
变量将保存二进制文件生成时间的字符串表示形式。 User
变量将保存构建二进制文件的用户的名称。 由于这两个变量将始终具有值,因此您无需像对Version
那样使用默认值初始化这些变量。
Save and exit the file.
保存并退出文件。
Next, open main.go
to add these variables to your application:
接下来,打开main.go
将这些变量添加到您的应用程序中:
- nano main.go 纳米main.go
Inside of main.go
, add the following highlighted lines:
在main.go
内部,添加以下突出显示的行:
package main
import (
"app/build"
"fmt"
)
var Version = "development"
func main() {
fmt.Println("Version:\t", Version)
fmt.Println("build.Time:\t", build.Time)
fmt.Println("build.User:\t", build.User)
}
In these lines, you first imported the app/build
package, then printed build.Time
and build.User
in the same way you printed Version
.
在这些行中,您首先导入了app /build
包,然后以与打印Version
相同的方式打印build.Time
和build.User
。
Save the file, then exit from your text editor.
保存文件,然后退出文本编辑器。
Next, to target these variables with ldflags
, you could use the import path app/build
followed by .User
or .Time
, since you already know the import path. However, to simulate a more complex situation in which the path to the variable is not evident, let’s instead use the nm
command in the Go tool chain.
接下来,要使用ldflags
定位这些变量,可以使用导入路径app /build
后跟.User
或.Time
,因为您已经知道导入路径。 但是,要模拟一个更复杂的情况(其中变量的路径不明显),让我们在Go工具链中使用nm
命令。
The go tool nm
command will output the symbols involved in a given executable, object file, or archive. In this case, a symbol refers to an object in the code, such as a defined or imported variable or function. By generating a symbol table with nm
and using grep
to search for a variable, you can quickly find information about its path.
go tool nm
命令将输出给定可执行文件,目标文件或归档文件中涉及的符号 。 在这种情况下,符号指代代码中的对象,例如已定义或导入的变量或函数。 通过使用nm
生成符号表并使用grep
搜索变量,您可以快速找到有关其路径的信息。
Note: The nm
command will not help you find the path of your variable if the package name has any non-ASCII characters, or a "
or %
character, as that is a limitation of the tool itself.
注意:如果程序包名称包含任何非ASCII字符或"
或%
字符,则nm
命令将无法帮助您找到变量的路径,因为这是工具本身的限制。
To use this command, first build the binary for app
:
要使用此命令,请首先为app
构建二进制文件:
- go build 去建造
Now that app
is built, point the nm
tool at it and search through the output:
现在已经构建了该app
,将nm
工具指向它并搜索输出:
go tool nm ./app | grep app
工具nm ./ app | grep 应用
When run, the nm
tool will output a lot of data. Because of this, the preceding command used |
to pipe the output to the grep
command, which then searched for terms that had the top-level app
in the title.
运行时, nm
工具将输出大量数据。 因此,前面的命令使用了|
将输出传递给grep
命令,然后该命令搜索标题中具有顶级app
术语。
You will receive output similar to this:
您将收到类似于以下的输出:
Output
55d2c0 D app/build.Time
55d2d0 D app/build.User
4069a0 T runtime.appendIntStr
462580 T strconv.appendEscapedRune
. . .
In this case, the first two lines of the result set contain the paths to the two variables you are looking for: app/build.Time
and app/build.User
.
在这种情况下,结果集的前两行包含您要查找的两个变量的路径: app /build.Time
和app /build.User
。
Now that you know the paths, build the application again, this time changing Version
, User
, and Time
at build time. To do this, pass multiple -X
flags to -ldflags
:
现在您已经知道了路径,再次构建应用程序,这次在构建时更改Version
, User
和Time
。 为此, -ldflags
多个-X
标志传递给-ldflags
:
- go build -v -ldflags="-X 'main.Version=v1.0.0' -X 'app/build.User=$(id -u -n)' -X 'app/build.Time=$(date)'" 去建立-v -ldflags =“-X'main.Version = v1.0.0'-X'app / build.User = $(id -u -n)'-X'app / build.Time = $(date) '”
Here you passed in the id -u -n
Bash command to list the current user, and the date
command to list the current date.
在这里,您传入了id -u -n
Bash命令以列出当前用户,并传递了date
命令以列出当前日期。
Once the executable is built, run the program:
生成可执行文件后,运行程序:
./app
./ 应用
This command, when run on a Unix system, will generate similar output to the following:
在Unix系统上运行时,此命令将产生与以下类似的输出:
Output
Version: v1.0.0
build.Time: Fri Oct 4 19:49:19 UTC 2019
build.User: sammy
Now you have a binary that contains versioning and build information that can provide vital assistance in production when resolving issues.
现在,您有了一个二进制文件,其中包含版本控制和构建信息,这些信息可以在解决问题时为生产提供重要的帮助。
结论 (Conclusion)
This tutorial showed how, when applied correctly, ldflags
can be a powerful tool for injecting valuable information into binaries at build time. This way, you can control feature flags, environment information, versioning information, and more without introducing changes to your source code. By adding ldflags
to your current build workflow you can maximize the benefits of Go’s self-contained binary distribution format.
本教程说明了正确应用ldflags
可以如何在构建时将有价值的信息注入二进制文件的强大工具。 这样,您可以控制功能标志,环境信息,版本信息等,而无需对源代码进行更改。 通过将ldflags
添加到当前的构建工作流程中,您可以最大限度地利用Go的自包含二进制分发格式的好处。
If you would like to learn more about the Go programming language, check out our full How To Code in Go series. If you are looking for more solutions for version control, try our How To Use Git reference guide.
如果您想了解有关Go编程语言的更多信息,请查看我们完整的“ 如何在Go中编码”系列文章 。 如果您正在寻找版本控制的更多解决方案,请尝试我们的《 如何使用Git》参考指南。
翻译自: https://www.digitalocean.com/community/tutorials/using-ldflags-to-set-version-information-for-go-applications
ldflags