From 212cc6fb36ee5234243d2b6e3dbb0bfa9ceed92b Mon Sep 17 00:00:00 2001 From: Alex Eagle Date: Mon, 12 Aug 2019 17:12:25 -0700 Subject: [PATCH] Update to latest rules_go --- WORKSPACE | 4 + package.bzl | 18 +- ...d82a001f378d0605cbbca3fb529979a1c3a6.patch | 1365 +++++++++++++++++ 3 files changed, 1384 insertions(+), 3 deletions(-) create mode 100644 revert_rules_go_commit_4442d82a001f378d0605cbbca3fb529979a1c3a6.patch diff --git a/WORKSPACE b/WORKSPACE index 00fe5027..f4157f29 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -35,6 +35,10 @@ yarn_install( load("@npm//:install_bazel_dependencies.bzl", "install_bazel_dependencies") install_bazel_dependencies() +load("@com_google_protobuf//:protobuf_deps.bzl", "protobuf_deps") + +protobuf_deps() + # Setup rules_go toolchain load("@io_bazel_rules_go//go:deps.bzl", "go_register_toolchains", "go_rules_dependencies") diff --git a/package.bzl b/package.bzl index 80522390..0f4ad482 100644 --- a/package.bzl +++ b/package.bzl @@ -47,11 +47,23 @@ def rules_typescript_dev_dependencies(): _maybe( http_archive, name = "io_bazel_rules_go", + patch_args = ["-p1"], + # Patch out a breaking change to runfiles support library + # See discussion on https://github.com/bazelbuild/rules_go/pull/2076 + patches = ["@build_bazel_rules_typescript//:revert_rules_go_commit_4442d82a001f378d0605cbbca3fb529979a1c3a6.patch"], + sha256 = "8df59f11fb697743cbb3f26cfb8750395f30471e9eabde0d174c3aebc7a1cd39", urls = [ - "https://mirror.bazel.build/github.com/bazelbuild/rules_go/releases/download/0.18.5/rules_go-0.18.5.tar.gz", - "https://github.com/bazelbuild/rules_go/releases/download/0.18.5/rules_go-0.18.5.tar.gz", + "https://storage.googleapis.com/bazel-mirror/github.com/bazelbuild/rules_go/releases/download/0.19.1/rules_go-0.19.1.tar.gz", + "https://github.com/bazelbuild/rules_go/releases/download/0.19.1/rules_go-0.19.1.tar.gz", ], - sha256 = "a82a352bffae6bee4e95f68a8d80a70e87f42c4741e6a448bec11998fcc82329", + ) + + _maybe( + http_archive, + name = "com_google_protobuf", + sha256 = "98e615d592d237f94db8bf033fba78cd404d979b0b70351a9e5aaff725398357", + strip_prefix = "protobuf-3.9.1", + urls = ["https://github.com/protocolbuffers/protobuf/archive/v3.9.1.tar.gz"], ) # go_repository is defined in bazel_gazelle diff --git a/revert_rules_go_commit_4442d82a001f378d0605cbbca3fb529979a1c3a6.patch b/revert_rules_go_commit_4442d82a001f378d0605cbbca3fb529979a1c3a6.patch new file mode 100644 index 00000000..e9235920 --- /dev/null +++ b/revert_rules_go_commit_4442d82a001f378d0605cbbca3fb529979a1c3a6.patch @@ -0,0 +1,1365 @@ +diff --git a/go/tools/bazel/BUILD.bazel b/go/tools/bazel/BUILD.bazel +index 6fbd4086..66656084 100644 +--- a/go/tools/bazel/BUILD.bazel ++++ b/go/tools/bazel/BUILD.bazel +@@ -4,6 +4,8 @@ go_library( + name = "go_default_library", + srcs = [ + "bazel.go", ++ "runfiledir.go", ++ "runfilemanifest.go", + "runfiles.go", + ], + importpath = "github.com/bazelbuild/rules_go/go/tools/bazel", +@@ -13,8 +15,13 @@ go_library( + go_test( + name = "go_default_test", + size = "small", +- srcs = ["bazel_test.go"], +- data = ["README.md"], ++ srcs = [ ++ "bazel_test.go", ++ "runfiles_test.go", ++ ], ++ data = [ ++ "README.md", ++ ], + embed = [":go_default_library"], + ) + +diff --git a/go/tools/bazel/bazel.go b/go/tools/bazel/bazel.go +index 08bf3496..37f1b8d1 100644 +--- a/go/tools/bazel/bazel.go ++++ b/go/tools/bazel/bazel.go +@@ -16,14 +16,76 @@ + package bazel + + import ( ++ "fmt" + "io/ioutil" + "os" ++ "path/filepath" ++ "sync" + ) + + const TEST_SRCDIR = "TEST_SRCDIR" + const TEST_TMPDIR = "TEST_TMPDIR" + const TEST_WORKSPACE = "TEST_WORKSPACE" + ++var ( ++ defaultTestWorkspace = "" ++ ++ runfileResolver runfilesResolver ++ runfileResolverErr error ++ runfileResolverOnce sync.Once ++) ++ ++func getRunfilesResolver() (runfilesResolver, error) { ++ runfileResolverOnce.Do(func() { ++ runfileResolver, runfileResolverErr = newRunfilesResolver() ++ }) ++ return runfileResolver, runfileResolverErr ++} ++ ++// Runfile returns an absolute path to the specified file in the runfiles directory of the running target. ++// It searches the current working directory, the runfiles path, and the workspace subdirectory of runfiles. ++// If a runfiles manifest is present, it will be used to resolve files not present in the working directory. ++// Returns an error if the file could not be found, or if an error occurs trying to find the runfiles env. ++func Runfile(path string) (string, error) { ++ // Search in working directory ++ if _, err := os.Stat(path); err == nil { ++ return path, nil ++ } ++ ++ resolver, err := getRunfilesResolver() ++ if err != nil { ++ return "", err ++ } ++ ++ // Search in runfiles. ++ searchPath := []string{path} ++ if workspace, err := TestWorkspace(); err == nil { ++ searchPath = append(searchPath, filepath.Join(workspace, path)) ++ } ++ ++ for _, path := range searchPath { ++ filename, ok := resolver.Resolve(path) ++ if !ok { ++ continue ++ } ++ ++ if _, err := os.Stat(filename); err == nil { ++ return filename, nil ++ } ++ } ++ ++ return "", fmt.Errorf("unable to find file %q", path) ++} ++ ++// RunfilesPath return the path to the run files tree for this test. ++// It returns an error if TEST_SRCDIR does not exist. ++func RunfilesPath() (string, error) { ++ if src, ok := os.LookupEnv(TEST_SRCDIR); ok { ++ return src, nil ++ } ++ return "", fmt.Errorf("environment variable %q is not defined, are you running with bazel test", TEST_SRCDIR) ++} ++ + // NewTmpDir creates a new temporary directory in TestTmpDir(). + func NewTmpDir(prefix string) (string, error) { + return ioutil.TempDir(TestTmpDir(), prefix) +@@ -37,3 +99,90 @@ func TestTmpDir() string { + } + return os.TempDir() + } ++ ++// TestWorkspace returns the name of the Bazel workspace for this test. ++// If TEST_WORKSPACE is not defined, it returns an error. ++func TestWorkspace() (string, error) { ++ if ws, ok := os.LookupEnv(TEST_WORKSPACE); ok { ++ return ws, nil ++ } ++ if defaultTestWorkspace != "" { ++ return defaultTestWorkspace, nil ++ } ++ return "", fmt.Errorf("Unable to find environment variable TEST_WORKSPACE") ++} ++ ++// SetDefaultTestWorkspace allows you to set a fake value for the ++// environment variable TEST_WORKSPACE if it is not defined. This is useful ++// when running tests on the command line and not through Bazel. ++func SetDefaultTestWorkspace(w string) { ++ defaultTestWorkspace = w ++} ++ ++// getCandidates returns the list of all possible "prefix/suffix" paths where there might be an ++// optional component in-between the two pieces. ++// ++// This function exists to cope with issues #1239 because we cannot tell where the built Go ++// binaries are located upfront. ++func getCandidates(prefix string, suffix string) []string { ++ candidates := []string{filepath.Join(prefix, suffix)} ++ if entries, err := ioutil.ReadDir(prefix); err == nil { ++ for _, entry := range entries { ++ candidate := filepath.Join(prefix, entry.Name(), suffix) ++ candidates = append(candidates, candidate) ++ } ++ } ++ return candidates ++} ++ ++// FindBinary locates the given executable within bazel-bin or the current directory. ++// ++// "pkg" indicates the relative path to the build package that contains the binary target, and ++// "binary" indicates the basename of the binary searched for. ++func FindBinary(pkg string, binary string) (string, bool) { ++ candidates := getCandidates(filepath.Join("bazel-bin", pkg), binary) ++ candidates = append(candidates, getCandidates(pkg, binary)...) ++ ++ for _, candidate := range candidates { ++ // Following symlinks here is intentional because Bazel generates symlinks in ++ // general and we don't care about that. ++ if fileInfo, err := os.Stat(candidate); err == nil { ++ if fileInfo.Mode()&os.ModeType == 0 && fileInfo.Mode()&0100 != 0 { ++ return candidate, true ++ } ++ } ++ } ++ return "", false ++} ++ ++// findRunfiles locates the directory under which a built binary can find its data dependencies ++// using relative paths. ++func findRunfiles(workspace string, pkg string, binary string, cookie string) (string, bool) { ++ candidates := getCandidates(filepath.Join("bazel-bin", pkg), filepath.Join(binary+".runfiles", workspace)) ++ candidates = append(candidates, ".") ++ ++ for _, candidate := range candidates { ++ if _, err := os.Stat(filepath.Join(candidate, cookie)); err == nil { ++ return candidate, true ++ } ++ } ++ return "", false ++} ++ ++// EnterRunfiles locates the directory under which a built binary can find its data dependencies ++// using relative paths, and enters that directory. ++// ++// "workspace" indicates the name of the current project, "pkg" indicates the relative path to the ++// build package that contains the binary target, "binary" indicates the basename of the binary ++// searched for, and "cookie" indicates an arbitrary data file that we expect to find within the ++// runfiles tree. ++func EnterRunfiles(workspace string, pkg string, binary string, cookie string) error { ++ runfiles, ok := findRunfiles(workspace, pkg, binary, cookie) ++ if !ok { ++ return fmt.Errorf("cannot find runfiles tree") ++ } ++ if err := os.Chdir(runfiles); err != nil { ++ return fmt.Errorf("cannot enter runfiles tree: %v", err) ++ } ++ return nil ++} +diff --git a/go/tools/bazel/bazel_test.go b/go/tools/bazel/bazel_test.go +index 66aa5876..41c9eed2 100644 +--- a/go/tools/bazel/bazel_test.go ++++ b/go/tools/bazel/bazel_test.go +@@ -126,6 +126,137 @@ func TestTestWorkspace(t *testing.T) { + } + } + ++func TestTestWorkspaceWithoutDefaultSet(t *testing.T) { ++ if oldVal, ok := os.LookupEnv(TEST_WORKSPACE); ok { ++ defer os.Setenv(TEST_WORKSPACE, oldVal) ++ } else { ++ t.Errorf("Terrible things are happening. You can't read env variables") ++ } ++ os.Unsetenv(TEST_WORKSPACE) ++ ++ workspace, err := TestWorkspace() ++ ++ if workspace != "" { ++ t.Errorf("Workspace should be left empty but was: %s", workspace) ++ } ++ ++ if err == nil { ++ t.Errorf("Expected error but instead passed") ++ } ++} ++ ++func TestTestWorkspaceWithDefaultSet(t *testing.T) { ++ if oldVal, ok := os.LookupEnv(TEST_WORKSPACE); ok { ++ defer os.Setenv(TEST_WORKSPACE, oldVal) ++ } else { ++ t.Errorf("Terrible things are happening. You can't read env variables") ++ } ++ os.Unsetenv(TEST_WORKSPACE) ++ ++ SetDefaultTestWorkspace("default_value") ++ workspace, err := TestWorkspace() ++ ++ if workspace == "" { ++ t.Errorf("Workspace is left empty") ++ } ++ ++ if err != nil { ++ t.Errorf("Unable to get workspace with error %s", err) ++ } ++} ++ ++func TestFindBinary(t *testing.T) { ++ testData := []struct { ++ name string ++ ++ pathsToCreate []string ++ wantBinary string ++ wantOk bool ++ }{ ++ { ++ "NoFiles", ++ []string{}, ++ "", ++ false, ++ }, ++ { ++ "CurrentDirectoryNoConfigurationInPath", ++ []string{ ++ "some/package/", ++ "some/package/bin*", ++ }, ++ "some/package/bin", ++ true, ++ }, ++ { ++ "CurrentDirectoryConfigurationInPath", ++ []string{ ++ "some/package/amd64/", ++ "some/package/arm64/", ++ "some/package/arm64/bin*", ++ "some/package/powerpc/", ++ }, ++ "some/package/arm64/bin", ++ true, ++ }, ++ { ++ "BazelBinNoConfigurationInPath", ++ []string{ ++ "bazel-bin/some/package/", ++ "bazel-bin/some/package/bin*", ++ "bin", // bazel-bin should be preferred. ++ }, ++ "bazel-bin/some/package/bin", ++ true, ++ }, ++ { ++ "BazelBinConfigurationInPath", ++ []string{ ++ "bazel-bin/some/package/amd64/", ++ "bazel-bin/some/package/arm64/", ++ "bazel-bin/some/package/arm64/bin*", ++ "bazel-bin/some/package/powerpc/", ++ "bin", // bazel-bin should be preferred. ++ "some/package/amd64/", ++ "some/package/amd64/bin", // bazel-bin should be preferred. ++ }, ++ "bazel-bin/some/package/arm64/bin", ++ true, ++ }, ++ { ++ "IgnoreNonExecutable", ++ []string{ ++ "bazel-bin/some/package/amd64/", ++ "bazel-bin/some/package/amd64/bin", ++ "bazel-bin/some/package/arm64/", ++ "bazel-bin/some/package/arm64/bin*", ++ "bazel-bin/some/package/powerpc/", ++ "bazel-bin/some/package/powerpc/bin", ++ }, ++ "bazel-bin/some/package/arm64/bin", ++ true, ++ }, ++ } ++ for _, d := range testData { ++ t.Run(d.name, func(t *testing.T) { ++ cleanup, err := makeAndEnterTempdir() ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer cleanup() ++ ++ if err := createPaths(d.pathsToCreate); err != nil { ++ t.Fatal(err) ++ } ++ ++ binary, ok := FindBinary("some/package", "bin") ++ if binary != d.wantBinary || ok != d.wantOk { ++ t.Errorf("Got %s, %v; want %s, %v", binary, ok, d.wantBinary, d.wantOk) ++ } ++ }) ++ } ++} ++ + func TestFindRunfiles(t *testing.T) { + testData := []struct { + name string +diff --git a/go/tools/bazel/runfiledir.go b/go/tools/bazel/runfiledir.go +new file mode 100644 +index 00000000..255d1452 +--- /dev/null ++++ b/go/tools/bazel/runfiledir.go +@@ -0,0 +1,32 @@ ++// Copyright 2018 The Bazel Authors. ++// ++// 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 bazel ++ ++import ( ++ "path/filepath" ++) ++ ++type directoryResolver string ++ ++// newDirectoryRunfilesResolver creates a new runfiles resolver that uses a runfiles directory to derive ++// filenames. ++func newDirectoryRunfilesResolver(directory string) (runfilesResolver, error) { ++ return directoryResolver(directory), nil ++} ++ ++// Resolve implements the Resolver interface. ++func (r directoryResolver) Resolve(n string) (string, bool) { ++ return filepath.Join(string(r), n), true ++} +diff --git a/go/tools/bazel/runfilemanifest.go b/go/tools/bazel/runfilemanifest.go +new file mode 100644 +index 00000000..8392a4b0 +--- /dev/null ++++ b/go/tools/bazel/runfilemanifest.go +@@ -0,0 +1,53 @@ ++// Copyright 2018 The Bazel Authors. ++// ++// 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 bazel ++ ++import ( ++ "bufio" ++ "errors" ++ "io" ++ "path/filepath" ++ "strings" ++) ++ ++var errManifestInvalid = errors.New("runfiles manifest syntax error") ++ ++type manifestResolver map[string]string ++ ++// newManifestRunfilesResolver creates a new runfiles resolver that uses a manifest file to resolve ++// filenames. ++func newManifestRunfilesResolver(manifest io.Reader) (runfilesResolver, error) { ++ resolver := manifestResolver{} ++ scanner := bufio.NewScanner(manifest) ++ ++ for scanner.Scan() { ++ a := strings.SplitN(scanner.Text(), " ", 2) ++ if len(a) != 2 { ++ return nil, errManifestInvalid ++ } ++ resolver[filepath.Clean(a[0])] = a[1] ++ } ++ ++ return resolver, nil ++} ++ ++// Resolve implements the Resolver interface. ++func (r manifestResolver) Resolve(n string) (string, bool) { ++ if fn, ok := r[filepath.Clean(n)]; ok { ++ return fn, true ++ } ++ ++ return "", false ++} +diff --git a/go/tools/bazel/runfiles.go b/go/tools/bazel/runfiles.go +index ed053e90..16ca3e16 100644 +--- a/go/tools/bazel/runfiles.go ++++ b/go/tools/bazel/runfiles.go +@@ -15,17 +15,8 @@ + package bazel + + import ( +- "bytes" + "errors" +- "fmt" +- "io/ioutil" + "os" +- "path" +- "path/filepath" +- "runtime" +- "sort" +- "strings" +- "sync" + ) + + const ( +@@ -33,393 +24,31 @@ const ( + RUNFILES_DIR = "RUNFILES_DIR" + ) + +-// Runfile returns an absolute path to the file named by "path", which +-// should be a relative path from the workspace root to the file within +-// the bazel workspace. +-// +-// Runfile may be called from tests invoked with 'bazel test' and +-// binaries invoked with 'bazel run'. On Windows, +-// only tests invoked with 'bazel test' are supported. +-func Runfile(path string) (string, error) { +- // Search in working directory +- if _, err := os.Stat(path); err == nil { +- return filepath.Abs(path) +- } +- +- if err := ensureRunfiles(); err != nil { +- return "", err +- } +- +- // Search manifest if we have one. +- if entry, ok := runfiles.index[path]; ok { +- return entry.Path, nil +- } +- +- // Search the main workspace. +- if runfiles.workspace != "" { +- mainPath := filepath.Join(runfiles.dir, runfiles.workspace, path) +- if _, err := os.Stat(mainPath); err == nil { +- return mainPath, nil +- } +- } +- +- // Search other workspaces. +- for _, w := range runfiles.workspaces { +- workPath := filepath.Join(runfiles.dir, w, path) +- if _, err := os.Stat(workPath); err == nil { +- return workPath, nil +- } +- } +- +- return "", fmt.Errorf("Runfile %s: could not locate file", path) +-} +- +-// FindBinary returns an absolute path to the binary built from a go_binary +-// rule in the given package with the given name. FindBinary is similar to +-// Runfile, but it accounts for varying configurations and file extensions, +-// which may cause the binary to have different paths on different platforms. +-// +-// FindBinary may be called from tests invoked with 'bazel test' and +-// binaries invoked with 'bazel run'. On Windows, +-// only tests invoked with 'bazel test' are supported. +-func FindBinary(pkg, name string) (string, bool) { +- if err := ensureRunfiles(); err != nil { +- return "", false +- } +- +- // If we've gathered a list of runfiles, either by calling ListRunfiles or +- // parsing the manifest on Windows, just use that instead of searching +- // directories. Return the first match. The manifest on Windows may contain +- // multiple entries for the same file. +- if runfiles.list != nil { +- if runtime.GOOS == "windows" { +- name += ".exe" +- } +- for _, entry := range runfiles.list { +- if path.Base(entry.ShortPath) != name { +- continue +- } +- pkgDir := path.Dir(path.Dir(entry.ShortPath)) +- if pkgDir == "." { +- pkgDir = "" +- } +- if pkgDir != pkg { +- continue +- } +- return entry.Path, true +- } +- return "", false +- } +- +- dir, err := Runfile(pkg) +- if err != nil { +- return "", false +- } +- var found string +- stopErr := errors.New("stop") +- err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { +- if err != nil { +- return err +- } +- base := filepath.Base(path) +- stem := strings.TrimSuffix(base, ".exe") +- if stem != name { +- return nil +- } +- if runtime.GOOS != "windows" { +- if st, err := os.Stat(path); err != nil { +- return err +- } else if st.Mode()&0111 == 0 { +- return nil +- } +- } +- if stem == name { +- found = path +- return stopErr +- } +- return nil +- }) +- if err == stopErr { +- return found, true +- } else { +- return "", false +- } +-} +- +-// A RunfileEntry describes a runfile. +-type RunfileEntry struct { +- // Workspace is the bazel workspace the file came from. For example, +- // this would be "io_bazel_rules_go" for a file in rules_go. +- Workspace string +- +- // ShortPath is a relative, slash-separated path from the workspace root +- // to the file. For non-binary files, this may be passed to Runfile +- // to locate a file. +- ShortPath string +- +- // Path is an absolute path to the file. +- Path string +-} +- +-// ListRunfiles returns a list of available runfiles. +-func ListRunfiles() ([]RunfileEntry, error) { +- if err := ensureRunfiles(); err != nil { +- return nil, err +- } +- +- if runfiles.list == nil && runfiles.dir != "" { +- runfiles.listOnce.Do(func() { +- var list []RunfileEntry +- haveWorkspaces := strings.HasSuffix(runfiles.dir, ".runfiles") && runfiles.workspace != "" +- +- err := filepath.Walk(runfiles.dir, func(path string, info os.FileInfo, err error) error { +- if err != nil { +- return err +- } +- rel, _ := filepath.Rel(runfiles.dir, path) +- rel = filepath.ToSlash(rel) +- if rel == "." { +- return nil +- } +- +- var workspace, shortPath string +- if haveWorkspaces { +- if i := strings.IndexByte(rel, '/'); i < 0 { +- return nil +- } else { +- workspace, shortPath = rel[:i], rel[i+1:] +- } +- } else { +- workspace, shortPath = "", rel +- } +- +- list = append(list, RunfileEntry{Workspace: workspace, ShortPath: shortPath, Path: path}) +- return nil +- }) +- if err != nil { +- runfiles.err = err +- return +- } +- runfiles.list = list +- }) +- } +- return runfiles.list, runfiles.err +-} +- +-// TestWorkspace returns the name of the Bazel workspace for this test. +-// TestWorkspace returns an error if the TEST_WORKSPACE environment variable +-// was not set or SetDefaultTestWorkspace was not called. +-func TestWorkspace() (string, error) { +- if err := ensureRunfiles(); err != nil { +- return "", err +- } +- if runfiles.workspace != "" { +- return runfiles.workspace, nil +- } +- return "", errors.New("TEST_WORKSPACE not set and SetDefaultTestWorkspace not called") +-} ++var errNoRunfilesEnv = errors.New("runfiles environment missing") + +-// SetDefaultTestWorkspace allows you to set a fake value for the +-// environment variable TEST_WORKSPACE if it is not defined. This is useful +-// when running tests on the command line and not through Bazel. +-func SetDefaultTestWorkspace(w string) { +- ensureRunfiles() +- runfiles.workspace = w ++// runfilesResolver is an interface for a resolver that can take a runfiles path and resolve it to a path on ++// disk. ++type runfilesResolver interface { ++ Resolve(string) (string, bool) + } + +-// RunfilesPath return the path to the runfiles tree. +-// It will return an error if there is no runfiles tree, for example because +-// the executable is run on Windows or was not invoked with 'bazel test' +-// or 'bazel run'. +-func RunfilesPath() (string, error) { +- if err := ensureRunfiles(); err != nil { +- return "", err +- } +- if runfiles.dir == "" { +- if runtime.GOOS == "windows" { +- return "", errors.New("RunfilesPath: no runfiles directory on windows") +- } else { +- return "", errors.New("could not locate runfiles directory") +- } +- } +- if runfiles.workspace == "" { +- return "", errors.New("could not locate runfiles workspace") +- } +- return filepath.Join(runfiles.dir, runfiles.workspace), nil +-} +- +-// EnterRunfiles locates the directory under which a built binary can find its data dependencies +-// using relative paths, and enters that directory. +-// +-// "workspace" indicates the name of the current project, "pkg" indicates the relative path to the +-// build package that contains the binary target, "binary" indicates the basename of the binary +-// searched for, and "cookie" indicates an arbitrary data file that we expect to find within the +-// runfiles tree. +-// +-// DEPRECATED: use RunfilesPath instead. +-func EnterRunfiles(workspace string, pkg string, binary string, cookie string) error { +- runfiles, ok := findRunfiles(workspace, pkg, binary, cookie) +- if !ok { +- return fmt.Errorf("cannot find runfiles tree") +- } +- if err := os.Chdir(runfiles); err != nil { +- return fmt.Errorf("cannot enter runfiles tree: %v", err) +- } +- return nil +-} +- +-var runfiles = struct { +- once, listOnce sync.Once +- +- // list is a list of known runfiles, either loaded from the manifest +- // or discovered by walking the runfile directory. +- list []RunfileEntry +- +- // index maps runfile short paths to absolute paths. +- index map[string]RunfileEntry +- +- // dir is a path to the runfile directory. Typically this is a directory +- // named .runfiles, with a subdirectory for each workspace. +- dir string +- +- // workspace is workspace where the binary or test was built. +- workspace string +- +- // workspaces is a list of other workspace names. +- workspaces []string +- +- // err is set when there is an error loading runfiles, for example, +- // parsing the manifest. +- err error +-}{} +- +-func ensureRunfiles() error { +- runfiles.once.Do(initRunfiles) +- return runfiles.err +-} +- +-func initRunfiles() { +- manifest := os.Getenv("RUNFILES_MANIFEST_FILE") ++// newRunfilesResolver creates a new runfiles resolver. The type of resolver and its parameters are derived ++// from the environment. ++func newRunfilesResolver() (runfilesResolver, error) { ++ manifest := os.Getenv(RUNFILES_MANIFEST_FILE) + if manifest != "" { +- // On Windows, Bazel doesn't create a symlink tree of runfiles because +- // Windows doesn't support symbolic links by default. Instead, runfile +- // locations are written to a manifest file. +- runfiles.index = make(map[string]RunfileEntry) +- data, err := ioutil.ReadFile(manifest) ++ f, err := os.Open(manifest) + if err != nil { +- runfiles.err = err +- return ++ return nil, err + } +- lineno := 0 +- for len(data) > 0 { +- i := bytes.IndexByte(data, '\n') +- var line []byte +- if i < 0 { +- line = data +- data = nil +- } else { +- line = data[:i] +- data = data[i+1:] +- } +- lineno++ +- line = bytes.TrimSpace(line) +- if len(line) == 0 { +- continue +- } +- e := bytes.SplitN(line, []byte(" "), 2) +- if len(e) < 2 { +- runfiles.err = fmt.Errorf("error parsing runfiles manifest: %s:%d: no space", manifest, lineno) +- return +- } +- +- entry := RunfileEntry{ShortPath: string(e[0]), Path: string(e[1])} +- if i := strings.IndexByte(entry.ShortPath, '/'); i >= 0 { +- entry.Workspace = entry.ShortPath[:i] +- entry.ShortPath = entry.ShortPath[i+1:] +- } +- if strings.HasPrefix(entry.ShortPath, "external/") { +- entry.ShortPath = entry.ShortPath[len("external/"):] +- if i := strings.IndexByte(entry.ShortPath, '/'); i >= 0 { +- entry.Workspace = entry.ShortPath[:i] +- entry.ShortPath = entry.ShortPath[i+1:] +- } +- } +- +- runfiles.list = append(runfiles.list, entry) +- runfiles.index[entry.ShortPath] = entry +- } +- } +- +- runfiles.workspace = os.Getenv("TEST_WORKSPACE") +- +- if dir := os.Getenv("RUNFILES_DIR"); dir != "" { +- runfiles.dir = dir +- } else if dir = os.Getenv("TEST_SRCDIR"); dir != "" { +- runfiles.dir = dir +- } else if runtime.GOOS != "windows" { +- dir, err := os.Getwd() +- if err != nil { +- runfiles.err = fmt.Errorf("error localting runfiles dir: %v", err) +- return +- } +- +- parent := filepath.Dir(dir) +- if strings.HasSuffix(parent, ".runfiles") { +- runfiles.dir = parent +- if runfiles.workspace == "" { +- runfiles.workspace = filepath.Base(dir) +- } +- } else { +- runfiles.err = errors.New("could not locate runfiles directory") +- return +- } +- } +- +- if runfiles.dir != "" { +- fis, err := ioutil.ReadDir(runfiles.dir) +- if err != nil { +- runfiles.err = fmt.Errorf("could not open runfiles directory: %v", err) +- return +- } +- for _, fi := range fis { +- if fi.IsDir() { +- runfiles.workspaces = append(runfiles.workspaces, fi.Name()) +- } +- } +- sort.Strings(runfiles.workspaces) ++ defer f.Close() ++ return newManifestRunfilesResolver(f) + } +-} + +-// getCandidates returns the list of all possible "prefix/suffix" paths where there might be an +-// optional component in-between the two pieces. +-// +-// This function exists to cope with issues #1239 because we cannot tell where the built Go +-// binaries are located upfront. +-// +-// DEPRECATED: only used by EnterRunfiles. +-func getCandidates(prefix string, suffix string) []string { +- candidates := []string{filepath.Join(prefix, suffix)} +- if entries, err := ioutil.ReadDir(prefix); err == nil { +- for _, entry := range entries { +- candidate := filepath.Join(prefix, entry.Name(), suffix) +- candidates = append(candidates, candidate) +- } ++ directory := os.Getenv(RUNFILES_DIR) ++ if directory != "" { ++ return newDirectoryRunfilesResolver(directory) + } +- return candidates +-} +- +-// findRunfiles locates the directory under which a built binary can find its data dependencies +-// using relative paths. +-// +-// DEPRECATED: only used by EnterRunfiles. +-func findRunfiles(workspace string, pkg string, binary string, cookie string) (string, bool) { +- candidates := getCandidates(filepath.Join("bazel-bin", pkg), filepath.Join(binary+".runfiles", workspace)) +- candidates = append(candidates, ".") + +- for _, candidate := range candidates { +- if _, err := os.Stat(filepath.Join(candidate, cookie)); err == nil { +- return candidate, true +- } +- } +- return "", false ++ return nil, errNoRunfilesEnv + } +diff --git a/go/tools/bazel/runfiles_test.go b/go/tools/bazel/runfiles_test.go +new file mode 100644 +index 00000000..b7bd44f0 +--- /dev/null ++++ b/go/tools/bazel/runfiles_test.go +@@ -0,0 +1,125 @@ ++// Copyright 2018 The Bazel Authors. ++// ++// 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 bazel ++ ++import ( ++ "io/ioutil" ++ "os" ++ "path/filepath" ++ "testing" ++) ++ ++func setenvForTest(key, value string) (cleanup func()) { ++ if old, ok := os.LookupEnv(key); ok { ++ cleanup = func() { os.Setenv(key, old) } ++ } else { ++ cleanup = func() { os.Unsetenv(key) } ++ } ++ os.Setenv(key, value) ++ return cleanup ++} ++ ++func setupResolverForTest() { ++ // Prevent initialization code from running. ++ runfileResolverOnce.Do(func() {}) ++ runfileResolver, runfileResolverErr = newRunfilesResolver() ++} ++ ++func TestManifestRunfiles(t *testing.T) { ++ dir, err := NewTmpDir("test") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer os.RemoveAll(dir) ++ ++ testStr := "This is a test" ++ mappedFilename := filepath.Join(dir, "mapped_file.txt") ++ if err := ioutil.WriteFile(mappedFilename, []byte(testStr), 0600); err != nil { ++ t.Fatal(err) ++ } ++ ++ manifestFilename := filepath.Join(dir, "MANIFEST") ++ if err := ioutil.WriteFile(manifestFilename, []byte("runfiles/test.txt "+mappedFilename), 0600); err != nil { ++ t.Fatal(err) ++ } ++ ++ cleanupManifestEnv := setenvForTest(RUNFILES_MANIFEST_FILE, manifestFilename) ++ defer cleanupManifestEnv() ++ cleanupDirEnv := setenvForTest(RUNFILES_DIR, "") ++ defer cleanupDirEnv() ++ ++ setupResolverForTest() ++ if runfileResolverErr != nil { ++ t.Fatal(runfileResolverErr) ++ } ++ if _, ok := runfileResolver.(manifestResolver); !ok { ++ t.Error("resolver should be manifest resolver") ++ } ++ ++ filename, err := Runfile("runfiles/test.txt") ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ d, err := ioutil.ReadFile(filename) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if string(d) != testStr { ++ t.Errorf("expected %s, got %s", testStr, string(d)) ++ } ++} ++ ++func TestDirectoryRunfiles(t *testing.T) { ++ dir, err := NewTmpDir("test") ++ if err != nil { ++ t.Fatal(err) ++ } ++ defer os.RemoveAll(dir) ++ ++ testStr := "This is a test" ++ mappedfn := filepath.Join(dir, "runfile.txt") ++ if err := ioutil.WriteFile(mappedfn, []byte(testStr), 0600); err != nil { ++ t.Fatal(err) ++ } ++ ++ cleanupManifestEnv := setenvForTest(RUNFILES_MANIFEST_FILE, "") ++ defer cleanupManifestEnv() ++ cleanupDirEnv := setenvForTest(RUNFILES_DIR, dir) ++ defer cleanupDirEnv() ++ ++ setupResolverForTest() ++ if runfileResolverErr != nil { ++ t.Fatal(runfileResolverErr) ++ } ++ if _, ok := runfileResolver.(directoryResolver); !ok { ++ t.Error("resolver should be directory resolver") ++ } ++ ++ filename, err := Runfile("runfile.txt") ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ d, err := ioutil.ReadFile(filename) ++ if err != nil { ++ t.Fatal(err) ++ } ++ ++ if string(d) != testStr { ++ t.Errorf("expected %s, got %s", testStr, string(d)) ++ } ++} +diff --git a/tests/core/runfiles/BUILD.bazel b/tests/core/runfiles/BUILD.bazel +deleted file mode 100644 +index c7db6d3d..00000000 +--- a/tests/core/runfiles/BUILD.bazel ++++ /dev/null +@@ -1,48 +0,0 @@ +-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library", "go_test") +- +-package(default_visibility = ["//visibility:public"]) +- +-test_suite( +- name = "runfiles_tests", +- tests = [ +- ":local_test", +- "@runfiles_remote_test//:remote_test", +- ], +-) +- +-go_test( +- name = "local_test", +- srcs = ["runfiles_test.go"], +- deps = [":check_runfiles"], +-) +- +-go_binary( +- name = "local_cmd", +- srcs = ["runfiles_cmd.go"], +- deps = [":check_runfiles"], +-) +- +-go_binary( +- name = "local_bin", +- srcs = ["empty_bin.go"], +-) +- +-go_library( +- name = "check_runfiles", +- srcs = ["check_runfiles.go"], +- data = [ +- "local_file.txt", +- ":local_bin", +- ":local_group", +- "@runfiles_remote_test//:remote_bin", +- "@runfiles_remote_test//:remote_file.txt", +- "@runfiles_remote_test//:remote_group", +- ], +- importpath = "github.com/bazelbuild/rules_go/tests/core/runfiles/check", +- deps = ["//go/tools/bazel:go_default_library"], +-) +- +-filegroup( +- name = "local_group", +- srcs = ["local_group.txt"], +-) +diff --git a/tests/core/runfiles/README.rst b/tests/core/runfiles/README.rst +deleted file mode 100644 +index ec357826..00000000 +--- a/tests/core/runfiles/README.rst ++++ /dev/null +@@ -1,23 +0,0 @@ +-Runfiles functionality +-===================== +- +-runfiles_tests +--------------- +- +-Checks that functions in ``//go/tools/bazel:go_default_library`` that +-provide access to runfiles behave correctly. In particular, this checks: +- +-* ``Runfile`` works for regular files. +-* ``FindBinary`` works for binaries. +-* ``ListRunfiles`` lists all expected files. +-* These functions work for runfiles in the local workspace and for files in +- external repositories (``@runfiles_remote_test`` is a ``local_repository`` +- that points to a subdirectory here). +-* These functions work in tests invoked with ``bazel test`` and +- binaries invoked with ``bazel run``. +-* These functions work on Windows and other platforms. Bazel doesn't +- create a symlink tree for runfiles on Windows since symbolic links +- can't be created without administrative privilege by default. +- +-TODO: Verify binary behavior in CI. The ``local_bin`` and ``remote_bin`` +-targets verify behavior for binaries, but they are not tests. +diff --git a/tests/core/runfiles/bin.go b/tests/core/runfiles/bin.go +deleted file mode 100644 +index 0ced7a9e..00000000 +--- a/tests/core/runfiles/bin.go ++++ /dev/null +@@ -1,17 +0,0 @@ +-// Copyright 2019 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 main +- +-func main() {} +diff --git a/tests/core/runfiles/check_runfiles.go b/tests/core/runfiles/check_runfiles.go +deleted file mode 100644 +index 8858a573..00000000 +--- a/tests/core/runfiles/check_runfiles.go ++++ /dev/null +@@ -1,121 +0,0 @@ +-// Copyright 2019 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 check +- +-import ( +- "fmt" +- "os" +- "path/filepath" +- "runtime" +- "sort" +- "strings" +- +- "github.com/bazelbuild/rules_go/go/tools/bazel" +-) +- +-type TestFile struct { +- Workspace, ShortPath, Path string +- Binary bool +-} +- +-var DefaultTestFiles = []TestFile{ +- {Workspace: "io_bazel_rules_go", Path: "tests/core/runfiles/local_file.txt"}, +- {Workspace: "io_bazel_rules_go", Path: "tests/core/runfiles/local_group.txt"}, +- {Workspace: "io_bazel_rules_go", Path: "tests/core/runfiles/local_bin", Binary: true}, +- {Workspace: "runfiles_remote_test", Path: "remote_file.txt"}, +- {Workspace: "runfiles_remote_test", Path: "remote_group.txt"}, +- {Workspace: "runfiles_remote_test", Path: "remote_bin", Binary: true}, +-} +- +-func CheckRunfiles(files []TestFile) error { +- // Check that the runfiles directory matches the current workspace. +- // There is no runfiles directory on Windows. +- if runtime.GOOS != "windows" { +- dir, err := bazel.RunfilesPath() +- if err != nil { +- return err +- } +- root, base := filepath.Dir(dir), filepath.Base(dir) +- if !strings.HasSuffix(root, ".runfiles") { +- return fmt.Errorf("RunfilesPath: %q is not a .runfiles directory", dir) +- } +- workspace := os.Getenv("TEST_WORKSPACE") +- if workspace != "" && workspace != base { +- return fmt.Errorf("RunfilesPath: %q does not match test workspace %s", dir, workspace) +- } +- if srcDir := os.Getenv("TEST_SRCDIR"); srcDir != "" && filepath.Join(srcDir, workspace) != dir { +- return fmt.Errorf("RunfilesPath: %q does not match TEST_SRCDIR %q", dir, srcDir) +- } +- } +- +- // Check that files can be found with Runfile or FindBinary. +- // Make sure the paths returned are absolute paths to actual files. +- seen := make(map[string]string) +- for _, f := range files { +- var got string +- var err error +- if !f.Binary { +- if got, err = bazel.Runfile(f.Path); err != nil { +- return err +- } +- if !filepath.IsAbs(got) { +- return fmt.Errorf("Runfile %s: got a relative path %q; want absolute", f.Path, got) +- } +- seen[f.Path] = got +- } else { +- var pkg, name string +- if i := strings.LastIndex(f.Path, "/"); i < 0 { +- name = f.Path +- } else { +- pkg = f.Path[:i] +- name = f.Path[i+1:] +- } +- var ok bool +- if got, ok = bazel.FindBinary(pkg, name); !ok { +- return fmt.Errorf("FindBinary %s %s: could not find binary", pkg, name) +- } +- if !filepath.IsAbs(got) { +- return fmt.Errorf("FindBinary %s %s: got a relative path %q; want absolute", pkg, name, got) +- } +- } +- +- if _, err := os.Stat(got); err != nil { +- return fmt.Errorf("%s: could not stat: %v", f.Path, err) +- } +- } +- +- // Check that the files can be listed. +- entries, err := bazel.ListRunfiles() +- if err != nil { +- return err +- } +- for _, e := range entries { +- if want, ok := seen[e.ShortPath]; ok && want != e.Path { +- return err +- } +- delete(seen, e.ShortPath) +- } +- +- if len(seen) > 0 { +- unseen := make([]string, 0, len(seen)) +- for short := range seen { +- unseen = append(unseen, short) +- } +- sort.Strings(unseen) +- return fmt.Errorf("ListRunfiles did not include files:\n\t%s", strings.Join(unseen, "\n\t")) +- } +- +- return nil +-} +diff --git a/tests/core/runfiles/empty_bin.go b/tests/core/runfiles/empty_bin.go +deleted file mode 100644 +index 38dd16da..00000000 +--- a/tests/core/runfiles/empty_bin.go ++++ /dev/null +@@ -1,3 +0,0 @@ +-package main +- +-func main() {} +diff --git a/tests/core/runfiles/local_file.txt b/tests/core/runfiles/local_file.txt +deleted file mode 100644 +index e69de29b..00000000 +diff --git a/tests/core/runfiles/local_group.txt b/tests/core/runfiles/local_group.txt +deleted file mode 100644 +index e69de29b..00000000 +diff --git a/tests/core/runfiles/runfiles_cmd.go b/tests/core/runfiles/runfiles_cmd.go +deleted file mode 100644 +index 0ab124b5..00000000 +--- a/tests/core/runfiles/runfiles_cmd.go ++++ /dev/null +@@ -1,28 +0,0 @@ +-// Copyright 2019 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 main +- +-import ( +- "fmt" +- "os" +- +- "github.com/bazelbuild/rules_go/tests/core/runfiles/check" +-) +- +-func main() { +- if err := check.CheckRunfiles(check.DefaultTestFiles); err != nil { +- fmt.Fprintln(os.Stderr, err.Error()) +- } +-} +diff --git a/tests/core/runfiles/runfiles_remote_test/BUILD.bazel b/tests/core/runfiles/runfiles_remote_test/BUILD.bazel +deleted file mode 100644 +index 112dcd35..00000000 +--- a/tests/core/runfiles/runfiles_remote_test/BUILD.bazel ++++ /dev/null +@@ -1,27 +0,0 @@ +-load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_test") +- +-package(default_visibility = ["//visibility:public"]) +- +-go_test( +- name = "remote_test", +- srcs = ["@io_bazel_rules_go//tests/core/runfiles:runfiles_test.go"], +- deps = ["@io_bazel_rules_go//tests/core/runfiles:check_runfiles"], +-) +- +-go_binary( +- name = "remote_cmd", +- srcs = ["@io_bazel_rules_go//tests/core/runfiles:runfiles_cmd.go"], +- deps = ["@io_bazel_rules_go//tests/core/runfiles:check_runfiles"], +-) +- +-go_binary( +- name = "remote_bin", +- srcs = ["@io_bazel_rules_go//tests/core/runfiles:empty_bin.go"], +-) +- +-filegroup( +- name = "remote_group", +- srcs = ["remote_group.txt"], +-) +- +-exports_files(["remote_file.txt"]) +diff --git a/tests/core/runfiles/runfiles_remote_test/WORKSPACE b/tests/core/runfiles/runfiles_remote_test/WORKSPACE +deleted file mode 100644 +index c9af3f85..00000000 +--- a/tests/core/runfiles/runfiles_remote_test/WORKSPACE ++++ /dev/null +@@ -1 +0,0 @@ +-workspace(name = "runfiles_remote_test") +diff --git a/tests/core/runfiles/runfiles_remote_test/remote_file.txt b/tests/core/runfiles/runfiles_remote_test/remote_file.txt +deleted file mode 100644 +index e69de29b..00000000 +diff --git a/tests/core/runfiles/runfiles_remote_test/remote_group.txt b/tests/core/runfiles/runfiles_remote_test/remote_group.txt +deleted file mode 100644 +index e69de29b..00000000 +diff --git a/tests/core/runfiles/runfiles_test.go b/tests/core/runfiles/runfiles_test.go +deleted file mode 100644 +index 62aab3af..00000000 +--- a/tests/core/runfiles/runfiles_test.go ++++ /dev/null +@@ -1,27 +0,0 @@ +-// Copyright 2019 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 main +- +-import ( +- "testing" +- +- "github.com/bazelbuild/rules_go/tests/core/runfiles/check" +-) +- +-func Test(t *testing.T) { +- if err := check.CheckRunfiles(check.DefaultTestFiles); err != nil { +- t.Fatal(err) +- } +-}