Go Wiki: GOPATH

GOPATH 变量

使用标准库之外的依赖项进行 Go 开发是使用 Go 模块完成的。使用 Go 模块时,GOPATH 变量(在 Unix 上默认为 $HOME/go,在 Windows 上默认为 %USERPROFILE%\go)用于以下目的:

  • go install 命令将二进制文件安装到 $GOBIN,默认为 $GOPATH/bin
  • go get 命令将下载的模块缓存到 $GOMODCACHE,默认为 $GOPATH/pkg/mod
  • go get 命令将下载的校验和数据库状态缓存到 $GOPATH/pkg/sumdb

有关 GOPATH 变量的完整详细信息,请参阅 go 命令文档。本页其余部分涉及 GOPATH 开发模式,该模式现已弃用。

GOPATH 开发模式

在 Go 模块之前,使用依赖项进行 Go 开发使用“GOPATH 开发模式”,简称“GOPATH 模式”。在 GOPATH 模式下,go 命令将 GOPATH 变量用于以下目的:

  • go install 命令将二进制文件安装到 $GOBIN,默认为 $GOPATH/bin
  • go install 命令将 import "example.com/y/z" 的编译包文件安装到 $GOPATH/pkg/example.com/y/z.a
  • go get 命令将满足 import "example.com/y/z" 的源代码下载到 $GOPATH/src/example.com/y/z

弃用并移除 GOPATH 开发模式

Go 模块是 GOPATH 开发模式的替代品,旨在将包版本概念引入整个 Go 生态系统。

从 GOPATH 开发模式到 Go 模块的过渡是渐进的,跨越了许多 Go 版本:

  • Go 1.11 (2018 年 8 月) 引入了 GO111MODULE 变量,默认为 auto。当 GO111MODULE=off 时,go 命令始终使用 GOPATH 模式。当 GO111MODULE=on 时,go 命令始终使用模块模式。当 GO111MODULE=auto(或未设置 GO111MODULE)时,go 命令根据当前目录决定模式。如果当前目录位于 $GOPATH/src 之外,并且位于其根目录中包含 go.mod 文件的源代码树中,则 go 命令使用 Go 模块模式。否则,go 命令使用 GOPATH 模式。此规则确保在 auto 模式下,在 $GOPATH/src 中运行的所有命令都不受影响,但允许用户在其他目录中试验模块。

  • Go 1.13 (2019 年 8 月) 调整了 GO111MODULE=auto 模式,移除了 $GOPATH/src 限制:如果 $GOPATH/src 内的目录具有 go.mod 文件,则在该目录或其子目录中运行的命令现在使用模块模式。这允许用户继续以基于导入的层次结构组织其签出代码,但为单个签出使用模块。

  • Go 1.16 (2021 年 2 月) 将默认值更改为 GO111MODULE=on,始终使用模块模式。也就是说,GOPATH 模式将默认完全禁用。需要为下一个版本使用 GOPATH 模式的用户可以明确设置 GO111MODULE=autoGO111MODULE=off

  • Go 1.NN (???) 将完全移除 GO111MODULE 设置和 GOPATH 模式,始终使用模块模式。

请注意,移除 GOPATH 开发模式并意味着移除 GOPATH 变量。它仍将用于本页顶部列出的目的。

常见问题

GOPATH 变量会被移除吗?

不会。GOPATH 变量(在环境中或通过 go env -w 设置)不会被移除。它仍将用于确定默认的二进制文件安装位置、模块缓存位置和校验和数据库缓存位置,如本页顶部所述。

我还能在 GOPATH/src/import/path 中编写代码吗?

是的。许多 Go 开发者欣赏这种约定提供的结构,并将其模块仓库签入其中。您的所有代码只需要一个 go.mod 文件即可开始使用模块。请参阅 go mod init

如何在 GOPATH/src 中的一个仓库中编译另一个仓库中进行的更改?

如果您想在构建另一个模块时使用一个模块中未发布的更改,您可以在另一个模块的 go.mod 中添加一个 replace 行。

例如,如果您已将 golang.org/x/websitegolang.org/x/tools 签出到 $GOPATH/src/golang.org/x/website$GOPATH/src/golang.org/x/tools,那么为了使您的 website 本地构建自动使用 tools 中的更改,您需要在 $GOPATH/src/golang.org/x/website/go.mod 中添加以下内容:

replace golang.org/x/tools => ../tools

当然,replace 指令对 $GOPATH 一无所知。如果您将这两个项目签出到 $HOME/mycode/website$HOME/mycode/tools,同样的行也会正常工作。

GOPATH 开发模式为何被移除?

GOPATH 开发模式的核心是自动提供所有这些 replace 行,以便您为依赖项构建的代码就是您恰好在计算机上签出的代码。这意味着您的构建会受到您可能忘记的旧签出的影响。这意味着您在一台机器上获得的构建可能与另一台机器上的不同,即使从相同顶级仓库的相同版本开始也是如此。这意味着您获得的构建可能与同一项目中的另一个开发者获得的构建不同。Go 模块解决了所有这些可重现性问题。所有这些问题的根本原因是 GOPATH 模式没有任何包版本的概念。

除了可重现性之外,Go 模块还提供了一种清晰的方式来处理代理和安全下载。当您 git clone 一个项目并获取其依赖项时,这些依赖项会经过加密检查(使用 go.sum 文件),以确保它们与原始开发者使用的位相同。唯一受信任的部分是顶级 git clone。同样,这只有在 Go 模块(与 GOPATH 模式相反)具有包版本概念的情况下才可能实现。

对于 Go 本身的未来演进,模块清楚地标记了特定文件树是用哪个版本的 Go 语言编写的。这使得在 Go 的后续版本中可以禁用有问题的功能——例如,string(1),许多人认为它会产生 "1",但实际上会产生 "\x01" (Ctrl-A)——同时保持旧程序能够构建(因为它们明确标记为为旧版本 Go 编写)。

还有更多类似的例子。

所有这些在目前的 GOPATH 开发模式下都不可能实现。我们无法在不淘汰 GOPATH 模式的情况下推动生态系统向前发展并真正依赖 Go 模块的这些重要特性。

(您可能还会问:为什么不直接将这些东西添加到 GOPATH 模式?答案是:我们已经这样做了,结果就是 Go 模块。)

何时决定弃用 GOPATH 开发模式?

最初的计划是在 Go 1.13 中弃用 GOPATH 模式,但我们想花更多时间使模块对尽可能多的 Go 用户更加健壮,因此弃用被推迟到那个版本之后。对 问题 #41330 和 golang-tools 组的讨论没有发现任何剩余的阻碍 GOPATH 弃用的问题,因此现在计划在 Go 1.16 中弃用,并在未来的版本中移除,如上面的时间线所述。

如果我对从 GOPATH 开发模式迁移到 Go 模块有更多疑问,该怎么办?

请参阅 golang.org/help 获取资源列表。如果这些都不合适,请随时在此处提交问题。我们希望每个人都能成功采用 Go 模块。


此内容是 Go Wiki 的一部分。