golang – 多模块工作区(multi-module workspaces)

作者: adm 分类: go 发布时间: 2022-09-07

go的多模块工作区是从1.18 版本开始的,所以要使用它首先要确认go 的版本。

$go version
go version go1.18 windows/amd64

一下教程介绍了一个多模块工作区的用例。在多模块工作区中创建两个模块,对这些模块进行更改,并在构建中查看这些更改的结果:

1. 创建项目
创建一个名为workspace 的目录,作为本次案例多模块的家目录

$ mkdir workspace

2. 创建项目
进入工作区目录如下指令创建一个目录作为hello 的项目,并通过go mo初始化

$ cd workspace
$ mkdir hello
$ cd hello
$ go mod init example.com/hello
go: creating new go.mod: module example.com/hello

在该项目下,创建hello.go 文件,并创建如下main 函数, 并通过go mod tidy 命令 同步第三方依赖包

package main

import (
  "fmt"
  "golang.org/x/example/stringutil"
)

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

3. 创建工作区
创建一个 go.work 文件来指定带有模块的工作区,在工作目录中(workspace 目录),指令如下:

// go work init [路径]
$ go work init ./hello

go work init 命令告诉 go 为包含 ./hello 目录中的模块的工作空间创建一个 go.work 文件。 go 命令生成一个如下所示的 go.work 文件:

    go 1.18

    use ./hello

go.work 文件的语法与 go.mod 相似。 go 指令告诉 Go 应该使用哪个版本的 Go 来解释文件。它类似于 go.mod 文件中的 go 指令。 use 指令告诉 Go 在构建时, hello 目录中的模块应该是主模块。 因此,在工作区的任何子目录中,该模块都将处于活动状态
在workspace目录中,运行hello 模块,此时可以正常运行

$ go run example.com/hello
olleH

工作区中的所有模块和go 命令都会被作为作为主模块。这允许我们在模块中引用一个包,甚至在该包在模块之外。在模块或工作区之外运行 go run 命令会导致错误,因为 go 命令不知道要使用哪些模块。 接下来,我们将 golang.org/x/example 模块的本地副本添加到工作区。然后,我们将向 stringutil 包添加一个新函数,我们可以使用它来代替 Reverse。

克隆golang.org/x/example
在工作区运行如下指令来克隆example代码

$ git clone https://go.googlesource.com/example
Cloning into 'example'...
remote: Total 165 (delta 27), reused 165 (delta 27)
Receiving objects: 100% (165/165), 434.18 KiB | 1022.00 KiB/s, done.
Resolving deltas: 100% (27/27), done.

通过如下指令将克隆的example 项目添加到工作目录中

go work use ./example

此时查看go.work 文件如下所示

go 1.18

use (
    ./example
    ./hello
)

该模块现在包括 example.com/hello 模块和 golang.org/x/example 模块。 这将允许我们使用我们将在 stringutil 模块的副本中编写的新代码,而不是使用 go get 命令下载的模块缓存中的模块版本.此时example和hello 模块相当于是本地私有模块,可以任意修改

修改example 目录中代码
我们将在 golang.org/x/example/stringutil 包中添加一个将字符串大写的新函数。 在 workspace/example/stringutil 目录中创建一个名为 toupper.go 的新文件,其中包含以下内容

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)
}

修改对应hello模块
修改hello模块中对字符串反转的函数为新的ToUpper 函数(上面新添加的函数)

package main

import (
    "fmt"

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

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

运行hello模块

$ go run example.com/hello
HELLO

Go 命令在 go.work 文件指定的hello 目录中找到命令行中指定的example.com/hello 模块,并使用类似的方法解析golang.org/x/example 导入go.work 文件。 可以使用 go.work 而不是添加 replace 指令来跨多个模块工作。 由于这两个模块位于同一个工作区中,因此很容易在一个模块中进行更改并在另一个模块中使用它。

4 指令集

go work init
用法:go work init [moddirs]

在当前目录中初始化并写入一个新的 go.work 文件,实际上是在当前目录中创建了一个新的工作空间。 go work init 可选择接受工作区模块的路径作为参数。如果省略该参数,将创建一个没有模块的空工作区。 每个参数路径都添加到 go.work 文件中的 use 指令中。当前的 go 版本也将列在 go.work 文件中。
利润上面例子中,通过 go mod init ./hello 指令在workspace 目录下,创建了一个go.work 文件,并将hello 模块路径添加到go.work 中,具体内容如下:

    go 1.18

    use ./hello
go work edit
用法:go work edit [editing flags] [go.work]

go work edit 命令提供了一个用于编辑 go.work 的命令行接口,主要供工具或脚本使用。它只读取 go.work;不查找有关所涉及模块的信息。如果没有指定文件,Edit 在当前目录及其父目录中查找 go.work 文件

editing flags:

fmt:
fmt 标志重新格式化 go.work 文件而不进行其他更改, 此标志只能单独使用,不能和其他标志同时使用,
示例:

go work edit -fmt

replace:
-replace=old[@v]=new[@v] 指定模块的新模块与旧模块的替代, 其中版本号(@v)可以被省略。如果旧模块中的版本号(@v)被省略了, 相当于指定了旧模块的所有版本; 如果新模块中的版本号(@v)被省略了,则新模块的路径应该是本地模块根目录,而不是模块路径

dropuse:
从 go.work 文件的模块目录集中删除一个 use 指令, 用法

go work edit -dropuse=待删除模块的路径

dropreplace:
删除给定模块路径和版本对的替换

go:
设置go.work 文件中的go 语言版本,

go work edit -go=1.18

go work use
用法:go work use [-r] [moddirs]

go work use 命令提供了一个命令行接口,用于将模块目录(-r: 可以选择以递归方式)添加到 go.work 文件中。
-r: 标志递归搜索参数目录中的模块

go work sync
用法:go work sync

go work sync 命令将工作区的构建列表同步回工作区的模块

如果觉得我的文章对您有用,请随意赞赏。您的支持将鼓励我继续创作!