Go 1.11 Modules翻译自 Go 官方wiki
# Go 1.11 Modules
根据[提议](https://golang.org/design/24301-versioned-go),Go 1.11包括了对版本化模块的初步支持。模块是Go 1.11中的一个实验性选择加入的功能,并[计划](https://blog.golang.org/modules2019)在Go 1.13整合反馈并最终确定功能。尽管某些细节可能会改变,但后续版本将支持使用Go 1.11或1.12定义的模块。
最初的原型(`vgo`)于2018年2月[发布](https://research.swtch.com/vgo)。2018年7月,对版本化模块的支持[合入](https://groups.google.com/d/msg/golang-dev/a5PqQuBljF4/61QK4JdtBgAJ)主库。Go 1.11于2018年8月发布。
请通过现有提[issues](https://github.com/golang/go/wiki/Modules#github-issues)或 [experience reports](https://github.com/golang/go/wiki/ExperienceReports)提供对于modules的反馈。
## 目录
The "Quick Start" and "New Concepts" sections are particularly important for someone who is starting to work with modules. The "How to..." sections cover more details on mechanics. The largest quantity of content on this page is in the FAQs answering more specific questions; it can be worthwhile to at least skim the FAQ one-liners listed here.
* [Quick Start](https://github.com/golang/go/wiki/Modules#quick-start)
* [Example](https://github.com/golang/go/wiki/Modules#example)
* [Daily Workflow](https://github.com/golang/go/wiki/Modules#daily-workflow)
* [New Concepts](https://github.com/golang/go/wiki/Modules#new-concepts)
* [Modules](https://github.com/golang/go/wiki/Modules#modules)
* [go.mod](https://github.com/golang/go/wiki/Modules#gomod)
* [Version Selection](https://github.com/golang/go/wiki/Modules#version-selection)
* [Semantic Import Versioning](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)
* [How to Use Modules](https://github.com/golang/go/wiki/Modules#how-to-use-modules)
* [How to Install and Activate Module Support](https://github.com/golang/go/wiki/Modules#how-to-install-and-activate-module-support)
* [How to Define a Module](https://github.com/golang/go/wiki/Modules#how-to-define-a-module)
* [How to Upgrade and Downgrade Dependencies](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies)
* [How to Prepare for a Release (All Versions)](https://github.com/golang/go/wiki/Modules#how-to-prepare-for-a-release)
* [How to Prepare for a Release (v2 or Higher)](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher)
* [Publishing a Release](https://github.com/golang/go/wiki/Modules#publishing-a-release)
* [Migrating to Modules](https://github.com/golang/go/wiki/Modules#migrating-to-modules)
* [Additional Resources](https://github.com/golang/go/wiki/Modules#additional-resources)
* [Changes Since the Initial Vgo Proposal](https://github.com/golang/go/wiki/Modules#changes-since-the-initial-vgo-proposal)
* [GitHub Issues](https://github.com/golang/go/wiki/Modules#github-issues)
* [FAQs](https://github.com/golang/go/wiki/Modules#faqs)
* [How are versions marked as incompatible?](https://github.com/golang/go/wiki/Modules#how-are-versions-marked-as-incompatible)
* [When do I get old behavior vs. new module-based behavior?](https://github.com/golang/go/wiki/Modules#when-do-i-get-old-behavior-vs-new-module-based-behavior)
* [Why does installing a tool via 'go get' fail with error 'cannot find main module'?](https://github.com/golang/go/wiki/Modules#why-does-installing-a-tool-via-go-get-fail-with-error-cannot-find-main-module)
* [How can I track tool dependencies for a module?](https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module)
* [What is the status of module support in IDEs, editors and standard tools like goimports, gorename, etc.?](https://github.com/golang/go/wiki/Modules#what-is-the-status-of-module-support-in-ides-editors-and-standard-tools-like-goimports-gorename-etc)
* [FAQs — Additional Control](https://github.com/golang/go/wiki/Modules#faqs--additional-control)
* [What community tooling exists for working with modules?](https://github.com/golang/go/wiki/Modules#what-community-tooling-exists-for-working-with-modules)
* [When should I use the 'replace' directive?](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive)
* [Can I work entirely outside of VCS on my local filesystem?](https://github.com/golang/go/wiki/Modules#can-i-work-entirely-outside-of-vcs-on-my-local-filesystem)
* [How do I use vendoring with modules? Is vendoring going away?](https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away)
* [Are there "always on" module repositories and enterprise proxies?](https://github.com/golang/go/wiki/Modules#are-there-always-on-module-repositories-and-enterprise-proxies)
* [Can I control when go.mod gets updated and when the go tools use the network to satisfy dependencies?](https://github.com/golang/go/wiki/Modules#can-i-control-when-gomod-gets-updated-and-when-the-go-tools-use-the-network-to-satisfy-dependencies)
* [How do I use modules with CI systems such as Travis or CircleCI?](https://github.com/golang/go/wiki/Modules#how-do-i-use-modules-with-ci-systems-such-as-travis-or-circleci)
* [FAQs — go.mod and go.sum](https://github.com/golang/go/wiki/Modules#faqs--gomod-and-gosum)
* [Why does 'go mod tidy' record indirect and test dependencies in my 'go.mod'?](https://github.com/golang/go/wiki/Modules#why-does-go-mod-tidy-record-indirect-and-test-dependencies-in-my-gomod)
* [Is 'go.sum' a lock file? Why does 'go.sum' include information for module versions I am no longer using?](https://github.com/golang/go/wiki/Modules#is-gosum-a-lock-file-why-does-gosum-include-information-for-module-versions-i-am-no-longer-using)
* [Should I still add a 'go.mod' file if I do not have any dependencies?](https://github.com/golang/go/wiki/Modules#should-i-still-add-a-gomod-file-if-i-do-not-have-any-dependencies)
* [Should I commit my 'go.sum' file as well as my 'go.mod' file?](https://github.com/golang/go/wiki/Modules#should-i-commit-my-gosum-file-as-well-as-my-gomod-file)
* [FAQs — Semantic Import Versioning](https://github.com/golang/go/wiki/Modules#faqs--semantic-import-versioning)
* [Why must major version numbers appear in import paths?](https://github.com/golang/go/wiki/Modules#why-must-major-version-numbers-appear-in-import-paths)
* [Why are major versions v0, v1 omitted from import paths?](https://github.com/golang/go/wiki/Modules#why-are-major-versions-v0-v1-omitted-from-import-paths)
* [What are some implications of tagging my project with major version v0, v1, or making breaking changes with v2+?](https://github.com/golang/go/wiki/Modules#what-are-some-implications-of-tagging-my-project-with-major-version-v0-v1-or-making-breaking-changes-with-v2)
* [Can a module consume a package that has not opted in to modules?](https://github.com/golang/go/wiki/Modules#can-a-module-consume-a-package-that-has-not-opted-in-to-modules)
* [Can a module consume a v2+ package that has not opted into modules? What does '+incompatible' mean?](https://github.com/golang/go/wiki/Modules#can-a-module-consume-a-v2-package-that-has-not-opted-into-modules-what-does-incompatible-mean)
* [How are v2+ modules treated in a build if modules support is not enabled? How does "minimal module compatibility" work in 1.9.7+, 1.10.3+, and 1.11?](https://github.com/golang/go/wiki/Modules#how-are-v2-modules-treated-in-a-build-if-modules-support-is-not-enabled-how-does-minimal-module-compatibility-work-in-197-1103-and-111)
* [What happens if I create a go.mod but do not apply semver tags to my repository?](https://github.com/golang/go/wiki/Modules#what-happens-if-i-create-a-gomod-but-do-not-apply-semver-tags-to-my-repository)
* [Can a module depend on a different version of itself?](https://github.com/golang/go/wiki/Modules#can-a-module-depend-on-a-different-version-of-itself)
* [FAQs — Multi-Module Repositories](https://github.com/golang/go/wiki/Modules#faqs--multi-module-repositories)
* [What are multi-module repositories?](https://github.com/golang/go/wiki/Modules#what-are-multi-module-repositories)
* [Should I have multiple modules in a single repository?](https://github.com/golang/go/wiki/Modules#should-i-have-multiple-modules-in-a-single-repository)
* [Is it possible to add a module to a multi-module repository?](https://github.com/golang/go/wiki/Modules#is-it-possible-to-add-a-module-to-a-multi-module-repository)
* [Is it possible to remove a module from a multi-module repository?](https://github.com/golang/go/wiki/Modules#is-it-possible-to-remove-a-module-from-a-multi-module-repository)
* [Can a module depend on an internal/ in another?](https://github.com/golang/go/wiki/Modules#can-a-module-depend-on-an-internal-in-another)
* [Can an additional go.mod exclude unnecessary content? Do modules have the equivalent of a .gitignore file?](https://github.com/golang/go/wiki/Modules#can-an-additional-gomod-exclude-unnecessary-content-do-modules-have-the-equivalent-of-a-gitignore-file)
* [FAQs — Minimal Version Selection](https://github.com/golang/go/wiki/Modules#faqs--minimal-version-selection)
* [Won't minimal version selection keep developers from getting important updates?](https://github.com/golang/go/wiki/Modules#wont-minimal-version-selection-keep-developers-from-getting-important-updates)
* [FAQs — Possible Problems](https://github.com/golang/go/wiki/Modules#faqs--possible-problems)
* [What are some general things I can spot check if I am seeing a problem?](https://github.com/golang/go/wiki/Modules#what-are-some-general-things-i-can-spot-check-if-i-am-seeing-a-problem)
* [What can I check if I am not seeing the expected version of a dependency?](https://github.com/golang/go/wiki/Modules#what-can-i-check-if-i-am-not-seeing-the-expected-version-of-a-dependency)
* [Why am I getting an error 'cannot find module providing package foo'?](https://github.com/golang/go/wiki/Modules#why-am-i-getting-an-error-cannot-find-module-providing-package-foo)
* [Why does 'go mod init' give the error 'cannot determine module path for source directory'?](https://github.com/golang/go/wiki/Modules#why-does-go-mod-init-give-the-error-cannot-determine-module-path-for-source-directory)
* [I have a problem with a complex dependency that has not opted in to modules. Can I use information from its current dependency manager?](https://github.com/golang/go/wiki/Modules#i-have-a-problem-with-a-complex-dependency-that-has-not-opted-in-to-modules-can-i-use-information-from-its-current-dependency-manager)
* [How can I resolve "parsing go.mod: unexpected module path" and "error loading module requirements" errors caused by a mismatch between import paths vs. declared module identity?](https://github.com/golang/go/wiki/Modules#how-can-i-resolve-parsing-gomod-unexpected-module-path-and-error-loading-module-requirements-errors-caused-by-a-mismatch-between-import-paths-vs-declared-module-identity)
* [Why does 'go build' require gcc, and why are prebuilt packages such as net/http not used?](https://github.com/golang/go/wiki/Modules#why-does-go-build-require-gcc-and-why-are-prebuilt-packages-such-as-nethttp-not-used)
* [Do modules work with relative imports like `import "./subdir"`?](https://github.com/golang/go/wiki/Modules#do-modules-work-with-relative-imports-like-import-subdir)
* [Some needed files may not be present in populated vendor directory](https://github.com/golang/go/wiki/Modules#some-needed-files-may-not-be-present-in-populated-vendor-directory)
## Quick Start
#### Example
这里是一个从零开始创建模块的简单示例,详细信息将在本页的其余部分中介绍。
GOPATH目录外创建一个目录:
```
$ mkdir -p /tmp/scratchpad/hello
$ cd /tmp/scratchpad/hello
```
初始化一个新的module:
```
$ go mod init github.com/you/hello
go: creating new go.mod: module github.com/you/hello
```
编写代码:
```
$ cat <
package main
import (
"fmt"
"rsc.io/quote"
)
func main() {
fmt.Println(quote.Hello())
}
EOF
```
构建并运行:
```
$ go build
$ ./hello
Hello, world.
```
`go.mod`文件会更新,其中包含了依赖的显式版本,这里的`v1.5.2`是一个[语义化版本](https://semver.org) tag:
```
$ cat go.mod
module github.com/you/hello
require rsc.io/quote v1.5.2
```
#### 日常工作流
注意,上面的示例中不需要`go get`。
您的典型日常工作流如下:
* 根据需要将import语句添加到`.go`代码中。
* 标准命令如`go build`或`go test`将根据需要自动添加新的依赖项以满足imports(更新`go.mod`并下载新的依赖项)。
* 在需要时,可以使用诸如`go get [email protected]`、`go get foo@master`、`go get foo@e3702bed2`或直接编辑`go.mod`等命令选择更具体的依赖项版本。
简要介绍您可能使用的其他常见功能:
*`go list-m all` - 查看构建中所有直接和间接依赖的最终版本([详细信息](https://github.com/golang/go/wiki/Modules#version-selection))
*`go list-u-m all` - 查看所有的直接和间接依赖的可用次要版本和补丁升级([详细信息](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies))
*`go get-u`或'go get-u=patch` - 将所有直接和间接依赖更新为最新的次要版本或补丁升级(忽略预发布版本)([详细信息](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies))
*`go build./…`或'go test./…` - 从模块根目录运行时,构建或测试模块中的所有包([详细信息](https://github.com/golang/go/wiki/modules#how-to-define-a-module))
*`go mod tidy`-从`go.mod`删除不再需要的依赖,添加添加操作系统、体系结构或构建tags的所需的依赖项([详细信息](https://github.com/golang/go/wiki/modules#how-to-prepare-for-a-release))
*`replace`或`gohack` - 使用fork、本地副本或依赖的确切版本([详细信息](https://github.com/golang/go/wiki/modules#when-should-i-use-the-replace-directive))
*`go mod vendor` - 可选步骤,创建`vendor`目录([详细信息](https://github.com/golang/go/wiki/modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away))
在阅读了接下来的四节“新概念”之后,您将获取足够的信息在大部分项目中开始使用Modules。查看上面的[目录](https://github.com/golang/go/wiki/Modules#table-of-contents)也很有用(包括常见问题的解答),以便熟悉更详细的主题列表。
## 新概念
这些部分提供了对主要新概念的高级介绍。欲了解更多细节和理由,请参阅这40分钟的介绍性视频[Russ Cox描述设计背后的理念](https://www.youtube.com/watch?v=f8nrpe0xwrg&list=plq2nv-sh8ebbijqgdzapofeffvvvv5bgoope&index=3&t=0s),[官方提议文件](https://golang.org/design/24301-versioned-go),或更详细的起源[vgo blog series](https://research.swtch.com/vgo)。
### Modules
*module*是作为一起进行版本化的相关go包的集合。
Modules记录精确的依赖性求并创建可复制的构建。
通常,版本控制库只包含存储库根目录中定义的一个模块。([一个存储库也支持多模块](https://github.com/golang/go/wiki/Modules#faqs--multi-module-repositories),但通常情况下,这会导致在单仓库单模块的基础上进行更多的工作)。
总结存储库、模块和包之间的关系:
*存储库包含一个或多个go模块。
*每个模块包含一个或多个GO包。
*每个包由单个目录中的一个或多个go源文件组成。
模块必须根据[semver](https://semver.org/)进行语义版本化,通常采用`v(major)(minor)(patch)`的形式,如`v0.1.0`、`v1.2.3`或`v1.5.0-rc.1`。前导`v`是必需的。如果使用git,则发布[tag](https://git-scm.com/book/en/v2/git-basics-taging)并提交其版本。公共和私有模块存储库和代理正变得可用(请参阅[下面的FAQ](https://github.com/golang/go/wiki/Modules#are-there-always-on-module-repositories-and-enterprise-proxies)。
### go.mod
一个模块由go源文件树定义,其根目录中有一个`go.mod`文件。
模块源代码可能位于GoPath之外。这里包含了四个指令:`module`、`require`、`replace`、`exclude`。
下面是定义`github.com/my/thing`模块的`go.mod`文件示例:
```
module github.com/my/thing
require (
github.com/some/dependency v1.2.3
github.com/another/dependency/v4 v4.0.0
)
```
模块通过`module`指令在`go.mod`中声明其标识,也提供了其_模块路径_。模块中所有包的导入路径公共享这个模块路径作为公共前缀。模块路径和从'go.mod'到包目录的相对路径合在一起决定了包的导入路径。
例如,如果要为存储库`github.com/my/repo`创建一个模块,该存储库将包含两个导入路径为`github.com/my/repo/foo`和`github.com/my/repo/bar`的包,则`go.mod`文件中的第一行通常会将模块路径声明为`module github.com/my/repo`,相应的磁盘结构如下:
```
repo/
├── go.mod
├── bar
│ └── bar.go
└── foo
└── foo.go
```
在go源代码中,包是使用完整路径(包括模块路径)导入的。例如,如果一个模块在其`go.mod`中声明其标识为`module example.com/my/module`,则用户可以执行以下操作:
```
import "example.com/my/module/mypkg"
```
这将从`example.com/my/module`模块导入`mypkg`包。
`exclude`和`replace`指令仅在当前模块(主模块)上起作用。在构建主模块时,主模块以外的模块中的`exclude`和`replace`指令将被忽略。因此,`replace`和`exclude`语句允许主模块完全控制自己的构建,而不受依赖项的完全控制。(参见 [下面的FAQ](https://github.com/golang/go/wiki/Modules#when-should-i-use-the-replace-directive) 了解何时使用`replace`指令)。
### 版本选择
如果你在源代码中添加了一个新的导入,而这个导入还没有在`go.mod`中的`require`中覆盖,那么大多数go命令如`go build`和`go test`会自动查找正确的模块并添加 *最高* 版本的新直接依赖到你的模块的`go.mod`的`require`指令下面。 例如,如果新导入的最新标记发布版本为`v1.2.3`的依赖项M,则模块的`go.mod`将以`require M v1.2.3`结束,这表示模块M是允许版本 >= v1.2.3的依赖(同时< v2,v2被认为与v1不兼容)。
*最小版本选择(minimal version selection)*算法用于选择构建中使用的所有模块的版本。对于构建中的每个模块,通过最小版本选择而选择的版本始终是主模块中的`require`指令或其依赖项之一明确列出的版本的语义化*最高*。
例如,如果你的模块依赖于具有`require D v1.0.0`的模块A,并且你的模块也依赖于具有`require D v1.1.1`的模块B,那么最小版本选择将选择`v1.1.1`的D包含在构建中(鉴于它是列出的最高`require`版本)。 这种`v1.1.1`的选择保持一致,即使一段时间后D的`v1.2.0`变得可用。 这是模块系统如何提供100%可重复构建的示例。一切就绪后,模块作者或用户可能会选择升级到最新的D版本或选择D的显式版本。
有关最小版本选择算法的简要原理和概述,请参阅官方提供的[高保真构建(High Fidelity Builds)部分](https://github.com/golang/proposal/blob/master/design/24301-versioned-go.md#update-timing--high-fidelity-builds),或参见[更详细的`vgo`博客系列](https://research.swtch.com/vgo)。
要查看所选模块版本的列表(包括间接依赖),请使用`go list -m all`。
参阅下面的 ["How to Upgrade and Downgrade Dependencies"](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies)部分,以及FAQ ["How are versions marked as incompatible?"](https://github.com/golang/go/wiki/Modules#how-are-versions-marked-as-incompatible) 。
### 语义导入版本控制
多年来,Go官方的FAQ 包含了关于包版本化的建议:
> "供公众使用的软件包应该尝试在它们发展时保持向前兼容性。Go 1兼容性指南在这里是一个很好的参考:不要删除导出的名称,鼓励标记的复合文字等。如果需要不同的功能,添加新名称而不是更改旧名称。如果需要完整决裂,请创建一个带有新导入路径的新包。"
最后一句特别重要 —— 如果破坏了兼容性,则应更改包的导入路径。Go 1.11模块中,该建议被定义_import兼容性规则_:
> "如果旧包和新包具有相同的导入路径,
> 新包必须向前兼容旧包。"
回想[semver](https://semver.org/) 在v1或更高版本的软件包进行向前不兼容的更改时更改主要版本。遵循导入兼容性规则和semver的结果称为_Semantic Import Versioning_,其中主要版本包含在导入路径中 —— 这可确保导入路径在主要版本因兼容性中断而增加时发生变化。
作为语义导入版本控制的结果,选择Go模块**的代码必须符合这些规则**:
* 遵循[semver](https://semver.org/)。 (例如 VCS 的tag是`v1.2.3`)。
* 如果模块版本为v2或更高版本,则模块的主要版本_必须_将`/vN`包含在`go.mod`文件中使用的模块路径的末尾(例如,`module github.com/my/mod/v2`,`require github.com/my/mod/v2 v2.0.0`)和包导入路径(例如,`import "github.com/my/mod/v2/mypkg"`)。
* 如果模块是版本v0或v1,请_不要_在模块路径或导入路径中包含主要版本。
通常,具有不同导入路径的包是不同的包。例如,`math/rand`与`crypto/rand`是不同的包。如果导入路径中出现不同的主要版本导致不同的导入路径,则也是如此。因此`example.com/my/mod/mypkg`与`example.com/my/mod/v2/mypkg`是不同的包,并且两者都可以在一个构建中导入,这有助于解决金字塔依赖问题,并且还允许v1模块在其v2替换方面实现,反之亦然。
有关语义导入版本控制的更多详细信息,请参阅`go`命令文档的["Module compatibility and semantic versioning"](https://golang.org/cmd/go/#hdr-Module_compatibility_and_semantic_versioning)部分,并参阅https://semver.org 了解有关语义版本控制的更多信息。
到目前为止,本节主要关注选择模块和从其他模块导入代码。但是,将主要版本放在v2+模块的导入路径中可能与旧版本的Go或未使用模块的代码不兼容。为了解决这个问题,上述行为和规则有三个重要的过渡特例或例外。随着越来越多的软件包选择使用模块,这些过渡异常将变得越来越不重要。
**三个过渡性例外**
1. **gopkg.in**
甚至在选择使用模块后,使用以`gopkg.in`开头的导入路径的现有代码(例如`gopkg.in/yaml.v1`和`gopkg.in/yaml.v2`)可以继续将这些形式用于其模块路径和导入路径。
2. **当导入非模块v2+包时'+incompatible'**
模块可以导入未使用模块的v2+包。具有有效v2+ [semver](https://semver.org)tag的非模块v2+包将在导入模块的`go.mod`文件中以`+incompatible`后缀记录。`+incompatible`后缀表示即使v2+包有一个有效的v2+ [semver](https://semver.org)tag,如`v2.0.0`,v2+包也没有主动使用模块,因此假设还_没有_创建v2+包,并且了解语义导入版本控制的含义以及如何在导入路径中使用主要版本。因此,在[模块模式](https://github.com/golang/go/wiki/Modules#when-do-i-get-old-behavior-vs-new-module-based-behavior)中操作时, `go`工具会将非模块v2+包视为包的v1版本系列的(不兼容)扩展,并假设包不清楚语义导入版本控制,并且`+incompatible`后缀表示`go`工具也是这样做的。
3. **未开启模块模式的"最小模块兼容性"**
为了帮助实现向前兼容,Go版本1.9.7+,1.10.3+和1.11已经更新,以便使使用这些版本构建的代码更容易正确使用v2+模块_而不_需要修改现有代码。此行为称为"最小模块兼容性",它仅在完全[模块模式](https://github.com/golang/go/wiki/Modules#when-do-i-get-old-behavior-vs-new-module-based-behavior) 被禁用时生效,例如你在Go 1.11中设置了`GO111MODULE=off`,或者使用Go版本1.9.7+或1.10.3+。当依赖于Go 1.9.7+,1.10.3+和1.11中的这种"最小模块兼容性"机制时,_还未_选择使用模块的软件包将_不会_包含任何导入的v2+模块的导入路径中的主要版本。相比之下,_已经_使用模块的包_必须_包含导入路径中的主要版本以导入任何v2+模块(为了在`go`工具以完全模块模式运行时正确导入v2+模块,并充分了解语义导入版本控制)。
有关发布v2+模块所需的确切机制,请参阅下述的["Releasing Modules (v2 or Higher)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher) 部分。
## 如何使用模块
### 如何安装和激活模块支持
使用模块,有两个安装选项:
* [安装最新的Go 1.11版本](https://golang.org/dl/)。
* [从源码安装Go工具链 ](https://golang.org/doc/install/source) `master`分支。
安装后,可以通过以下两种方式之一激活模块支持:
* 在`$GOPATH/src`树外部的目录中调用`go`命令,当前目录或其任何父目录中有有效的`go.mod`文件,并且未设置环境变量`GO111MODULE`(或显式设置为`auto`)。
* 调用`go`命令时使用`GO111MODULE=on`环境变量集。
### 如何定义模块
为现有项目创建`go.mod`:
1. 定位到GOPATH外部模块源树的根目录:
```
$ cd
```
请注意,在GOPATH之外,不需要设置`GO111MODULE`来激活模块模式。
或者,想在自己的GOPATH中起作用:
```
$ export GO111MODULE=on # 手动激活模块模式
$ cd $GOPATH/src/
```
2. 创建初始模块定义并将其写入`go.mod`文件:
```
$ go mod init
```
这个步骤从任何现有的[`dep`](https://github.com/golang/dep) `Gopkg.lock`文件或者其他[共支持九种依赖关系格式 ](https://tip.golang.org/pkg/cmd/go/internal/modconv/?m=all#pkg-variables), 添加依赖语句以匹配现有配置。
`go mod init`通常可以使用辅助数据(如VCS元数据)自动确定适当的模块路径,但如果`go mod init`声明它不能自动确定模块路径,或者如果需要覆盖该路径,则可以提供[模块路径](https://github.com/golang/go/wiki/Modules#gomod)作为 `go mod init`的可选参数,例如:
```
$ go mod init github.com/my/repo
```
请注意,如果依赖项包括v2+模块,或者如果你正在初始化v2+模块,那么在运行`go mod init`之后,可能还需要编辑`go.mod`和`.go`代码以添加`/vN`以导入路径和模块路径,如["语义导入版本控制"](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)部分所述。即使`go mod init`自动从`dep`或其他依赖关系管理转换了依赖关系信息,这同样适用。(因此,在运行`go mod init`之后,通常不应该运行`go mod tidy`,直到运行`go build ./...`或类似的程序成功,这也是本节中显示的顺序)。
3. 构建模块。当执行来自一个模块的根目录时,`./...`模式匹配所有在当前模块内的包。`go build`将根据需要自动添加缺少的或未转换的依赖项,以满足特定构建调用的导入:
```
$ go build ./...
```
4. 按配置测试模块,以确保其与所选版本匹配:
```
$ go test ./...
```
5. (可选)运行模块测试以及所有直接和间接依赖项的测试,以检查不兼容性:
```
$ go test all
```
在标记发布之前,请参见下面章节["How to Prepare for a Release"](https://github.com/golang/go/wiki/Modules#how-to-prepare-for-a-release).
有关所有这些主题的更多信息,官方模块文档的主要入口点是[golang.org提供](https://golang.org/cmd/go/#hdr-Modules__module_versions__and_more).
## 如何升级和降级依赖项
应该使用`go get`来完成日常升级和降级依赖项,这将自动更新`go.mod`文件。 或者,你也可以直接编辑`go.mod`。
此外,诸如'go build','go test'或甚至'go list'之类的命令将根据需要自动添加新的依赖项以满足导入(更新`go.mod`并下载新的依赖项)。
要查看所有直接和间接依赖项的可用次要和补丁升级,请运行`go list -u -m all`。
更新当前模块的所有直接和间接依赖到最新版本:
* 运行`go get -u`去使用最新的*次要或补丁*版本
* 运行`go get -u=patch`去使用最新*补丁*版本
`go get foo`更新到最新版本的`foo`。 `go get foo`相当于`go get foo @latest` —— 换句话说,如果没有指定`@`版本,`@latest`是默认值。
在本节中,"latest"是带有[semver](https://semver.org/)标记的最新版本,如果没有semver标记,则是最新的已知commit提交。 除非存储库中没有其他semver标记,否则预发布标记不会被选为"最新"([详细信息](https://golang.org/cmd/go/#hdr-Module_aware_go_get))。
一个常见的错误是认为`go get -u foo`只能获得`foo`的最新版本。 实际上,`go get -u foo`或`go get -u foo@latest`中的`-u`意味着_也可以_获取`foo`的直接和间接依赖_所有_的最新版本。 升级`foo`的一个常见起点是运行`go get foo`或`go get foo@latest`而不用`-u`(一切正常后,再考虑`go get -u=patch foo`,`go get -u=patch`,`go get -u foo`或`go get -u`)。
要升级或降级到更具体的版本,'go get'允许通过添加@version后缀或["模块查询"](https://golang.org/cmd/go/#hdr-Module_queries) 来覆盖版本选择的包参数,例如`go get foo @v1.6.2`,`go get foo@e3702bed2`,或`go get foo @' 使用诸如`go get foo@master`之类的分支名称是获取最新提交的一种方式,无论它是否具有semver标记。 通常,不解析为semver标记的模块查询将在`go.mod`文件中记录为[伪版本](https://tip.golang.org/cmd/go/#hdr-Pseudo_versions)。 请参阅["模块感知go get"](https://golang.org/cmd/go/#hdr-Module_aware_go_get)和["模块查询"](https://golang.org/cmd/go/#hdr-Module_queries)中`go`命令文档的部分,以获取有关这些主题的更多信息。 模块能够使用尚未引入模块的软件包,包括在`go.mod`中记录任何可用的semver标签,并使用这些semver标签进行升级或降级。 模块也可以使用没有任何正确的semver标签的软件包(在这种情况下,它们将使用`go.mod`中的伪版本进行记录)。 在升级或降级任何依赖项之后,你可能希望为构建(包括直接和间接依赖)的所有包运行测试以检查不兼容性: ``` $ go test all ``` ## 如何准备发布 ### 发布模块(所有版本) 创建模块发布的最佳实践预计将作为初始模块实验的一部分出现。其中许多最终可能会是自动化[未来的'go release'工具](https://github.com/golang/go/issues/26420)。 在标记发布之前要考虑的一些当前建议的最佳实践: * 运行`go mod tidy`删除任何多余的依赖 (如[这里](https://tip.golang.org/cmd/go/#hdr-Maintaining_module_requirements)所示),同时确保当前的go.mod反映了所有可能的构建tag/OS/架构组合(如[这里](https://github.com/golang/go/issues/25971#issuecomment-399091682)所示)。 * 相比之下,`go build`和`go test`等其他命令不会从`go.mod`中删除不再需要的依赖项,只会根据当前构建调用的tags/OS/架构更新`go.mod`。 * 运行go test all`测试模块(包括运行直接和间接依赖的测试),以验证当前选定的包版本是否兼容。 * 可能的版本组合的数量在模块数量上是指数级的,因此一般来说,您不能期望依赖已经针对它们的依赖的所有可能组合进行了测试。 * 作为模块工作的一部分,`go test all`[重新定义为更有用](https://research.swtch.com/vgo-cmd)对于当前模块包含的所有包,以及它们通过一个或多个依赖的导入的所有包,同时排除在当前模块不相关的包。 * 确保`go.sum`随着`go.mod`一起提交. 参见[下述FAQ](https://github.com/golang/go/wiki/Modules#should-i-commit-my-gosum-file-as-well-as-my-gomod-file)获取更过信息。 ### 模块发布 (v2或更版本) 如果要发布v2或更高版本的模块,请首先查看上面的["语义导入版本控制"]((https://github.com/golang/go/wiki/Modules#semantic-import-versioning) )部分中的讨论,其中包括为什么主要版本包含在v2+模块的模块路径和导入路径中。同时,Go版本1.9.7+和1.10.3+已经更新以简化转换。 请注意,如果在采用模块之前,第一次为已经存在的仓库或一组已标记为`v2.0.0`或更高版本的包采用模块,则[建议的最佳实践](https://github.com/golang/go/issues/25967#issuecomment-422828770)将升级第一次采用模块时的主要版本。例如,如果您是`foo`的作者,而`foo`仓库的最新标记是`v2.2.2`,而`foo`尚未采用模块,则最佳做法是将`v3.0.0`用于第一个采用模块的`foo`版本的`foo`(因此,第一个包含`go.mod`文件的`foo`版本)。在这种情况下,升级主版本可以为`foo`的使用者提供更清晰的信息,如果需要,允许在`foo`的v2系列上附加非模块补丁或次要版本,并为`foo`的基于模块的使用者提供一个强大的信号,如果`import "foo"`和相应的`require foo v2.2.2+incompatible`,与`import "foo/v3"`和相应的`require foo/v3 v3.0.0`。(请注意,有关首次采用模块时升级主要版本的建议不适用于最新版本为v0.x.x或v1.x.x的包)。 发布v2或更高版本模块有两种可选机制。请注意,使用这两种技术,当模块作者推送新标签时,新的模块版本对消费者是可用的。使用创建`v3.0.0`版本的示例,有两个选项: 1. **主要分支**: 更新`go.mod`文件,在在`module`指令模块路径的末尾包含`/v3`(例如`module github.com/my/module/v3`)。同时使用`/v3`更新导入更新模块中的import语句(例如`import "github.com/my/module/v3/mypkg"`)。给发布打标签为 `v3.0.0`。 * Go版本1.9.7+、1.10.3+和1.11能够正确使用此方法创建的v2+模块,而无需更新尚未选择使用模块的使用者代码(如["语义导入版本控制"](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)中所述)。 * 社区工具[github.com/marwan-at-work/mod](https://github.com/marwan-at-work/mod)帮助自动执行此过程。参见[repository](https://github.com/marwan-at-work/mod) 或者[community tooling FAQ](https://github.com/golang/go/wiki/Modules#what-community-tooling-exists-for-working-with-modules)。 * 为了避免与此方法混淆,请考虑将模块的`v3.*.*`提交放在单独的v3分支上。 * **注意:** 创建新分支_不是_必须的。如果您以前在master上发布过,并且希望在master上标记`v3.0.0`,那么这是一个可行的选项。(但是,请注意,在`master`中引入不兼容的API更改可能会导致使用`go get -u`而不清楚[semver](https://semver.org)的非模块用户出现问题,因为`go` 具在go 1.11之前或[模块模式](https://github.com/golang/go/wiki/Modules#when-do-i-get-old-behavior-vs-new-module-based-behavior)在go 1.11+中未启用。 * 现有的依赖关系管理解决方案(如`dep`)目前在使用以这种方式创建的v2+模块时可能会遇到问题。请参见[dep#1962](https://github.com/golang/dep/issues/1962)。 2. **主要子目录**: 创建一个新的`v3`子目录 (例如`my/module/v3`),并且放一个`go.mod`文件。 模块路径必须以`/v3`结尾. 拷贝或移动代码到`v3`子目录。用`/v3`更新模块中import语句 (例如`import "github.com/my/module/v3/mypkg"`)。给发布打标签为 `v3.0.0`。 * 这提供了更大的向前兼容性。特别是,早于1.9.7和1.10.3的Go版本也能够正确地使用和构建使用这种方法创建的v2+模块。 * 这里更复杂的方法可以利用类型别名(在Go1.9中引入)并在位于不同子目录的主要版本之间传递。这可以提供额外的兼容性,并允许根据另一个主要版本实现一个主要版本,但对于模块作者来说需要做更多的工作。一个正在进行的自动化工具是`goforward`。请参阅[此处](https://golang.org/cl/137076)了解更多详细信息和基本原理,以及`goforward`的初始版本。 * 以前存在的依赖关系管理解决方案(如`dep`)应该能够使用以这种方式创建的v2+模块。 有关这些备选方案的更深入讨论,请参阅https://research.swtch.com/vgo-module。 ### 发布release版本 可以通过将标签推送到包含模块源代码的仓库来发布新模块版本。标签是通过连接两个字符串形成的:*前缀*和*版本*。 *版本*是该版本的语义导入版本。 应该遵循以下[语义化导入版本](#semantic-import-versioning)规则来选择。 *前缀*表示在仓库中定义模块的位置。 如果模块在仓库的根目录中定义,则前缀为空,标签只是版本。 但是,在[多模块仓库](#faqs--multi-module-repositories)中,前缀区分不同模块的版本。前缀是仓库中定义模块的目录。 如果仓库遵循上述主要子目录模式,则前缀不包括主要版本后缀。 例如,假设我们有一个模块 `example.com/repo/sub/v2`,我们想要发布版本`v2.1.6`。 仓库根对应于`example.com/repo`,模块在仓库中的`sub/v2/go.mod`中定义。该模块的前缀是`sub/`。 此版本的完整标签应为`sub/v2.1.6`。 ## 迁移到模块 本节试图简要列举迁移到模块时要做出的主要决策,以及列出其他与迁移相关的主题。 有关详细信息,请参考其他部分。 这些材料主要基于作为模块实验的一部分从社区中出现的最佳实践; 因此,这是一个正在进行中的部分,随着社区获得更多经验,该部分将随着时间的推移而改进。 摘要: * 模块系统旨在允许整个Go生态系统中的不同包可以不同的速率进行引入。 * 已经在v2或更高版本上的软件包具有更多迁移注意事项,主要是由于[语义导入版本控制](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)的原因。 * 采用模块时,v0或v1版本以及新软件包的考虑因素要少得多。 * 使用Go 1.11定义的模块可以由较旧的Go版本使用(尽管确切的Go版本取决于主模块及其依赖关系所使用的策略,如下所述)。 迁移主题: #### 从先前依赖管理自动迁移 * `go mod init` 自动化从[dep, glide, govendor, godep和其他5个先前存在的依赖管理](https://tip.golang.org/pkg/cmd/go/internal/modconv/?m=all#pkg-variables) 转换所需的信息到可以产生一致性构建的`go.mod`文件。 * 如果创建v2+模块,确保转换后的`go.mod`中的`module`指令包含相应的`/vN`(例如`module foo/v3`)。 * 注意,如果要导入v2+模块,则可能需要在初始转换后进行一些手动调整,以便将`/vN`添加到之前的依赖管理转换后`go mod init`生成的`require`语句后。有关详细信息,请参阅上面的["如何定义模块"](https://github.com/golang/go/wiki/Modules#how-to-define-a-module)部分。 * 另外,`go mod init`不会编辑你的`.go`代码来为import语句添加任何必需的`/vN`。 请参阅["语义导入版本控制"](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) 和["模块发布 (v2或更版本)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher)章节,介绍了所需的步骤,包括围绕自动转换的的社区工具一些选项。 #### 为旧版本Go和非模块消费者提供依赖性信息 * 较早版本的Go了解如何使用`go mod vendor`创建的vendor目录,当禁用模块模式时,Go 1.11和1.12+也是如此。因此,vendoring是模块为不完全理解模块的旧版Go提供依赖关系的一种方式,也为未启用模块的消费者提供了一种方式。 请参阅[vendoring FAQ](https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away)和`go` 命令[文档](https://tip.golang.org/cmd/go/#hdr-Modules_and_vendoring)了解更多详情。 #### 更新预先存在的安装说明 * 在模块前,安装说明通常包含`go get -u foo`。 如果要发布模块`foo`,请考虑在针对基于模块的使用者的说明中删除`-u`。 * `-u`要求`go`工具升级`foo`的所有直接和间接依赖关系。 * 模块使用者可以选择稍后再运行`go get -u foo`,但是["High Fidelity Builds"](https://github.com/golang/proposal/blob/master/design/24301-versioned-go.md#update-timing--high-fidelity-builds)这样做有更多好处,如果`-u'不是初始安装说明的一部分。 详情参见["How to Upgrade and Downgrade Dependencies"](https://github.com/golang/go/wiki/Modules#how-to-upgrade-and-downgrade-dependencies)。 * `go get -u foo`仍然有效,并且仍然可以作为安装说明的有效选择。 * 另外,对于基于模块的使用者来说,并不一定需要`go get foo`。 * 只需添加一个导入语句`import "foo"` 就足够了。 (后续的命令,例如go build或go test将自动下载foo并根据需要更新go.mod)。 * 默认情况下,基于模块的使用者将不使用`vendor`目录。 * 如果在`go`工具中启用了模块模式,则在使用模块时(严格根据`go.mod`中包含的信息和`go.sum`中的加密校验和进行比较),`vendor`是非必须的,但某些现有的安装说明假定 `go` 工具默认情况下将使用`vendor`。详见[vendoring FAQ](https://github.com/golang/go/wiki/Modules#how-do-i-use-vendoring-with-modules-is-vendoring-going-away)。 * 在某些情况下,安装包含`go get foo/...`的说明可能会出现问题(详见[#27215](https://github.com/golang/go/issues/27215#issuecomment-427672781)中的讨论)。 #### 避免破坏现有的导入路径 模块通过`module`指令(例如`module github.com/my/module`)在其go.mod中声明其身份。 任何模块支持的使用者都必须使用与模块声明的模块路径匹配的导入路径(确切地说是针对根软件包,或将模块路径作为导入路径的前缀)导入模块内的所有软件包。如果导入路径与相应模块的声明模块路径不匹配,那么`go`命令将报告`unexpected module path`错误。 在为一组预先存在的软件包采用模块时,应注意避免破坏现有使用者使用的现有导入路径,除非在采用模块时增加主版本。 例如,如果之前存在的README文件一直在告诉消费者使用`import "gopkg.in/foo.v1"`,然后如果采用模块到v1版本,那么最初的`go.mod`应该几乎可以读为 `module gopkg.in/foo.v1`。 如果您想放弃使用`gopkg.in`,那对当前的消费者来说将是一个巨大的改变。 一种方法是,如果您后来转到v2,则更改为类似`module github.com/repo/foo/v2`之类的东西。 请注意,模块路径和导入路径区分大小写。例如,将模块从`github.com/Sirupsen/logrus`更改为`github.com/sirupsen/logrus`对消费者来说是重大更改,即使GitHub自动从一个存储库名称转发到新的存储库名称。 在采用模块之后,更改`go.mod`中的模块路径是一项重大更改。 总的来说,这类似于通过 ["import path comments"](https://golang.org/cmd/go/#hdr-Import_path_checking)对规范导入路径的模块前强制实施,有时也称为"import pragmas"或"import path enforcement"。 例如,软件包`go.uber.org/zap`当前托管在`github.com/uber-go/zap`,但是使用导入路径注释[在软件包声明旁边]((https://github.com/uber-go/zap/blob/8a2ee5670ced5d94154bf385dc6a362722945daf/doc.go#L113))对于基于github的导入路径如`package zap // import "go.uber.org/zap"`的预模块使用者会触发一个错误。 go.mod文件的module语句已淘汰了导入路径注释。 #### 首次采用模块的v2+软件包增加主要版本 * 如果您在采用模块之前已将其软件包标记为V2.0.0或更高版本,则建议的最佳实践是在首次采用模块时增加主要版本。例如,如果您使用的是`v2.0.1`版本,但尚未采用模块,则对于采用模块的第一个发行版,应使用`v3.0.0`版本。查看 ["Releasing Modules (v2 or Higher)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher) 章节获取更多信息。 #### v2+模块允许单个构建中的多个主要版本 * 如果某个模块在V2或更高版本上,则意味着多个主要版本可以位于一个内部版本中(例如,`foo`和`foo/v3`可能会在单个内部版本中结束)。 * 这自然源于"具有不同导入路径的包是不同的包"的规则。 * 发生这种情况时,将有多个软件包级别状态的副本(例如,`foo`的软件包级别状态和`foo/v3`的软件包级别状态),并且每个主要版本都将运行其自己的`init`方法。 * 这种方法有助于模块系统的多个方面,包括帮助解决钻石依赖问题,在大型代码库中逐步迁移到新版本,以及允许将主要版本实现为围绕其他主要版本的补充。 * 查看https://research.swtch.com/vgo-import的"Avoiding Singleton Problems"或[#27514](https://github.com/golang/go/issues/27514)获取相关讨论。 #### 使用非模块代码的模块 * 模块能够使用尚未采用模块的软件包,并在导入模块的`go.mod`中记录适当的软件包版本信息。模块可以使用尚没有适当的semver标签的软件包。 有关更多信息,请参见FAQ [below](https://github.com/golang/go/wiki/Modules#can-a-module-consume-a-package-that-has-not-opted-in-to-modules)获取更多解答。 * 模块也可以导入未选择模块的v2+软件包。如果导入的v2+软件包具有有效的semver标签,则将以`+incompatible`后缀记录。请参见FAQ [below](https://github.com/golang/go/wiki/Modules#can-a-module-consume-a-v2-package-that-has-not-opted-into-modules-what-does-incompatible-mean) 获取更多细节。 #### 非模块代码使用模块 * **非模块代码使用v0和v1模块**: * 尚未选择使用模块的代码可以使用和构建v0和v1模块(与使用的Go版本无关)。 * **非模块代码使用v2+模块**: * Go版本1.9.7+,1.10.3+和1.11已更新,因此使用这些发行版构建的代码可以正确使用v2+模块,而无需按照上述章节["Semantic Import versioning"](https://github.com/golang/go/wiki/Modules#semantic-import-versioning)和["Releasing Modules (v2 or Higher)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher)中所述修改现有代码。 * 如果按照["Releasing Modules (v2 or Higher)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher)中概述的"主要子目录"方法创建了v2+模块,则1.9.7和1.10.3之前的Go版本可以使用v2+模块。 #### 预先存在v2+包的作者策略 对于考虑使用模块的预先存在的v2+软件包的作者,总结替代方法的一种方法是在三种顶级策略之间进行选择。每个选择都有后续的决定和变化(如上所述)。 这些替代的顶级策略是: 1. **要求客户端使用Go版本1.9.7+,1.10.3+或1.11+**。 该方法使用"主要分支"方法,并依赖"最小模块感知",该感知被反向移植到1.9.7和1.10.3。 请参阅["Semantic Import versioning"](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) 和["Releasing Modules (v2 or Higher)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher)章节获取更多细节。 2. **允许客户端使用像1.8的更老Go版本**. 该方法使用"主要子目录"方法,涉及创建子目录,例如`/v2`或`/v3`。请参阅["Semantic Import versioning"](https://github.com/golang/go/wiki/Modules#semantic-import-versioning) 和["Releasing Modules (v2 or Higher)"](https://github.com/golang/go/wiki/Modules#releasing-modules-v2-or-higher)章节获取更多细节。 3. **等待使用模块**. 在此策略中,使用模块的客户端代码以及未使用模块的客户端代码一起使用。随着时间的流逝,Go版本1.9.7+,1.10.3+和1.11+的发布时间将越来越长,并且在将来的某个时候,要求Go版本变得更加自然或对客户友好1.9.7+/1.10.3+/1.11 +,此时,您可以实施以上策略1(需要Go版本1.9.7+,1.10.3+或1.11+),甚至可以实施以上策略2(但是如果最终要采用上述策略2来支持1.8等旧版Go,那么您现在就可以这样做)。