walk/walk.go
func Walk(c *config.Config, cexts []config.Configurer, dirs []string, mode Mode, wf WalkFunc) {
	knownDirectives := make(map[string]bool)
	for _, cext := range cexts {
		for _, d := range cext.KnownDirectives() {
			knownDirectives[d] = true
		}
	}

	symlinks := symlinkResolver{visited: []string{c.RepoRoot}}

	updateRels := buildUpdateRelMap(c.RepoRoot, dirs)

	var visit func(*config.Config, string, string, bool)
	visit = func(c *config.Config, dir, rel string, updateParent bool) {
		haveError := false

		// TODO: OPT: ReadDir stats all the files, which is slow. We just care about
		// names and modes, so we should use something like
		// golang.org/x/tools/internal/fastwalk to speed this up.
		files, err := ioutil.ReadDir(dir)
		if err != nil {
			log.Print(err)
			return
		}

		f, err := loadBuildFile(c, rel, dir, files)
		if err != nil {
			log.Print(err)
			haveError = true
		}

		c = configure(cexts, knownDirectives, c, rel, f)
		wc := getWalkConfig(c)

		if wc.isExcluded(rel, ".") {
			return
		}

		var subdirs, regularFiles []string
		for _, fi := range files {
			base := fi.Name()
			switch {
			case base == "" || wc.isExcluded(rel, base):
				continue

			case fi.IsDir() || fi.Mode()&os.ModeSymlink != 0 && symlinks.follow(c, dir, rel, base):
				subdirs = append(subdirs, base)

			default:
				regularFiles = append(regularFiles, base)
			}
		}

		shouldUpdate := shouldUpdate(rel, mode, updateParent, updateRels)
		for _, sub := range subdirs {
			if subRel := path.Join(rel, sub); shouldVisit(subRel, mode, updateRels) {
				visit(c, filepath.Join(dir, sub), subRel, shouldUpdate)
			}
		}

		update := !haveError && !wc.ignore && shouldUpdate
		if shouldCall(rel, mode, updateRels) {
			genFiles := findGenFiles(wc, f)
			wf(dir, rel, c, update, f, subdirs, regularFiles, genFiles)
		}
	}
	visit(c, c.RepoRoot, "", false)
}
func Walk(c *config.Config, cexts []config.Configurer, dirs []string, mode Mode, wf WalkFunc) {
	knownDirectives := make(map[string]bool)
	for _, cext := range cexts {
		for _, d := range cext.KnownDirectives() {
			knownDirectives[d] = true
		}
	}

	symlinks := symlinkResolver{visited: []string{c.RepoRoot}}

	updateRels := buildUpdateRelMap(c.RepoRoot, dirs)

	var visit func(*config.Config, string, string, bool)
	visit = func(c *config.Config, dir, rel string, updateParent bool) {
		haveError := false

		// TODO: OPT: ReadDir stats all the files, which is slow. We just care about
		// names and modes, so we should use something like
		// golang.org/x/tools/internal/fastwalk to speed this up.
		files, err := ioutil.ReadDir(dir)
		if err != nil {
			log.Print(err)
			return
		}

		f, err := loadBuildFile(c, rel, dir, files)
		if err != nil {
			log.Print(err)
			haveError = true
		}

		c = configure(cexts, knownDirectives, c, rel, f)
		wc := getWalkConfig(c)

		if wc.isExcluded(rel, ".") {
			return
		}

		var subdirs, regularFiles []string
		for _, fi := range files {
			base := fi.Name()
			switch {
			case base == "" || wc.isExcluded(rel, base):
				continue

			case fi.IsDir() || fi.Mode()&os.ModeSymlink != 0 && symlinks.follow(c, dir, rel, base):
				subdirs = append(subdirs, base)

			default:
				regularFiles = append(regularFiles, base)
			}
		}

		shouldUpdate := shouldUpdate(rel, mode, updateParent, updateRels)
		for _, sub := range subdirs {
			if subRel := path.Join(rel, sub); shouldVisit(subRel, mode, updateRels) {
				visit(c, filepath.Join(dir, sub), subRel, shouldUpdate)
			}
		}

		update := !haveError && !wc.ignore && shouldUpdate
		if shouldCall(rel, mode, updateRels) {
			genFiles := findGenFiles(wc, f)
			wf(dir, rel, c, update, f, subdirs, regularFiles, genFiles)
		}
	}
	visit(c, c.RepoRoot, "", false)
}
// Walk traverses the directory tree rooted at c.RepoRoot. Walk visits
// subdirectories in depth-first post-order.
//
// When Walk visits a directory, it lists the files and subdirectories within
// that directory. If a build file is present, Walk reads the build file and
// applies any directives to the configuration (a copy of the parent directory's
// configuration is made, and the copy is modified). After visiting
// subdirectories, the callback wf may be called, depending on the mode.
//
// c is the root configuration to start with. This includes changes made by
// command line flags, but not by the root build file. This configuration
// should not be modified.
//
// cexts is a list of configuration extensions. When visiting a directory,
// before visiting subdirectories, Walk makes a copy of the parent configuration
// and Configure for each extension on the copy. If Walk sees a directive
// that is not listed in KnownDirectives of any extension, an error will
// be logged.
//
// dirs is a list of absolute, canonical file system paths of directories
// to visit.
//
// mode determines whether subdirectories of dirs should be visited recursively,
// when the wf callback should be called, and when the "update" argument
// to the wf callback should be set.
//
// wf is a function that may be called in each directory.