aboutsummaryrefslogtreecommitdiff
path: root/gazelle/python/target.go
diff options
context:
space:
mode:
Diffstat (limited to 'gazelle/python/target.go')
-rw-r--r--gazelle/python/target.go157
1 files changed, 157 insertions, 0 deletions
diff --git a/gazelle/python/target.go b/gazelle/python/target.go
new file mode 100644
index 0000000..fdc99fc
--- /dev/null
+++ b/gazelle/python/target.go
@@ -0,0 +1,157 @@
+// Copyright 2023 The Bazel Authors. All rights reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package python
+
+import (
+ "github.com/bazelbuild/bazel-gazelle/config"
+ "github.com/bazelbuild/bazel-gazelle/rule"
+ "github.com/emirpasic/gods/sets/treeset"
+ godsutils "github.com/emirpasic/gods/utils"
+ "path/filepath"
+)
+
+// targetBuilder builds targets to be generated by Gazelle.
+type targetBuilder struct {
+ kind string
+ name string
+ pythonProjectRoot string
+ bzlPackage string
+ srcs *treeset.Set
+ siblingSrcs *treeset.Set
+ deps *treeset.Set
+ resolvedDeps *treeset.Set
+ visibility *treeset.Set
+ main *string
+ imports []string
+ testonly bool
+}
+
+// newTargetBuilder constructs a new targetBuilder.
+func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string, siblingSrcs *treeset.Set) *targetBuilder {
+ return &targetBuilder{
+ kind: kind,
+ name: name,
+ pythonProjectRoot: pythonProjectRoot,
+ bzlPackage: bzlPackage,
+ srcs: treeset.NewWith(godsutils.StringComparator),
+ siblingSrcs: siblingSrcs,
+ deps: treeset.NewWith(moduleComparator),
+ resolvedDeps: treeset.NewWith(godsutils.StringComparator),
+ visibility: treeset.NewWith(godsutils.StringComparator),
+ }
+}
+
+// addSrc adds a single src to the target.
+func (t *targetBuilder) addSrc(src string) *targetBuilder {
+ t.srcs.Add(src)
+ return t
+}
+
+// addSrcs copies all values from the provided srcs to the target.
+func (t *targetBuilder) addSrcs(srcs *treeset.Set) *targetBuilder {
+ it := srcs.Iterator()
+ for it.Next() {
+ t.srcs.Add(it.Value().(string))
+ }
+ return t
+}
+
+// addModuleDependency adds a single module dep to the target.
+func (t *targetBuilder) addModuleDependency(dep module) *targetBuilder {
+ fileName := dep.Name + ".py"
+ if dep.From != "" {
+ fileName = dep.From + ".py"
+ }
+ if t.siblingSrcs.Contains(fileName) && fileName != filepath.Base(dep.Filepath) {
+ // importing another module from the same package, converting to absolute imports to make
+ // dependency resolution easier
+ dep.Name = importSpecFromSrc(t.pythonProjectRoot, t.bzlPackage, fileName).Imp
+ }
+ t.deps.Add(dep)
+ return t
+}
+
+// addModuleDependencies copies all values from the provided deps to the target.
+func (t *targetBuilder) addModuleDependencies(deps *treeset.Set) *targetBuilder {
+ it := deps.Iterator()
+ for it.Next() {
+ t.addModuleDependency(it.Value().(module))
+ }
+ return t
+}
+
+// addResolvedDependency adds a single dependency the target that has already
+// been resolved or generated. The Resolver step doesn't process it further.
+func (t *targetBuilder) addResolvedDependency(dep string) *targetBuilder {
+ t.resolvedDeps.Add(dep)
+ return t
+}
+
+// addVisibility adds a visibility to the target.
+func (t *targetBuilder) addVisibility(visibility string) *targetBuilder {
+ t.visibility.Add(visibility)
+ return t
+}
+
+// setMain sets the main file to the target.
+func (t *targetBuilder) setMain(main string) *targetBuilder {
+ t.main = &main
+ return t
+}
+
+// setTestonly sets the testonly attribute to true.
+func (t *targetBuilder) setTestonly() *targetBuilder {
+ t.testonly = true
+ return t
+}
+
+// generateImportsAttribute generates the imports attribute.
+// These are a list of import directories to be added to the PYTHONPATH. In our
+// case, the value we add is on Bazel sub-packages to be able to perform imports
+// relative to the root project package.
+func (t *targetBuilder) generateImportsAttribute() *targetBuilder {
+ p, _ := filepath.Rel(t.bzlPackage, t.pythonProjectRoot)
+ p = filepath.Clean(p)
+ if p == "." {
+ return t
+ }
+ t.imports = []string{p}
+ return t
+}
+
+// build returns the assembled *rule.Rule for the target.
+func (t *targetBuilder) build() *rule.Rule {
+ r := rule.NewRule(t.kind, t.name)
+ if !t.srcs.Empty() {
+ r.SetAttr("srcs", t.srcs.Values())
+ }
+ if !t.visibility.Empty() {
+ r.SetAttr("visibility", t.visibility.Values())
+ }
+ if t.main != nil {
+ r.SetAttr("main", *t.main)
+ }
+ if t.imports != nil {
+ r.SetAttr("imports", t.imports)
+ }
+ if !t.deps.Empty() {
+ r.SetPrivateAttr(config.GazelleImportsKey, t.deps)
+ }
+ if t.testonly {
+ r.SetAttr("testonly", true)
+ }
+ r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps)
+ return r
+}