From a3d1b7cc09ca23ec78d2bbb857dd41e1d4b51a3e Mon Sep 17 00:00:00 2001 From: Jason Bedard Date: Wed, 21 Aug 2024 15:31:47 -0700 Subject: [PATCH] perf: reduce path.join invocations --- walk/config.go | 32 +++++++++++++------------------- walk/walk.go | 36 ++++++++++++++++++++++++++---------- 2 files changed, 39 insertions(+), 29 deletions(-) diff --git a/walk/config.go b/walk/config.go index c7fc675c3..80808fa27 100644 --- a/walk/config.go +++ b/walk/config.go @@ -25,7 +25,6 @@ import ( "os" "path" "strings" - "sync" "github.com/bazelbuild/bazel-gazelle/config" "github.com/bazelbuild/bazel-gazelle/rule" @@ -42,7 +41,6 @@ type walkConfig struct { excludes []string ignore bool follow []string - loadOnce *sync.Once } const walkName = "_walk" @@ -51,15 +49,15 @@ func getWalkConfig(c *config.Config) *walkConfig { return c.Exts[walkName].(*walkConfig) } -func (wc *walkConfig) isExcluded(rel, base string) bool { - if base == ".git" { +func (wc *walkConfig) isExcluded(p string) bool { + if strings.HasPrefix(p, ".git") && (len(p) < 5 || p[4] == '/') { return true } - return matchAnyGlob(wc.excludes, path.Join(rel, base)) + return matchAnyGlob(wc.excludes, p) } -func (wc *walkConfig) shouldFollow(rel, base string) bool { - return matchAnyGlob(wc.follow, path.Join(rel, base)) +func (wc *walkConfig) shouldFollow(p string) bool { + return matchAnyGlob(wc.follow, p) } var _ config.Configurer = (*Configurer)(nil) @@ -67,7 +65,7 @@ var _ config.Configurer = (*Configurer)(nil) type Configurer struct{} func (*Configurer) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) { - wc := &walkConfig{loadOnce: &sync.Once{}} + wc := &walkConfig{} c.Exts[walkName] = wc fs.Var(&gzflag.MultiFlag{Values: &wc.excludes}, "exclude", "pattern that should be ignored (may be repeated)") } @@ -84,12 +82,6 @@ func (cr *Configurer) Configure(c *config.Config, rel string, f *rule.File) { *wcCopy = *wc wcCopy.ignore = false - wc.loadOnce.Do(func() { - if err := cr.loadBazelIgnore(c.RepoRoot, wcCopy); err != nil { - log.Printf("error loading .bazelignore: %v", err) - } - }) - if f != nil { for _, d := range f.Directives { switch d.Key { @@ -114,17 +106,19 @@ func (cr *Configurer) Configure(c *config.Config, rel string, f *rule.File) { c.Exts[walkName] = wcCopy } -func (c *Configurer) loadBazelIgnore(repoRoot string, wc *walkConfig) error { +func loadBazelIgnore(repoRoot string) (map[string]struct{}, error) { ignorePath := path.Join(repoRoot, ".bazelignore") file, err := os.Open(ignorePath) if errors.Is(err, fs.ErrNotExist) { - return nil + return nil, nil } if err != nil { - return fmt.Errorf(".bazelignore exists but couldn't be read: %v", err) + return nil, fmt.Errorf(".bazelignore exists but couldn't be read: %v", err) } defer file.Close() + excludes := make(map[string]struct{}) + scanner := bufio.NewScanner(file) for scanner.Scan() { ignore := strings.TrimSpace(scanner.Text()) @@ -142,9 +136,9 @@ func (c *Configurer) loadBazelIgnore(repoRoot string, wc *walkConfig) error { // the exclude matching won't work correctly. ignore = path.Clean(ignore) - wc.excludes = append(wc.excludes, ignore) + excludes[ignore] = struct{}{} } - return nil + return excludes, nil } func checkPathMatchPattern(pattern string) error { diff --git a/walk/walk.go b/walk/walk.go index 81550a2fa..791aea8ec 100644 --- a/walk/walk.go +++ b/walk/walk.go @@ -117,6 +117,11 @@ func Walk(c *config.Config, cexts []config.Configurer, dirs []string, mode Mode, updateRels := NewUpdateFilter(c.RepoRoot, dirs, mode) + bazelExcludes, bazelExcludesErr := loadBazelIgnore(c.RepoRoot) + if bazelExcludesErr != nil { + log.Printf("error loading .bazelignore: %v", bazelExcludesErr) + } + var visit func(*config.Config, string, string, bool) visit = func(c *config.Config, dir, rel string, updateParent bool) { haveError := false @@ -142,16 +147,31 @@ func Walk(c *config.Config, cexts []config.Configurer, dirs []string, mode Mode, } c = configure(cexts, knownDirectives, c, rel, f) + + if _, bazelExcluded := bazelExcludes[rel]; bazelExcluded { + return + } + wc := getWalkConfig(c) - if wc.isExcluded(rel, ".") { + if wc.isExcluded(rel) { return } var subdirs, regularFiles []string for _, ent := range ents { base := ent.Name() - ent := resolveFileInfo(wc, dir, rel, ent) + relBase := path.Join(rel, base) + + if _, bazelExcluded := bazelExcludes[relBase]; bazelExcluded { + continue + } + + if wc.isExcluded(relBase) { + continue + } + + ent := resolveFileInfo(wc, c.RepoRoot, relBase, ent) switch { case ent == nil: continue @@ -317,27 +337,23 @@ func findGenFiles(wc *walkConfig, f *rule.File) []string { var genFiles []string for _, s := range strs { - if !wc.isExcluded(f.Pkg, s) { + if !wc.isExcluded(path.Join(f.Pkg, s)) { genFiles = append(genFiles, s) } } return genFiles } -func resolveFileInfo(wc *walkConfig, dir, rel string, ent fs.DirEntry) fs.DirEntry { - base := ent.Name() - if base == "" || wc.isExcluded(rel, base) { - return nil - } +func resolveFileInfo(wc *walkConfig, dir, repoRel string, ent fs.DirEntry) fs.DirEntry { if ent.Type()&os.ModeSymlink == 0 { // Not a symlink, use the original FileInfo. return ent } - if !wc.shouldFollow(rel, ent.Name()) { + if !wc.shouldFollow(repoRel) { // A symlink, but not one we should follow. return nil } - fi, err := os.Stat(path.Join(dir, base)) + fi, err := os.Stat(path.Join(dir, repoRel)) if err != nil { // A symlink, but not one we could resolve. return nil