1. 本教程介绍Go中多模块工作区的基础知识。使用多模块工作区,您可以告诉Go命令您正在同时在多个模块中编写代码,并轻松地在这些模块中构建和运行代码。
  2. 在本教程中,您将在共享的多模块工作区中创建两个模块,对这些模块进行更改,并在构建中查看这些更改的结果。
  3. 必须条件:
    1. Go 1.18或更高版本的安装。
    2. 用于编辑代码的工具。 您拥有的任何文本编辑器都可以正常工作。
    3. 一个命令终端。Go在Linux和Mac上的任何终端以及Windows中的PowerShell或cmd上都能很好地工作。
  4. 本教程需要go1.18或更高版本。 使用go.dev/dl中的链接,确保您已在Go 1.18或更高版本中安装了Go。

创建一个模块

  1. 首先,为您要编写的代码创建一个模块。
  2. 打开命令提示符并切换到您的主目录。
    • 在Linux或Mac上:cd
    • 在Windows上:C:\> cd %HOMEPATH%
    • 本教程的其余部分将显示$作为提示。您使用的命令也可以在Windows上运行。
  3. 在命令提示符下,为您的代码创建一个名为工作区的目录。
1
2
$ mkdir workspace
$ cd workspace

初始化模块
  1. 我们的示例将创建一个hello依赖于golang.org/x/example模块的新模块。
  2. 创建hello模块:
1
2
3
4
$ mkdir hello
$ cd hello
$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello
  1. 使用go get添加对golang.org/x/example模块的依赖(如果有需要)。
1
2
3
$ go get golang.org/x/example
go: downloading golang.org/x/example v0.0.0-20220304235025-ad95e7f791d8
go get: added golang.org/x/example v0.0.0-20220304235025-ad95e7f791d8
  1. 在hello目录下创建hello.go,内容如下:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import (
	"fmt"

	"golang.org/x/example/stringutil"
)

func main() {
	fmt.Println(stringutil.Reverse("Hello"))
}
  1. 现在,运行hello程序:
1
2
3
$ go mod tidy
$ go run .
olleH
  1. 目录结构为:
1
2
3
4
|--workspace
|----hello
|------hello.go
|------go.mod

创建工作区

  1. 在这一步中,我们将创建一个go.work文件来指定模块的工作区。

初始化工作区

  1. workspace目录中,运行:
1
2
3
# go1.18beta2.exe work init ./hello 	## 本地用的go1.18beta2版本
# 当前目录是 hello 的上级目录下,执行完go work init后会生成一个go.work文件
$ go work init ./hello
  1. go work init命令告诉go为包含./hello目录中的模块的工作空间创建一个go.work文件。
  2. go命令生成一个如下所示的go.work文件:
go 1.18

use ./hello
  1. go.work文件的语法与go.mod相似。
  2. go指令告诉Go应该使用哪个版本的Go来解释文件。它类似于go.mod文件中的go指令。
  3. use指令告诉Go在构建时hello目录中的模块应该是主模块。
  4. 因此,在工作区的任何子目录中,该模块都将处于活动状态。
  5. 目录结构为:
1
2
3
4
5
|--workspace
|----hello
|------hello.go
|------go.mod
|----go.work

运行工作区下的目录

  1. workspace目录中,运行:
1
2
3
# go1.18beta2.exe run example.com/hello
$ go run example.com/hello
olleH
  1. Go命令包括工作区中的所有模块作为主模块。这允许我们在模块中引用一个包,甚至在模块之外。
  2. 在模块或工作区之外运行go run命令会导致错误,因为go命令不知道要使用哪些模块。
  3. 接下来,我们将golang.org/x/example模块的本地副本添加到工作区。然后,我们将向stringutil包添加一个新函数,我们可以使用它来代替Reverse。

下载和修改golang.org/x/example模块

  1. 在这一步中,我们将下载包含golang.org/x/example模块的Git存储库的副本,将其添加到工作区,然后向其中添加一个我们将从hello程序中使用的新函数。
  2. 克隆存储库,在workspace目录中,运行git命令来克隆存储库:
1
2
3
4
5
6
7
8
$ git clone https://github.com/golang/example
Cloning into 'example'...
remote: Enumerating objects: 182, done.
remote: Counting objects: 100% (30/30), done.
remote: Compressing objects: 100% (20/20), done.
remote: Total 182 (delta 6), reused 16 (delta 4), pack-reused 152R
Receiving objects: 100% (182/182), 138.39 KiB | 1.05 MiB/s, done.
Resolving deltas: 100% (74/74), done.
  1. 将模块添加到工作区。
1
2
# go1.18beta2.exe work use ./example
$ go work use ./example
  1. go work use命令将一个新模块添加到go.work文件中。它现在看起来像这样:
go 1.18

use (
    ./hello
    ./example
)
  1. 目录结构:
1
2
3
4
5
6
7
8
|--workspace
|----hello
|------hello.go
|------go.mod
|----example
|------stringutil
|--------reverse.go
|----go.work
  1. 该模块现在包括example.com/hello模块和golang.org/x/example模块。
  2. 这将允许我们使用我们将在stringutil模块的副本中编写的新代码,而不是使用go get命令下载的模块缓存中的模块版本。
  3. 添加新功能:
    • 我们将在golang.org/x/example/stringutil包中添加一个将字符串大写的新函数。
    • 将新文件夹添加到包含以下内容的workspace/example/stringutil目录:并命令为upper.go文件。
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
package stringutil

import "unicode"

// ToUpper uppercases all the runes in its argument string.
func ToUpper(s string) string {
    r := []rune(s)

    for i := range r {
        r[i] = unicode.ToUpper(r[i])
    }

    return string(r)
}
  1. 修改hello程序以使用该功能,修改workspace/hello/hello.go的内容,包含以下内容:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
package main

import (
    "fmt"

    "golang.org/x/example/stringutil"
)

func main() {
    fmt.Println(stringutil.ToUpper("Hello"))
}

运行workspace目录下代码

  1. 从workspace目录,运行:
1
2
# go1.18beta2.exe run example.com/hello
$ go run example.com/hello
  1. Go命令在go.work文件指定的hello目录中查找命令行中指定的example.com/hello模块,同样使用go.work文件解析golang.org/x/example导入。
  2. 可以使用go.work而不是添加替换指令来跨多个模块工作。
  3. 由于这两个模块位于同一个工作区中,因此很容易在一个模块中进行更改并在另一个模块中使用它。