blob: 2b260679b647573e3d54684aa8200801627d64e3 [file] [log] [blame]
package python
import (
"path/filepath"
"github.com/bazelbuild/bazel-gazelle/config"
"github.com/bazelbuild/bazel-gazelle/rule"
"github.com/emirpasic/gods/sets/treeset"
godsutils "github.com/emirpasic/gods/utils"
)
// targetBuilder builds targets to be generated by Gazelle.
type targetBuilder struct {
kind string
name string
pythonProjectRoot string
bzlPackage string
uuid string
srcs *treeset.Set
deps *treeset.Set
resolvedDeps *treeset.Set
visibility *treeset.Set
main *string
imports []string
}
// newTargetBuilder constructs a new targetBuilder.
func newTargetBuilder(kind, name, pythonProjectRoot, bzlPackage string) *targetBuilder {
return &targetBuilder{
kind: kind,
name: name,
pythonProjectRoot: pythonProjectRoot,
bzlPackage: bzlPackage,
srcs: treeset.NewWith(godsutils.StringComparator),
deps: treeset.NewWith(moduleComparator),
resolvedDeps: treeset.NewWith(godsutils.StringComparator),
visibility: treeset.NewWith(godsutils.StringComparator),
}
}
// setUUID sets the given UUID for the target. It's used to index the generated
// target based on this value in addition to the other ways the targets can be
// imported. py_{binary,test} targets in the same Bazel package can add a
// virtual dependency to this UUID that gets resolved in the Resolver interface.
func (t *targetBuilder) setUUID(uuid string) *targetBuilder {
t.uuid = uuid
return t
}
// 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 {
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.deps.Add(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
}
// 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.uuid != "" {
r.SetPrivateAttr(uuidKey, t.uuid)
}
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)
}
r.SetPrivateAttr(resolvedDepsKey, t.resolvedDeps)
return r
}