golang – 多模块工作区(multi-module workspaces)
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 命令将工作区的构建列表同步回工作区的模块