Add alias support
PiperOrigin-RevId: 208220162
diff --git a/ts_auto_deps/analyze/loader.go b/ts_auto_deps/analyze/loader.go
index e3a70cb..bb99ea8 100644
--- a/ts_auto_deps/analyze/loader.go
+++ b/ts_auto_deps/analyze/loader.go
@@ -2,8 +2,10 @@
import (
"bytes"
+ "context"
"fmt"
"os/exec"
+ "path/filepath"
"strings"
"github.com/bazelbuild/buildtools/edit"
@@ -13,17 +15,6 @@
appb "github.com/bazelbuild/buildtools/build_proto"
)
-var (
- commonModuleLocations = []string{}
-
- ts_auto_depsManagedRuleClasses = []string{
- "ts_library",
- "ts_declaration",
- "ng_module",
- "js_library",
- }
-)
-
// QueryBasedTargetLoader uses Bazel query to load targets from BUILD files.
type QueryBasedTargetLoader struct {
workdir string
@@ -61,7 +52,7 @@
// LoadImportPaths uses Bazel Query to load targets associated with import
// paths from BUILD files.
-func (q *QueryBasedTargetLoader) LoadImportPaths(_ string, paths []string) (map[string]*appb.Rule, error) {
+func (q *QueryBasedTargetLoader) LoadImportPaths(ctx context.Context, workspaceRoot string, paths []string) (map[string]*appb.Rule, error) {
var remainingImportPaths []string
results := make(map[string]*appb.Rule)
for _, path := range paths {
@@ -96,7 +87,7 @@
remainingImportPaths = append(remainingImportPaths, path)
}
}
- var potentialCommonImports []string
+
// Attempt to locate the file rooted in the workspace even though it isn't
// prefixed by 'google3/'.
for _, imp := range remainingImportPaths {
@@ -122,11 +113,9 @@
results[imp] = rule
continue
}
- potentialCommonImports = append(potentialCommonImports, imp)
+
}
- if err := q.resolveImportsInCommonLocations(results, potentialCommonImports); err != nil {
- return nil, err
- }
+
return results, nil
}
@@ -167,49 +156,6 @@
return nil, fmt.Errorf("failed to resolved a target for file label %q", fileLabel)
}
-// searchCommonLocations searches through common locations like third_party to
-// find a target providing imports which cannot be resolved using other
-// techniques.
-func (q *QueryBasedTargetLoader) resolveImportsInCommonLocations(results map[string]*appb.Rule, paths []string) error {
- var queries []string
- labelToPath := make(map[string]string)
-
- for _, path := range paths {
- // third_party uses '_' instead of '-' since the latter is not allowed
- // in target labels
- underscored := strings.Replace(path, "-", "_", -1)
- file := underscored
- module := underscored
- if i := strings.Index(underscored, "/"); i >= 0 {
- // Use the slash in the import path as a separator between the
- // module name and the path under the module.
- file = underscored[i+1:]
- module = commonModuleName(underscored[:i])
- }
- for _, l := range commonModuleLocations {
- // Construct the potential target label in the common location.
- target := fmt.Sprintf("%s:%s", fmt.Sprintf(l, module), file)
- queries = append(queries, target)
- labelToPath[target] = path
- }
- }
- r, err := q.batchQuery(queries)
- if err != nil {
- return err
- }
- for _, target := range r.GetTarget() {
- r := target.GetRule()
- // TODO(jdhamlik): Determine if it's required that the alias resolves to
- // an allowedRuleClass.
- // Allow alias rules to provide imports. Alias rules should only appear
- // in this context if they are special-cased above.
- if c := r.GetRuleClass(); isTazeManagedRuleClass(c) || c == "alias" {
- results[labelToPath[r.GetName()]] = r
- }
- }
- return nil
-}
-
// batchQuery runs a set of queries with a single call to Bazel query and the
// '--keep_going' flag.
func (q *QueryBasedTargetLoader) batchQuery(queries []string) (*appb.QueryResult, error) {
@@ -265,18 +211,62 @@
return uniqueLabels
}
-// commonModuleName maps module names to their common names. If no common name
-// is set for a module, it returns the module's name as is.
-func commonModuleName(path string) string {
- return path
-}
-
// isTazeManagedRuleClass checks if a class is a ts_auto_deps-managed rule class.
func isTazeManagedRuleClass(class string) bool {
- for _, c := range ts_auto_depsManagedRuleClasses {
+ for _, c := range []string{
+ "ts_library",
+ "ts_declaration",
+ "ng_module",
+ "js_library",
+ } {
if c == class {
return true
}
}
return false
}
+
+// typeScriptRules returns all TypeScript rules in rules.
+func typeScriptRules(rules []*appb.Rule) []*appb.Rule {
+ var tsRules []*appb.Rule
+ for _, rule := range rules {
+ for _, supportedRuleClass := range []string{
+ "ts_library",
+ "ts_declaration",
+ "ng_module",
+ } {
+ if rule.GetRuleClass() == supportedRuleClass {
+ tsRules = append(tsRules, rule)
+ break
+ }
+ }
+ }
+ return tsRules
+}
+
+// resolveAgainstModuleRoot resolves imported against moduleRoot and moduleName.
+func resolveAgainstModuleRoot(label, moduleRoot, moduleName, imported string) string {
+ if moduleRoot == "" && moduleName == "" {
+ return imported
+ }
+ trim := strings.TrimPrefix(imported, moduleName)
+ if trim == imported {
+ return imported
+ }
+ _, pkg, _ := edit.ParseLabel(label)
+ return filepath.Join(pkg, moduleRoot, trim)
+}
+
+// parsePackageName parses and returns the scope and package of imported. For
+// example, "@foo/bar" would have a scope of "@foo" and a package of "bar".
+func parsePackageName(imported string) (string, string) {
+ firstSlash := strings.Index(imported, "/")
+ if firstSlash == -1 {
+ return imported, ""
+ }
+ afterSlash := imported[firstSlash+1:]
+ if secondSlash := strings.Index(afterSlash, "/"); secondSlash > -1 {
+ return imported[:firstSlash], afterSlash[:secondSlash]
+ }
+ return imported[:firstSlash], afterSlash
+}
diff --git a/ts_auto_deps/analyze/loader_test.go b/ts_auto_deps/analyze/loader_test.go
new file mode 100644
index 0000000..ddb460c
--- /dev/null
+++ b/ts_auto_deps/analyze/loader_test.go
@@ -0,0 +1,50 @@
+package analyze
+
+import (
+ "testing"
+
+ "github.com/golang/protobuf/proto"
+
+ appb "github.com/bazelbuild/buildtools/build_proto"
+)
+
+func TestResolveAgainstModuleRoot(t *testing.T) {
+ tests := []struct {
+ label, moduleRoot, moduleName, imported string
+ expectedResolution string
+ }{
+ {"//a", "", "", "foo", "foo"},
+ {"//b", "", "foo", "bar", "bar"},
+ {"//c", "", "foo", "foo/bar", "c/bar"},
+ {"//actual/loc:target", "mod/root", "foo/bar", "foo/bar/baz/bam", "actual/loc/mod/root/baz/bam"},
+ }
+ for _, test := range tests {
+ if resolution := resolveAgainstModuleRoot(test.label, test.moduleRoot, test.moduleName, test.imported); resolution != test.expectedResolution {
+ t.Errorf("resolveAgainstModuleRoot(%q): got %q, want %q", test.label, resolution, test.expectedResolution)
+ }
+ }
+}
+
+func TestParsePackageName(t *testing.T) {
+ tests := []struct {
+ input, scope, pkg string
+ }{
+ {"foo/bar", "foo", "bar"},
+ {"foo/bar/baz", "foo", "bar"},
+ {"foo", "foo", ""},
+ {"", "", ""},
+ }
+ for _, test := range tests {
+ if scope, pkg := parsePackageName(test.input); scope != test.scope || pkg != test.pkg {
+ t.Errorf("moduleName(%q): got %q, %q, want %q, %q", test.input, scope, pkg, test.scope, test.pkg)
+ }
+ }
+}
+
+func parseRuleLiteral(literal string) (*appb.Rule, error) {
+ var rule appb.Rule
+ if err := proto.UnmarshalText(literal, &rule); err != nil {
+ return nil, err
+ }
+ return &rule, nil
+}
diff --git a/ts_auto_deps/analyze/query.go b/ts_auto_deps/analyze/query.go
index 66336a3..302dac3 100644
--- a/ts_auto_deps/analyze/query.go
+++ b/ts_auto_deps/analyze/query.go
@@ -3,6 +3,7 @@
package analyze
import (
+ "context"
"fmt"
"os"
"path/filepath"
@@ -50,7 +51,7 @@
// no target is found associated with a provided import path, the import
// path should be excluded from the returned mapping but an error should
// not be returned.
- LoadImportPaths(root string, paths []string) (map[string]*appb.Rule, error)
+ LoadImportPaths(ctx context.Context, root string, paths []string) (map[string]*appb.Rule, error)
}
// Analyzer uses a BuildLoader to generate dependency reports.
@@ -67,7 +68,7 @@
//
// dir is the directory that ts_auto_deps should execute in. Must be a sub-directory
// of google3.
-func (a *Analyzer) Analyze(dir string, labels []string) ([]*arpb.DependencyReport, error) {
+func (a *Analyzer) Analyze(ctx context.Context, dir string, labels []string) ([]*arpb.DependencyReport, error) {
root, err := workspace.Root(dir)
if err != nil {
return nil, err
@@ -76,7 +77,7 @@
if err != nil {
return nil, err
}
- resolved, err := a.resolveImportsForTargets(root, targets)
+ resolved, err := a.resolveImportsForTargets(ctx, root, targets)
if err != nil {
return nil, err
}
@@ -137,7 +138,7 @@
// resolveImportsForTargets attempts to resolve the imports in the sources of
// each target in targets.
-func (a *Analyzer) resolveImportsForTargets(root string, allTargets map[string]*appb.Rule) (map[string]*resolvedTarget, error) {
+func (a *Analyzer) resolveImportsForTargets(ctx context.Context, root string, allTargets map[string]*appb.Rule) (map[string]*resolvedTarget, error) {
targets := make(map[string]*resolvedTarget)
var allDeps, allSrcs []string
for _, t := range allTargets {
@@ -185,7 +186,7 @@
}
}
}
- if err := a.resolveImports(root, targets); err != nil {
+ if err := a.resolveImports(ctx, root, targets); err != nil {
return nil, err
}
return targets, nil
@@ -193,7 +194,7 @@
// resolveImports finds targets which provide the imported file or library
// for imports without known targets.
-func (a *Analyzer) resolveImports(root string, targets map[string]*resolvedTarget) error {
+func (a *Analyzer) resolveImports(ctx context.Context, root string, targets map[string]*resolvedTarget) error {
var paths []string
needingResolution := make(map[string][]*ts_auto_depsImport)
for _, target := range targets {
@@ -226,7 +227,7 @@
if len(needingResolution) == 0 {
return nil
}
- res, err := a.loader.LoadImportPaths(root, paths)
+ res, err := a.loader.LoadImportPaths(ctx, root, paths)
if err != nil {
return err
}
@@ -287,7 +288,7 @@
// redirectedLabel looks in the target's tags for a tag starting with
// 'alt_dep=' followed by a label. If such a tag is found, the label is
-// returned. Otherwise, the target's own label is shortened and returned.
+// returned. Otherwise, the target's own label is returned.
func redirectedLabel(target *appb.Rule) string {
for _, tag := range listAttribute(target, "tags") {
if trimmedTag := strings.TrimPrefix(tag, "alt_dep="); trimmedTag != tag {
@@ -295,7 +296,7 @@
}
}
// No 'alt_dep=' tag was present on the target so no redirects need to occur.
- return edit.ShortenLabel(target.GetName(), "")
+ return target.GetName()
}
// sources creates an array of all sources listed in the 'srcs' attribute
diff --git a/ts_auto_deps/analyze/query_test.go b/ts_auto_deps/analyze/query_test.go
index ae82d56..4b39267 100644
--- a/ts_auto_deps/analyze/query_test.go
+++ b/ts_auto_deps/analyze/query_test.go
@@ -1,6 +1,7 @@
package analyze
import (
+ "context"
"fmt"
"io/ioutil"
"os"
@@ -51,7 +52,7 @@
bl.targetsByLabels[label] = value
}
-func (bl *fakeTargetLoader) LoadImportPaths(_ string, paths []string) (map[string]*appb.Rule, error) {
+func (bl *fakeTargetLoader) LoadImportPaths(_ context.Context, _ string, paths []string) (map[string]*appb.Rule, error) {
return bl.loadTargets(bl.targetsByImportPaths, paths)
}
@@ -112,7 +113,7 @@
loader.byImportPath(i, r)
}
}
- r, err := New(loader).Analyze(testTmpDir, labels)
+ r, err := New(loader).Analyze(context.Background(), testTmpDir, labels)
if err != nil {
t.Errorf("Analyze(%q): failed to generate reports: %q", labels, err)
return nil
diff --git a/ts_auto_deps/updater/updater.go b/ts_auto_deps/updater/updater.go
index 39beef7..a34e3fe 100644
--- a/ts_auto_deps/updater/updater.go
+++ b/ts_auto_deps/updater/updater.go
@@ -525,7 +525,7 @@
if err != nil {
return nil, nil, err
}
- reports, err := analyze.New(analyze.NewQueryBasedTargetLoader(root, bazelBinary())).Analyze(buildFilePath, targets)
+ reports, err := analyze.New(analyze.NewQueryBasedTargetLoader(root, bazelBinary())).Analyze(context.Background(), buildFilePath, targets)
if err != nil {
return nil, nil, err
}