介绍
在 Go 中, 构建标记 或构建约束是添加到一段代码的标识符,用于确定在 build
过程中何时应将文件包含在包中。 这允许您从相同的源代码构建不同版本的 Go 应用程序,并以快速且有条理的方式在它们之间切换。 许多开发人员使用构建标签来改进构建跨平台兼容应用程序的工作流程,例如需要更改代码以解决不同操作系统之间差异的程序。 构建标签也用于 集成测试 ,允许您在集成代码和具有 模拟服务或存根 的代码之间快速切换,并用于应用程序中不同级别的功能集.
让我们以不同客户功能集的问题为例。 在编写某些应用程序时,您可能希望控制在二进制文件中包含哪些功能,例如提供 Free、Pro 和 Enterprise 级别的应用程序。 随着客户在这些应用程序中提高订阅级别,更多功能将解锁并可用。 要解决此问题,您可以维护单独的项目并尝试通过使用 import
语句使它们彼此同步。 虽然这种方法可行,但随着时间的推移,它会变得乏味且容易出错。 另一种方法是使用构建标签。
在本文中,您将使用 Go 中的构建标签生成不同的可执行二进制文件,这些二进制文件提供示例应用程序的免费、专业和企业功能集。 每个都有一组不同的可用功能,免费版本是默认设置。
先决条件
要遵循本文中的示例,您将需要:
- 按照 如何安装 Go 并设置本地编程环境 设置的 Go 工作区。
构建免费版本
让我们从构建应用程序的免费版本开始,因为在没有任何构建标签的情况下运行 go build
时它将是默认版本。 稍后,我们将使用构建标签来选择性地将其他部分添加到我们的程序中。
在 src
目录中,使用您的应用程序名称创建一个文件夹。 本教程将使用 app
:
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
的 slice,它包含两个 字符串 ,它们代表了我们的免费应用程序的功能。 应用程序中的 main()
函数使用 for 循环来确定 到 features
切片的范围,并打印屏幕上可用的所有特征。
保存并退出文件。 现在此文件已保存,我们将不再需要在本文的其余部分对其进行编辑。 相反,我们将使用构建标签来更改我们将从它构建的二进制文件的功能。
构建并运行程序:
go build ./app
您将收到以下输出:
Output> Free Feature #1 > Free Feature #2
该程序打印了我们的两个免费功能,完成了我们应用程序的免费版本。
到目前为止,您创建了一个具有非常基本功能集的应用程序。 接下来,您将构建一种在构建时向应用程序添加更多功能的方法。
使用 go build
添加 Pro 功能
到目前为止,我们一直避免对 main.go
进行更改,模拟了一个常见的生产环境,在该环境中需要添加代码而不更改并可能破坏主代码。 由于我们无法编辑 main.go
文件,我们需要使用另一种机制来使用构建标签将更多功能注入到 features
切片中。
让我们创建一个名为 pro.go
的新文件,它将使用 init() 函数将更多特征附加到 features
切片:
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
编译并运行应用程序:
go build
由于我们当前目录中现在有两个文件(pro.go
和 main.go
),go build
将从这两个文件中创建一个二进制文件。 执行这个二进制文件:
./app
这将为您提供以下功能集:
Output> 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
标签。 通过在 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
构建标签构建应用程序时才能获得额外的功能。
如果只有两个版本,这很好,但是当您添加更多标签时,事情会变得复杂。 为了在下一步中添加我们应用程序的企业版,我们将使用多个构建标签与布尔逻辑连接在一起。
构建标签布尔逻辑
当一个 Go 包中有多个构建标签时,标签之间使用 布尔逻辑 进行交互。 为了演示这一点,我们将使用 pro
标记和 enterprise
标记来添加应用程序的企业级。
为了构建 Enterprise 二进制文件,我们需要同时包含默认功能、Pro 级别功能和一组新的 Enterprise 功能。 首先,打开一个编辑器并创建一个新文件 enterprise.go
,它将添加新的 Enterprise 功能:
nano enterprise.go
enterprise.go
的内容看起来几乎与 pro.go
相同,但包含新功能。 将以下行添加到文件中:
企业.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
时才应该包含它。 在这种情况下,您只需要一个构建标签即可完成目标。 但是,在添加新的 Enterprise 功能时,您首先还必须拥有 Pro 功能。
让我们首先为 enterprise.go
添加对 pro
构建标签的支持。 使用文本编辑器打开文件:
nano enterprise.go
接下来在 package main
声明之前添加构建标记,并确保在构建标记之后包含换行符:
企业.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
构建标签并再次构建并运行应用程序:
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
和 enterprise
功能都可用。
Go 构建系统通过允许在构建标签系统中使用一些基本的布尔逻辑来解决这种情况。
让我们再次打开enterprise.go
:
nano enterprise.go
在 pro
标签所在的同一行添加另一个构建标签 enterprise
:
企业.go
// +build pro enterprise package main func init() { features = append(features, "Enterprise Feature #1", "Enterprise Feature #2", ) }
保存并关闭文件。
现在让我们使用新的 enterprise
构建标签来编译和运行应用程序。
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
构建标签或 enterprise
构建,则将构建 enterprise.go
文件标签存在。 我们需要正确设置构建标签以要求 both' 并改用 AND
逻辑。
如果我们将它们放在不同的行上,而不是将两个标签放在同一行上,那么 go build
将使用 AND
逻辑来解释这些标签。
再次打开 enterprise.go
,让我们将构建标签分成多行。
企业.go
// +build pro // +build enterprise package main func init() { features = append(features, "Enterprise Feature #1", "Enterprise Feature #2", ) }
现在使用新的 enterprise
构建标签编译并运行应用程序。
go build -tags enterprise ./app
您将收到以下输出:
Output> Free Feature #1 > Free Feature #2
仍然不完全存在:因为 AND
语句需要同时考虑两个元素 true
,我们需要同时使用 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 pro enterprise
|
pro 或 enterprise
|
逗号分隔的元素 | // +build pro,enterprise
|
pro 和 enterprise
|
感叹号元素 | // +build !pro
|
不是 pro
|
结论
在本教程中,您使用构建标签来控制哪些代码被编译到二进制文件中。 首先,您声明了构建标签并将它们与 go build
一起使用,然后您将多个标签与布尔逻辑组合在一起。 然后,您构建了一个代表 Free、Pro 和 Enterprise 版本的不同功能集的程序,展示了构建标签可以为您提供对项目的强大控制级别。
如果您想了解有关构建标签的更多信息,请查看有关该主题的 Golang 文档,或继续探索我们的 如何在 Go 系列中编码 。