xref: /aosp_15_r20/external/bazelbuild-rules_python/gazelle/python/resolve.go (revision 60517a1edbc8ecf509223e9af94a7adec7d736b8)
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