| package language |
| |
| import ( |
| "io/ioutil" |
| "log" |
| "path/filepath" |
| "sort" |
| |
| "github.com/bazelbuild/bazel-gazelle/config" |
| "github.com/bazelbuild/bazel-gazelle/language" |
| "github.com/bazelbuild/bazel-gazelle/rule" |
| |
| "go.skia.org/infra/bazel/gazelle/cpp/common" |
| "go.skia.org/infra/bazel/gazelle/cpp/configurer" |
| "go.skia.org/infra/bazel/gazelle/cpp/parsers" |
| "go.skia.org/infra/bazel/gazelle/cpp/resolver" |
| ) |
| |
| // CppLanguage implements the language.CppLanguage interface. |
| type CppLanguage struct { |
| configurer.CppConfigurer |
| resolver.CppResolver |
| } |
| |
| // Kinds implements the language.CppLanguage interface. |
| // |
| // Interface documentation: |
| // |
| // Kinds returns a map of maps rule names (kinds) and information on how to |
| // match and merge attributes that may be found in rules of those kinds. All |
| // kinds of rules generated for this language may be found here. |
| func (l *CppLanguage) Kinds() map[string]rule.KindInfo { |
| return map[string]rule.KindInfo{ |
| common.GeneratedCCAtomRule: { |
| // This extension generates content for deps, hdrs, and srcs, so that's why they are |
| // specified here. |
| ResolveAttrs: map[string]bool{"deps": true, "hdrs": true, "srcs": true}, |
| // NonEmptyAttrs must be non-nil, otherwise no rules will get deleted. |
| // We don't have any fields that should prevent a rule from being deleted if the |
| // underlying source file or header file is missing, so an empty map suffices. |
| NonEmptyAttrs: map[string]bool{}, |
| }, |
| } |
| } |
| |
| // Loads implements the language.CppLanguage interface. |
| // |
| // Interface documentation: |
| // |
| // Loads returns .bzl files and symbols they define. Every rule generated by |
| // GenerateRules, now or in the past, should be loadable from one of these |
| // files. |
| func (l *CppLanguage) Loads() []rule.LoadInfo { |
| return []rule.LoadInfo{ |
| { |
| Name: "//bazel:macros.bzl", |
| Symbols: []string{ |
| common.GeneratedCCAtomRule, |
| }, |
| }, |
| } |
| } |
| |
| // GenerateRules implements the language.CppLanguage interface. |
| // |
| // GenerateRules generates build rules for source files in a directory. GenerateRules is called in |
| // each directory where an update is requested in depth-first order. |
| // |
| // This method does not populate the deps argument of any generate rules. Dependencies are resolved |
| // in CppResolver.Resolve(), which happens after GenerateRules is called in each directory where an |
| // update is requested. |
| func (l *CppLanguage) GenerateRules(args language.GenerateArgs) language.GenerateResult { |
| allFiles := append(args.RegularFiles, args.GenFiles...) |
| rules := makeRulesFromFiles(allFiles) |
| imports := getImportsFromRules(rules, args.Dir, l.ThirdPartyFileMap) |
| |
| var existingRules []*rule.Rule |
| if args.File != nil { |
| existingRules = args.File.Rules |
| } |
| |
| return language.GenerateResult{ |
| Gen: rules, |
| Imports: imports, |
| Empty: findEmptyRules(existingRules, rules), |
| } |
| } |
| |
| // findEmptyRules returns any generated_cc_atom rules which were in the rules from BUILD.bazel, |
| // but did not show up when we recreated all the rules from the source files. This means the |
| // underlying source files have been deleted or moved and the rules should as well. |
| func findEmptyRules(previous, new []*rule.Rule) []*rule.Rule { |
| ruleCache := map[string]bool{} |
| for _, r := range new { |
| if r.Kind() != common.GeneratedCCAtomRule { |
| continue |
| } |
| ruleCache[r.Name()] = true |
| } |
| |
| var empty []*rule.Rule |
| for _, r := range previous { |
| if r.Kind() != common.GeneratedCCAtomRule { |
| continue |
| } |
| if _, ok := ruleCache[r.Name()]; !ok { |
| empty = append(empty, rule.NewRule(common.GeneratedCCAtomRule, r.Name())) |
| } |
| } |
| return empty |
| } |
| |
| // makeRulesFromFiles returns a single rule per file. Header rules will have the _hdr suffix and |
| // source rules will have the _src suffix so as to distinguish them, but have them be named |
| // in a predictable way. |
| func makeRulesFromFiles(names []string) []*rule.Rule { |
| var rv []*rule.Rule |
| for _, name := range names { |
| var r *rule.Rule |
| if common.IsCppHeader(name) { |
| r = rule.NewRule(common.GeneratedCCAtomRule, common.TrimCSuffix(name)+"_hdr") |
| r.SetAttr("hdrs", []string{name}) |
| } else if common.IsCppSource(name) { |
| r = rule.NewRule(common.GeneratedCCAtomRule, common.TrimCSuffix(name)+"_src") |
| r.SetAttr("srcs", []string{name}) |
| } else { |
| continue |
| } |
| r.SetAttr("visibility", []string{"//:__subpackages__"}) |
| rv = append(rv, r) |
| } |
| // Sort for determinism |
| sort.Slice(rv, func(i, j int) bool { |
| return rv[i].Name() < rv[j].Name() |
| }) |
| return rv |
| } |
| |
| // getImportsFromRules returns a list of common.ImportsParsedFromRuleSources, one per rule, that |
| // relate to the files that were listed via #include. |
| func getImportsFromRules(rules []*rule.Rule, dir string, thirdPartyMap map[string]string) []interface{} { |
| var rv []interface{} |
| for _, r := range rules { |
| var repoHeaders, systemHeaders []string |
| hdr := r.AttrStrings("hdrs") |
| if len(hdr) == 1 { |
| rh, sh := extractImportsFromCFile(filepath.Join(dir, hdr[0])) |
| repoHeaders = append(repoHeaders, rh...) |
| systemHeaders = append(systemHeaders, sh...) |
| } |
| src := r.AttrStrings("srcs") |
| if len(src) == 1 { |
| rh, sh := extractImportsFromCFile(filepath.Join(dir, src[0])) |
| repoHeaders = append(repoHeaders, rh...) |
| systemHeaders = append(systemHeaders, sh...) |
| } |
| rv = append(rv, common.NewImports(repoHeaders, systemHeaders, thirdPartyMap)) |
| } |
| return rv |
| } |
| |
| // extractImportsFromCFile opens ups the given file and parses the contents. |
| func extractImportsFromCFile(path string) ([]string, []string) { |
| b, err := ioutil.ReadFile(path) |
| if err != nil { |
| log.Panicf("Error reading file %q: %v", path, err) |
| } |
| return parsers.ParseCIncludes(string(b[:])) |
| } |
| |
| // Fix implements the language.CppLanguage interface. |
| func (l *CppLanguage) Fix(*config.Config, *rule.File) {} |
| |
| var _ language.Language = &CppLanguage{} |