在编译k8s程序中,第一个被执行的go语言的程序是go2make。
这个程序的目的是将指定目录中的go文件罗列出来,方便后面使用。
这个程序解读的难点是:
- 递归模式
- 内置函数
调用方式
1
2
3
4
5
| go2make k8s.io/kubernetes/... \
--prune k8s.io/kubernetes/staging \
--prune k8s.io/kubernetes/vendor \
k8s.io/kubernetes/vendor/k8s.io/... \
github.com/jteeuwen/go-bindata/go-bindata/...
|
程序执行顺序
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| func main() {
for _, in := range pflag.Args() {
if strings.HasSuffix(in, "/...") {
// Recurse.
debug("starting", in)
pkgName := strings.TrimSuffix(in, "/...")
if err := WalkPkg(pkgName, visitPkg); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
} else {
// Import one package.
if err := saveImport(in); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(2)
}
}
}
}
|
如果参数含有三个点,则执行上面的if,如果不包含则执行下面的语句。
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
| func WalkPkg(pkgName string, visit VisitFunc) error {
// Visit the package itself.
pkg, err := findPackage(pkgName) //找到Package的信息
if err != nil {
return err
}
if err := visit(pkg.ImportPath, pkg.Dir); err == ErrSkipPkg { //浏览实际目录中Go文件列表
return nil
} else if err != nil {
return err
}
// Read all of the child dirents and find sub-packages.
infos, err := readDirInfos(pkg.Dir) // 找出当前目录下所有的子目录
if err != nil {
return err
}
for _, info := range infos {
if !info.IsDir() {
continue
}
name := info.Name()
if name[0] == '_' || (len(name) > 1 && name[0] == '.') || name == "testdata" {
continue
}
// Don't use path.Join() because it drops leading `./` via path.Clean().
err := WalkPkg(pkgName+"/"+name, visit) //嵌套递归这个子目录
if err != nil {
return err
}
}
return nil
}
|
- visit(pkg.ImportPath, pkg.Dir) 查找当前目录下面的Go文件
- WalkPkg(pkgName+”/”+name, visit) 递归调用当前目录下面的子目录
1
2
3
4
| func visitPkg(importPath, absPath string) error {
debug("visit", importPath)
return saveImport(importPath)
}
|
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
| func saveImport(pkgName string) error {
if cache[pkgName] != nil {
return nil
}
if prune(pkgName) { //目录是需要排除的,则退出
debug("prune", pkgName)
return ErrSkipPkg
}
pkg, err := loadPackage(pkgName) //加载package信息
if err != nil {
return err
}
debug("save", pkgName)
cache[pkgName] = pkg
debug("recurse", pkgName)
defer func() { debug("done ", pkgName) }()
if !pkg.Goroot && (len(pkg.GoFiles)+len(pkg.Imports) > 0) { //Root目录下面的不用管
// Process deps of this package before the package itself.
for _, impName := range pkg.Imports {
if impName == "C" {
continue
}
debug("depends on", impName)
saveImport(impName) //通过importName嵌套调用
}
// Emit a variable for each package.
var buf bytes.Buffer //准备要输出的信息
buf.WriteString(pkgName)
buf.WriteString(" := ")
// Packages depend on their own directories, their own files, and
// transitive list of all deps' directories and files.
all := map[string]struct{}{}
all[pkg.Dir+"/"] = struct{}{}
filesForPkg(pkg, all) //将当前pkg中的文件添加到all中去
for _, imp := range pkg.Imports {
pkg := cache[imp]
if pkg == nil || pkg.Goroot {
continue
}
all[pkg.Dir+"/"] = struct{}{}
filesForPkg(pkg, all) //将pkg.Imports对应的目录下的file添加到all中
}
// Sort and de-dup them.
files := flatten(all) //变成字符串数组,并排序
for _, f := range files { //循环写入Buffer
buf.WriteString(" \\\n ")
buf.WriteString(f)
}
fmt.Println(buf.String()) //打印到输出窗口
}
return nil
}
|
1
2
3
4
5
6
7
8
9
10
11
12
| // 加载package信息
func loadPackage(pkgName string) (*build.Package, error) {
debug("load", pkgName)
pkg, err := build.Import(pkgName, getwd(), 0)
if err != nil {
// We can ignore NoGoError. Anything else is real.
if _, ok := err.(*build.NoGoError); !ok {
return nil, err
}
}
return pkg, nil
}
|
1
2
3
4
5
6
7
8
9
| //将所有的文件添加到all中去
func filesForPkg(pkg *build.Package, all map[string]struct{}) {
for _, file := range pkg.GoFiles {
if pkg.Dir != "." {
file = pkg.Dir + "/" + file
}
all[file] = struct{}{}
}
}
|
1
2
3
4
5
6
7
8
9
| //将之前的map变成字符串数组,并排序
func flatten(all map[string]struct{}) []string {
list := make([]string, 0, len(all))
for k := range all {
list = append(list, k)
}
sort.Strings(list)
return list
}
|