| package resolver |
| |
| import ( |
| "log" |
| "sort" |
| "strings" |
| |
| "github.com/bazelbuild/bazel-gazelle/config" |
| "github.com/bazelbuild/bazel-gazelle/label" |
| "github.com/bazelbuild/bazel-gazelle/repo" |
| "github.com/bazelbuild/bazel-gazelle/resolve" |
| "github.com/bazelbuild/bazel-gazelle/rule" |
| |
| "go.skia.org/infra/bazel/gazelle/cpp/common" |
| "go.skia.org/infra/go/util" |
| ) |
| |
| const ( |
| // gazelleExtensionName is the extension name passed to Gazelle. |
| // |
| // This name can be used to enable or disable this Gazelle extension via the --lang flag, e.g. |
| // |
| // $ bazel run gazelle -- update --lang go,cpp |
| gazelleExtensionName = "cpp" |
| |
| // If a third_party dep starts with this prefix, the file will be ignored and not included |
| // in the generated rule. A common use is when a #include is needed for a non-Bazel build. |
| ignoreThirdPartyFilePrefix = "SK_GAZELLE_IGNORE" |
| ) |
| |
| // CppResolver implements the resolve.Resolver interface. |
| // |
| // Interface documentation: |
| // |
| // CppResolver is an interface that language extensions can implement to resolve |
| // dependencies in rules they generate. |
| type CppResolver struct{} |
| |
| // Name implements the resolve.Resolver interface. |
| // |
| // Interface documentation: |
| // |
| // Name returns the name of the language. This should be a prefix of the |
| // kinds of rules generated by the language, e.g., "go" for the Go extension |
| // since it generates "go_library" rules. |
| func (rslv *CppResolver) Name() string { |
| return gazelleExtensionName |
| } |
| |
| // Imports implements the resolve.Resolver interface. |
| // |
| // This method always returns an empty slice, which results in an empty resolve.RuleIndex, |
| // but that is OK because we do not use it. |
| func (rslv *CppResolver) Imports(*config.Config, *rule.Rule, *rule.File) []resolve.ImportSpec { |
| return nil |
| } |
| |
| // Embeds implements the resolve.Resolver interface. |
| func (rslv *CppResolver) Embeds(*rule.Rule, label.Label) []label.Label { return nil } |
| |
| // Resolve implements the resolve.Resolver interface. |
| // |
| // Resolve takes a (rule, ImportsParsedFromRuleSources) pair generated by Language.GenerateRules() |
| // and resolves the rule's dependencies based on its imports. It populates the rule's deps argument |
| // with the result of mapping each import to the label of a rule that provides the import. It does |
| // so by using the convention that each generated_cc_atom for which this rule depends on will |
| // expose a similarly named file. For example, needing "src/foo/Bar.h" assumes that a rule with |
| // label "//src/foo:Bar_hdr" exists. |
| // |
| // Gazelle calls this method once for each (rule, ImportsParsedFromRuleSources) pair generated by |
| // successive calls to Language.GenerateRules(). |
| func (rslv *CppResolver) Resolve(_ *config.Config, _ *resolve.RuleIndex, _ *repo.RemoteCache, r *rule.Rule, imports interface{}, from label.Label) { |
| importsFromRuleSources := imports.(common.ImportsParsedFromRuleSources) |
| thirdPartyMap := importsFromRuleSources.GetThirdPartyMap() |
| switch r.Kind() { |
| case common.GeneratedCCAtomRule: |
| var deps []label.Label |
| // Look through repo includes for thirdPartyMap matches first. Anything not in third party |
| // is assumed to be another header or source file in the repo. |
| for _, importPath := range importsFromRuleSources.GetRepoIncludes() { |
| if d, ok := thirdPartyMap[importPath]; ok { |
| if strings.HasPrefix(d, ignoreThirdPartyFilePrefix) { |
| continue |
| } |
| deps = append(deps, thirdPartyDep(d)) |
| continue |
| } |
| newLabel := resolveDepsForCImport(importPath) |
| if newLabel.Equal(label.NoLabel) { |
| log.Printf("Could not resolve %s in %s. Maybe it needs a third_party entry?\n", importPath, from.String()) |
| continue |
| } |
| newLabel.Repo = from.Repo |
| deps = append(deps, newLabel) |
| } |
| // Look through system includes for anything in the thirdPartyMap - skip it otherwise. |
| for _, importPath := range importsFromRuleSources.GetSystemIncludes() { |
| if d, ok := thirdPartyMap[importPath]; ok { |
| if strings.HasPrefix(d, ignoreThirdPartyFilePrefix) { |
| continue |
| } |
| deps = append(deps, thirdPartyDep(d)) |
| } |
| } |
| setDeps(r, from, deps) |
| |
| default: |
| panic("Unknown rule kind " + r.Kind()) |
| } |
| } |
| |
| // thirdPartyDep parses a label string into a proper Label. |
| func thirdPartyDep(d string) label.Label { |
| lbl, err := label.Parse(d) |
| if err != nil { |
| log.Fatalf("Invalid third_party dep %s - %s", d, err) |
| } |
| return lbl |
| } |
| |
| // setDeps sets the dependencies of a rule. |
| func setDeps(r *rule.Rule, thisLabel label.Label, deps []label.Label) { |
| const depsAttr = "deps" |
| r.DelAttr(depsAttr) |
| |
| var depsAsStrings []string |
| for _, dep := range deps { |
| // Filter out self-imports, e.g. when the .cpp file includes the .h file. |
| if thisLabel.Equal(dep) { |
| continue |
| } |
| // Try to use a relative include if possible |
| dep = dep.Rel(thisLabel.Repo, thisLabel.Pkg) |
| depsAsStrings = append(depsAsStrings, dep.String()) |
| } |
| |
| if len(depsAsStrings) > 0 { |
| depsAsStrings = util.SSliceDedup(depsAsStrings) |
| sort.Strings(depsAsStrings) |
| r.SetAttr(depsAttr, depsAsStrings) |
| } |
| } |
| |
| // resolveDepsForCImport takes a given path to a file and assumes that it is available by a rule |
| // with the same name, except the file extension is removed. |
| // For example, the file "alpha/beta/gamma/Delta.h" is assumed to be in the package |
| // "alpha/beta/gamma" with a rule called "Delta". |
| func resolveDepsForCImport(importPath string) label.Label { |
| idx := strings.LastIndex(importPath, "/") |
| if idx < 0 { |
| return label.NoLabel |
| } |
| pkg := importPath[:idx] |
| r := importPath[idx+1:] |
| if common.IsCppHeader(r) { |
| return label.New("", pkg, common.TrimCSuffix(r)+"_hdr") |
| } |
| return label.New("", pkg, common.TrimCSuffix(r)+"_src") |
| } |
| |
| var _ resolve.Resolver = &CppResolver{} |