aboutsummaryrefslogtreecommitdiff
path: root/gazelle
diff options
context:
space:
mode:
authorAndrew Z Allen <me@andrewzallen.com>2020-06-26 09:04:12 -0600
committerGitHub <noreply@github.com>2020-06-26 17:04:12 +0200
commitd35e8d7bc6ad7a3a53e9a1d2ec8d3a904cc54ff7 (patch)
tree0d68a04a18e17d85f22262310994ca530e356d31 /gazelle
parentb10f2cb0fc013218f727de8c631d6dd1cfc078ea (diff)
downloadbazel-skylib-d35e8d7bc6ad7a3a53e9a1d2ec8d3a904cc54ff7.tar.gz
Create Gazelle language for Starlark (#251)
Diffstat (limited to 'gazelle')
-rw-r--r--gazelle/BUILD53
-rw-r--r--gazelle/README.md13
-rw-r--r--gazelle/gazelle.go315
-rw-r--r--gazelle/gazelle_test.go140
-rw-r--r--gazelle/testdata/README.md56
-rw-r--r--gazelle/testdata/defaultvisibility/BUILD.in0
-rw-r--r--gazelle/testdata/defaultvisibility/BUILD.out8
-rw-r--r--gazelle/testdata/defaultvisibility/WORKSPACE0
-rw-r--r--gazelle/testdata/defaultvisibility/foo.bzl7
-rw-r--r--gazelle/testdata/defaultvisibility/nested/dir/BUILD.in1
-rw-r--r--gazelle/testdata/defaultvisibility/nested/dir/BUILD.out8
-rw-r--r--gazelle/testdata/defaultvisibility/nested/dir/bar.bzl6
-rw-r--r--gazelle/testdata/empty/BUILD.in13
-rw-r--r--gazelle/testdata/empty/BUILD.out7
-rw-r--r--gazelle/testdata/empty/WORKSPACE0
-rw-r--r--gazelle/testdata/empty/foo.bzl0
-rw-r--r--gazelle/testdata/external/BUILD.in8
-rw-r--r--gazelle/testdata/external/BUILD.out15
-rw-r--r--gazelle/testdata/external/WORKSPACE0
-rw-r--r--gazelle/testdata/external/foo.bzl7
-rw-r--r--gazelle/testdata/import/BUILD.in0
-rw-r--r--gazelle/testdata/import/BUILD.out14
-rw-r--r--gazelle/testdata/import/WORKSPACE0
-rw-r--r--gazelle/testdata/import/bar.bzl6
-rw-r--r--gazelle/testdata/import/foo.bzl7
-rw-r--r--gazelle/testdata/multidir/BUILD.in0
-rw-r--r--gazelle/testdata/multidir/BUILD.out8
-rw-r--r--gazelle/testdata/multidir/WORKSPACE0
-rw-r--r--gazelle/testdata/multidir/foo.bzl7
-rw-r--r--gazelle/testdata/multidir/nested/dir/BUILD.out7
-rw-r--r--gazelle/testdata/multidir/nested/dir/bar.bzl6
-rw-r--r--gazelle/testdata/nobuildfiles/BUILD.out7
-rw-r--r--gazelle/testdata/nobuildfiles/WORKSPACE0
-rw-r--r--gazelle/testdata/nobuildfiles/foo.bzl0
-rw-r--r--gazelle/testdata/private/BUILD.in0
-rw-r--r--gazelle/testdata/private/BUILD.out8
-rw-r--r--gazelle/testdata/private/WORKSPACE0
-rw-r--r--gazelle/testdata/private/foo.bzl7
-rw-r--r--gazelle/testdata/private/private/BUILD.out6
-rw-r--r--gazelle/testdata/private/private/bar.bzl6
-rw-r--r--gazelle/testdata/simple/BUILD.in6
-rw-r--r--gazelle/testdata/simple/BUILD.out14
-rw-r--r--gazelle/testdata/simple/WORKSPACE0
-rw-r--r--gazelle/testdata/simple/foo.bzl0
-rw-r--r--gazelle/testdata/tests/BUILD.in6
-rw-r--r--gazelle/testdata/tests/BUILD.out14
-rw-r--r--gazelle/testdata/tests/WORKSPACE0
-rw-r--r--gazelle/testdata/tests/foo.bzl0
-rw-r--r--gazelle/testdata/tests/foo_tests.bzl0
49 files changed, 786 insertions, 0 deletions
diff --git a/gazelle/BUILD b/gazelle/BUILD
new file mode 100644
index 0000000..d20212c
--- /dev/null
+++ b/gazelle/BUILD
@@ -0,0 +1,53 @@
+load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
+load("@bazel_gazelle//:def.bzl", "gazelle", "gazelle_binary")
+
+# gazelle:exclude testdata
+
+go_library(
+ name = "go_default_library",
+ srcs = ["gazelle.go"],
+ importpath = "github.com/bazelbuild/bazel-skylib/gazelle",
+ visibility = ["//visibility:public"],
+ deps = [
+ "@bazel_gazelle//config:go_default_library",
+ "@bazel_gazelle//label:go_default_library",
+ "@bazel_gazelle//language:go_default_library",
+ "@bazel_gazelle//pathtools:go_default_library",
+ "@bazel_gazelle//repo:go_default_library",
+ "@bazel_gazelle//resolve:go_default_library",
+ "@bazel_gazelle//rule:go_default_library",
+ "@com_github_bazelbuild_buildtools//build:go_default_library",
+ ],
+)
+
+go_test(
+ name = "go_default_test",
+ srcs = ["gazelle_test.go"],
+ data = [
+ ":gazelle-skylib",
+ ] + glob([
+ "testdata/**",
+ ]),
+ embed = [":go_default_library"],
+ deps = [
+ "@bazel_gazelle//testtools:go_default_library",
+ "@io_bazel_rules_go//go/tools/bazel:go_default_library",
+ ],
+)
+
+# This gazelle binary is used exclusively for testing the gazelle language
+# extension and thus only has the skylib language installed.
+gazelle_binary(
+ name = "gazelle-skylib",
+ languages = [":go_default_library"],
+ visibility = [
+ # Also make the binary available in the root of the repo for use, but
+ # not externally.
+ "//:__pkg__",
+ ],
+)
+
+gazelle(
+ name = "gazelle",
+ gazelle = "//gazelle:gazelle-skylib",
+)
diff --git a/gazelle/README.md b/gazelle/README.md
new file mode 100644
index 0000000..8b7cb7b
--- /dev/null
+++ b/gazelle/README.md
@@ -0,0 +1,13 @@
+# Gazelle
+
+Gazelle is a `BUILD` file generator for Bazel. This directory contains a
+language extension for the Gazelle generator that allows it to automatically
+parse valid `bzl_library` targets for all `.bzl` files in a repo in which it
+runs. It will additionally include a `deps` entry tracking every `.bzl` that is
+`load`ed into the primary file.
+
+This can be used, for example, to generate
+[`stardoc`](https://github.com/bazelbuild/stardoc) documentation for your
+`.bzl` files, both simplify the task of and improve the quality of
+documentation.
+
diff --git a/gazelle/gazelle.go b/gazelle/gazelle.go
new file mode 100644
index 0000000..b45ef3d
--- /dev/null
+++ b/gazelle/gazelle.go
@@ -0,0 +1,315 @@
+/* Copyright 2020 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 gazelle generates a `bzl_library` target for every `.bzl` file in
+// each package.
+//
+// The `bzl_library` rule is provided by
+// https://github.com/bazelbuild/bazel-skylib.
+//
+// This extension is experimental and subject to change. It is not included
+// in the default Gazelle binary.
+package gazelle
+
+import (
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "log"
+ "path/filepath"
+ "sort"
+ "strings"
+
+ "github.com/bazelbuild/bazel-gazelle/config"
+ "github.com/bazelbuild/bazel-gazelle/label"
+ "github.com/bazelbuild/bazel-gazelle/language"
+ "github.com/bazelbuild/bazel-gazelle/pathtools"
+ "github.com/bazelbuild/bazel-gazelle/repo"
+ "github.com/bazelbuild/bazel-gazelle/resolve"
+ "github.com/bazelbuild/bazel-gazelle/rule"
+
+ "github.com/bazelbuild/buildtools/build"
+)
+
+const languageName = "starlark"
+const fileType = ".bzl"
+
+var ignoreSuffix = suffixes{
+ "_tests.bzl",
+ "_test.bzl",
+}
+
+type suffixes []string
+
+func (s suffixes) Matches(test string) bool {
+ for _, v := range s {
+ if strings.HasSuffix(test, v) {
+ return true
+ }
+ }
+ return false
+}
+
+type bzlLibraryLang struct{}
+
+// NewLanguage is called by Gazelle to install this language extension in a binary.
+func NewLanguage() language.Language {
+ return &bzlLibraryLang{}
+}
+
+// Name returns the name of the language. This should be a prefix of the
+// kinds of rules generated by the language, e.g., "go" for the Go extension
+// since it generates "go_library" rules.
+func (*bzlLibraryLang) Name() string { return languageName }
+
+// The following methods are implemented to satisfy the
+// https://pkg.go.dev/github.com/bazelbuild/bazel-gazelle/resolve?tab=doc#Resolver
+// interface, but are otherwise unused.
+func (*bzlLibraryLang) RegisterFlags(fs *flag.FlagSet, cmd string, c *config.Config) {}
+func (*bzlLibraryLang) CheckFlags(fs *flag.FlagSet, c *config.Config) error { return nil }
+func (*bzlLibraryLang) KnownDirectives() []string { return nil }
+func (*bzlLibraryLang) Configure(c *config.Config, rel string, f *rule.File) {}
+
+// Kinds returns a map of maps rule names (kinds) and information on how to
+// match and merge attributes that may be found in rules of those kinds. All
+// kinds of rules generated for this language may be found here.
+func (*bzlLibraryLang) Kinds() map[string]rule.KindInfo {
+ return kinds
+}
+
+// Loads returns .bzl files and symbols they define. Every rule generated by
+// GenerateRules, now or in the past, should be loadable from one of these
+// files.
+func (*bzlLibraryLang) Loads() []rule.LoadInfo {
+ return []rule.LoadInfo{{
+ Name: "@bazel_skylib//:bzl_library.bzl",
+ Symbols: []string{"bzl_library"},
+ }}
+}
+
+// Fix repairs deprecated usage of language-specific rules in f. This is
+// called before the file is indexed. Unless c.ShouldFix is true, fixes
+// that delete or rename rules should not be performed.
+func (*bzlLibraryLang) Fix(c *config.Config, f *rule.File) {}
+
+// Imports returns a list of ImportSpecs that can be used to import the rule
+// r. This is used to populate RuleIndex.
+//
+// If nil is returned, the rule will not be indexed. If any non-nil slice is
+// returned, including an empty slice, the rule will be indexed.
+func (b *bzlLibraryLang) Imports(c *config.Config, r *rule.Rule, f *rule.File) []resolve.ImportSpec {
+ srcs := r.AttrStrings("srcs")
+ imports := make([]resolve.ImportSpec, len(srcs))
+
+ for _, src := range srcs {
+ spec := resolve.ImportSpec{
+ // Lang is the language in which the import string appears (this should
+ // match Resolver.Name).
+ Lang: languageName,
+ // Imp is an import string for the library.
+ Imp: fmt.Sprintf("//%s:%s", f.Pkg, src),
+ }
+
+ imports = append(imports, spec)
+ }
+
+ return imports
+}
+
+// Embeds returns a list of labels of rules that the given rule embeds. If
+// a rule is embedded by another importable rule of the same language, only
+// the embedding rule will be indexed. The embedding rule will inherit
+// the imports of the embedded rule.
+// Since SkyLark doesn't support embedding this should always return nil.
+func (*bzlLibraryLang) Embeds(r *rule.Rule, from label.Label) []label.Label { return nil }
+
+// Resolve translates imported libraries for a given rule into Bazel
+// dependencies. Information about imported libraries is returned for each
+// rule generated by language.GenerateRules in
+// language.GenerateResult.Imports. Resolve generates a "deps" attribute (or
+// the appropriate language-specific equivalent) for each import according to
+// language-specific rules and heuristics.
+func (*bzlLibraryLang) Resolve(c *config.Config, ix *resolve.RuleIndex, rc *repo.RemoteCache, r *rule.Rule, importsRaw interface{}, from label.Label) {
+ imports := importsRaw.([]string)
+
+ r.DelAttr("deps")
+
+ if len(imports) == 0 {
+ return
+ }
+
+ deps := make([]string, 0, len(imports))
+ for _, imp := range imports {
+ if strings.HasPrefix(imp, "@") || !c.IndexLibraries {
+ // This is a dependency that is external to the current repo, or indexing
+ // is disabled so take a guess at what hte target name should be.
+ deps = append(deps, strings.TrimSuffix(imp, fileType))
+ } else {
+ res := resolve.ImportSpec{
+ Lang: languageName,
+ Imp: imp,
+ }
+ matches := ix.FindRulesByImport(res, languageName)
+
+ if len(matches) == 0 {
+ log.Printf("%s: %q was not found in dependency index. Skipping. This may result in an incomplete deps section and require manual BUILD file intervention.\n", from.String(), imp)
+ }
+
+ for _, m := range matches {
+ deps = append(deps, m.Label.String())
+ }
+ }
+ }
+
+ sort.Strings(deps)
+ if len(deps) > 0 {
+ r.SetAttr("deps", deps)
+ }
+}
+
+var kinds = map[string]rule.KindInfo{
+ "bzl_library": {
+ NonEmptyAttrs: map[string]bool{"srcs": true, "deps": true},
+ MergeableAttrs: map[string]bool{"srcs": true},
+ },
+}
+
+// GenerateRules extracts build metadata from source files in a directory.
+// GenerateRules is called in each directory where an update is requested
+// in depth-first post-order.
+//
+// args contains the arguments for GenerateRules. This is passed as a
+// struct to avoid breaking implementations in the future when new
+// fields are added.
+//
+// A GenerateResult struct is returned. Optional fields may be added to this
+// type in the future.
+//
+// Any non-fatal errors this function encounters should be logged using
+// log.Print.
+func (*bzlLibraryLang) GenerateRules(args language.GenerateArgs) language.GenerateResult {
+ var rules []*rule.Rule
+ var imports []interface{}
+ for _, f := range append(args.RegularFiles, args.GenFiles...) {
+ if !isBzlSourceFile(f) {
+ continue
+ }
+ name := strings.TrimSuffix(f, fileType)
+ r := rule.NewRule("bzl_library", name)
+
+ r.SetAttr("srcs", []string{f})
+
+ if args.File == nil || !args.File.HasDefaultVisibility() {
+ inPrivateDir := pathtools.Index(args.Rel, "private") >= 0
+ if !inPrivateDir {
+ r.SetAttr("visibility", []string{"//visibility:public"})
+ }
+ }
+
+ fullPath := filepath.Join(args.Dir, f)
+ loads, err := getBzlFileLoads(fullPath)
+ if err != nil {
+ log.Printf("%s: contains syntax errors: %v", fullPath, err)
+ // Don't `continue` since it is reasonable to create a target even
+ // without deps.
+ }
+
+ rules = append(rules, r)
+ imports = append(imports, loads)
+ }
+
+ return language.GenerateResult{
+ Gen: rules,
+ Imports: imports,
+ Empty: generateEmpty(args),
+ }
+}
+
+func getBzlFileLoads(path string) ([]string, error) {
+ f, err := ioutil.ReadFile(path)
+ if err != nil {
+ return nil, fmt.Errorf("ioutil.ReadFile(%q) error: %v", path, err)
+ }
+ ast, err := build.ParseBuild(path, f)
+ if err != nil {
+ return nil, fmt.Errorf("build.Parse(%q) error: %v", f, err)
+ }
+
+ var loads []string
+ build.WalkOnce(ast, func(expr *build.Expr) {
+ n := *expr
+ if l, ok := n.(*build.LoadStmt); ok {
+ loads = append(loads, l.Module.Value)
+ }
+ })
+ sort.Strings(loads)
+
+ return loads, nil
+}
+
+func isBzlSourceFile(f string) bool {
+ return strings.HasSuffix(f, fileType) && !ignoreSuffix.Matches(f)
+}
+
+// generateEmpty generates the list of rules that don't need to exist in the
+// BUILD file any more.
+// For each bzl_library rule in args.File that only has srcs that aren't in
+// args.RegularFiles or args.GenFiles, add a bzl_library with no srcs or deps.
+// That will let Gazelle delete bzl_library rules after the corresponding .bzl
+// files are deleted.
+func generateEmpty(args language.GenerateArgs) []*rule.Rule {
+ var ret []*rule.Rule
+ if args.File == nil {
+ return ret
+ }
+ for _, r := range args.File.Rules {
+ if r.Kind() != "bzl_library" {
+ continue
+ }
+ name := r.AttrString("name")
+
+ exists := make(map[string]bool)
+ for _, f := range args.RegularFiles {
+ exists[f] = true
+ }
+ for _, f := range args.GenFiles {
+ exists[f] = true
+ }
+ for _, r := range args.File.Rules {
+ srcsExist := false
+ for _, f := range r.AttrStrings("srcs") {
+ if exists[f] {
+ srcsExist = true
+ break
+ }
+ }
+ if !srcsExist {
+ ret = append(ret, rule.NewRule("bzl_library", name))
+ }
+ }
+ }
+ return ret
+}
+
+type srcsList []string
+
+func (s srcsList) Contains(m string) bool {
+ for _, e := range s {
+ if e == m {
+ return true
+ }
+ }
+ return false
+}
diff --git a/gazelle/gazelle_test.go b/gazelle/gazelle_test.go
new file mode 100644
index 0000000..a22f7f6
--- /dev/null
+++ b/gazelle/gazelle_test.go
@@ -0,0 +1,140 @@
+package gazelle
+
+/* Copyright 2020 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.
+*/
+
+import (
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "testing"
+
+ "github.com/bazelbuild/bazel-gazelle/testtools"
+ "github.com/bazelbuild/rules_go/go/tools/bazel"
+)
+
+var gazellePath = findGazelle()
+
+const testDataPath = "gazelle/testdata/"
+
+// TestGazelleBinary runs a gazelle binary with starlib installed on each
+// directory in `testdata/*`. Please see `testdata/README.md` for more
+// information on each test.
+func TestGazelleBinary(t *testing.T) {
+ tests := map[string][]bazel.RunfileEntry{}
+
+ files, err := bazel.ListRunfiles()
+ if err != nil {
+ t.Fatalf("bazel.ListRunfiles() error: %v", err)
+ }
+ for _, f := range files {
+ if strings.HasPrefix(f.ShortPath, testDataPath) {
+ relativePath := strings.TrimPrefix(f.ShortPath, testDataPath)
+ parts := strings.SplitN(relativePath, "/", 2)
+ if len(parts) < 2 {
+ // This file is not a part of a testcase since it must be in a dir that
+ // is the test case and then have a path inside of that.
+ continue
+ }
+
+ tests[parts[0]] = append(tests[parts[0]], f)
+ }
+ }
+
+ for testName, files := range tests {
+ testPath(t, testName, files)
+ }
+}
+
+func testPath(t *testing.T, name string, files []bazel.RunfileEntry) {
+ t.Run(name, func(t *testing.T) {
+ var inputs []testtools.FileSpec
+ var goldens []testtools.FileSpec
+
+ for _, f := range files {
+ path := f.Path
+ trim := testDataPath + name + "/"
+ shortPath := strings.TrimPrefix(f.ShortPath, trim)
+ info, err := os.Stat(path)
+ if err != nil {
+ t.Fatalf("os.Stat(%q) error: %v", path, err)
+ }
+
+ // Skip dirs.
+ if info.IsDir() {
+ continue
+ }
+
+ content, err := ioutil.ReadFile(path)
+ if err != nil {
+ t.Errorf("ioutil.ReadFile(%q) error: %v", path, err)
+ }
+
+ // Now trim the common prefix off.
+ if strings.HasSuffix(shortPath, ".in") {
+ inputs = append(inputs, testtools.FileSpec{
+ Path: strings.TrimSuffix(shortPath, ".in"),
+ Content: string(content),
+ })
+ } else if strings.HasSuffix(shortPath, ".out") {
+ goldens = append(goldens, testtools.FileSpec{
+ Path: strings.TrimSuffix(shortPath, ".out"),
+ Content: string(content),
+ })
+ } else {
+ inputs = append(inputs, testtools.FileSpec{
+ Path: shortPath,
+ Content: string(content),
+ })
+ goldens = append(goldens, testtools.FileSpec{
+ Path: shortPath,
+ Content: string(content),
+ })
+ }
+ }
+
+ dir, cleanup := testtools.CreateFiles(t, inputs)
+ defer cleanup()
+
+ cmd := exec.Command(gazellePath, "-build_file_name=BUILD")
+ cmd.Stdout = os.Stdout
+ cmd.Stderr = os.Stderr
+ cmd.Dir = dir
+ if err := cmd.Run(); err != nil {
+ t.Fatal(err)
+ }
+
+ testtools.CheckFiles(t, dir, goldens)
+ if t.Failed() {
+ filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
+ if err != nil {
+ return err
+ }
+ t.Logf("%q exists", path)
+ return nil
+ })
+ }
+ })
+}
+
+func findGazelle() string {
+ gazellePath, ok := bazel.FindBinary("gazelle", "gazelle-skylib")
+ if !ok {
+ panic("could not find gazelle binary")
+ }
+ return gazellePath
+}
diff --git a/gazelle/testdata/README.md b/gazelle/testdata/README.md
new file mode 100644
index 0000000..a6a3603
--- /dev/null
+++ b/gazelle/testdata/README.md
@@ -0,0 +1,56 @@
+# Gazelle test cases
+
+This directory contains a suite of test cases for the Skylark language plugin
+for Gazelle.
+
+Please note that there are no `BUILD` or `BUILD.bazel` files in subdirs, insted
+there are `BUILD.in` and `BUILD.out` describing what the `BUILD` should look
+like initially and what the `BUILD` file should look like after the run. These
+names are special because they are not recognized by Bazel as a proper `BUILD`
+file, and therefore are included in the data dependency by the recursive data
+glob in `//gazelle:go_default_test`. If you would like to include any extremely
+complicated tests that contain proper `BUILD` files you will need to manually
+add them to the `//gazelle:go_default_test` target's `data` section.
+
+## `simple`
+
+Simple is a base test case that was used to validate the parser.
+
+## `nobuildfiles`
+
+A test just like `simple` that has no `BUILD` files at the beginning.
+
+## `import`
+
+Import is a test case that imports a `.bzl` from the same directory.
+
+## `multidir`
+
+Multidir is a test that has a `.bzl` that imports from a different dirrectory.
+
+## `tests`
+
+Using the skylib as an example, this test has `.bzl` files that end in
+`_tests.bzl` which are `load`ed into `BUILD` files and never imported by
+another repo.
+
+## `private`
+
+Using the skylib as an example, this test has `.bzl` files that live in a
+directory called `private` which is used to indicate that they are repo private.
+Note that this is distict from the expectations of go's `internal` where the
+relative position in the hierarchy determines the visibility.
+
+## `defaultvisibility`
+
+If the package declares a `default_visibility` the generated `bzl_library`
+should not set its own `visibility`.
+
+## `external`
+
+This test demonstrates that if you load from another repo, it is able to
+generate a `deps` entry for the dependency.
+
+## `empty`
+
+Gazelle has the ability to remove old and unused targets. Test that.
diff --git a/gazelle/testdata/defaultvisibility/BUILD.in b/gazelle/testdata/defaultvisibility/BUILD.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/BUILD.in
diff --git a/gazelle/testdata/defaultvisibility/BUILD.out b/gazelle/testdata/defaultvisibility/BUILD.out
new file mode 100644
index 0000000..44c1495
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/BUILD.out
@@ -0,0 +1,8 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["//nested/dir:bar"],
+)
diff --git a/gazelle/testdata/defaultvisibility/WORKSPACE b/gazelle/testdata/defaultvisibility/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/WORKSPACE
diff --git a/gazelle/testdata/defaultvisibility/foo.bzl b/gazelle/testdata/defaultvisibility/foo.bzl
new file mode 100644
index 0000000..1191303
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/foo.bzl
@@ -0,0 +1,7 @@
+"""
+Doc string
+"""
+
+load("//nested/dir:bar.bzl", "func")
+
+func()
diff --git a/gazelle/testdata/defaultvisibility/nested/dir/BUILD.in b/gazelle/testdata/defaultvisibility/nested/dir/BUILD.in
new file mode 100644
index 0000000..830932a
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/nested/dir/BUILD.in
@@ -0,0 +1 @@
+package(default_visibility = ["//:__pkg__"])
diff --git a/gazelle/testdata/defaultvisibility/nested/dir/BUILD.out b/gazelle/testdata/defaultvisibility/nested/dir/BUILD.out
new file mode 100644
index 0000000..8394b9c
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/nested/dir/BUILD.out
@@ -0,0 +1,8 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+package(default_visibility = ["//:__pkg__"])
+
+bzl_library(
+ name = "bar",
+ srcs = ["bar.bzl"],
+)
diff --git a/gazelle/testdata/defaultvisibility/nested/dir/bar.bzl b/gazelle/testdata/defaultvisibility/nested/dir/bar.bzl
new file mode 100644
index 0000000..8ab1fcb
--- /dev/null
+++ b/gazelle/testdata/defaultvisibility/nested/dir/bar.bzl
@@ -0,0 +1,6 @@
+"""
+Doc string
+"""
+
+def asdf():
+ pass
diff --git a/gazelle/testdata/empty/BUILD.in b/gazelle/testdata/empty/BUILD.in
new file mode 100644
index 0000000..e1e154c
--- /dev/null
+++ b/gazelle/testdata/empty/BUILD.in
@@ -0,0 +1,13 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "weirdly_named_target_that_will_be_removed",
+ srcs = ["nonexistant.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+bzl_library(
+ name = "weirdly_named_target_that_will_be_renamed",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+)
diff --git a/gazelle/testdata/empty/BUILD.out b/gazelle/testdata/empty/BUILD.out
new file mode 100644
index 0000000..ac0e990
--- /dev/null
+++ b/gazelle/testdata/empty/BUILD.out
@@ -0,0 +1,7 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+)
diff --git a/gazelle/testdata/empty/WORKSPACE b/gazelle/testdata/empty/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/empty/WORKSPACE
diff --git a/gazelle/testdata/empty/foo.bzl b/gazelle/testdata/empty/foo.bzl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/empty/foo.bzl
diff --git a/gazelle/testdata/external/BUILD.in b/gazelle/testdata/external/BUILD.in
new file mode 100644
index 0000000..e7bc836
--- /dev/null
+++ b/gazelle/testdata/external/BUILD.in
@@ -0,0 +1,8 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+# Some comment to be preserved
+
+filegroup(
+ name = "allfiles",
+ srcs = glob(["**"]),
+)
diff --git a/gazelle/testdata/external/BUILD.out b/gazelle/testdata/external/BUILD.out
new file mode 100644
index 0000000..c70685f
--- /dev/null
+++ b/gazelle/testdata/external/BUILD.out
@@ -0,0 +1,15 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+# Some comment to be preserved
+
+filegroup(
+ name = "allfiles",
+ srcs = glob(["**"]),
+)
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["@external_repo//path/to:file"],
+)
diff --git a/gazelle/testdata/external/WORKSPACE b/gazelle/testdata/external/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/external/WORKSPACE
diff --git a/gazelle/testdata/external/foo.bzl b/gazelle/testdata/external/foo.bzl
new file mode 100644
index 0000000..ad548a5
--- /dev/null
+++ b/gazelle/testdata/external/foo.bzl
@@ -0,0 +1,7 @@
+"""
+Test sample code.
+"""
+
+load("@external_repo//path/to:file.bzl", "func")
+
+func()
diff --git a/gazelle/testdata/import/BUILD.in b/gazelle/testdata/import/BUILD.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/import/BUILD.in
diff --git a/gazelle/testdata/import/BUILD.out b/gazelle/testdata/import/BUILD.out
new file mode 100644
index 0000000..a19a11a
--- /dev/null
+++ b/gazelle/testdata/import/BUILD.out
@@ -0,0 +1,14 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "bar",
+ srcs = ["bar.bzl"],
+ visibility = ["//visibility:public"],
+)
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["//:bar"],
+)
diff --git a/gazelle/testdata/import/WORKSPACE b/gazelle/testdata/import/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/import/WORKSPACE
diff --git a/gazelle/testdata/import/bar.bzl b/gazelle/testdata/import/bar.bzl
new file mode 100644
index 0000000..7ffea51
--- /dev/null
+++ b/gazelle/testdata/import/bar.bzl
@@ -0,0 +1,6 @@
+"""
+Doc string
+"""
+
+def func():
+ pass
diff --git a/gazelle/testdata/import/foo.bzl b/gazelle/testdata/import/foo.bzl
new file mode 100644
index 0000000..eb8d33c
--- /dev/null
+++ b/gazelle/testdata/import/foo.bzl
@@ -0,0 +1,7 @@
+"""
+Doc string
+"""
+
+load("//:bar.bzl", "func")
+
+func()
diff --git a/gazelle/testdata/multidir/BUILD.in b/gazelle/testdata/multidir/BUILD.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/multidir/BUILD.in
diff --git a/gazelle/testdata/multidir/BUILD.out b/gazelle/testdata/multidir/BUILD.out
new file mode 100644
index 0000000..44c1495
--- /dev/null
+++ b/gazelle/testdata/multidir/BUILD.out
@@ -0,0 +1,8 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["//nested/dir:bar"],
+)
diff --git a/gazelle/testdata/multidir/WORKSPACE b/gazelle/testdata/multidir/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/multidir/WORKSPACE
diff --git a/gazelle/testdata/multidir/foo.bzl b/gazelle/testdata/multidir/foo.bzl
new file mode 100644
index 0000000..1191303
--- /dev/null
+++ b/gazelle/testdata/multidir/foo.bzl
@@ -0,0 +1,7 @@
+"""
+Doc string
+"""
+
+load("//nested/dir:bar.bzl", "func")
+
+func()
diff --git a/gazelle/testdata/multidir/nested/dir/BUILD.out b/gazelle/testdata/multidir/nested/dir/BUILD.out
new file mode 100644
index 0000000..07ec8ce
--- /dev/null
+++ b/gazelle/testdata/multidir/nested/dir/BUILD.out
@@ -0,0 +1,7 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "bar",
+ srcs = ["bar.bzl"],
+ visibility = ["//visibility:public"],
+)
diff --git a/gazelle/testdata/multidir/nested/dir/bar.bzl b/gazelle/testdata/multidir/nested/dir/bar.bzl
new file mode 100644
index 0000000..7ffea51
--- /dev/null
+++ b/gazelle/testdata/multidir/nested/dir/bar.bzl
@@ -0,0 +1,6 @@
+"""
+Doc string
+"""
+
+def func():
+ pass
diff --git a/gazelle/testdata/nobuildfiles/BUILD.out b/gazelle/testdata/nobuildfiles/BUILD.out
new file mode 100644
index 0000000..ac0e990
--- /dev/null
+++ b/gazelle/testdata/nobuildfiles/BUILD.out
@@ -0,0 +1,7 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+)
diff --git a/gazelle/testdata/nobuildfiles/WORKSPACE b/gazelle/testdata/nobuildfiles/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/nobuildfiles/WORKSPACE
diff --git a/gazelle/testdata/nobuildfiles/foo.bzl b/gazelle/testdata/nobuildfiles/foo.bzl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/nobuildfiles/foo.bzl
diff --git a/gazelle/testdata/private/BUILD.in b/gazelle/testdata/private/BUILD.in
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/private/BUILD.in
diff --git a/gazelle/testdata/private/BUILD.out b/gazelle/testdata/private/BUILD.out
new file mode 100644
index 0000000..e65b00f
--- /dev/null
+++ b/gazelle/testdata/private/BUILD.out
@@ -0,0 +1,8 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+ deps = ["//private:bar"],
+)
diff --git a/gazelle/testdata/private/WORKSPACE b/gazelle/testdata/private/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/private/WORKSPACE
diff --git a/gazelle/testdata/private/foo.bzl b/gazelle/testdata/private/foo.bzl
new file mode 100644
index 0000000..7e1dcd1
--- /dev/null
+++ b/gazelle/testdata/private/foo.bzl
@@ -0,0 +1,7 @@
+"""
+Test sample code.
+"""
+
+load("//private:bar.bzl", "func")
+
+func()
diff --git a/gazelle/testdata/private/private/BUILD.out b/gazelle/testdata/private/private/BUILD.out
new file mode 100644
index 0000000..eb2e935
--- /dev/null
+++ b/gazelle/testdata/private/private/BUILD.out
@@ -0,0 +1,6 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+bzl_library(
+ name = "bar",
+ srcs = ["bar.bzl"],
+)
diff --git a/gazelle/testdata/private/private/bar.bzl b/gazelle/testdata/private/private/bar.bzl
new file mode 100644
index 0000000..c8a4871
--- /dev/null
+++ b/gazelle/testdata/private/private/bar.bzl
@@ -0,0 +1,6 @@
+"""
+Test sample code.
+"""
+
+def func():
+ pass
diff --git a/gazelle/testdata/simple/BUILD.in b/gazelle/testdata/simple/BUILD.in
new file mode 100644
index 0000000..e267b5a
--- /dev/null
+++ b/gazelle/testdata/simple/BUILD.in
@@ -0,0 +1,6 @@
+# Some comment to be preserved
+
+filegroup(
+ name = "allfiles",
+ srcs = glob(["**"]),
+)
diff --git a/gazelle/testdata/simple/BUILD.out b/gazelle/testdata/simple/BUILD.out
new file mode 100644
index 0000000..3c71d56
--- /dev/null
+++ b/gazelle/testdata/simple/BUILD.out
@@ -0,0 +1,14 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+# Some comment to be preserved
+
+filegroup(
+ name = "allfiles",
+ srcs = glob(["**"]),
+)
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+)
diff --git a/gazelle/testdata/simple/WORKSPACE b/gazelle/testdata/simple/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/simple/WORKSPACE
diff --git a/gazelle/testdata/simple/foo.bzl b/gazelle/testdata/simple/foo.bzl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/simple/foo.bzl
diff --git a/gazelle/testdata/tests/BUILD.in b/gazelle/testdata/tests/BUILD.in
new file mode 100644
index 0000000..e267b5a
--- /dev/null
+++ b/gazelle/testdata/tests/BUILD.in
@@ -0,0 +1,6 @@
+# Some comment to be preserved
+
+filegroup(
+ name = "allfiles",
+ srcs = glob(["**"]),
+)
diff --git a/gazelle/testdata/tests/BUILD.out b/gazelle/testdata/tests/BUILD.out
new file mode 100644
index 0000000..3c71d56
--- /dev/null
+++ b/gazelle/testdata/tests/BUILD.out
@@ -0,0 +1,14 @@
+load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
+
+# Some comment to be preserved
+
+filegroup(
+ name = "allfiles",
+ srcs = glob(["**"]),
+)
+
+bzl_library(
+ name = "foo",
+ srcs = ["foo.bzl"],
+ visibility = ["//visibility:public"],
+)
diff --git a/gazelle/testdata/tests/WORKSPACE b/gazelle/testdata/tests/WORKSPACE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/tests/WORKSPACE
diff --git a/gazelle/testdata/tests/foo.bzl b/gazelle/testdata/tests/foo.bzl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/tests/foo.bzl
diff --git a/gazelle/testdata/tests/foo_tests.bzl b/gazelle/testdata/tests/foo_tests.bzl
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gazelle/testdata/tests/foo_tests.bzl