跳转到主要内容

介绍


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

让我们以不同客户功能集的问题为例。在编写某些应用程序时,您可能希望控制在二进制文件中包含哪些功能,例如提供 Free、Pro 和 Enterprise 级别的应用程序。随着客户在这些应用程序中提高订阅级别,更多功能将解锁并可用。要解决此问题,您可以维护单独的项目并尝试通过使用 import 语句使它们彼此同步。虽然这种方法可行,但随着时间的推移,它会变得乏味且容易出错。另一种方法是使用构建标签。

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

先决条件


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

  • 按照如何安装 Go 和设置本地编程环境设置的 Go 工作区。


构建自由版本


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

在 src 目录中,使用您的应用程序的名称创建一个文件夹。本教程将使用应用程序:

mkdir app


移入此文件夹:

cd app


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

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 循环来遍历特征切片并打印屏幕可用的所有特征。

保存并退出文件。现在此文件已保存,我们将不再需要在本文的其余部分对其进行编辑。相反,我们将使用构建标签来更改我们将从它构建的二进制文件的功能。

构建并运行程序:

go build
./app


您将收到以下输出:

Output
> Free Feature #1
> Free Feature #2


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

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

使用 go build 添加 Pro 功能


到目前为止,我们一直避免对 main.go 进行更改,模拟一个常见的生产环境,在该环境中需要添加代码而不更改并且可能破坏主代码。由于我们无法编辑 main.go 文件,我们需要使用另一种机制来使用构建标签将更多功能注入到功能切片中。

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

nano pro.go


编辑器打开文件后,添加以下行:

pro.go

package main

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

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

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

go build


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

./app


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

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


该应用程序现在包括 Pro 和 Free 功能。然而,这是不可取的:因为版本之间没有区别,免费版现在包含了应该只在专业版中可用的功能。要解决此问题,您可以包含更多代码来管理应用程序的不同层,或者您可以使用构建标签来告诉 Go 工具链要构建哪些 .go 文件以及忽略哪些文件。让我们在下一步中添加构建标签。

添加构建标签


您现在可以使用构建标签来区分应用程序的专业版和免费版。

让我们从检查构建标签的样子开始:

// +build tag_name


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

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.go 文件的顶部添加这个标签,go build 命令现在将只包含带有 pro 标签的 pro.go 文件。

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

go build
./app


您将收到以下输出:




Output

> Free Feature #1 

> Free Feature #2


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

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

go build -tags pro


这将输出以下内容:

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


现在我们只有在使用 pro build 标签构建应用程序时才能获得额外的功能。

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

构建标签布尔逻辑


当 Go 包中有多个构建标签时,标签之间使用布尔逻辑进行交互。为了证明这一点,我们将使用 pro 标签和企业标签添加应用程序的企业级别。

为了构建 Enterprise 二进制文件,我们需要同时包含默认功能、Pro 级别功能和一组新的 Enterprise 功能。首先,打开一个编辑器并创建一个新文件 enterprise.go,它将添加新的 Enterprise 功能:

nano enterprise.go


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

enterprise.go

package main

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

保存并退出文件。

目前 enterprise.go 文件没有任何 build 标签,正如您在添加 pro.go 时了解到的,这意味着这些功能将在执行 go.build 时添加到 Free 版本中。 对于 pro.go,您在文件顶部添加了 // +build pro 和换行符,告诉 go build 只有在使用 -tags pro 时才应该包含它。 在这种情况下,您只需要一个构建标签即可完成目标。 但是,在添加新的 Enterprise 功能时,您首先还必须拥有 Pro 功能。

让我们先在 enterprise.go 中添加对 pro build 标签的支持。 使用文本编辑器打开文件:

nano enterprise.go


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

enterprise.go

// +build pro

package main

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

保存并退出文件。

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

go build
./app


您将收到以下输出:

Output
> Free Feature #1
> Free Feature #2


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

go build -tags pro
./app


您将收到以下输出:

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


这仍然不是我们所需要的:当我们尝试构建 Pro 版本时,企业功能现在会出现。为了解决这个问题,我们需要使用另一个构建标签。然而,与 pro 标签不同,我们现在需要确保 pro 和企业功能都可用。

Go 构建系统通过允许在构建标签系统中使用一些基本的布尔逻辑来解决这种情况。

让我们再次打开 enterprise.go:

nano enterprise.go

在 pro 标签所在的同一行添加另一个构建标签 enterprise:

enterprise.go

// +build pro enterprise

package main

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

保存并关闭文件。

现在让我们使用新的企业构建标签来编译和运行应用程序。

go build -tags enterprise
./app


这将给出以下内容:

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


现在我们失去了 Pro 功能。 这是因为当我们将多个构建标签放在 .go 文件的同一行时,go build 会将它们解释为使用 OR 逻辑。 添加行 // +build pro enterprise 后,如果 pro build 标签或 enterprise build 标签存在,则将构建 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",
  )
}

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

go build -tags enterprise
./app


您将收到以下输出:

Output
> Free Feature #1
> Free Feature #2


仍然不完全存在:因为 AND 语句要求两个元素都被认为是真实的,所以我们需要同时使用 pro 和 enterprise 构建标签。

让我们再试一次:

go build -tags "enterprise pro"
./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 一起使用,然后您将多个标签与布尔逻辑组合在一起。然后,您构建了一个代表 Free、Pro 和 Enterprise 版本的不同功能集的程序,展示了构建标签可以为您提供对项目的强大控制级别。

文章链接