1*60517a1eSAndroid Build Coastguard Worker// Copyright 2023 The Bazel Authors. All rights reserved. 2*60517a1eSAndroid Build Coastguard Worker// 3*60517a1eSAndroid Build Coastguard Worker// Licensed under the Apache License, Version 2.0 (the "License"); 4*60517a1eSAndroid Build Coastguard Worker// you may not use this file except in compliance with the License. 5*60517a1eSAndroid Build Coastguard Worker// You may obtain a copy of the License at 6*60517a1eSAndroid Build Coastguard Worker// 7*60517a1eSAndroid Build Coastguard Worker// http://www.apache.org/licenses/LICENSE-2.0 8*60517a1eSAndroid Build Coastguard Worker// 9*60517a1eSAndroid Build Coastguard Worker// Unless required by applicable law or agreed to in writing, software 10*60517a1eSAndroid Build Coastguard Worker// distributed under the License is distributed on an "AS IS" BASIS, 11*60517a1eSAndroid Build Coastguard Worker// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*60517a1eSAndroid Build Coastguard Worker// See the License for the specific language governing permissions and 13*60517a1eSAndroid Build Coastguard Worker// limitations under the License. 14*60517a1eSAndroid Build Coastguard Worker 15*60517a1eSAndroid Build Coastguard Workerpackage python 16*60517a1eSAndroid Build Coastguard Worker 17*60517a1eSAndroid Build Coastguard Workerimport ( 18*60517a1eSAndroid Build Coastguard Worker "fmt" 19*60517a1eSAndroid Build Coastguard Worker "log" 20*60517a1eSAndroid Build Coastguard Worker "os" 21*60517a1eSAndroid Build Coastguard Worker "path/filepath" 22*60517a1eSAndroid Build Coastguard Worker "strings" 23*60517a1eSAndroid Build Coastguard Worker 24*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/bazel-gazelle/config" 25*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/bazel-gazelle/label" 26*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/bazel-gazelle/repo" 27*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/bazel-gazelle/resolve" 28*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/bazel-gazelle/rule" 29*60517a1eSAndroid Build Coastguard Worker bzl "github.com/bazelbuild/buildtools/build" 30*60517a1eSAndroid Build Coastguard Worker "github.com/emirpasic/gods/sets/treeset" 31*60517a1eSAndroid Build Coastguard Worker godsutils "github.com/emirpasic/gods/utils" 32*60517a1eSAndroid Build Coastguard Worker 33*60517a1eSAndroid Build Coastguard Worker "github.com/bazelbuild/rules_python/gazelle/pythonconfig" 34*60517a1eSAndroid Build Coastguard Worker) 35*60517a1eSAndroid Build Coastguard Worker 36*60517a1eSAndroid Build Coastguard Workerconst languageName = "py" 37*60517a1eSAndroid Build Coastguard Worker 38*60517a1eSAndroid Build Coastguard Workerconst ( 39*60517a1eSAndroid Build Coastguard Worker // resolvedDepsKey is the attribute key used to pass dependencies that don't 40*60517a1eSAndroid Build Coastguard Worker // need to be resolved by the dependency resolver in the Resolver step. 41*60517a1eSAndroid Build Coastguard Worker resolvedDepsKey = "_gazelle_python_resolved_deps" 42*60517a1eSAndroid Build Coastguard Worker) 43*60517a1eSAndroid Build Coastguard Worker 44*60517a1eSAndroid Build Coastguard Worker// Resolver satisfies the resolve.Resolver interface. It resolves dependencies 45*60517a1eSAndroid Build Coastguard Worker// in rules generated by this extension. 46*60517a1eSAndroid Build Coastguard Workertype Resolver struct{} 47*60517a1eSAndroid Build Coastguard Worker 48*60517a1eSAndroid Build Coastguard Worker// Name returns the name of the language. This is the prefix of the kinds of 49*60517a1eSAndroid Build Coastguard Worker// rules generated. E.g. py_library and py_binary. 50*60517a1eSAndroid Build Coastguard Workerfunc (*Resolver) Name() string { return languageName } 51*60517a1eSAndroid Build Coastguard Worker 52*60517a1eSAndroid Build Coastguard Worker// Imports returns a list of ImportSpecs that can be used to import the rule 53*60517a1eSAndroid Build Coastguard Worker// r. This is used to populate RuleIndex. 54*60517a1eSAndroid Build Coastguard Worker// 55*60517a1eSAndroid Build Coastguard Worker// If nil is returned, the rule will not be indexed. If any non-nil slice is 56*60517a1eSAndroid Build Coastguard Worker// returned, including an empty slice, the rule will be indexed. 57*60517a1eSAndroid Build Coastguard Workerfunc (py *Resolver) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec { 58*60517a1eSAndroid Build Coastguard Worker cfgs := c.Exts[languageName].(pythonconfig.Configs) 59*60517a1eSAndroid Build Coastguard Worker cfg := cfgs[f.Pkg] 60*60517a1eSAndroid Build Coastguard Worker srcs := r.AttrStrings("srcs") 61*60517a1eSAndroid Build Coastguard Worker provides := make([]resolve.ImportSpec, 0, len(srcs)+1) 62*60517a1eSAndroid Build Coastguard Worker for _, src := range srcs { 63*60517a1eSAndroid Build Coastguard Worker ext := filepath.Ext(src) 64*60517a1eSAndroid Build Coastguard Worker if ext != ".py" { 65*60517a1eSAndroid Build Coastguard Worker continue 66*60517a1eSAndroid Build Coastguard Worker } 67*60517a1eSAndroid Build Coastguard Worker if cfg.PerFileGeneration() && len(srcs) > 1 && src == pyLibraryEntrypointFilename { 68*60517a1eSAndroid Build Coastguard Worker // Do not provide import spec from __init__.py when it is being included as 69*60517a1eSAndroid Build Coastguard Worker // part of another module. 70*60517a1eSAndroid Build Coastguard Worker continue 71*60517a1eSAndroid Build Coastguard Worker } 72*60517a1eSAndroid Build Coastguard Worker pythonProjectRoot := cfg.PythonProjectRoot() 73*60517a1eSAndroid Build Coastguard Worker provide := importSpecFromSrc(pythonProjectRoot, f.Pkg, src) 74*60517a1eSAndroid Build Coastguard Worker provides = append(provides, provide) 75*60517a1eSAndroid Build Coastguard Worker } 76*60517a1eSAndroid Build Coastguard Worker if len(provides) == 0 { 77*60517a1eSAndroid Build Coastguard Worker return nil 78*60517a1eSAndroid Build Coastguard Worker } 79*60517a1eSAndroid Build Coastguard Worker return provides 80*60517a1eSAndroid Build Coastguard Worker} 81*60517a1eSAndroid Build Coastguard Worker 82*60517a1eSAndroid Build Coastguard Worker// importSpecFromSrc determines the ImportSpec based on the target that contains the src so that 83*60517a1eSAndroid Build Coastguard Worker// the target can be indexed for import statements that match the calculated src relative to the its 84*60517a1eSAndroid Build Coastguard Worker// Python project root. 85*60517a1eSAndroid Build Coastguard Workerfunc importSpecFromSrc(pythonProjectRoot, bzlPkg, src string) resolve.ImportSpec { 86*60517a1eSAndroid Build Coastguard Worker pythonPkgDir := filepath.Join(bzlPkg, filepath.Dir(src)) 87*60517a1eSAndroid Build Coastguard Worker relPythonPkgDir, err := filepath.Rel(pythonProjectRoot, pythonPkgDir) 88*60517a1eSAndroid Build Coastguard Worker if err != nil { 89*60517a1eSAndroid Build Coastguard Worker panic(fmt.Errorf("unexpected failure: %v", err)) 90*60517a1eSAndroid Build Coastguard Worker } 91*60517a1eSAndroid Build Coastguard Worker if relPythonPkgDir == "." { 92*60517a1eSAndroid Build Coastguard Worker relPythonPkgDir = "" 93*60517a1eSAndroid Build Coastguard Worker } 94*60517a1eSAndroid Build Coastguard Worker pythonPkg := strings.ReplaceAll(relPythonPkgDir, "/", ".") 95*60517a1eSAndroid Build Coastguard Worker filename := filepath.Base(src) 96*60517a1eSAndroid Build Coastguard Worker if filename == pyLibraryEntrypointFilename { 97*60517a1eSAndroid Build Coastguard Worker if pythonPkg != "" { 98*60517a1eSAndroid Build Coastguard Worker return resolve.ImportSpec{ 99*60517a1eSAndroid Build Coastguard Worker Lang: languageName, 100*60517a1eSAndroid Build Coastguard Worker Imp: pythonPkg, 101*60517a1eSAndroid Build Coastguard Worker } 102*60517a1eSAndroid Build Coastguard Worker } 103*60517a1eSAndroid Build Coastguard Worker } 104*60517a1eSAndroid Build Coastguard Worker moduleName := strings.TrimSuffix(filename, ".py") 105*60517a1eSAndroid Build Coastguard Worker var imp string 106*60517a1eSAndroid Build Coastguard Worker if pythonPkg == "" { 107*60517a1eSAndroid Build Coastguard Worker imp = moduleName 108*60517a1eSAndroid Build Coastguard Worker } else { 109*60517a1eSAndroid Build Coastguard Worker imp = fmt.Sprintf("%s.%s", pythonPkg, moduleName) 110*60517a1eSAndroid Build Coastguard Worker } 111*60517a1eSAndroid Build Coastguard Worker return resolve.ImportSpec{ 112*60517a1eSAndroid Build Coastguard Worker Lang: languageName, 113*60517a1eSAndroid Build Coastguard Worker Imp: imp, 114*60517a1eSAndroid Build Coastguard Worker } 115*60517a1eSAndroid Build Coastguard Worker} 116*60517a1eSAndroid Build Coastguard Worker 117*60517a1eSAndroid Build Coastguard Worker// Embeds returns a list of labels of rules that the given rule embeds. If 118*60517a1eSAndroid Build Coastguard Worker// a rule is embedded by another importable rule of the same language, only 119*60517a1eSAndroid Build Coastguard Worker// the embedding rule will be indexed. The embedding rule will inherit 120*60517a1eSAndroid Build Coastguard Worker// the imports of the embedded rule. 121*60517a1eSAndroid Build Coastguard Workerfunc (py *Resolver) Embeds(r *rule.Rule, from label.Label) []label.Label { 122*60517a1eSAndroid Build Coastguard Worker // TODO(f0rmiga): implement. 123*60517a1eSAndroid Build Coastguard Worker return make([]label.Label, 0) 124*60517a1eSAndroid Build Coastguard Worker} 125*60517a1eSAndroid Build Coastguard Worker 126*60517a1eSAndroid Build Coastguard Worker// Resolve translates imported libraries for a given rule into Bazel 127*60517a1eSAndroid Build Coastguard Worker// dependencies. Information about imported libraries is returned for each 128*60517a1eSAndroid Build Coastguard Worker// rule generated by language.GenerateRules in 129*60517a1eSAndroid Build Coastguard Worker// language.GenerateResult.Imports. Resolve generates a "deps" attribute (or 130*60517a1eSAndroid Build Coastguard Worker// the appropriate language-specific equivalent) for each import according to 131*60517a1eSAndroid Build Coastguard Worker// language-specific rules and heuristics. 132*60517a1eSAndroid Build Coastguard Workerfunc (py *Resolver) Resolve( 133*60517a1eSAndroid Build Coastguard Worker c *config.Config, 134*60517a1eSAndroid Build Coastguard Worker ix *resolve.RuleIndex, 135*60517a1eSAndroid Build Coastguard Worker rc *repo.RemoteCache, 136*60517a1eSAndroid Build Coastguard Worker r *rule.Rule, 137*60517a1eSAndroid Build Coastguard Worker modulesRaw interface{}, 138*60517a1eSAndroid Build Coastguard Worker from label.Label, 139*60517a1eSAndroid Build Coastguard Worker) { 140*60517a1eSAndroid Build Coastguard Worker // TODO(f0rmiga): may need to be defensive here once this Gazelle extension 141*60517a1eSAndroid Build Coastguard Worker // join with the main Gazelle binary with other rules. It may conflict with 142*60517a1eSAndroid Build Coastguard Worker // other generators that generate py_* targets. 143*60517a1eSAndroid Build Coastguard Worker deps := treeset.NewWith(godsutils.StringComparator) 144*60517a1eSAndroid Build Coastguard Worker if modulesRaw != nil { 145*60517a1eSAndroid Build Coastguard Worker cfgs := c.Exts[languageName].(pythonconfig.Configs) 146*60517a1eSAndroid Build Coastguard Worker cfg := cfgs[from.Pkg] 147*60517a1eSAndroid Build Coastguard Worker pythonProjectRoot := cfg.PythonProjectRoot() 148*60517a1eSAndroid Build Coastguard Worker modules := modulesRaw.(*treeset.Set) 149*60517a1eSAndroid Build Coastguard Worker it := modules.Iterator() 150*60517a1eSAndroid Build Coastguard Worker explainDependency := os.Getenv("EXPLAIN_DEPENDENCY") 151*60517a1eSAndroid Build Coastguard Worker hasFatalError := false 152*60517a1eSAndroid Build Coastguard Worker MODULES_LOOP: 153*60517a1eSAndroid Build Coastguard Worker for it.Next() { 154*60517a1eSAndroid Build Coastguard Worker mod := it.Value().(module) 155*60517a1eSAndroid Build Coastguard Worker moduleParts := strings.Split(mod.Name, ".") 156*60517a1eSAndroid Build Coastguard Worker possibleModules := []string{mod.Name} 157*60517a1eSAndroid Build Coastguard Worker for len(moduleParts) > 1 { 158*60517a1eSAndroid Build Coastguard Worker // Iterate back through the possible imports until 159*60517a1eSAndroid Build Coastguard Worker // a match is found. 160*60517a1eSAndroid Build Coastguard Worker // For example, "from foo.bar import baz" where baz is a module, we should try `foo.bar.baz` first, then 161*60517a1eSAndroid Build Coastguard Worker // `foo.bar`, then `foo`. 162*60517a1eSAndroid Build Coastguard Worker // In the first case, the import could be file `baz.py` in the directory `foo/bar`. 163*60517a1eSAndroid Build Coastguard Worker // Or, the import could be variable `baz` in file `foo/bar.py`. 164*60517a1eSAndroid Build Coastguard Worker // The import could also be from a standard module, e.g. `six.moves`, where 165*60517a1eSAndroid Build Coastguard Worker // the dependency is actually `six`. 166*60517a1eSAndroid Build Coastguard Worker moduleParts = moduleParts[:len(moduleParts)-1] 167*60517a1eSAndroid Build Coastguard Worker possibleModules = append(possibleModules, strings.Join(moduleParts, ".")) 168*60517a1eSAndroid Build Coastguard Worker } 169*60517a1eSAndroid Build Coastguard Worker errs := []error{} 170*60517a1eSAndroid Build Coastguard Worker POSSIBLE_MODULE_LOOP: 171*60517a1eSAndroid Build Coastguard Worker for _, moduleName := range possibleModules { 172*60517a1eSAndroid Build Coastguard Worker imp := resolve.ImportSpec{Lang: languageName, Imp: moduleName} 173*60517a1eSAndroid Build Coastguard Worker if override, ok := resolve.FindRuleWithOverride(c, imp, languageName); ok { 174*60517a1eSAndroid Build Coastguard Worker if override.Repo == "" { 175*60517a1eSAndroid Build Coastguard Worker override.Repo = from.Repo 176*60517a1eSAndroid Build Coastguard Worker } 177*60517a1eSAndroid Build Coastguard Worker if !override.Equal(from) { 178*60517a1eSAndroid Build Coastguard Worker if override.Repo == from.Repo { 179*60517a1eSAndroid Build Coastguard Worker override.Repo = "" 180*60517a1eSAndroid Build Coastguard Worker } 181*60517a1eSAndroid Build Coastguard Worker dep := override.Rel(from.Repo, from.Pkg).String() 182*60517a1eSAndroid Build Coastguard Worker deps.Add(dep) 183*60517a1eSAndroid Build Coastguard Worker if explainDependency == dep { 184*60517a1eSAndroid Build Coastguard Worker log.Printf("Explaining dependency (%s): "+ 185*60517a1eSAndroid Build Coastguard Worker "in the target %q, the file %q imports %q at line %d, "+ 186*60517a1eSAndroid Build Coastguard Worker "which resolves using the \"gazelle:resolve\" directive.\n", 187*60517a1eSAndroid Build Coastguard Worker explainDependency, from.String(), mod.Filepath, moduleName, mod.LineNumber) 188*60517a1eSAndroid Build Coastguard Worker } 189*60517a1eSAndroid Build Coastguard Worker continue MODULES_LOOP 190*60517a1eSAndroid Build Coastguard Worker } 191*60517a1eSAndroid Build Coastguard Worker } else { 192*60517a1eSAndroid Build Coastguard Worker if dep, ok := cfg.FindThirdPartyDependency(moduleName); ok { 193*60517a1eSAndroid Build Coastguard Worker deps.Add(dep) 194*60517a1eSAndroid Build Coastguard Worker if explainDependency == dep { 195*60517a1eSAndroid Build Coastguard Worker log.Printf("Explaining dependency (%s): "+ 196*60517a1eSAndroid Build Coastguard Worker "in the target %q, the file %q imports %q at line %d, "+ 197*60517a1eSAndroid Build Coastguard Worker "which resolves from the third-party module %q from the wheel %q.\n", 198*60517a1eSAndroid Build Coastguard Worker explainDependency, from.String(), mod.Filepath, moduleName, mod.LineNumber, mod.Name, dep) 199*60517a1eSAndroid Build Coastguard Worker } 200*60517a1eSAndroid Build Coastguard Worker continue MODULES_LOOP 201*60517a1eSAndroid Build Coastguard Worker } else { 202*60517a1eSAndroid Build Coastguard Worker matches := ix.FindRulesByImportWithConfig(c, imp, languageName) 203*60517a1eSAndroid Build Coastguard Worker if len(matches) == 0 { 204*60517a1eSAndroid Build Coastguard Worker // Check if the imported module is part of the standard library. 205*60517a1eSAndroid Build Coastguard Worker if isStdModule(module{Name: moduleName}) { 206*60517a1eSAndroid Build Coastguard Worker continue MODULES_LOOP 207*60517a1eSAndroid Build Coastguard Worker } else if cfg.ValidateImportStatements() { 208*60517a1eSAndroid Build Coastguard Worker err := fmt.Errorf( 209*60517a1eSAndroid Build Coastguard Worker "%[1]q, line %[2]d: %[3]q is an invalid dependency: possible solutions:\n"+ 210*60517a1eSAndroid Build Coastguard Worker "\t1. Add it as a dependency in the requirements.txt file.\n"+ 211*60517a1eSAndroid Build Coastguard Worker "\t2. Use the '# gazelle:resolve py %[3]s TARGET_LABEL' BUILD file directive to resolve to a known dependency.\n"+ 212*60517a1eSAndroid Build Coastguard Worker "\t3. Ignore it with a comment '# gazelle:ignore %[3]s' in the Python file.\n", 213*60517a1eSAndroid Build Coastguard Worker mod.Filepath, mod.LineNumber, moduleName, 214*60517a1eSAndroid Build Coastguard Worker ) 215*60517a1eSAndroid Build Coastguard Worker errs = append(errs, err) 216*60517a1eSAndroid Build Coastguard Worker continue POSSIBLE_MODULE_LOOP 217*60517a1eSAndroid Build Coastguard Worker } 218*60517a1eSAndroid Build Coastguard Worker } 219*60517a1eSAndroid Build Coastguard Worker filteredMatches := make([]resolve.FindResult, 0, len(matches)) 220*60517a1eSAndroid Build Coastguard Worker for _, match := range matches { 221*60517a1eSAndroid Build Coastguard Worker if match.IsSelfImport(from) { 222*60517a1eSAndroid Build Coastguard Worker // Prevent from adding itself as a dependency. 223*60517a1eSAndroid Build Coastguard Worker continue MODULES_LOOP 224*60517a1eSAndroid Build Coastguard Worker } 225*60517a1eSAndroid Build Coastguard Worker filteredMatches = append(filteredMatches, match) 226*60517a1eSAndroid Build Coastguard Worker } 227*60517a1eSAndroid Build Coastguard Worker if len(filteredMatches) == 0 { 228*60517a1eSAndroid Build Coastguard Worker continue POSSIBLE_MODULE_LOOP 229*60517a1eSAndroid Build Coastguard Worker } 230*60517a1eSAndroid Build Coastguard Worker if len(filteredMatches) > 1 { 231*60517a1eSAndroid Build Coastguard Worker sameRootMatches := make([]resolve.FindResult, 0, len(filteredMatches)) 232*60517a1eSAndroid Build Coastguard Worker for _, match := range filteredMatches { 233*60517a1eSAndroid Build Coastguard Worker if strings.HasPrefix(match.Label.Pkg, pythonProjectRoot) { 234*60517a1eSAndroid Build Coastguard Worker sameRootMatches = append(sameRootMatches, match) 235*60517a1eSAndroid Build Coastguard Worker } 236*60517a1eSAndroid Build Coastguard Worker } 237*60517a1eSAndroid Build Coastguard Worker if len(sameRootMatches) != 1 { 238*60517a1eSAndroid Build Coastguard Worker err := fmt.Errorf( 239*60517a1eSAndroid Build Coastguard Worker "%[1]q, line %[2]d: multiple targets (%[3]s) may be imported with %[4]q: possible solutions:\n"+ 240*60517a1eSAndroid Build Coastguard Worker "\t1. Disambiguate the above multiple targets by removing duplicate srcs entries.\n"+ 241*60517a1eSAndroid Build Coastguard Worker "\t2. Use the '# gazelle:resolve py %[4]s TARGET_LABEL' BUILD file directive to resolve to one of the above targets.\n", 242*60517a1eSAndroid Build Coastguard Worker mod.Filepath, mod.LineNumber, targetListFromResults(filteredMatches), moduleName) 243*60517a1eSAndroid Build Coastguard Worker errs = append(errs, err) 244*60517a1eSAndroid Build Coastguard Worker continue POSSIBLE_MODULE_LOOP 245*60517a1eSAndroid Build Coastguard Worker } 246*60517a1eSAndroid Build Coastguard Worker filteredMatches = sameRootMatches 247*60517a1eSAndroid Build Coastguard Worker } 248*60517a1eSAndroid Build Coastguard Worker matchLabel := filteredMatches[0].Label.Rel(from.Repo, from.Pkg) 249*60517a1eSAndroid Build Coastguard Worker dep := matchLabel.String() 250*60517a1eSAndroid Build Coastguard Worker deps.Add(dep) 251*60517a1eSAndroid Build Coastguard Worker if explainDependency == dep { 252*60517a1eSAndroid Build Coastguard Worker log.Printf("Explaining dependency (%s): "+ 253*60517a1eSAndroid Build Coastguard Worker "in the target %q, the file %q imports %q at line %d, "+ 254*60517a1eSAndroid Build Coastguard Worker "which resolves from the first-party indexed labels.\n", 255*60517a1eSAndroid Build Coastguard Worker explainDependency, from.String(), mod.Filepath, moduleName, mod.LineNumber) 256*60517a1eSAndroid Build Coastguard Worker } 257*60517a1eSAndroid Build Coastguard Worker continue MODULES_LOOP 258*60517a1eSAndroid Build Coastguard Worker } 259*60517a1eSAndroid Build Coastguard Worker } 260*60517a1eSAndroid Build Coastguard Worker } // End possible modules loop. 261*60517a1eSAndroid Build Coastguard Worker if len(errs) > 0 { 262*60517a1eSAndroid Build Coastguard Worker // If, after trying all possible modules, we still haven't found anything, error out. 263*60517a1eSAndroid Build Coastguard Worker joinedErrs := "" 264*60517a1eSAndroid Build Coastguard Worker for _, err := range errs { 265*60517a1eSAndroid Build Coastguard Worker joinedErrs = fmt.Sprintf("%s%s\n", joinedErrs, err) 266*60517a1eSAndroid Build Coastguard Worker } 267*60517a1eSAndroid Build Coastguard Worker log.Printf("ERROR: failed to validate dependencies for target %q:\n\n%v", from.String(), joinedErrs) 268*60517a1eSAndroid Build Coastguard Worker hasFatalError = true 269*60517a1eSAndroid Build Coastguard Worker } 270*60517a1eSAndroid Build Coastguard Worker } 271*60517a1eSAndroid Build Coastguard Worker if hasFatalError { 272*60517a1eSAndroid Build Coastguard Worker os.Exit(1) 273*60517a1eSAndroid Build Coastguard Worker } 274*60517a1eSAndroid Build Coastguard Worker } 275*60517a1eSAndroid Build Coastguard Worker resolvedDeps := r.PrivateAttr(resolvedDepsKey).(*treeset.Set) 276*60517a1eSAndroid Build Coastguard Worker if !resolvedDeps.Empty() { 277*60517a1eSAndroid Build Coastguard Worker it := resolvedDeps.Iterator() 278*60517a1eSAndroid Build Coastguard Worker for it.Next() { 279*60517a1eSAndroid Build Coastguard Worker deps.Add(it.Value()) 280*60517a1eSAndroid Build Coastguard Worker } 281*60517a1eSAndroid Build Coastguard Worker } 282*60517a1eSAndroid Build Coastguard Worker if !deps.Empty() { 283*60517a1eSAndroid Build Coastguard Worker r.SetAttr("deps", convertDependencySetToExpr(deps)) 284*60517a1eSAndroid Build Coastguard Worker } 285*60517a1eSAndroid Build Coastguard Worker} 286*60517a1eSAndroid Build Coastguard Worker 287*60517a1eSAndroid Build Coastguard Worker// targetListFromResults returns a string with the human-readable list of 288*60517a1eSAndroid Build Coastguard Worker// targets contained in the given results. 289*60517a1eSAndroid Build Coastguard Workerfunc targetListFromResults(results []resolve.FindResult) string { 290*60517a1eSAndroid Build Coastguard Worker list := make([]string, len(results)) 291*60517a1eSAndroid Build Coastguard Worker for i, result := range results { 292*60517a1eSAndroid Build Coastguard Worker list[i] = result.Label.String() 293*60517a1eSAndroid Build Coastguard Worker } 294*60517a1eSAndroid Build Coastguard Worker return strings.Join(list, ", ") 295*60517a1eSAndroid Build Coastguard Worker} 296*60517a1eSAndroid Build Coastguard Worker 297*60517a1eSAndroid Build Coastguard Worker// convertDependencySetToExpr converts the given set of dependencies to an 298*60517a1eSAndroid Build Coastguard Worker// expression to be used in the deps attribute. 299*60517a1eSAndroid Build Coastguard Workerfunc convertDependencySetToExpr(set *treeset.Set) bzl.Expr { 300*60517a1eSAndroid Build Coastguard Worker deps := make([]bzl.Expr, set.Size()) 301*60517a1eSAndroid Build Coastguard Worker it := set.Iterator() 302*60517a1eSAndroid Build Coastguard Worker for it.Next() { 303*60517a1eSAndroid Build Coastguard Worker dep := it.Value().(string) 304*60517a1eSAndroid Build Coastguard Worker deps[it.Index()] = &bzl.StringExpr{Value: dep} 305*60517a1eSAndroid Build Coastguard Worker } 306*60517a1eSAndroid Build Coastguard Worker return &bzl.ListExpr{List: deps} 307*60517a1eSAndroid Build Coastguard Worker} 308