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