Go 博客

熟悉工作区

Beth Brown,Go 团队
2022 年 4 月 5 日

Go 1.18 增加了工作区模式,让您可以同时处理多个模块。

您可以通过访问 下载 页面来获取 Go 1.18。 发行说明 包含所有更改的更多详细信息。

工作区

Go 1.18 中的 工作区 允许您同时处理多个模块,而无需编辑每个模块的 go.mod 文件。工作区内的每个模块在解析依赖项时都会被视为主模块。

以前,要在其中一个模块中添加一个功能并在另一个模块中使用它,您需要发布第一个模块的更改,或者使用 replace 指令 编辑 go.mod 文件以引用您本地的、未发布的模块更改。为了无误地发布,您必须在将本地更改发布到第一个模块后,从依赖模块的 go.mod 文件中删除 replace 指令。

使用 Go 工作区,您可以通过工作区目录根目录中的 go.work 文件来控制所有依赖项。go.work 文件包含 usereplace 指令,它们会覆盖单个 go.mod 文件,因此无需单独编辑每个 go.mod 文件。

要创建工作区,请在工作区目录列表上运行 go work init,并将模块目录作为空格分隔的参数。工作区不需要包含您正在处理的模块。init 命令会创建一个 go.work 文件,其中列出了工作区中的模块。如果您在没有参数的情况下运行 go work init,该命令将创建一个空工作区。

要将模块添加到工作区,请运行 go work use [moddir] 或手动编辑 go.work 文件。运行 go work use -r . 可以递归地将参数目录中带有 go.mod 文件的目录添加到您的工作区。如果一个目录没有 go.mod 文件,或者不再存在,则该目录的 use 指令将从您的 go.work 文件中移除。

go.work 文件的语法与 go.mod 文件类似,并包含以下指令:

  • go:go 工具链版本,例如 go 1.18
  • use:将磁盘上的模块添加到工作区中的主模块集。其参数是包含模块 go.mod 文件的目录的相对路径。use 指令不会添加指定目录子目录中的模块。
  • replace:与 go.mod 文件中的 replace 指令类似,go.work 文件中的 replace 指令会将模块的 *特定版本* 或 *所有版本* 的内容替换为在其他地方找到的内容。

工作流

工作区非常灵活,支持各种工作流。以下部分将简要概述我们认为最常见的工作流。

向外部模块添加功能并在您自己的模块中使用

  1. 为您的工作区创建一个目录。

  2. 克隆您要编辑的外部模块。

  3. 在外部模块的本地版本中添加您的功能。

  4. 在工作区文件夹中运行 go work init [path-to-upstream-mod-dir]

  5. 修改您自己的模块,以实现添加到外部模块中的功能。

  6. 在工作区文件夹中运行 go work use [path-to-your-module]

    go work use 命令将您的模块路径添加到您的 go.work 文件中。

    go 1.18
    
    use (
           ./path-to-upstream-mod-dir
           ./path-to-your-module
    )
    
  7. 运行并测试您模块中使用新添加的外部模块功能。

  8. 发布带有新功能的外部模块。

  9. 发布使用新功能的您的模块。

在同一存储库中处理多个相互依赖的模块

在同一存储库中处理多个模块时,go.work 文件定义了工作区,而不是使用每个模块 go.mod 文件中的 replace 指令。

  1. 为您的工作区创建一个目录。

  2. 克隆包含您要编辑的模块的存储库。这些模块不必放在您的工作区文件夹中,因为您可以使用 use 指令指定每个模块的相对路径。

  3. 在您的工作区目录中运行 go work init [path-to-module-one] [path-to-module-two]

    示例:您正在处理 example.com/x/tools/groundhog,它依赖于 example.com/x/tools 模块中的其他包。

    克隆存储库,然后在您的工作区文件夹中运行 go work init tools tools/groundhog

    您的 go.work 文件的内容如下所示:

    go 1.18
    
    use (
            ./tools
            ./tools/groundhog
    )
    

    tools 模块中进行的任何本地更改都将由您工作区中的 tools/groundhog 使用。

