diff --git a/walk/config.go b/walk/config.go index c7fc675c3..044b59a79 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" @@ -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,23 @@ 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 { +type isIgnoredFunc = func(string) bool + +var nothingIgnored isIgnoredFunc = func(string) bool { return false } + +func loadBazelIgnore(repoRoot string) (isIgnoredFunc, error) { ignorePath := path.Join(repoRoot, ".bazelignore") file, err := os.Open(ignorePath) if errors.Is(err, fs.ErrNotExist) { - return nil + return nothingIgnored, nil } if err != nil { - return fmt.Errorf(".bazelignore exists but couldn't be read: %v", err) + return nothingIgnored, 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 +140,15 @@ 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{}{} + } + + isIgnored := func(p string) bool { + _, ok := excludes[p] + return ok } - return nil + + return isIgnored, nil } func checkPathMatchPattern(pattern string) error { diff --git a/walk/walk.go b/walk/walk.go index 81550a2fa..e8371641c 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) + isBazelIgnored, err := loadBazelIgnore(c.RepoRoot) + if err != nil { + log.Printf("error loading .bazelignore: %v", err) + } + var visit func(*config.Config, string, string, bool) visit = func(c *config.Config, dir, rel string, updateParent bool) { haveError := false @@ -141,6 +146,10 @@ func Walk(c *config.Config, cexts []config.Configurer, dirs []string, mode Mode, haveError = true } + if isBazelIgnored(rel) { + return + } + c = configure(cexts, knownDirectives, c, rel, f) wc := getWalkConfig(c) @@ -151,6 +160,9 @@ func Walk(c *config.Config, cexts []config.Configurer, dirs []string, mode Mode, var subdirs, regularFiles []string for _, ent := range ents { base := ent.Name() + if isBazelIgnored(path.Join(rel, base)) || wc.isExcluded(rel, base) { + continue + } ent := resolveFileInfo(wc, dir, rel, ent) switch { case ent == nil: @@ -326,14 +338,14 @@ func findGenFiles(wc *walkConfig, f *rule.File) []string { func resolveFileInfo(wc *walkConfig, dir, rel string, ent fs.DirEntry) fs.DirEntry { base := ent.Name() - if base == "" || wc.isExcluded(rel, base) { + if base == "" { return nil } if ent.Type()&os.ModeSymlink == 0 { // Not a symlink, use the original FileInfo. return ent } - if !wc.shouldFollow(rel, ent.Name()) { + if !wc.shouldFollow(rel, base) { // A symlink, but not one we should follow. return nil }