使用构建标签自定义Go二进制文件

介绍

在Go语言中,构建标签或构建约束是一个添加到代码段的标识符,用于确定在构建过程中何时应将文件包含在包中。这允许您从同一源代码构建Go应用程序的不同版本,并以快速和有序的方式在它们之间切换。许多开发人员使用构建标签来改进构建跨平台兼容应用程序的工作流程,例如需要更改代码以适应不同操作系统之间差异的程序。构建标签也用于集成测试,允许您在集成代码与带有模拟服务或存根的代码之间快速切换,以及在应用程序内不同级别的功能集之间使用。

以不同客户功能集的问题为例。在编写一些应用程序时,您可能希望控制哪些功能包含在二进制文件中,例如一个提供免费专业企业级别的应用程序。随着客户在这些应用程序中提高订阅级别,更多的功能将被解锁并可用。为了解决这个问题,您可以维护单独的项目,并尝试通过使用import语句使它们保持同步。虽然这种方法可以工作,但随着时间的推移,它会变得单调且容易出错。另一种方法将是使用构建标签。

在本文中,您将使用Go中的构建标签来生成具有免费、专业和企业功能集的不同可执行二进制文件的一个示例应用程序。每个版本都将有一组不同的功能可用,其中免费版本是默认的。

先决条件

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

构建免费版本

首先,我们开始构建应用程序的免费版本,因为当运行go build时没有任何构建标签,它将是默认版本。稍后,我们将使用构建标签有选择地向程序中添加其他部分。

src目录下,创建一个以应用程序命名的文件夹。本教程将使用app

  1. mkdir app

进入这个文件夹:

  1. cd app

接下来,在您选择的文本编辑器中创建一个名为main.go的新文本文件:

  1. nano main.go

现在,我们将定义应用程序的免费版本。将以下内容添加到main.go中:

main.go
package main

import "fmt"

var features = []string{
  "Free Feature #1",
  "Free Feature #2",
}

func main() {
  for _, f := range features {
    fmt.Println(">", f)
  }
}

在这个文件中,我们创建了一个程序,它声明了一个名为features切片,其中包含两个代表我们免费应用程序功能的字符串。应用程序中的main()函数使用一个for循环来range遍历features切片,并将所有可用的功能打印到屏幕上。

保存并退出文件。现在文件已经保存,我们将在本文剩余部分不再需要编辑它。相反,我们将使用构建标签来更改我们将从中构建的二进制文件的特征。

构建并运行程序:

  1. go build
  2. ./app

您将收到以下输出:

Output
> Free Feature #1 > Free Feature #2

程序打印出了我们的两个免费功能,完成了我们应用的免费版本。

到目前为止,您创建了一个具有非常基本功能集的应用程序。接下来,您将构建一种在构建时向应用程序添加更多功能的方法。

使用 go build 添加专业功能

到目前为止,我们避免了更改 main.go,模拟了一个常见的生产环境,其中需要添加代码而无需更改和可能破坏主代码。由于我们不能编辑 main.go 文件,因此我们将需要使用另一种机制,即使用构建标签将更多功能注入到 features 切片中。

让我们创建一个名为 pro.go 的新文件,它将使用 init() 函数将更多功能附加到 features 切片:

  1. nano pro.go

一旦编辑器打开文件,请添加以下行:

pro.go
package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

在这段代码中,我们使用了init()在应用程序的main()函数之前运行代码,然后使用append()将Pro功能添加到features切片中。保存并退出文件。

使用go build编译并运行应用程序:

  1. go build

由于我们当前目录下现在有两个文件(pro.gomain.go),go build将从中创建一个二进制文件。执行这个二进制文件:

  1. ./app

这将为您提供以下功能集:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

应用程序现在包括Pro和Free功能。然而,这不是我们所期望的:由于版本之间没有区别,免费版本现在包括了应该只在Pro版本中提供的功能。为了修复这个问题,您可以添加更多代码来管理应用程序的不同层级,或者您可以使用构建标签来告诉Go工具链要构建哪些.go文件以及忽略哪些。我们将在下一步添加构建标签。

添加构建标签

现在,您可以使用构建标签来区分应用程序的Pro版本和Free版本。

让我们从检查构建标签的外观开始:

// +build tag_name

将以下代码作为您的包的第一行,并将替换为您的构建标签的名称,您将此包标记为可以选择性地包含在最终二进制文件中的代码。让我们通过向pro.go文件添加构建标签来查看其实际效果,以告诉go build命令在未指定标签时忽略它。打开文件编辑器:

  1. nano pro.go

然后添加以下突出显示的行:

pro.go
// +build pro

package main

func init() {
  features = append(features,
    "Pro Feature #1",
    "Pro Feature #2",
  )
}

在pro.go文件的顶部,我们添加了// +build pro,后面跟着一个空的新行。这个尾随的新行是必需的,否则Go会将其解释为注释。构建标签声明也必须位于.go文件的顶部。在构建标签之上不能有任何内容,即使是注释。

+build声明告诉go build命令这不是注释,而是一个构建标签。第二部分是pro标签。通过在pro.go文件顶部添加这个标签,go build命令现在仅在存在pro标签时包含pro.go文件。

再次编译并运行应用程序:

  1. go build
  2. ./app

您将收到以下输出:

Output
> Free Feature #1 > Free Feature #2

