Ensure runfiles.{,root_}symlinks makes it into tarballs
Right now we only add files. This is problematic when creating a tarball
of an sh_binary(). Starting with Bazel 9, sh_binary() is no longer
provided by Bazel itself. In that case rules_shell will declare its own
copy of runfiles.bash, putting a symlink at the old location in the
runfiles directory.
diff --git a/pkg/install.bzl b/pkg/install.bzl
index ee3d195..02787ee 100644
--- a/pkg/install.bzl
+++ b/pkg/install.bzl
@@ -75,7 +75,10 @@
my_runfiles = ctx.runfiles(
files = [manifest_file],
- transitive_files = depset(transitive = mapping_context.file_deps),
+ transitive_files = depset(
+ direct = mapping_context.file_deps_direct,
+ transitive = mapping_context.file_deps_transitive,
+ ),
)
return [
diff --git a/pkg/private/pkg_files.bzl b/pkg/private/pkg_files.bzl
index 1f1c902..d766534 100644
--- a/pkg/private/pkg_files.bzl
+++ b/pkg/private/pkg_files.bzl
@@ -73,7 +73,8 @@
doc = """Fields passed to process_* methods.""",
fields = {
"content_map": "in/out The content_map we are building up",
- "file_deps": "in/out list of file Depsets represented in the map",
+ "file_deps_direct": "in/out list of files represented in the map",
+ "file_deps_transitive": "in/out list of file Depsets represented in the map",
"label": "ctx.label",
# Behaviors
@@ -119,7 +120,8 @@
return _MappingContext(
content_map = dict(),
- file_deps = list(),
+ file_deps_direct = list(),
+ file_deps_transitive = list(),
label = label,
allow_duplicates_with_different_content = allow_duplicates_with_different_content,
strip_prefix = strip_prefix,
@@ -265,7 +267,7 @@
# Gather the files for every srcs entry here, even if it is not from
# a pkg_* rule.
if DefaultInfo in src:
- mapping_context.file_deps.append(src[DefaultInfo].files)
+ mapping_context.file_deps_transitive.append(src[DefaultInfo].files)
found_info = False
if PackageFilesInfo in src:
_process_pkg_files(
@@ -431,18 +433,16 @@
if include_runfiles:
runfiles = src[DefaultInfo].default_runfiles
- if runfiles and runfiles.files:
- mapping_context.file_deps.append(runfiles.files)
-
+ if runfiles:
# Computing the runfiles root is subtle. It should be based off of
# the executable, but that is not always obvious. When in doubt,
# the first file of DefaultInfo.files should be the right target.
base_file = the_executable or all_files[0]
base_file_path = dest_path(base_file, data_path, data_path_without_prefix)
- base_path = base_file_path + ".runfiles/" + workspace_name
+ base_root_path = base_file_path + ".runfiles"
+ base_path = base_root_path + "/" + workspace_name
- for rf in runfiles.files.to_list():
- d_path = mapping_context.path_mapper(base_path + "/" + rf.short_path)
+ def add_mapped_file(d_path, rf):
fmode = "0755" if rf == the_executable else mapping_context.default_mode
_check_dest(mapping_context.content_map, d_path, rf, src.label, mapping_context.allow_duplicates_with_different_content)
if hasattr(rf, "is_symlink") and rf.is_symlink: # File.is_symlink is Bazel 8+
@@ -463,11 +463,27 @@
gid = mapping_context.default_gid,
)
+ if runfiles.files:
+ mapping_context.file_deps_transitive.append(runfiles.files)
+ for rf in runfiles.files.to_list():
+ d_path = mapping_context.path_mapper(base_path + "/" + rf.short_path)
+ add_mapped_file(d_path, rf)
+ if runfiles.symlinks:
+ for se in runfiles.symlinks.to_list():
+ mapping_context.file_deps_direct.append(se.target_file)
+ d_path = mapping_context.path_mapper(base_path + "/" + se.path)
+ add_mapped_file(d_path, se.target_file)
+ if runfiles.root_symlinks:
+ for se in runfiles.root_symlinks.to_list():
+ mapping_context.file_deps_direct.append(se.target_file)
+ d_path = mapping_context.path_mapper(base_root_path + "/" + se.path)
+ add_mapped_file(d_path, se.target_file)
+
# if repo_mapping manifest exists (for e.g. with --enable_bzlmod),
# create _repo_mapping under runfiles directory
repo_mapping_manifest = get_repo_mapping_manifest(src)
if repo_mapping_manifest:
- mapping_context.file_deps.append(depset([repo_mapping_manifest]))
+ mapping_context.file_deps_direct.append(repo_mapping_manifest)
# TODO: This should really be a symlink into .runfiles/_repo_mapping
# that also respects remap_paths. For now this is duplicated with the
diff --git a/pkg/private/tar/tar.bzl b/pkg/private/tar/tar.bzl
index ae86834..d3114de 100644
--- a/pkg/private/tar/tar.bzl
+++ b/pkg/private/tar/tar.bzl
@@ -141,7 +141,7 @@
target_files = target[DefaultInfo].files.to_list()
if len(target_files) != 1:
fail("Each input must describe exactly one file.", attr = "files")
- mapping_context.file_deps.append(depset([target_files[0]]))
+ mapping_context.file_deps_direct.append(target_files[0])
add_single_file(
mapping_context,
f_dest_path,
@@ -188,8 +188,8 @@
args.add("--preserve_mtime")
inputs = depset(
- direct = ctx.files.deps + files,
- transitive = mapping_context.file_deps,
+ direct = mapping_context.file_deps_direct + ctx.files.deps + files,
+ transitive = mapping_context.file_deps_transitive,
)
ctx.actions.run(
diff --git a/pkg/private/zip/zip.bzl b/pkg/private/zip/zip.bzl
index 5531bc2..6810c5e 100644
--- a/pkg/private/zip/zip.bzl
+++ b/pkg/private/zip/zip.bzl
@@ -63,7 +63,10 @@
args.set_param_file_format("multiline")
args.use_param_file("@%s")
- all_inputs = depset(direct = inputs, transitive = mapping_context.file_deps)
+ all_inputs = depset(
+ direct = mapping_context.file_deps_direct + inputs,
+ transitive = mapping_context.file_deps_transitive,
+ )
ctx.actions.run(
mnemonic = "PackageZip",
diff --git a/tests/tar/BUILD b/tests/tar/BUILD
index fe45dfa..e109d61 100644
--- a/tests/tar/BUILD
+++ b/tests/tar/BUILD
@@ -15,6 +15,7 @@
"""Tests for pkg_tar."""
load("@rules_python//python:defs.bzl", "py_binary", "py_test")
+load("@rules_shell//shell:sh_binary.bzl", "sh_binary")
# buildifier: disable=bzl-visibility
load("//pkg:mappings.bzl", "pkg_attributes", "pkg_files", "pkg_mkdirs", "pkg_mklink")
@@ -852,3 +853,32 @@
True,
False,
]]
+
+sh_binary(
+ name = "program_with_rules_shell_runfiles",
+ srcs = ["program_with_rules_shell_runfiles.sh"],
+ deps = ["@rules_shell//shell/runfiles"],
+)
+
+pkg_tar(
+ name = "program_with_rules_shell_runfiles_tar",
+ srcs = [":program_with_rules_shell_runfiles"],
+ include_runfiles = True,
+)
+
+# When Bazel does not provide an integrated version of sh_binary(),
+# rules_shell will provide a local copy of the runfiles library. In that
+# case it uses runfiles.root_symlinks to ensure that runfiles.bash is
+# available at its traditional location.
+verify_archive_test(
+ name = "program_with_rules_shell_runfiles_test",
+ must_contain = select({
+ "@platforms//os:windows": [
+ "program_with_rules_shell_runfiles.exe.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash",
+ ],
+ "//conditions:default": [
+ "program_with_rules_shell_runfiles.runfiles/bazel_tools/tools/bash/runfiles/runfiles.bash",
+ ],
+ }),
+ target = ":program_with_rules_shell_runfiles_tar",
+)
diff --git a/tests/tar/program_with_rules_shell_runfiles.sh b/tests/tar/program_with_rules_shell_runfiles.sh
new file mode 100755
index 0000000..7e548ad
--- /dev/null
+++ b/tests/tar/program_with_rules_shell_runfiles.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo Hello