diff options
-rw-r--r-- | go/core.rst | 60 | ||||
-rw-r--r-- | go/private/tools/path.bzl | 209 | ||||
-rw-r--r-- | go/private/tools/vet.bzl | 4 | ||||
-rw-r--r-- | go/providers.rst | 29 | ||||
-rw-r--r-- | go/tools/builders/BUILD.bazel | 6 | ||||
-rw-r--r-- | go/tools/builders/go_path.go | 202 | ||||
-rw-r--r-- | tests/README.rst | 2 | ||||
-rw-r--r-- | tests/core/README.rst | 6 | ||||
-rw-r--r-- | tests/core/go_path/BUILD.bazel | 33 | ||||
-rw-r--r-- | tests/core/go_path/README.rst | 12 | ||||
-rw-r--r-- | tests/core/go_path/cmd/bin/BUILD.bazel | 14 | ||||
-rw-r--r-- | tests/core/go_path/cmd/bin/bin.go | 4 | ||||
-rw-r--r-- | tests/core/go_path/extra.txt | 0 | ||||
-rw-r--r-- | tests/core/go_path/go_path_test.go | 147 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/BUILD.bazel | 40 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/data.txt | 0 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/embed_test.go | 1 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/external_test.go | 1 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/internal_test.go | 1 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/lib.go | 3 | ||||
-rw-r--r-- | tests/core/go_path/pkg/lib/vendored.go | 1 |
21 files changed, 687 insertions, 88 deletions
diff --git a/go/core.rst b/go/core.rst index b6396b78..f31f9089 100644 --- a/go/core.rst +++ b/go/core.rst @@ -8,6 +8,7 @@ Core go rules .. _GoLibrary: providers.rst#GoLibrary .. _GoSource: providers.rst#GoSource .. _GoArchive: providers.rst#GoArchive +.. _GoPath: providers.rst#GoPath .. _cgo: http://golang.org/cmd/cgo/ .. _"Make variable": https://docs.bazel.build/versions/master/be/make-variables.html .. _Bourne shell tokenization: https://docs.bazel.build/versions/master/be/common-definitions.html#sh-tokenization @@ -622,6 +623,65 @@ Attributes | Subject to `"Make variable"`_ substitution and `Bourne shell tokenization`_. | +----------------------------+-----------------------------+---------------------------------------+ +go_path +~~~~~~~ + +``go_path`` builds a directory structure that can be used with tools that +understand the ``GOPATH`` directory layout. This directory structure can be +built by zipping, copying, or linking files. + +``go_path`` can depend on one or more Go targets (i.e., `go_library`_, +`go_binary`_, or `go_test`_). It will include packages from those targets, as +well as their transitive dependencies. Packages will be in subdirectories named +after their ``importpath`` or ``importmap`` attributes under a ``src/`` +directory. + +Attributes +^^^^^^^^^^ + ++----------------------------+-----------------------------+---------------------------------------+ +| **Name** | **Type** | **Default value** | ++----------------------------+-----------------------------+---------------------------------------+ +| :param:`name` | :type:`string` | |mandatory| | ++----------------------------+-----------------------------+---------------------------------------+ +| A unique name for this rule. | ++----------------------------+-----------------------------+---------------------------------------+ +| :param:`deps` | :type:`label_list` | :value:`[]` | ++----------------------------+-----------------------------+---------------------------------------+ +| A list of targets that build Go packages. A directory will be generated from | +| files in these targets and their transitive dependencies. All targets must | +| provide GoArchive_ (`go_library`_, `go_binary`_, `go_test`_, and similar | +| rules have this). | +| | +| Only targets with explicit ``importpath`` attributes will be included in the | +| generated directory. Synthetic packages (like the main package produced by | +| `go_test`_) and packages with inferred import paths will not be | +| included. The values of ``importmap`` attributes may influence the placement | +| of packages within the generated directory (for example, in vendor | +| directories). | +| | +| The generated directory will contain original source files, including .go, | +| .s, .h, and .c files compiled by cgo. It will not contain files generated by | +| tools like cover and cgo, but it will contain generated files passed in | +| ``srcs`` attributes like .pb.go files. The generated directory will also | +| contain runfiles found in ``data`` attributes. | ++----------------------------+-----------------------------+---------------------------------------+ +| :param:`data` | :type:`label_list` | :value:`[]` | ++----------------------------+-----------------------------+---------------------------------------+ +| A list of targets producing data files that will be stored next to the | +| ``src/`` directory. Useful for including things like licenses and readmes. | ++----------------------------+-----------------------------+---------------------------------------+ +| :param:`mode` | :type:`string` | :value:`"copy"` | ++----------------------------+-----------------------------+---------------------------------------+ +| Determines how the generated directory is provided. May be one of: | +| | +| * ``"archive"``: The generated directory is packaged as a single .zip file. | +| * ``"copy"``: The generated directory is a single tree artifact. Source files | +| are copied into the tree. | +| * ``"link"``: Source files are symlinked into the tree. All of the symlink | +| files are provided as separate output files. | ++----------------------------+-----------------------------+---------------------------------------+ + go_rule ~~~~~~~ diff --git a/go/private/tools/path.bzl b/go/private/tools/path.bzl index e5a4bdad..ffad6b37 100644 --- a/go/private/tools/path.bzl +++ b/go/private/tools/path.bzl @@ -15,122 +15,165 @@ load( "@io_bazel_rules_go//go/private:context.bzl", "go_context", + "EXPLICIT_PATH", ) load( "@io_bazel_rules_go//go/private:providers.bzl", - "GoLibrary", + "GoArchive", "GoPath", "get_archive", ) load( "@io_bazel_rules_go//go/private:common.bzl", "as_iterable", + "as_list", ) load( "@io_bazel_rules_go//go/private:rules/rule.bzl", "go_rule", ) -def _tag(go, path, outputs): - """this generates a existance tag file for dependencies, and returns the path to the tag file""" - tag = go.declare_file(go, path=path+".tag") - path, _, _ = tag.short_path.rpartition("/") - go.actions.write(tag, content="") - outputs.append(tag) - return path - def _go_path_impl(ctx): - print(""" -EXPERIMENTAL: the go_path rule is still very experimental -Please do not rely on it for production use, but feel free to use it and file issues -""") - go = go_context(ctx) - #TODO: non specific mode? - # First gather all the library rules - golibs = depset() - archives_runfiles = {} + # Gather all archives. Note that there may be multiple packages with the same + # importpath (e.g., multiple vendored libraries, internal tests). + direct_archives = [] + transitive_archives = [] for dep in ctx.attr.deps: archive = get_archive(dep) - golibs += archive.transitive - importpath = archive.source.library.importpath - if importpath: - archives_runfiles[importpath] = archive.source.runfiles + direct_archives.append(archive.data) + transitive_archives.append(archive.transitive) + archives = depset(direct = direct_archives, transitive = transitive_archives) + + # Collect sources and data files from archives. Merge archives into packages. + pkg_map = {} # map from package path to structs + for archive in as_iterable(archives): + importpath, pkgpath = _get_importpath_pkgpath(archive) + if importpath == "": + continue # synthetic archive or inferred location + out_prefix = "src/" + pkgpath + pkg = struct( + importpath = importpath, + dir = out_prefix, + srcs = as_list(archive.orig_srcs), + data = as_list(archive.data_files), + ) + if pkgpath in pkg_map: + _merge_pkg(pkg_map[pkgpath], pkg) + else: + pkg_map[pkgpath] = pkg + + # Build a manifest file that includes all files to copy/link/zip. + inputs = [] + manifest_entries = [] + for pkg in pkg_map.values(): + for f in pkg.srcs + pkg.data: + manifest_entries.append(struct( + src = f.path, + dst = pkg.dir + "/" + f.basename, + )) + inputs.append(f) + for f in ctx.files.data: + manifest_entries.append(struct( + src = f.path, + dst = f.basename, + )) + inputs.append(f) + manifest_file = ctx.actions.declare_file(ctx.label.name + "~manifest") + manifest_entries_json = [e.to_json() for e in manifest_entries] + manifest_content = "[\n " + ",\n ".join(manifest_entries_json) + "\n]" + ctx.actions.write(manifest_file, manifest_content) + inputs.append(manifest_file) + + # Execute the builder + if ctx.attr.mode == "archive": + out = ctx.actions.declare_file(ctx.label.name + ".zip") + out_path = out.path + out_short_path = out.short_path + outputs = [out] + elif ctx.attr.mode == "copy": + out = ctx.actions.declare_directory(ctx.label.name) + out_path = out.path + out_short_path = out.short_path + outputs = [out] + else: # link + # Declare individual outputs in link mode. Symlinks can't point outside + # tree artifacts. + outputs = [ctx.actions.declare_file(ctx.label.name + "/" + e.dst) + for e in manifest_entries] + tag = ctx.actions.declare_file(ctx.label.name + "/.tag") + ctx.actions.write(tag, "") + out_path = tag.dirname + out_short_path = tag.short_path.rpartition("/")[0] + args = [ + "-manifest=" + manifest_file.path, + "-out=" + out_path, + "-mode=" + ctx.attr.mode, + ] + ctx.actions.run( + outputs = outputs, + inputs = inputs, + mnemonic = "GoPath", + executable = ctx.executable._go_path, + arguments = args, + ) - # Now scan them for sources - seen_libs = {} - seen_paths = {} - outputs = [] - packages = [] - for golib in as_iterable(golibs): - if not golib.importpath: - print("Missing importpath on {}".format(golib.label)) - continue - if golib.importpath in seen_libs: - # We found two different library rules that map to the same import path - # This is legal in bazel, but we can't build a valid go path for it. - # TODO: we might be able to ignore this if the content is identical - print("""Duplicate package -Found {} in - {} - {} -""".format(golib.importpath, golib.label, seen_libs[golib.importpath].label)) - # for now we don't fail if we see duplicate packages - # the most common case is the same source from two different workspaces - continue - seen_libs[golib.importpath] = golib - package_files = [] - prefix = "src/" + golib.importpath + "/" - golib_files = golib.srcs - if golib.importpath in archives_runfiles: - golib_files = list(golib.srcs) + as_iterable(archives_runfiles[golib.importpath].files) - for src in golib_files: - outpath = prefix + src.basename - if outpath in seen_paths: - # If we see the same path twice, it's a fatal error - fail("Duplicate path {}".format(outpath)) - seen_paths[outpath] = True - out = go.declare_file(go, path=outpath) - package_files += [out] - outputs += [out] - if ctx.attr.mode == "copy": - ctx.actions.expand_template(template=src, output=out, substitutions={}) - elif ctx.attr.mode == "link": - ctx.actions.run_shell( - command='ln -s $(readlink "$1") "$2"', - arguments=[src.path, out.path], - mnemonic = "GoLn", - inputs=[src], - outputs=[out], - ) - else: - fail("Invalid go path mode '{}'".format(ctx.attr.mode)) - packages += [struct( - golib = golib, - dir = _tag(go, prefix, outputs), - files = package_files, - )] - gopath = _tag(go, "", outputs) return [ DefaultInfo( files = depset(outputs), + runfiles = ctx.runfiles(files = outputs), ), GoPath( - gopath = gopath, - packages = packages, - srcs = outputs, - ) + gopath = out_short_path, + packages = pkg_map.values(), + ), ] -go_path = go_rule( +go_path = rule( _go_path_impl, attrs = { - "deps": attr.label_list(providers = [GoLibrary]), + "deps": attr.label_list(providers = [GoArchive]), + "data": attr.label_list( + allow_files = True, + cfg = "data", + ), "mode": attr.string( default = "copy", values = [ - "link", + "archive", "copy", + "link", ], ), + "_go_path": attr.label( + default = "@io_bazel_rules_go//go/tools/builders:go_path", + executable = True, + cfg = "host", + ), }, ) + +def _get_importpath_pkgpath(archive): + if archive.pathtype != EXPLICIT_PATH: + return "", "" + importpath = archive.importpath + importmap = archive.importmap + if importpath.endswith("_test"): importpath = importpath[:-len("_test")] + if importmap.endswith("_test"): importmap = importmap[:-len("_test")] + parts = importmap.split("/") + if "vendor" not in parts: + # Unusual case not handled by go build. Just return importpath. + return importpath, importpath + elif len(parts) > 2 and archive.label.workspace_root == "external/" + parts[0]: + # Common case for importmap set by Gazelle in external repos. + return importpath, importmap[len(parts[0]):] + else: + # Vendor directory somewhere in the main repo. Leave it alone. + return importpath, importmap + +def _merge_pkg(x, y): + x_srcs = {f.path: None for f in x.srcs} + x_data = {f.path: None for f in x.data} + x.srcs.extend([f for f in y.srcs if f.path not in x_srcs]) + x.data.extend([f for f in y.data if f.path not in x_srcs]) + + diff --git a/go/private/tools/vet.bzl b/go/private/tools/vet.bzl index 9c83a193..08999e4d 100644 --- a/go/private/tools/vet.bzl +++ b/go/private/tools/vet.bzl @@ -39,7 +39,7 @@ Please do not rely on it for production use, but feel free to use it and file is for data in ctx.attr.data: entry = data[GoPath] gopath += [entry.gopath] - packages += [package.dir for package in entry.packages] + packages += [entry.gopath + "/" + package.dir for package in entry.packages] ctx.actions.write(output=script_file, is_executable=True, content=""" export GOPATH="{gopath}" {go} tool vet {packages} @@ -75,4 +75,4 @@ def go_vet_test(name, data, **kwargs): srcs=[script_name], data=data, **kwargs - )
\ No newline at end of file + ) diff --git a/go/providers.rst b/go/providers.rst index 1939539a..3114a314 100644 --- a/go/providers.rst +++ b/go/providers.rst @@ -6,6 +6,7 @@ Go providers .. _go_library: core.rst#go_library .. _go_binary: core.rst#go_binary .. _go_test: core.rst#go_test +.. _go_path: core.rst#go_path .. _cc_library: https://docs.bazel.build/versions/master/be/c-cpp.html#cc_library .. _flatbuffers: http://google.github.io/flatbuffers/ .. _static linking: modes.rst#building-static-binaries @@ -272,3 +273,31 @@ This is used when compiling and linking dependant libraries or binaries. +--------------------------------+-----------------------------------------------------------------+ | The files needed to run anything that includes this library. | +--------------------------------+-----------------------------------------------------------------+ + +GoPath +~~~~~~ + +GoPath is produced by the `go_path`_ rule. It gives a list of packages used to +build the ``go_path`` directory and provides a list of original files for +each package. + ++--------------------------------+-----------------------------------------------------------------+ +| **Name** | **Type** | ++--------------------------------+-----------------------------------------------------------------+ +| :param:`gopath` | :type:`string` | ++--------------------------------+-----------------------------------------------------------------+ +| The short path to the output file or directory. Useful for constructing | +| ``runfiles`` paths. | ++--------------------------------+-----------------------------------------------------------------+ +| :param:`packages` | :type:`list of struct` | ++--------------------------------+-----------------------------------------------------------------+ +| A list of structs representing packages used to build the ``go_path`` | +| directory. Each struct has the following fields: | +| | +| * ``importpath``: the import path of the package. | +| * ``dir``: the subdirectory of the package within the ``go_path``, including | +| the ``src/`` prefix. May different from ``importpath`` due to vendoring. | +| * ``srcs``: list of source ``File``s. | +| * ``data``: list of data ``File``s. | ++--------------------------------+-----------------------------------------------------------------+ + diff --git a/go/tools/builders/BUILD.bazel b/go/tools/builders/BUILD.bazel index 634dda2b..553019c5 100644 --- a/go/tools/builders/BUILD.bazel +++ b/go/tools/builders/BUILD.bazel @@ -69,6 +69,12 @@ go_tool_binary( ) go_tool_binary( + name = "go_path", + srcs = ["go_path.go"], + visibility = ["//visibility:public"], +) + +go_tool_binary( name = "info", srcs = [ "env.go", diff --git a/go/tools/builders/go_path.go b/go/tools/builders/go_path.go new file mode 100644 index 00000000..53261db8 --- /dev/null +++ b/go/tools/builders/go_path.go @@ -0,0 +1,202 @@ +// Copyright 2018 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 ( + "archive/zip" + "encoding/json" + "errors" + "flag" + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" +) + +type mode int + +const ( + invalidMode mode = iota + archiveMode + copyMode + linkMode +) + +func modeFromString(s string) (mode, error) { + switch s { + case "archive": + return archiveMode, nil + case "copy": + return copyMode, nil + case "link": + return linkMode, nil + default: + return invalidMode, fmt.Errorf("invalid mode: %s", s) + } +} + +type manifestEntry struct { + Src, Dst string +} + +func main() { + log.SetFlags(0) + if err := run(os.Args[1:]); err != nil { + log.Fatal(err) + } +} + +func run(args []string) error { + var manifest, out string + flags := flag.NewFlagSet("go_path", flag.ContinueOnError) + flags.StringVar(&manifest, "manifest", "", "name of json file listing files to include") + flags.StringVar(&out, "out", "", "output file or directory") + modeFlag := flags.String("mode", "", "copy, link, or archive") + if err := flags.Parse(args); err != nil { + return err + } + if manifest == "" { + return errors.New("-manifest not set") + } + if out == "" { + return errors.New("-out not set") + } + if *modeFlag == "" { + return errors.New("-mode not set") + } + mode, err := modeFromString(*modeFlag) + if err != nil { + return err + } + + entries, err := readManifest(manifest) + if err != nil { + return err + } + + switch mode { + case archiveMode: + err = archivePath(out, entries) + case copyMode: + err = copyPath(out, entries) + case linkMode: + err = linkPath(out, entries) + } + return err +} + +func readManifest(path string) ([]manifestEntry, error) { + data, err := ioutil.ReadFile(path) + if err != nil { + return nil, fmt.Errorf("error reading manifest: %v", err) + } + var entries []manifestEntry + if err := json.Unmarshal(data, &entries); err != nil { + return nil, fmt.Errorf("error unmarshalling manifest %s: %v", path, err) + } + return entries, nil +} + +func archivePath(out string, manifest []manifestEntry) (err error) { + outFile, err := os.Create(out) + if err != nil { + return err + } + defer func() { + if e := outFile.Close(); err == nil && e != nil { + err = fmt.Errorf("error closing archive %s: %v", out, e) + } + }() + outZip := zip.NewWriter(outFile) + + for _, entry := range manifest { + srcFile, err := os.Open(entry.Src) + if err != nil { + return err + } + w, err := outZip.Create(entry.Dst) + if err != nil { + srcFile.Close() + return err + } + if _, err := io.Copy(w, srcFile); err != nil { + srcFile.Close() + return err + } + if err := srcFile.Close(); err != nil { + return err + } + } + + if err := outZip.Close(); err != nil { + return fmt.Errorf("error constructing archive %s: %v", out, err) + } + return nil +} + +func copyPath(out string, manifest []manifestEntry) error { + if err := os.MkdirAll(out, 0777); err != nil { + return err + } + for _, entry := range manifest { + dst := filepath.Join(out, filepath.FromSlash(entry.Dst)) + if err := os.MkdirAll(filepath.Dir(dst), 0777); err != nil { + return err + } + srcFile, err := os.Open(entry.Src) + if err != nil { + return err + } + dstFile, err := os.Create(dst) + if err != nil { + srcFile.Close() + return err + } + if _, err := io.Copy(dstFile, srcFile); err != nil { + dstFile.Close() + srcFile.Close() + return err + } + srcFile.Close() + if err := dstFile.Close(); err != nil { + return err + } + } + return nil +} + +func linkPath(out string, manifest []manifestEntry) error { + // out directory may already exist and may contain old symlinks. Delete. + if err := os.RemoveAll(out); err != nil { + return err + } + if err := os.MkdirAll(out, 0777); err != nil { + return err + } + for _, entry := range manifest { + dst := filepath.Join(out, filepath.FromSlash(entry.Dst)) + dstDir := filepath.Dir(dst) + src, _ := filepath.Rel(dstDir, entry.Src) + if err := os.MkdirAll(dstDir, 0777); err != nil { + return err + } + if err := os.Symlink(src, dst); err != nil { + return err + } + } + return nil +} diff --git a/tests/README.rst b/tests/README.rst index b39d9432..707d9526 100644 --- a/tests/README.rst +++ b/tests/README.rst @@ -6,10 +6,10 @@ Main test areas .. Child list start -* `Go rules examples <examples/README.rst>`_ * `Core Go rules tests <core/README.rst>`_ * `Integration tests <integration/README.rst>`_ * `Legacy tests <legacy/README.rst>`_ +* `Go rules examples <examples/README.rst>`_ .. Child list end diff --git a/tests/core/README.rst b/tests/core/README.rst index 003a00a6..6e25c9fb 100644 --- a/tests/core/README.rst +++ b/tests/core/README.rst @@ -8,11 +8,13 @@ Contents .. Child list start +* `go_proto_library importmap <go_proto_library_importmap/README.rst>`_ * `Cross compilation <cross/README.rst>`_ * `Basic go_proto_library functionality <go_proto_library/README.rst>`_ -* `Import maps <importmap/README.rst>`_ +* `c-archive / c-shared linkmodes <c_linkmodes/README.rst>`_ * `Basic go_test functionality <go_test/README.rst>`_ -* `go_proto_library importmap <go_proto_library_importmap/README.rst>`_ +* `Import maps <importmap/README.rst>`_ +* `Basic go_path functionality <go_path/README.rst>`_ .. Child list end diff --git a/tests/core/go_path/BUILD.bazel b/tests/core/go_path/BUILD.bazel new file mode 100644 index 00000000..ea2662d3 --- /dev/null +++ b/tests/core/go_path/BUILD.bazel @@ -0,0 +1,33 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_path", "go_test") + +test_suite(name = "go_path") + +[go_path( + name = mode + "_path", + deps = [ + "//tests/core/go_path/cmd/bin", + "//tests/core/go_path/pkg/lib:go_default_library", + "//tests/core/go_path/pkg/lib:embed_test", + "//tests/core/go_path/pkg/lib:go_default_test", + "//tests/core/go_path/pkg/lib:vendored", + ], + data = ["extra.txt"], + mode = mode, + testonly = True, +) for mode in ("archive", "copy", "link")] + +go_test( + name = "go_path_test", + srcs = ["go_path_test.go"], + args = [ + "-archive_path=$(location :archive_path)", + "-copy_path=$(location :copy_path)", + "-link_path=tests/core/go_path/link_path", # can't use location; not a single file + ], + data = [ + ":archive_path", + ":copy_path", + ":link_path", + ], + rundir = ".", +) diff --git a/tests/core/go_path/README.rst b/tests/core/go_path/README.rst new file mode 100644 index 00000000..a9c676a9 --- /dev/null +++ b/tests/core/go_path/README.rst @@ -0,0 +1,12 @@ +Basic go_path functionality +=========================== + +.. _go_path: /go/core.rst#_go_path + +Tests to ensure the basic features of `go_path`_ are working as expected. + +go_path_test +------------ + +Consumes `go_path`_ rules built for the same set of packages in archive, copy, +and link modes and verifies that expected files are present in each mode. diff --git a/tests/core/go_path/cmd/bin/BUILD.bazel b/tests/core/go_path/cmd/bin/BUILD.bazel new file mode 100644 index 00000000..02bf1725 --- /dev/null +++ b/tests/core/go_path/cmd/bin/BUILD.bazel @@ -0,0 +1,14 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_binary", "go_library") + +go_library( + name = "go_default_library", + srcs = ["bin.go"], + importpath = "example.com/repo/cmd/bin", + visibility = ["//visibility:public"], +) + +go_binary( + name = "bin", + embed = [":go_default_library"], + visibility = ["//visibility:public"], +) diff --git a/tests/core/go_path/cmd/bin/bin.go b/tests/core/go_path/cmd/bin/bin.go new file mode 100644 index 00000000..da29a2ca --- /dev/null +++ b/tests/core/go_path/cmd/bin/bin.go @@ -0,0 +1,4 @@ +package main + +func main() { +} diff --git a/tests/core/go_path/extra.txt b/tests/core/go_path/extra.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_path/extra.txt diff --git a/tests/core/go_path/go_path_test.go b/tests/core/go_path/go_path_test.go new file mode 100644 index 00000000..943c1ff2 --- /dev/null +++ b/tests/core/go_path/go_path_test.go @@ -0,0 +1,147 @@ +/* Copyright 2018 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 go_path + +import ( + "archive/zip" + "flag" + "io" + "io/ioutil" + "os" + "path/filepath" + "strings" + "testing" +) + +var copyPath, linkPath, archivePath string + +var files = []string{ + "extra.txt", + "src/", + "-src/example.com/repo/cmd/bin/bin", + "-src/testmain/testmain.go", + "src/example.com/repo/cmd/bin/bin.go", + "src/example.com/repo/pkg/lib/lib.go", + "src/example.com/repo/pkg/lib/embed_test.go", + // TODO: how about go_test without embed? + // "src/example.com/repo/pkg/lib/internal_test.go", + // "src/example.com/repo/pkg/lib/external_test.go", + "-src/example.com/repo/pkg/lib_test/embed_test.go", + "src/example.com/repo/pkg/lib/data.txt", + "src/example.com/repo/vendor/example.com/repo2/vendored.go", +} + +func TestMain(m *testing.M) { + flag.StringVar(©Path, "copy_path", "", "path to copied go_path") + flag.StringVar(&linkPath, "link_path", "", "path to symlinked go_path") + flag.StringVar(&archivePath, "archive_path", "", "path to archive go_path") + flag.Parse() + os.Exit(m.Run()) +} + +func TestCopyPath(t *testing.T) { + if copyPath == "" { + t.Fatal("-copy_path not set") + } + checkPath(t, copyPath, files, os.FileMode(0)) +} + +func TestLinkPath(t *testing.T) { + if linkPath == "" { + t.Fatal("-link_path not set") + } + checkPath(t, linkPath, files, os.ModeSymlink) +} + +func TestArchivePath(t *testing.T) { + if archivePath == "" { + t.Fatal("-archive_path not set") + } + dir, err := ioutil.TempDir(os.Getenv("TEST_TEMPDIR"), "TestArchivePath") + if err != nil { + t.Fatal(err) + } + defer os.RemoveAll(dir) + + z, err := zip.OpenReader(archivePath) + if err != nil { + t.Fatalf("error opening zip: %v", err) + } + defer z.Close() + for _, f := range z.File { + r, err := f.Open() + if err != nil { + t.Fatalf("error reading file %s: %v", f.Name, err) + } + dstPath := filepath.Join(dir, filepath.FromSlash(f.Name)) + if err := os.MkdirAll(filepath.Dir(dstPath), 0777); err != nil { + t.Fatalf("error creating directory %s: %v", filepath.Dir(dstPath), err) + } + w, err := os.Create(dstPath) + if err != nil { + t.Fatalf("error creating file %s: %v", dstPath, err) + } + if _, err := io.Copy(w, r); err != nil { + w.Close() + t.Fatalf("error writing file %s: %v", dstPath, err) + } + if err := w.Close(); err != nil { + t.Fatalf("error closing file %s: %v", dstPath, err) + } + } + + checkPath(t, dir, files, os.FileMode(0)) +} + +// checkPath checks that dir contains a list of files. files is a list of +// slash-separated paths relative to dir. Files that start with "-" should be +// absent. Files that end with "/" should be directories. Other files should +// be of fileType. +func checkPath(t *testing.T, dir string, files []string, fileType os.FileMode) { + for _, f := range files { + wantType := fileType + wantAbsent := false + if strings.HasPrefix(f, "-") { + f = f[1:] + wantAbsent = true + } + if strings.HasSuffix(f, "/") { + wantType = os.ModeDir + } + path := filepath.Join(dir, filepath.FromSlash(f)) + st, err := os.Lstat(path) + if wantAbsent { + if err == nil { + t.Errorf("found %s: should not be present", path) + } else if !os.IsNotExist(err) { + t.Error(err) + } + } else { + if err != nil { + if os.IsNotExist(err) { + t.Errorf("%s is missing", path) + } else { + t.Error(err) + } + continue + } + gotType := st.Mode() & os.ModeType + if gotType != wantType { + t.Errorf("%s: got type %s; want type %s .. %s", path, gotType, wantType, st.Mode()) + } + } + } +} diff --git a/tests/core/go_path/pkg/lib/BUILD.bazel b/tests/core/go_path/pkg/lib/BUILD.bazel new file mode 100644 index 00000000..820ed336 --- /dev/null +++ b/tests/core/go_path/pkg/lib/BUILD.bazel @@ -0,0 +1,40 @@ +load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test") + +go_library( + name = "go_default_library", + srcs = ["lib.go"], + cgo = True, + data = ["data.txt"], + importpath = "example.com/repo/pkg/lib", + visibility = ["//visibility:public"], +) + +go_test( + name = "go_default_test", + srcs = [ + "external_test.go", + "internal_test.go", + ], + visibility = ["//visibility:public"], +) + +go_test( + name = "embed_test", + embed = [":embed_lib"], + visibility = ["//visibility:public"], +) + +go_library( + name = "embed_lib", + srcs = ["embed_test.go"], + importpath = "example.com/repo/pkg/lib", + visibility = ["//visibility:public"], +) + +go_library( + name = "vendored", + srcs = ["vendored.go"], + importpath = "example.com/repo2", + importmap = "example.com/repo/vendor/example.com/repo2", + visibility = ["//visibility:public"], +) diff --git a/tests/core/go_path/pkg/lib/data.txt b/tests/core/go_path/pkg/lib/data.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/tests/core/go_path/pkg/lib/data.txt diff --git a/tests/core/go_path/pkg/lib/embed_test.go b/tests/core/go_path/pkg/lib/embed_test.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/go_path/pkg/lib/embed_test.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/go_path/pkg/lib/external_test.go b/tests/core/go_path/pkg/lib/external_test.go new file mode 100644 index 00000000..df64f213 --- /dev/null +++ b/tests/core/go_path/pkg/lib/external_test.go @@ -0,0 +1 @@ +package lib_test diff --git a/tests/core/go_path/pkg/lib/internal_test.go b/tests/core/go_path/pkg/lib/internal_test.go new file mode 100644 index 00000000..55c21f80 --- /dev/null +++ b/tests/core/go_path/pkg/lib/internal_test.go @@ -0,0 +1 @@ +package lib diff --git a/tests/core/go_path/pkg/lib/lib.go b/tests/core/go_path/pkg/lib/lib.go new file mode 100644 index 00000000..592be3ae --- /dev/null +++ b/tests/core/go_path/pkg/lib/lib.go @@ -0,0 +1,3 @@ +package lib + +import "C" diff --git a/tests/core/go_path/pkg/lib/vendored.go b/tests/core/go_path/pkg/lib/vendored.go new file mode 100644 index 00000000..2355022f --- /dev/null +++ b/tests/core/go_path/pkg/lib/vendored.go @@ -0,0 +1 @@ +package vendored |