Home > Archives > k8s go2make

k8s go2make

Publish:

在编译k8s程序中,第一个被执行的go语言的程序是go2make。

这个程序的目的是将指定目录中的go文件罗列出来,方便后面使用。

这个程序解读的难点是:

  1. 递归模式
  2. 内置函数

调用方式

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/...

程序执行顺序

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,如果不包含则执行下面的语句。

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
}
  1. visit(pkg.ImportPath, pkg.Dir) 查找当前目录下面的Go文件
  2. WalkPkg(pkgName+”/”+name, visit) 递归调用当前目录下面的子目录
func visitPkg(importPath, absPath string) error {
	debug("visit", importPath)
	return saveImport(importPath)
}
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
}
// 加载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
}
 //将所有的文件添加到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{}{}
	}
}
//将之前的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
}

声明: 本文采用 BY-NC-SA 授权。转载请注明转自: Ding Bao Guo