在依赖项配置之间切换

要在不同的依赖项配置下测试您的模块,您可以创建多个具有单独 go.work 文件的工作区,或者保留一个工作区并在单个 go.work 文件中注释掉您不想要的 use 指令。

创建多个工作区

  1. 为不同的依赖项需求创建单独的目录。
  2. 在每个工作区目录中运行 go work init
  3. 通过 go work use [path-to-dependency] 在每个目录中添加您想要的依赖项。
  4. 在每个工作区目录中运行 go run [path-to-your-module],以使用其 go.work 文件指定的依赖项。

要在同一工作区中测试不同的依赖项,请打开 go.work 文件并添加或注释掉所需的依赖项。

仍在 Gopath 下工作?

也许使用工作区会改变您的想法。GOPATH 用户可以使用位于其 GOPATH 目录根目录下的 go.work 文件来解析其依赖项。工作区并非旨在完全重现所有 GOPATH 工作流,但它们可以创建一个共享 GOPATH 部分便利性,同时仍提供模块优势的设置。

为 GOPATH 创建工作区

  1. 在您的 GOPATH 目录的根目录下运行 go work init
  2. 要在您的工作区中使用本地模块或特定版本作为依赖项,请运行 go work use [path-to-module]
  3. 要替换您模块 go.mod 文件中的现有依赖项,请使用 go work replace [path-to-module]
  4. 要添加 GOPATH 或任何目录中的所有模块,请运行 go work use -r 以递归地将带有 go.mod 文件的目录添加到您的工作区。如果一个目录没有 go.mod 文件,或者不再存在,则该目录的 use 指令将从您的 go.work 文件中移除。

注意:如果您有不带 go.mod 文件但您想添加到工作区的项目,请进入其项目目录并运行 go mod init,然后使用 go work use [path-to-module] 将新模块添加到您的工作区。

工作区命令

除了 go work initgo work use,Go 1.18 还为工作区引入了以下命令:

  • go work sync:将 go.work 文件中的依赖项推回到每个工作区模块的 go.mod 文件中。
  • go work edit:提供一个命令行接口来编辑 go.work,主要供工具或脚本使用。

模块感知构建命令和一些 go mod 子命令会检查 GOWORK 环境变量,以确定它们是否处于工作区上下文中。

如果 GOWORK 变量指向一个以 .work 结尾的文件路径,则启用工作区模式。要确定正在使用哪个 go.work 文件,请运行 go env GOWORK。如果 go 命令不在工作区模式下,输出将为空。

启用工作区模式后,将解析 go.work 文件,以确定工作区模式的三个参数:Go 版本、目录列表和替换列表。

一些可以在工作区模式下尝试的命令(前提是您已经知道它们的作用!)

go work init
go work sync
go work use
go list
go build
go test
go run
go vet

编辑器体验改进

我们特别高兴 Go 的语言服务器 goplsVSCode Go 扩展 的升级,它们使得在 LSP 兼容编辑器中处理多个模块的体验流畅且富有成效。

在工作区内的模块之间,查找引用、代码补全和跳转到定义都可以正常工作。gopls 版本 0.8.1 引入了对 go.work 文件的诊断、补全、格式化和悬停功能。您可以利用这些 gopls 功能与任何 LSP 兼容编辑器。

编辑器特定说明

  • 最新的 vscode-go 版本 通过 Go 状态栏的快速选择菜单,可以快速访问工作区的 go.work 文件。

Access the go.work file via the Go status bar’s Quick Pick menu

  • GoLand 支持工作区,并计划为 go.work 文件添加语法高亮和代码补全功能。

有关在不同编辑器中使用 gopls 的更多信息,请参阅 gopls文档

下一步是什么?

下一篇文章: 何时使用泛型
上一篇文章: Go 如何缓解供应链攻击
博客索引