由于pro.go文件需要存在pro标签,文件被忽略,应用程序在没有它的情况下编译。

在运行go build命令时,我们可以使用-tags标志通过添加标签本身作为参数来有条件地包含编译源代码中的代码。让我们为pro标签执行此操作:

  1. go build -tags pro

以下是输出结果:

Output
> Free Feature #1 > Free Feature #2 > Pro Feature #1 > Pro Feature #2

现在,我们仅在 使用 pro 构建标签构建应用程序时获得额外功能。

如果只有两个版本,这样是可以的,但是当你添加更多的标签时,事情就会变得复杂。在下一步中,为了添加我们应用程序的企业版,我们将使用布尔逻辑连接多个构建标签。

构建标签布尔逻辑

当Go包中有多个构建标签时,这些标签会使用 布尔逻辑 相互作用。为了演示这一点,我们将使用 pro 标签和 enterprise 标签添加我们应用程序的企业级别。

为了构建企业版二进制文件,我们将需要包含默认功能、专业级功能以及一组新的企业功能。首先,打开编辑器并创建一个新文件 enterprise.go,它将添加新的企业功能:

  1. nano enterprise.go

enterprise.go 的内容几乎与 pro.go 相同,但将包含新功能。将以下行添加到文件中:

enterprise.go
package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

保存并退出文件。

当前enterprise.go文件中没有构建标签,正如你在添加pro.go时所了解的,这意味着在执行go.build时,这些功能将被添加到免费版本中。对于pro.go,你在文件顶部添加了// +build pro和一个新行,以告诉go build仅在-tags pro使用时才包含它。在这种情况下,你只需要一个构建标签就能达到目标。然而,在添加新的企业功能时,你首先也必须拥有专业版功能。

  1. nano enterprise.go

首先,让我们为enterprise.go添加对pro构建标签的支持。用你的文本编辑器打开文件:

enterprise.go
// +build pro

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

接下来,在package main声明之前添加构建标签,并确保在构建标签后包含一个新行:

保存并退出文件。

  1. go build
  2. ./app

编译并运行不带任何标签的应用程序:

Output
> Free Feature #1 > Free Feature #2

你将收到以下输出:

  1. go build -tags pro
  2. ./app

企业版功能不再显示在免费版本中。现在,让我们添加pro构建标签,并再次构建和运行应用程序:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

你将收到以下输出:

这仍然不是我们需要的:企业在我们尝试构建专业版本时显示功能。为了解决这个问题,我们需要使用另一个构建标签。然而,与pro标签不同,我们现在需要确保proenterprise功能都可用。Go构建系统通过允许在构建标签系统中使用一些基本的布尔逻辑来处理这种情况。

再次打开enterprise.go

  1. nano enterprise.go

在与pro标签同一行添加另一个构建标签enterprise

enterprise.go
// +build pro enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

保存并关闭文件。

现在,使用新的enterprise构建标签编译并运行应用程序。

  1. go build -tags enterprise
  2. ./app

这将给出以下结果:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2

现在我们失去了Pro功能。这是因为当我们在.go文件的同一行上放置多个构建标签时,go build将它们解释为使用OR逻辑。在添加了行// +build pro enterprise之后,如果存在任一pro构建标签或enterprise构建标签,enterprise.go文件将被构建。我们需要正确设置构建标签,以要求同时存在并使用AND逻辑。

与其将两个标签放在同一行,不如将它们放在不同的行上,这样go build将使用AND逻辑解释这些标签。

再次打开enterprise.go,让我们将构建标签分开到多行。

enterprise.go
// +build pro
// +build enterprise

package main

func init() {
  features = append(features,
    "Enterprise Feature #1",
    "Enterprise Feature #2",
  )
}

现在,使用新的enterprise构建标签编译并运行应用程序。

  1. go build -tags enterprise
  2. ./app

您将收到以下输出:

Output
> Free Feature #1 > Free Feature #2

仍然不太对:因为AND语句需要两个元素都被认为是true,我们需要同时使用proenterprise构建标签。

我们再试一次:

  1. go build -tags "enterprise pro"
  2. ./app

您将收到以下输出:

Output
> Free Feature #1 > Free Feature #2 > Enterprise Feature #1 > Enterprise Feature #2 > Pro Feature #1 > Pro Feature #2

现在,我们的应用程序可以从同一个源代码树以多种方式构建,相应地解锁应用程序的功能。

在这个例子中,我们使用了一个新的// +build标签来表示AND逻辑,但还有其他方法可以用构建标签表示布尔逻辑。以下表格列出了一些构建标签的其他语法格式及其对应的布尔等价物:

Build Tag Syntax Build Tag Sample Boolean Statement
Space-separated elements // +build pro enterprise pro OR enterprise
Comma-separated elements // +build pro,enterprise pro AND enterprise
Exclamation point elements // +build !pro NOT pro

结论

在本教程中,您使用了构建标签来控制哪些代码被编译到二进制文件中。首先,您声明了构建标签并使用它们与go build,然后结合了多个标签与布尔逻辑。然后,您构建了一个程序,该程序代表了免费、专业和企业版的 不同功能集,展示了构建标签可以给您项目带来的强大控制级别。

如果您想了解更多关于构建标签的信息,请查看Golang的相关文档,或者继续探索我们的《如何在Go中编程》系列

Source:
https://www.digitalocean.com/community/tutorials/customizing-go-binaries-with-build-tags