...

Text file src/github.com/bazelbuild/rules_go/go/private/tools/path.bzl

Documentation: github.com/bazelbuild/rules_go/go/private/tools

     1# Copyright 2014 The Bazel Authors. All rights reserved.
     2#
     3# Licensed under the Apache License, Version 2.0 (the "License");
     4# you may not use this file except in compliance with the License.
     5# You may obtain a copy of the License at
     6#
     7#    http://www.apache.org/licenses/LICENSE-2.0
     8#
     9# Unless required by applicable law or agreed to in writing, software
    10# distributed under the License is distributed on an "AS IS" BASIS,
    11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12# See the License for the specific language governing permissions and
    13# limitations under the License.
    14
    15load(
    16    "//go/private:providers.bzl",
    17    "GoArchive",
    18    "GoPath",
    19    "effective_importpath_pkgpath",
    20    "get_archive",
    21)
    22load(
    23    "//go/private:common.bzl",
    24    "as_iterable",
    25    "as_list",
    26)
    27load(
    28    "@bazel_skylib//lib:paths.bzl",
    29    "paths",
    30)
    31
    32def _go_path_impl(ctx):
    33    # Gather all archives. Note that there may be multiple packages with the same
    34    # importpath (e.g., multiple vendored libraries, internal tests). The same
    35    # package may also appear in different modes.
    36    mode_to_deps = {}
    37    for dep in ctx.attr.deps:
    38        archive = get_archive(dep)
    39        if archive.mode not in mode_to_deps:
    40            mode_to_deps[archive.mode] = []
    41        mode_to_deps[archive.mode].append(archive)
    42    mode_to_archive = {}
    43    for mode, archives in mode_to_deps.items():
    44        direct = [a.data for a in archives]
    45        transitive = []
    46        if ctx.attr.include_transitive:
    47            transitive = [a.transitive for a in archives]
    48        mode_to_archive[mode] = depset(direct = direct, transitive = transitive)
    49
    50    # Collect sources and data files from archives. Merge archives into packages.
    51    pkg_map = {}  # map from package path to structs
    52    for mode, archives in mode_to_archive.items():
    53        for archive in as_iterable(archives):
    54            importpath, pkgpath = effective_importpath_pkgpath(archive)
    55            if importpath == "":
    56                continue  # synthetic archive or inferred location
    57            pkg = struct(
    58                importpath = importpath,
    59                dir = "src/" + pkgpath,
    60                srcs = as_list(archive.orig_srcs),
    61                data = as_list(archive.data_files),
    62                embedsrcs = as_list(archive._embedsrcs),
    63                pkgs = {mode: archive.file},
    64            )
    65            if pkgpath in pkg_map:
    66                _merge_pkg(pkg_map[pkgpath], pkg)
    67            else:
    68                pkg_map[pkgpath] = pkg
    69
    70    # Build a manifest file that includes all files to copy/link/zip.
    71    inputs = []
    72    manifest_entries = []
    73    manifest_entry_map = {}
    74    for pkg in pkg_map.values():
    75        # src_dir is the path to the directory holding the source.
    76        # Paths to embedded sources will be relative to this path.
    77        src_dir = None
    78
    79        for f in pkg.srcs:
    80            src_dir = f.dirname
    81            dst = pkg.dir + "/" + f.basename
    82            _add_manifest_entry(manifest_entries, manifest_entry_map, inputs, f, dst)
    83        for f in pkg.embedsrcs:
    84            if src_dir == None:
    85                fail("cannot relativize {}: src_dir is unset".format(f.path))
    86            embedpath = paths.relativize(f.path, f.root.path)
    87            dst = pkg.dir + "/" + paths.relativize(embedpath.lstrip(ctx.bin_dir.path + "/"), src_dir.lstrip(ctx.bin_dir.path + "/"))
    88            _add_manifest_entry(manifest_entries, manifest_entry_map, inputs, f, dst)
    89    if ctx.attr.include_pkg:
    90        for pkg in pkg_map.values():
    91            for mode, f in pkg.pkgs.items():
    92                # TODO(jayconrod): include other mode attributes, e.g., race.
    93                installsuffix = mode.goos + "_" + mode.goarch
    94                dst = "pkg/" + installsuffix + "/" + pkg.dir[len("src/"):] + ".a"
    95                _add_manifest_entry(manifest_entries, manifest_entry_map, inputs, f, dst)
    96    if ctx.attr.include_data:
    97        for pkg in pkg_map.values():
    98            for f in pkg.data:
    99                parts = f.path.split("/")
   100                if "testdata" in parts:
   101                    i = parts.index("testdata")
   102                    dst = pkg.dir + "/" + "/".join(parts[i:])
   103                else:
   104                    dst = pkg.dir + "/" + f.basename
   105                _add_manifest_entry(manifest_entries, manifest_entry_map, inputs, f, dst)
   106    for f in ctx.files.data:
   107        _add_manifest_entry(
   108            manifest_entries,
   109            manifest_entry_map,
   110            inputs,
   111            f,
   112            f.basename,
   113        )
   114    manifest_file = ctx.actions.declare_file(ctx.label.name + "~manifest")
   115    manifest_entries_json = [e.to_json() for e in manifest_entries]
   116    manifest_content = "[\n  " + ",\n  ".join(manifest_entries_json) + "\n]"
   117    ctx.actions.write(manifest_file, manifest_content)
   118    inputs.append(manifest_file)
   119
   120    # Execute the builder
   121    if ctx.attr.mode == "archive":
   122        out = ctx.actions.declare_file(ctx.label.name + ".zip")
   123        out_path = out.path
   124        out_short_path = out.short_path
   125        outputs = [out]
   126        out_file = out
   127    elif ctx.attr.mode == "copy":
   128        out = ctx.actions.declare_directory(ctx.label.name)
   129        out_path = out.path
   130        out_short_path = out.short_path
   131        outputs = [out]
   132        out_file = out
   133    else:  # link
   134        # Declare individual outputs in link mode. Symlinks can't point outside
   135        # tree artifacts.
   136        outputs = [
   137            ctx.actions.declare_file(ctx.label.name + "/" + e.dst)
   138            for e in manifest_entries
   139        ]
   140        tag = ctx.actions.declare_file(ctx.label.name + "/.tag")
   141        ctx.actions.write(tag, "")
   142        out_path = tag.dirname
   143        out_short_path = tag.short_path.rpartition("/")[0]
   144        out_file = tag
   145    args = ctx.actions.args()
   146    args.add("-manifest", manifest_file)
   147    args.add("-out", out_path)
   148    args.add("-mode", ctx.attr.mode)
   149    ctx.actions.run(
   150        outputs = outputs,
   151        inputs = inputs,
   152        mnemonic = "GoPath",
   153        executable = ctx.executable._go_path,
   154        arguments = [args],
   155    )
   156
   157    return [
   158        DefaultInfo(
   159            files = depset(outputs),
   160            runfiles = ctx.runfiles(files = outputs),
   161        ),
   162        GoPath(
   163            gopath = out_short_path,
   164            gopath_file = out_file,
   165            packages = pkg_map.values(),
   166        ),
   167    ]
   168
   169go_path = rule(
   170    _go_path_impl,
   171    attrs = {
   172        "deps": attr.label_list(
   173            providers = [GoArchive],
   174            doc = """A list of targets that build Go packages. A directory will be generated from
   175            files in these targets and their transitive dependencies. All targets must
   176            provide [GoArchive] ([go_library], [go_binary], [go_test], and similar
   177            rules have this).
   178
   179            Only targets with explicit `importpath` attributes will be included in the
   180            generated directory. Synthetic packages (like the main package produced by
   181            [go_test]) and packages with inferred import paths will not be
   182            included. The values of `importmap` attributes may influence the placement
   183            of packages within the generated directory (for example, in vendor
   184            directories).
   185
   186            The generated directory will contain original source files, including .go,
   187            .s, .h, and .c files compiled by cgo. It will not contain files generated by
   188            tools like cover and cgo, but it will contain generated files passed in
   189            `srcs` attributes like .pb.go files. The generated directory will also
   190            contain runfiles found in `data` attributes.
   191            """,
   192        ),
   193        "data": attr.label_list(
   194            allow_files = True,
   195            doc = """
   196            A list of targets producing data files that will be stored next to the
   197            `src/` directory. Useful for including things like licenses and readmes.
   198            """,
   199        ),
   200        "mode": attr.string(
   201            default = "copy",
   202            values = [
   203                "archive",
   204                "copy",
   205                "link",
   206            ],
   207            doc = """
   208            Determines how the generated directory is provided. May be one of:
   209            <ul>
   210                <li><code>"archive"</code>: The generated directory is packaged as a single .zip file.</li>
   211                <li><code>"copy"</code>: The generated directory is a single tree artifact. Source files
   212                are copied into the tree.</li>
   213                <li><code>"link"</code>: <b>Unmaintained due to correctness issues</b>. Source files
   214                are symlinked into the tree. All of the symlink files are provided as separate output
   215                files.</li>
   216            </ul>
   217
   218            ***Note:*** In <code>"copy"</code> mode, when a <code>GoPath</code> is consumed as a set of input
   219            files or run files, Bazel may provide symbolic links instead of regular files.
   220            Any program that consumes these files should dereference links, e.g., if you
   221            run <code>tar</code>, use the <code>--dereference</code> flag.
   222            """,
   223        ),
   224        "include_data": attr.bool(
   225            default = True,
   226            doc = """
   227            When true, data files referenced by libraries, binaries, and tests will be
   228            included in the output directory. Files listed in the `data` attribute
   229            for this rule will be included regardless of this attribute.
   230            """,
   231        ),
   232        "include_pkg": attr.bool(
   233            default = False,
   234            doc = """
   235            When true, a `pkg` subdirectory containing the compiled libraries will be created in the
   236            generated `GOPATH` containing compiled libraries.
   237            """,
   238        ),
   239        "include_transitive": attr.bool(
   240            default = True,
   241            doc = """
   242            When true, the transitive dependency graph will be included in the generated `GOPATH`. This is
   243            the default behaviour. When false, only the direct dependencies will be included in the
   244            generated `GOPATH`.
   245            """,
   246        ),
   247        "_go_path": attr.label(
   248            default = "//go/tools/builders:go_path",
   249            executable = True,
   250            cfg = "exec",
   251        ),
   252    },
   253    doc = """`go_path` builds a directory structure that can be used with
   254    tools that understand the GOPATH directory layout. This directory structure
   255    can be built by zipping, copying, or linking files.
   256    `go_path` can depend on one or more Go targets (i.e., [go_library], [go_binary], or [go_test]).
   257    It will include packages from those targets, as well as their transitive dependencies.
   258    Packages will be in subdirectories named after their `importpath` or `importmap` attributes under a `src/` directory.
   259    """,
   260)
   261
   262def _merge_pkg(x, y):
   263    x_srcs = {f.path: None for f in x.srcs}
   264    x_data = {f.path: None for f in x.data}
   265    x_embedsrcs = {f.path: None for f in x.embedsrcs}
   266    x.srcs.extend([f for f in y.srcs if f.path not in x_srcs])
   267    x.data.extend([f for f in y.data if f.path not in x_data])
   268    x.embedsrcs.extend([f for f in y.embedsrcs if f.path not in x_embedsrcs])
   269    x.pkgs.update(y.pkgs)
   270
   271def _add_manifest_entry(entries, entry_map, inputs, src, dst):
   272    if dst in entry_map:
   273        if entry_map[dst] != src.path:
   274            fail("{}: references multiple files ({} and {})".format(dst, entry_map[dst], src.path))
   275        return
   276    entries.append(struct(src = src.path, dst = dst))
   277    entry_map[dst] = src.path
   278    inputs.append(src)

View as plain text