Golang 挖坑之旅——module replace

Golang 挖坑之旅——module replace

0. 问题描述

replace命令是go module中的四个命令之一,主要作用是替换当前require中的某些module成别的module,但是我在实践中,对于replace的作用域产生了一丢丢疑问,所以这里特别做个小实验,并加以记录。

问题1:replaceindirect是否有效

问题2:replace是否会直接覆盖全局

1. 实验设计

实验环境:goland 2020, go 1.14, windows 10 pro

上述两个问题可以通过一个实验来验证,下面给出项目目录:

1
2
3
4
5
6
7
8
9
10
# 这个项目内包含三个module,其中mod1引用mod2,mod2引用mod3
mod1
    go.mod
    main.go
mod2
    go.mod
    mod2.go
mod3
    go.mod
    mod3.go

项目结构相对好理解,为了简便,module中的内容我也尽量简化,以下是文件内容:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// mod1/go.mod
module mod1

go 1.14

require (
    nothing.com/mod2 v0.0.0  // 随便起的域名,经过替换repalce,不会下载东西
    nothing.com/mod3 v0.0.0 // indirect
)

replace nothing.com/mod2 => ../mod2
replace nothing.com/mod3 => ../mod3

// mod1/main.go
package main

import (
    "nothing.com/mod2"  
)

func main() {
    mod2.Message()

}


// mod2/go.mod
module mod2

go 1.14

require nothing.com/mod3 v0.0.0
replace nothing.com/mod3 ../notExist

// mod2/mod2.go
package mod2

import "fmt"
import "nothing.com/mod3"

func Message() {
    fmt.Println("Now in mod2")
    mod3.Message()
}

// mod3/go.mod
module mod3

go 1.14


// mod3/mod3.go
import "fmt"

func Message() {
    fmt.Println("Now in mod3")
}

简单说一下实验设计的目的:

上面mod1是我要进行编译的modulemod1直接引用mod2mod2直接依赖mod3,所以mod1间接依赖mod3mod1/go.mod也证明了这点。但是注意,mod2/go.mod中,我给出的replace是一个错误的地址,这个文件夹是不存在的,此外,我在mod1/go.mod给出了正确的replace

2. 实验结果

1
2
3
4
5
> cd mod1
> go build
> .\mod1.exe
Now in mod2
Now in mod3

可以看到,是可以正常编译并执行的,这至少说明了两个问题:

  1. mod1中的replace直接覆盖了全局的replace,不然的话,mod2是肯定找不到mod3
  2. replace会对间接依赖生效,因为mod1就是间接依赖mod3

3. 结果分析

golang wiki:https://github.com/golang/go/wiki/Modules

exclude and replace directives only operate on the current (“main”) module. exclude and replace directives in modules other than the main module are ignored when building the main module. The replace and exclude statements, therefore, allow the main module complete control over its own build, without also being subject to complete control by dependencies. (See FAQ below for a discussion of when to use a replace directive).

个人认为,最关键在这句directives in modules other than the main module are ignored when building the main module,我的理解是,只有当前buildmodulereplace会被包留,其它的都会被忽略,所以才有了上面的实验结果。当然这些都是我结合实验和文档给出的一些理解推测,可能会有一定的偏差。