/
module.go
141 lines (126 loc) · 2.92 KB
/
module.go
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
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
// Copyright(C) 2022 github.com/fsgo All Rights Reserved.
// Author: fsgo
// Date: 2022/3/6
package common
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"sync"
"golang.org/x/mod/modfile"
)
var goModPathCache = &sync.Map{}
// FindGoModPath 查找文件对应的 go.mod 文件
func FindGoModPath(fileName string) (string, error) {
ap, err := filepath.Abs(fileName)
if err != nil {
return "", err
}
pd := filepath.Dir(ap)
if v, has := goModPathCache.Load(pd); has {
return v.(string), nil
}
// 限定最大往上查找 128 次,避免
for i := 0; i < 128; i++ {
modPath := filepath.Join(pd, "go.mod")
info, err := os.Stat(modPath)
if err == nil && !info.IsDir() {
goModPathCache.Store(pd, modPath)
return modPath, nil
}
cpd := filepath.Dir(pd)
if cpd == pd {
break
}
pd = cpd
}
return "", errors.New("cannot found go.mod")
}
// ModuleByFile 解析 go.mod 文件里的 module 的值
func ModuleByFile(goModPath string) (string, error) {
goModBuf, err := os.ReadFile(goModPath)
if err != nil {
return "", err
}
module := modfile.ModulePath(goModBuf)
if len(module) == 0 {
return "", fmt.Errorf("parser %s failed", goModPath)
}
return module, nil
}
// InModule 判断指定 pkg 的 是否属于 module
func InModule(pkg string, module string) bool {
pkg1 := pkg + "/"
module1 := module + "/"
return strings.HasPrefix(pkg1, module1)
}
// Modules 模块列表
type Modules []string
// PkgIn 判断 pkg 是否属于 模块列表范围
func (ms Modules) PkgIn(pkg string) bool {
for _, m := range ms {
if InModule(pkg, m) {
return true
}
}
return false
}
// key:dir,string
// value: Modules
var modulesCache = &sync.Map{}
// ListModules 找到指定目录下的所有子 module
//
// 可能是这样的:
//
// a.go
// go.mod
// + world (目录)
// say.go // 这个和 下面的 hello 就是两个不同的 module
// + hello (目录) // 这是一个独立的 module
// hello.go
// go.mod
func ListModules(dir string) (Modules, error) {
if v, has := modulesCache.Load(dir); has {
return v.(Modules), nil
}
root := filepath.Join(dir, "go.mod")
var result Modules
err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {
if info.IsDir() || info.Name() != "go.mod" || root == path {
return nil
}
m, err1 := ModuleByFile(path)
if err1 != nil {
return err1
}
result = append(result, m)
return nil
})
if err == nil {
modulesCache.Store(dir, result)
}
return result, err
}
// goVersionByFile 通过文件名查找模块的 go 版本
// 若查找不到,会返回默认值 go1.19
func goVersionByFile(fileName string) string {
fp, err := FindGoModPath(fileName)
if err != nil {
return "1.19"
}
content, err := os.ReadFile(fp)
if err != nil {
return "1.19"
}
f, err := modfile.Parse(fp, content, nil)
if err != nil {
return "1.19"
}
if f.Go != nil && len(f.Go.Version) > 0 {
return f.Go.Version
}
return "1.19"
}