xref: /aosp_15_r20/external/bazelbuild-rules_python/gazelle/python/target.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	"github.com/bazelbuild/bazel-gazelle/config"
19*60517a1eSAndroid Build Coastguard Worker	"github.com/bazelbuild/bazel-gazelle/rule"
20*60517a1eSAndroid Build Coastguard Worker	"github.com/emirpasic/gods/sets/treeset"
21*60517a1eSAndroid Build Coastguard Worker	godsutils "github.com/emirpasic/gods/utils"
22*60517a1eSAndroid Build Coastguard Worker	"path/filepath"
23*60517a1eSAndroid Build Coastguard Worker)
24*60517a1eSAndroid Build Coastguard Worker
25*60517a1eSAndroid Build Coastguard Worker// targetBuilder builds targets to be generated by Gazelle.
26*60517a1eSAndroid Build Coastguard Workertype targetBuilder struct {
27*60517a1eSAndroid Build Coastguard Worker	kind              string
28*60517a1eSAndroid Build Coastguard Worker	name              string
29*60517a1eSAndroid Build Coastguard Worker	pythonProjectRoot string
30*60517a1eSAndroid Build Coastguard Worker	bzlPackage        string
31*60517a1eSAndroid Build Coastguard Worker	srcs              *treeset.Set
32*60517a1eSAndroid Build Coastguard Worker	siblingSrcs       *treeset.Set
33*60517a1eSAndroid Build Coastguard Worker	deps              *treeset.Set
34*60517a1eSAndroid Build Coastguard Worker	resolvedDeps      *treeset.Set
35*60517a1eSAndroid Build Coastguard Worker	visibility        *treeset.Set
36*60517a1eSAndroid Build Coastguard Worker	main              *string
37*60517a1eSAndroid Build Coastguard Worker	imports           []string
38*60517a1eSAndroid Build Coastguard Worker	testonly          bool
39*60517a1eSAndroid Build Coastguard Worker}
40*60517a1eSAndroid Build Coastguard Worker
41*60517a1eSAndroid Build Coastguard Worker// newTargetBuilder constructs a new targetBuilder.
42*60517a1eSAndroid Build Coastguard Workerfunc newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string, siblingSrcs *treeset.Set) *targetBuilder {
43*60517a1eSAndroid Build Coastguard Worker	return &targetBuilder{
44*60517a1eSAndroid Build Coastguard Worker		kind:              kind,
45*60517a1eSAndroid Build Coastguard Worker		name:              name,
46*60517a1eSAndroid Build Coastguard Worker		pythonProjectRoot: pythonProjectRoot,
47*60517a1eSAndroid Build Coastguard Worker		bzlPackage:        bzlPackage,
48*60517a1eSAndroid Build Coastguard Worker		srcs:              treeset.NewWith(godsutils.StringComparator),
49*60517a1eSAndroid Build Coastguard Worker		siblingSrcs:       siblingSrcs,
50*60517a1eSAndroid Build Coastguard Worker		deps:              treeset.NewWith(moduleComparator),
51*60517a1eSAndroid Build Coastguard Worker		resolvedDeps:      treeset.NewWith(godsutils.StringComparator),
52*60517a1eSAndroid Build Coastguard Worker		visibility:        treeset.NewWith(godsutils.StringComparator),
53*60517a1eSAndroid Build Coastguard Worker	}
54*60517a1eSAndroid Build Coastguard Worker}
55*60517a1eSAndroid Build Coastguard Worker
56*60517a1eSAndroid Build Coastguard Worker// addSrc adds a single src to the target.
57*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addSrc(src string) *targetBuilder {
58*60517a1eSAndroid Build Coastguard Worker	t.srcs.Add(src)
59*60517a1eSAndroid Build Coastguard Worker	return t
60*60517a1eSAndroid Build Coastguard Worker}
61*60517a1eSAndroid Build Coastguard Worker
62*60517a1eSAndroid Build Coastguard Worker// addSrcs copies all values from the provided srcs to the target.
63*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addSrcs(srcs *treeset.Set) *targetBuilder {
64*60517a1eSAndroid Build Coastguard Worker	it := srcs.Iterator()
65*60517a1eSAndroid Build Coastguard Worker	for it.Next() {
66*60517a1eSAndroid Build Coastguard Worker		t.srcs.Add(it.Value().(string))
67*60517a1eSAndroid Build Coastguard Worker	}
68*60517a1eSAndroid Build Coastguard Worker	return t
69*60517a1eSAndroid Build Coastguard Worker}
70*60517a1eSAndroid Build Coastguard Worker
71*60517a1eSAndroid Build Coastguard Worker// addModuleDependency adds a single module dep to the target.
72*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addModuleDependency(dep module) *targetBuilder {
73*60517a1eSAndroid Build Coastguard Worker	fileName := dep.Name + ".py"
74*60517a1eSAndroid Build Coastguard Worker	if dep.From != "" {
75*60517a1eSAndroid Build Coastguard Worker		fileName = dep.From + ".py"
76*60517a1eSAndroid Build Coastguard Worker	}
77*60517a1eSAndroid Build Coastguard Worker	if t.siblingSrcs.Contains(fileName) && fileName != filepath.Base(dep.Filepath) {
78*60517a1eSAndroid Build Coastguard Worker		// importing another module from the same package, converting to absolute imports to make
79*60517a1eSAndroid Build Coastguard Worker		// dependency resolution easier
80*60517a1eSAndroid Build Coastguard Worker		dep.Name = importSpecFromSrc(t.pythonProjectRoot, t.bzlPackage, fileName).Imp
81*60517a1eSAndroid Build Coastguard Worker	}
82*60517a1eSAndroid Build Coastguard Worker	t.deps.Add(dep)
83*60517a1eSAndroid Build Coastguard Worker	return t
84*60517a1eSAndroid Build Coastguard Worker}
85*60517a1eSAndroid Build Coastguard Worker
86*60517a1eSAndroid Build Coastguard Worker// addModuleDependencies copies all values from the provided deps to the target.
87*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addModuleDependencies(deps *treeset.Set) *targetBuilder {
88*60517a1eSAndroid Build Coastguard Worker	it := deps.Iterator()
89*60517a1eSAndroid Build Coastguard Worker	for it.Next() {
90*60517a1eSAndroid Build Coastguard Worker		t.addModuleDependency(it.Value().(module))
91*60517a1eSAndroid Build Coastguard Worker	}
92*60517a1eSAndroid Build Coastguard Worker	return t
93*60517a1eSAndroid Build Coastguard Worker}
94*60517a1eSAndroid Build Coastguard Worker
95*60517a1eSAndroid Build Coastguard Worker// addResolvedDependency adds a single dependency the target that has already
96*60517a1eSAndroid Build Coastguard Worker// been resolved or generated. The Resolver step doesn't process it further.
97*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addResolvedDependency(dep string) *targetBuilder {
98*60517a1eSAndroid Build Coastguard Worker	t.resolvedDeps.Add(dep)
99*60517a1eSAndroid Build Coastguard Worker	return t
100*60517a1eSAndroid Build Coastguard Worker}
101*60517a1eSAndroid Build Coastguard Worker
102*60517a1eSAndroid Build Coastguard Worker// addResolvedDependencies adds multiple dependencies, that have already been
103*60517a1eSAndroid Build Coastguard Worker// resolved or generated, to the target.
104*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addResolvedDependencies(deps []string) *targetBuilder {
105*60517a1eSAndroid Build Coastguard Worker	for _, dep := range deps {
106*60517a1eSAndroid Build Coastguard Worker		t.addResolvedDependency(dep)
107*60517a1eSAndroid Build Coastguard Worker	}
108*60517a1eSAndroid Build Coastguard Worker	return t
109*60517a1eSAndroid Build Coastguard Worker}
110*60517a1eSAndroid Build Coastguard Worker
111*60517a1eSAndroid Build Coastguard Worker// addVisibility adds visibility labels to the target.
112*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) addVisibility(visibility []string) *targetBuilder {
113*60517a1eSAndroid Build Coastguard Worker	for _, item := range visibility {
114*60517a1eSAndroid Build Coastguard Worker		t.visibility.Add(item)
115*60517a1eSAndroid Build Coastguard Worker	}
116*60517a1eSAndroid Build Coastguard Worker	return t
117*60517a1eSAndroid Build Coastguard Worker}
118*60517a1eSAndroid Build Coastguard Worker
119*60517a1eSAndroid Build Coastguard Worker// setMain sets the main file to the target.
120*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) setMain(main string) *targetBuilder {
121*60517a1eSAndroid Build Coastguard Worker	t.main = &main
122*60517a1eSAndroid Build Coastguard Worker	return t
123*60517a1eSAndroid Build Coastguard Worker}
124*60517a1eSAndroid Build Coastguard Worker
125*60517a1eSAndroid Build Coastguard Worker// setTestonly sets the testonly attribute to true.
126*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) setTestonly() *targetBuilder {
127*60517a1eSAndroid Build Coastguard Worker	t.testonly = true
128*60517a1eSAndroid Build Coastguard Worker	return t
129*60517a1eSAndroid Build Coastguard Worker}
130*60517a1eSAndroid Build Coastguard Worker
131*60517a1eSAndroid Build Coastguard Worker// generateImportsAttribute generates the imports attribute.
132*60517a1eSAndroid Build Coastguard Worker// These are a list of import directories to be added to the PYTHONPATH. In our
133*60517a1eSAndroid Build Coastguard Worker// case, the value we add is on Bazel sub-packages to be able to perform imports
134*60517a1eSAndroid Build Coastguard Worker// relative to the root project package.
135*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) generateImportsAttribute() *targetBuilder {
136*60517a1eSAndroid Build Coastguard Worker	if t.pythonProjectRoot == "" {
137*60517a1eSAndroid Build Coastguard Worker		// When gazelle:python_root is not set or is at the root of the repo, we don't need
138*60517a1eSAndroid Build Coastguard Worker		// to set imports, because that's the Bazel's default.
139*60517a1eSAndroid Build Coastguard Worker		return t
140*60517a1eSAndroid Build Coastguard Worker	}
141*60517a1eSAndroid Build Coastguard Worker	p, _ := filepath.Rel(t.bzlPackage, t.pythonProjectRoot)
142*60517a1eSAndroid Build Coastguard Worker	p = filepath.Clean(p)
143*60517a1eSAndroid Build Coastguard Worker	if p == "." {
144*60517a1eSAndroid Build Coastguard Worker		return t
145*60517a1eSAndroid Build Coastguard Worker	}
146*60517a1eSAndroid Build Coastguard Worker	t.imports = []string{p}
147*60517a1eSAndroid Build Coastguard Worker	return t
148*60517a1eSAndroid Build Coastguard Worker}
149*60517a1eSAndroid Build Coastguard Worker
150*60517a1eSAndroid Build Coastguard Worker// build returns the assembled *rule.Rule for the target.
151*60517a1eSAndroid Build Coastguard Workerfunc (t *targetBuilder) build() *rule.Rule {
152*60517a1eSAndroid Build Coastguard Worker	r := rule.NewRule(t.kind, t.name)
153*60517a1eSAndroid Build Coastguard Worker	if !t.srcs.Empty() {
154*60517a1eSAndroid Build Coastguard Worker		r.SetAttr("srcs", t.srcs.Values())
155*60517a1eSAndroid Build Coastguard Worker	}
156*60517a1eSAndroid Build Coastguard Worker	if !t.visibility.Empty() {
157*60517a1eSAndroid Build Coastguard Worker		r.SetAttr("visibility", t.visibility.Values())
158*60517a1eSAndroid Build Coastguard Worker	}
159*60517a1eSAndroid Build Coastguard Worker	if t.main != nil {
160*60517a1eSAndroid Build Coastguard Worker		r.SetAttr("main", *t.main)
161*60517a1eSAndroid Build Coastguard Worker	}
162*60517a1eSAndroid Build Coastguard Worker	if t.imports != nil {
163*60517a1eSAndroid Build Coastguard Worker		r.SetAttr("imports", t.imports)
164*60517a1eSAndroid Build Coastguard Worker	}
165*60517a1eSAndroid Build Coastguard Worker	if !t.deps.Empty() {
166*60517a1eSAndroid Build Coastguard Worker		r.SetPrivateAttr(config.GazelleImportsKey, t.deps)
167*60517a1eSAndroid Build Coastguard Worker	}
168*60517a1eSAndroid Build Coastguard Worker	if t.testonly {
169*60517a1eSAndroid Build Coastguard Worker		r.SetAttr("testonly", true)
170*60517a1eSAndroid Build Coastguard Worker	}
171*60517a1eSAndroid Build Coastguard Worker	r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps)
172*60517a1eSAndroid Build Coastguard Worker	return r
173*60517a1eSAndroid Build Coastguard Worker}
174