Skip to content

Commit

Permalink
feat: tar includes runfiles (#595)
Browse files Browse the repository at this point in the history
* feat: tar includes runfiles

* chore: try to fix red circleci

* fix: tracked down problem

* chore: document tar#srcs supports runfiles

* chore: add comment about logic for trimming manifest suffix

* chore: missed a replacement spot

* chore: give up on the listing test for now
  • Loading branch information
alexeagle authored Oct 9, 2023
1 parent 4bfe557 commit 472bf9b
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 15 deletions.
2 changes: 1 addition & 1 deletion docs/tar.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 7 additions & 6 deletions lib/private/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,13 @@ bzl_library(
deps = [":repo_utils"],
)

bzl_library(
name = "tar",
srcs = ["tar.bzl"],
visibility = ["//lib:__subpackages__"],
deps = ["@aspect_bazel_lib//lib:paths"],
)

bzl_library(
name = "copy_common",
srcs = ["copy_common.bzl"],
Expand Down Expand Up @@ -320,9 +327,3 @@ bzl_library(
srcs = ["strings.bzl"],
visibility = ["//lib:__subpackages__"],
)

bzl_library(
name = "tar",
srcs = ["tar.bzl"],
visibility = ["//lib:__subpackages__"],
)
55 changes: 48 additions & 7 deletions lib/private/tar.bzl
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
"Implementation of tar rule"

load("@aspect_bazel_lib//lib:paths.bzl", "to_rlocation_path")

_tar_attrs = {
"args": attr.string_list(
doc = "Additional flags permitted by BSD tar; see the man page.",
),
"srcs": attr.label_list(
doc = "Files and directories that are placed into the tar",
doc = """\
Files, directories, or other targets whose default outputs are placed into the tar.
If any of the srcs are binaries with runfiles, those are copied into the resulting tar as well.
""",
mandatory = True,
allow_files = True,
),
Expand Down Expand Up @@ -67,6 +74,23 @@ def _add_compress_options(compress, args):
if compress == "zstd":
args.add("--zstd")

def _runfile_path(ctx, file, runfiles_dir):
return "/".join([runfiles_dir, to_rlocation_path(ctx, file)])

def _calculate_runfiles_dir(default_info):
manifest = default_info.files_to_run.runfiles_manifest

# Newer versions of Bazel put the manifest besides the runfiles with the suffix .runfiles_manifest.
# For example, the runfiles directory is named my_binary.runfiles then the manifest is beside the
# runfiles directory and named my_binary.runfiles_manifest
# Older versions of Bazel put the manifest file named MANIFEST in the runfiles directory
# See similar logic:
# https://github.com/aspect-build/rules_js/blob/c50bd3f797c501fb229cf9ab58e0e4fc11464a2f/js/private/bash.bzl#L63
if manifest.short_path.endswith("_manifest") or manifest.short_path.endswith("/MANIFEST"):
# Trim last 9 characters, as that's the length in both cases
return manifest.short_path[:-9]
fail("manifest path {} seems malformed".format(manifest.short_path))

def _tar_impl(ctx):
bsdtar = ctx.toolchains["@aspect_bazel_lib//lib:tar_toolchain_type"]
inputs = ctx.files.srcs[:]
Expand All @@ -89,7 +113,10 @@ def _tar_impl(ctx):

ctx.actions.run(
executable = bsdtar.tarinfo.binary,
inputs = depset(direct = inputs, transitive = [bsdtar.default.files]),
inputs = depset(direct = inputs, transitive = [bsdtar.default.files] + [
src[DefaultInfo].default_runfiles.files
for src in ctx.attr.srcs
]),
outputs = [out],
arguments = [args],
mnemonic = "Tar",
Expand All @@ -99,17 +126,17 @@ def _tar_impl(ctx):

def _default_mtree_line(file):
# Functions passed to map_each cannot take optional arguments.
return _mtree_line(file)
return _mtree_line(file.short_path, file.path, "dir" if file.is_directory else "file")

def _mtree_line(file, uid = "0", gid = "0", time = "1672560000", mode = "0755"):
def _mtree_line(file, content, type, uid = "0", gid = "0", time = "1672560000", mode = "0755"):
return " ".join([
file.short_path,
file,
"uid=" + uid,
"gid=" + gid,
"time=" + time,
"mode=" + mode,
"type=" + ("dir" if file.is_directory else "file"),
"content=" + file.path,
"type=" + type,
"content=" + content,
])

def _mtree_impl(ctx):
Expand All @@ -118,6 +145,20 @@ def _mtree_impl(ctx):
content = ctx.actions.args()
content.set_param_file_format("multiline")
content.add_all(ctx.files.srcs, map_each = _default_mtree_line)

for s in ctx.attr.srcs:
default_info = s[DefaultInfo]
if not default_info.files_to_run.runfiles_manifest:
continue

runfiles_dir = _calculate_runfiles_dir(default_info)
for file in depset(transitive = [s.default_runfiles.files]).to_list():
destination = _runfile_path(ctx, file, runfiles_dir)
content.add("{} uid=0 gid=0 mode=0755 time=1672560000 type=file content={}".format(
destination,
file.path,
))

ctx.actions.write(out, content = content)

return DefaultInfo(files = depset([out]), runfiles = ctx.runfiles([out]))
Expand Down
1 change: 0 additions & 1 deletion lib/private/tar_toolchain.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ BSDTAR_PLATFORMS = {
"@platforms//cpu:x86_64",
],
),
# TODO(alexeagle): download from somewhere
"linux_arm64": struct(
compatible_with = [
"@platforms//os:linux",
Expand Down
36 changes: 36 additions & 0 deletions lib/tests/tar/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
load("@aspect_bazel_lib//lib:tar.bzl", "mtree_spec", "tar")
load("@aspect_bazel_lib//lib:testing.bzl", "assert_archive_contains")
load("@bazel_skylib//:bzl_library.bzl", "bzl_library")
load("@bazel_skylib//rules:diff_test.bzl", "diff_test")
load("@bazel_skylib//rules:write_file.bzl", "write_file")
load(":asserts.bzl", "assert_tar_listing")

Expand Down Expand Up @@ -152,3 +153,38 @@ bzl_library(
"@bazel_skylib//rules:write_file",
],
)

# Case 6: Runfiles
sh_binary(
name = "cat_src_file",
srcs = ["cat_src_file.sh"],
data = ["src_file"],
deps = ["@bazel_tools//tools/bash/runfiles"],
)

tar(
name = "tar_runfiles",
srcs = [":cat_src_file"],
)

genrule(
name = "run_program_with_runfiles",
srcs = [":tar_runfiles"],
outs = ["cat_src_file_output"],
cmd = """\
export DIR=$$(mktemp -d)
$(BSDTAR_BIN) --extract --file $(execpath :tar_runfiles) --directory $$DIR
(
cd $$DIR
./lib/tests/tar/cat_src_file
) > $@
""",
toolchains = ["@bsd_tar_toolchains//:resolved_toolchain"],
)

diff_test(
name = "test_runfiles",
timeout = "short",
file1 = "src_file",
file2 = "cat_src_file_output",
)
1 change: 1 addition & 0 deletions lib/tests/tar/asserts.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ def assert_tar_listing(name, actual, expected):
name = name,
file1 = actual_listing,
file2 = expected_listing,
timeout = "short",
)
15 changes: 15 additions & 0 deletions lib/tests/tar/cat_src_file.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
#!/usr/bin/env bash
# A trivial program that only works when runfiles are resolved at runtime.

# --- begin runfiles.bash initialization v3 ---
# Copy-pasted from the Bazel Bash runfiles library v3.
set -uo pipefail; set +e; f=bazel_tools/tools/bash/runfiles/runfiles.bash
source "${RUNFILES_DIR:-/dev/null}/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "${RUNFILES_MANIFEST_FILE:-/dev/null}" | cut -f2- -d' ')" 2>/dev/null || \
source "$0.runfiles/$f" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
source "$(grep -sm1 "^$f " "$0.exe.runfiles_manifest" | cut -f2- -d' ')" 2>/dev/null || \
{ echo>&2 "ERROR: cannot find $f"; exit 1; }; f=; set -e
# --- end runfiles.bash initialization v3 ---

cat $(rlocation aspect_bazel_lib/lib/tests/tar/src_file)

0 comments on commit 472bf9b

Please sign in to comment.