多模块工作区

使用多模块工作区,告诉 Go 命令您正在同时在多个模块中编写代码,并轻松地在这些模块中构建和运行代码。


在本教程中,您将在共享的多模块工作区中创建两个模块,对这些模块进行更改,并在构建中查看这些更改的结果。

本教程需要 go 1.18 或更高版本。

创建一个模块

  1. 打开命令提示符并切换到您的主目录,为您的代码创建一个名为工作区的目录。

    $ mkdir workspace
    $ cd workspace
    
  2. 初始化模块

    创建一个依赖于 golang.org/x/example 模块的新模块 hello

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

    使用go get命令添加对 golang.org/x/example 模块的依赖项。

    $ go get golang.org/x/example
    
  3. 在 hello 目录下创建 hello.go,内容如下:

    package main
    
    import (
        "fmt"
    
        "golang.org/x/example/stringutil"
    )
    
    func main() {
        fmt.Println(stringutil.Reverse("Hello"))
    }
    
  4. 运行hello程序:

    $ go run example.com/hello
    olleH
    

创建工作区

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

  1. 初始化工作区

    workspace 目录中,运行:

    $ 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的任何子目录中模块都会被激活。

  2. 运行工作区目录下的程序

    在workspace目录中,运行:

    $ go run example.com/hello
    olleH
    

    Go 命令包括工作区中的所有模块作为主模块。这允许我们在模块中引用一个包,甚至在模块之外。在模块或工作区之外运行go run命令会导致错误,因为该go命令不知道要使用哪些模块。

    接下来,我们将golang.org/x/example模块的本地副本添加到工作区。然后,我们将向stringutil包中添加一个新函数,我们可以使用它来代替Reverse.

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

在这一步中,我们将下载包含golang.org/x/example模块的 Git 存储库的副本,将其添加到工作区,然后向其中添加一个我们将从 hello 程序中使用的新函数。

  1. 克隆存储库

    在 workspace 目录中,运行 git 命令克隆存储库:

    $ 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.
    
  2. example模块添加到工作区

    $ go work use ./example
    

    go work use命令将一个新模块添加到 go.work 文件中。它现在看起来像这样:

    go 1.18
    
    use (
        ./hello
        ./example
    )
    

    模块现在包括example.com/hello模块和golang.org/x/example模块。

    这将允许我们使用我们将在stringutil模块副本中编写的新代码,而不是使用命令go get下载的模块缓存中的模块版本。

  3. 添加新函数

    我们将向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)
    }
    
  4. 修改hello程序以使用该函数。

    修改workspace/hello/hello.go文件,内容如下:

    package main
    
    import (
        "fmt"
    
        "golang.org/x/example/stringutil"
    )
    
    func main() {
        fmt.Println(stringutil.ToUpper("Hello"))
    }
    
  5. 在workspace目录下,运行代码

    $ go run example.com/hello
    HELLO
    

    Go 命令在文件go.work指定的目录hello中查找命令行中指定的模块example.com/hello,同样使用文件go.work解析导入golang.org/x/example

    go.work可以用来代替添加replace 指令以跨多个模块工作。

    由于这两个模块在同一个工作区中,因此很容易在一个模块中进行更改并在另一个模块中使用它。

  6. 未来的一步

    现在,要正确发布这些模块,我们需要发布golang.org/x/example 模块,例如在v0.1.0。这通常通过在模块的版本控制存储库上标记提交来完成。有关更多详细信息,请参阅 模块发布工作流程文档 。发布完成后,我们可以在hello/go.mod中增加对 golang.org/x/example 模块的要求:

    cd hello
    go get golang.org/x/example@v0.1.0
    

    这样,该go命令可以正确解析工作区之外的模块。

了解有关工作区的更多信息

除了我们在教程前面看到的go work init之外,go命令还有几个用于处理工作区的子命令:

  • go work use [-r] [dir] 如果目录dir存在,则将use指令添加到文件go.work中,如果参数目录不存在,则删除该use目录。标志-r递归地检查dir子目录。
  • go work edit 编辑go.work文件类似于go mod edit
  • go work sync将工作区构建列表中的依赖项同步到每个工作区模块中。

有关workspaces(工作区)和go.work文件的更多详细信息,请参阅 Go Modules Reference 中的 Workspaces

参考资料

https://go.dev/doc/tutorial/workspaces