The package management of Go has been criticized by people. Some people have proposed solutions, such as godep, Govendor and other tools. However, in G1.11, the Go official put forward the Go Module scheme very imperiously, which has become the de facto package management scheme although it has been ridiculed.
Go officially introduces Go Modules through a series of blogs, and this is the first article in the series.
Original address: blog.golang.org/using-go-mo…
Introduction to the
This is the first in a series of five articles:
-
Using the Go Modules
-
Migrate to Go Modules
-
Release the Go Modules
-
Go Modules: V2 and later versions
-
Keep Modules compatible
The new dependency management system that initially supports Modules, Go in versions 1.11 and 1.12 makes dependency version information clearer and easier to manage. This article will cover the basic use of Go Modules.
A Module is a tree of Go packages with a go.mod file in the root directory. The go.mod file defines the Module path, which is also the import path of the root directory, and dependencies, which are other Modules needed to build the application. Each dependency has a Module path and semantic version number.
Starting with Go1.11, module-related commands can be used as long as there is a go.mod file in the current directory or any parent directory that is outside of GOPATH/ SRC. (Outside of GOPATH/ SRC. (Outside of GOPATH/ SRC. (In the GOPATH/ SRC directory, you can only run older versions of commands, even if the go.mod file exists, for compatibility purposes. See the command documentation for more details), starting with Go1.13, module will be the default development mode.
This article introduces a series of common operations when developing using Go Modules:
-
Create the module
-
Add the dependent
-
Update the rely on
-
Add a major version number for the dependency
-
Update the major version number for dependencies
-
Remove useless dependencies
Create the module
Start by creating a new Module.
Create a new directory outside the $GOPATH/ SRC directory and go to this directory and create a new source file hello.go:
package hello
func Hello() string {
return "Hello, world."
}
Copy the code
Then write a test, hello_test.go:
package hello import "testing" func TestHello(t *testing.T) { want := "Hello, world." if got := Hello(); got ! = want { t.Errorf("Hello() = %q, want %q", got, want) } }Copy the code
At this point, the directory contains a package, but it’s not module yet, because there’s no go.mod file. /home/gopher/hello /go test
$go test PASS OK _/home/gopher/hello 0.020s $Copy the code
The last line shows the test. Because it is not currently under $GOPATH or any module, the go command knows that the current directory has no package path (Import path) and creates a fake package path based on the current directory name: _/home/gopher/hello.
Next create a module using go mod init in the current directory and run go test again:
$ go mod init example.com/hello go: creating new go.mod: Module example.com/hello $go test PASS ok example.com/hello 0.020s $Copy the code
Congratulations, you have written and tested your first Module.
The go mod init command creates a go.mod file:
$cat go.mod module example.com/hello go 1.12 $Copy the code
The go.mod file will only appear in the root directory of the Module. The package path of a package in a subdirectory consists of a Module path and a subdirectory path. For example, now that you have created a subdirectory, world, you don’t need to run go mod init again in that directory. This package is automatically identified as part of example.com/hello Module with the package path example.com/hello/world.
Increased reliance on
The primary motivation behind the creation of Go Modules is to improve the experience of using other developers’ code (that is, increase dependencies).
IO /quote: hello.go: rsc. IO /quote: hello
package hello
import "rsc.io/quote"
func Hello() string {
return quote.Hello()
}
Copy the code
Now run the test again:
IO /quote v1.5.2 GO: Downloading Rsc. IO /quote v1.5.2 GO: Complete rsc. IO /quote v1.5.2 GO: IO /sampler V1.3.0 go: Finding golang.org/x/text v0.0.0-20170915032832-14c0d48eAD0c go: IO/Sampler V1.3.0 GO: Downloading RSC. IO/Sampler V1.3.0 GO: Downloading golang.org/x/text v0.0.0-20170915032832-14 c0d48ead0c go: Fully achieving golang.org/x/text v0.0.0-20170915032832-14c0D48eAD0C PASS OK example.com/hello 0.023s $Copy the code
The go command parses these imports through the specific dependency module version specified in go.mod. When some imports belong to dependencies that are not defined in go.mod, the go command automatically adds the latest version of those dependencies to the go.mod file. (Latest indicates the stable (non-pre-released) version of the Latest tag, the pre-released version of the Latest tag, or the Latest untagged version.) In this example, go test will parse rcs. IO /quote to the RCS. IO /quote module, version 1.5.2. IO /quote dependencies will also be downloaded: rsc. IO /sampler and golang.org/x/text. Only direct dependencies are logged into the go.mod file:
$cat go.mod module example.com/hello go 1.12 require rsc. IO /quote v1.5.2 $Copy the code
Those who run Go test again will not download dependencies again because go.mod is now up to date and downloaded dependencies are cached locally (at $GOPATH/ PKG /mod).
$go test PASS OK example.com/hello 0.020s $Copy the code
It is important to note that while the go command makes it quick and easy to add dependencies, it comes at a cost. The correctness of your module’s functionality, its safety, and the licensing of new dependencies that you introduce are just a few of the issues. For more in-depth thinking, check out Russ Cox’s blog on our software dependency issues.
As you saw earlier, dependencies that are introduced directly often also introduce some indirect dependencies. Go list -m all can list the current module and all dependencies:
$go list -m all example.com/hello golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c rsc. IO /quote v1.5.2 RSC. IO/sampler v1.3.0 $Copy the code
In the output of the Go list, the current module, also known as the main module, appears in the first line, followed by the dependent module path.
The version number of golang.org/x/text, v0.0.0-20170915032832-14c0d48ead0c, is called pseudo-versions, which is the go command’s version syntax for unmarked commits.
In addition, for go.mod, the go command maintains a go.sum file that contains a hash value produced by the particular version number of all dependencies:
$cat go.sum golang.org/x/text v0.0.0-20170915032832-14c0d48eAD0c h1:qgOY6WgZO... $cat go.sum golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c h1:qgOY6WgZO... Golang.org/x/text v0.0.0 c0d48ead0c/go - 20170915032832-14 mod h1: Nq... RSC. IO/quote v1.5.2 h1: w5fcysjrx7yqtD/aO + QwRjYZOKnaM9Uh2b40tElTs3... RSC. IO/quote v1.5.2. / go mod h1: LzX7hefJvL54yjefDEDHNONDjII0t9xZLPX... RSC. IO/sampler v1.3.0 h1:7 uvkifmebqhfdjd + gZwtXXI + RODJ2Wc4O7MPEh/Q... RSC. IO/sampler v1.3.0 / go mod h1: T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9... $Copy the code
The go command uses the go.sum file to ensure that subsequent downloads of these modules will yield the same content as the first download, ensuring that the modules on which the project depends will not be accidentally changed, whether maliciously, accidentally, or otherwise. Both go.mod and go.sum should be included in version management.
Update the rely on
In modules of Go, version numbers are expressed using semantic versions. A semantic version has three parts: a major version number, a minor version number, and a patch version number. For example, v0.1.2, major version 0, minor version 1, patch version 2. Let’s look at the next version update. In the next section, we’ll look at the major version number update.
From the go list -m all output, we see that golang.org/x/text uses an unmarked version number. We updated it to the latest markup version, and the above code passed the test as well:
$go get golang.org/x/text go: Finding golang.org/x/text v0.3.0 go: Downloading golang.org/x/text v0.3.0 go: Fully realize golang.org/x/text v0.3.0 $GO test PASS OK example.com/hello 0.013s $Copy the code
Go list -m all: go list -m all:
$ go list -m all
example.com/hello
golang.org/x/text v0.3.0
rsc.io/quote v1.5.2
rsc.io/sampler v1.3.0
$ cat go.mod
module example.com/hello
go 1.12
require (
golang.org/x/text v0.3.0 // indirect
rsc.io/quote v1.5.2
)
$
Copy the code
golang.org/x/text has been updated to the latest markup version (V0.3.0). The go.mod file has also been updated to version 0.3.0. Indirect indicates that the dependency is not used directly by a module, but only indirectly by other modules. You can check out the Go Help Modules for more details.
Now, let me try to update the minor version number of rsc. IO /sample in the same way, using the go get command and then the go test command:
IO/Sampler GO: Downloading Rsc. IO/Sampler v1.99.99 GO: Downloading rsc. IO/Sampler v1.99.99 Go: downloading rsc. IO/Sampler v1.99.99 Rsc. IO /sampler V1.99.99 $GO Test -- FAIL: TestHello (0.00s) Hello_test. Go :8: Hello() = "99 bottles of beer on the wall, 99 bottles of beer, ..." , want "Hello, world." FAIL exit status 1 FAIL example.com/hello 0.014s $Copy the code
The error message shows that the latest version of rsc. IO /sampler is not compatible with the program. Take a look at all the tag versions available for this module:
IO /sampler Rsc. IO /sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99 $go list-m -versions rsc. IO /sampler v1.0.0 v1.2.0 v1.2.1 v1.3.0 v1.3.1 v1.99.99 $Copy the code
V1.3.0 has been used above, v1.99.99 does not look suitable. Let’s try v1.3.1:
$go get Rsc. IO /[email protected] go: Finding Rsc. IO /sampler v1.3.1 GO: Downloading rsc. IO /sampler v1.3.1 go: Rsc. IO/Sampler V1.3.1 $GO test PASS OK example.com/hello 0.022 S $Copy the code
Be sure to specify the @v1.3.1 version number explicitly in go Get. In general, the Go Get command accepts a specific version number, which by default is @latest, indicating the latest version defined previously.
Dependency add major version number
Let’s add a new method to the package. The Proverb method returns a concurrent Proverb of Go, provided by the Quote.concurrency method in rsc.io/quote/v3. First add a new method to hello.go:
package hello
import (
"rsc.io/quote"
quoteV3 "rsc.io/quote/v3"
)
func Hello() string {
return quote.Hello()
}
func Proverb() string {
return quoteV3.Concurrency()
}
Copy the code
Then add a test hello_test.go:
func TestProverb(t *testing.T) { want := "Concurrency is not parallelism." if got := Proverb(); got ! = want { t.Errorf("Proverb() = %q, want %q", got, want) } }Copy the code
Run the test:
IO /quote/v3 v3.1.0 go: Downloading rsc. IO /quote/v3 v3.1.0 go: downloading rsc. IO /quote/v3 v3.1.0 go: Rsc. IO /quote/v3 V3.1.0 PASS OK example.com/hello 0.024s $Copy the code
IO /quote/v3 / rsc. IO /quote/v3
$ go list -m rsc.io/q... Rsc. IO /quote v1.5.2 rsc. IO /quote/v3 v3.1.0 $Copy the code
The Go module uses a different module path for each different major version number (v1, v2, and so on), starting with v2, the path must end with the major version number. IO /quote/v3 is not rsc. IO /quote, but rsc. IO /quote/v3. This habit, called semantically imported versioning, gives incompatible packages (with different major version numbers) different names. IO /quote, on the other hand, must be backwardly compatible with V1.5.2, so rsc. IO /quote is reused. (In previous versions, rsc. IO/Sampler V1.99.99 was supposed to be backward compatible with Rsc. IO/Sample v1.3.0, but this behavior is possible because of bugs or incorrect clients.)
IO /quote, one Rsc. IO /qutoe/v2, one Rsc. IO /quote/v3, and so on. IO /quote v1.5.2 and RSC. IO /quote v1.6.0 cannot appear in the same build. Also, allowing modules with different major versions to appear in the same build (because they have different paths) also gives module consumers the ability to incrementally upgrade the major version. In this example, we want to call the quote.Concurrency method in RSC /quote/v3 V3.1.0, but this method is not yet implemented in RSC. IO/Quote v1.5.2. This ability to migrate incrementally is important in large programs or code bases.
Depends on updating the major version number
IO /quote to rsc. IO /quote/v3. We believe that some oF the apis may have been removed, renamed, or otherwise made incompatible changes due to the major release being subject to change. Reading the documentation, we can see that Hello has been upgraded to HelloV3:
$ go doc rsc.io/quote/v3
package quote // import "rsc.io/quote/v3"
Package quote collects pithy sayings.
func Concurrency() string
func GlassV3() string
func GoV3() string
func HelloV3() string
func OptV3() string
$
Copy the code
In hello.go, we can replace quote.Hello() with v3.hellov3 () :
package hello
import quoteV3 "rsc.io/quote/v3"
func Hello() string {
return quoteV3.HelloV3()
}
func Proverb() string {
return quoteV3.Concurrency()
}
Copy the code
Also, you no longer need to rename the import, so you can undo the rename:
package hello
import "rsc.io/quote/v3"
func Hello() string {
return quote.HelloV3()
}
func Proverb() string {
return quote.Concurrency()
}
Copy the code
Let’s rerun the test to make sure everything works:
$go test PASS OK example.com/hello 0.014sCopy the code
Remove useless dependencies
IO /quote: rsc. IO /quote: rsc. IO /quote: rsc. IO /quote: rsc. IO /quote
$go list -m all example.com/hello golang.org/x/text v0.3.0 rsc. IO /quote v1.5.2 rsc. IO /quote/v3 v3.1.0 rsc. IO /sampler V1.3.1 $cat go.mod module example.com/hello go 1.12 require (golang.org/x/text v0.3.0 // indirect rsc. IO /quote v1.5.2 Rsc. IO/v3.0.0 rsc. IO /sampler v1.3.1 // indirect) $Copy the code
Why is that? Because when building individual packages, such as Go Build or Go Test, it is clear which dependencies are missing and need to be added, but it is not clear which dependencies can be safely removed. Only after checking all packages in the module and all possible build combinations of those packages can a dependency be removed if it is still not used. Normal build commands do not do these checks and cannot safely remove dependencies.
Use Go Mod Tidy to clean up these useless dependencies:
$go mod Tidy $go list -m all example.com/hello golang.org/x/text v0.3.0 rsc. IO /quote/v3 v3.1.0 rsc. IO /sampler v1.3.1 $ Cat go.mod module example.com/hello go 1.12 require (golang.org/x/text v0.3.0 // indirect rsc. IO /quote/v3 v3.1.0 Rsc. IO /sampler v1.3.1 // indirect) $go test PASS OK example.com/hello 0.020s $Copy the code
summary
Go Modules are the future of Go dependency management. Module functionality is available in all supported versions of Go (including Go1.11 and Go1.12).
This article introduces these features of Go Modules:
-
Go mod init Creates a new module and initializes the go.mod file that describes the module
-
Go build, go test and other in-package build commands add required new dependencies to the go.mod file
-
Go list -m all Prints all dependencies of the current module
-
Go Get Changes the new version of the current dependency (or adds a new dependency)
-
Go mod Tidy removes useless dependencies
We encourage you to start using module functionality by adding go.mod and go.sum to your project in your local development. Please send us bug reports and experience reports to help us improve Go’s dependency management features.
Thank you for all your feedback and suggestions to help improve the module.
In addition, Tencent cloud blockchain direction in a large number of recruitment, including front-end, back-end, architect, product and many other positions, if interested, please send your resume to [email protected].
Translation/Rayjun