@@ -8,6 +8,7 @@ load("@tar.bzl", "mtree_mutate", "mtree_spec", "tar")
88load (":asserts.bzl" , "assert_tar_listing" , "assert_unused_listing" )
99load (":directory.bzl" , "directory" )
1010load (":node_modules_tree.bzl" , "node_modules_tree" )
11+ load (":cross_linking_tree.bzl" , "venv_binary" , "venv_binary_independent" , "whl_install" , "whl_install_from_file" )
1112load (":runfiles_symlinks.bzl" , "runfiles_symlinks" )
1213
1314# The examples below work with both source files and generated files.
@@ -827,3 +828,133 @@ assert_tar_listing(
827828 "-rw-r--r-- 0 0 0 21 Jan 1 2023 src_file" ,
828829 ],
829830)
831+
832+ #############
833+ # Example 22: compute_unused_inputs with directory-level cross-TreeArtifact symlinks.
834+ #
835+ # node_modules_tree produces TreeArtifacts where dep_symlink_b_to_a and node_modules_a
836+ # are directory-level symlinks to store_a. Both the symlink directories AND store_a
837+ # itself are in e17_binary's runfiles, so the mtree contains content= entries for
838+ # store_a's files directly. The pruner therefore correctly does not prune store_a.
839+ #
840+ # This is a regression test: it verifies that directory-level symlink TreeArtifacts
841+ # in runfiles are handled correctly and nothing is incorrectly pruned.
842+
843+ mtree_spec (
844+ name = "mtree22" ,
845+ srcs = [":e17_binary" ],
846+ )
847+
848+ tar (
849+ name = "tar22" ,
850+ srcs = [":e17_binary" ],
851+ out = "22.tar" ,
852+ compute_unused_inputs = 1 ,
853+ mtree = ":mtree22" ,
854+ )
855+
856+ # store_a backing files must NOT appear in unused_inputs; they are needed to
857+ # resolve dep_symlink_b_to_a → store_a and node_modules_a → store_a at runtime.
858+ assert_unused_listing (
859+ name = "test22_cross_dir_symlink_treeartifact_not_pruned" ,
860+ actual = ":tar22" ,
861+ expected = [],
862+ )
863+
864+ #############
865+ # Example 23: compute_unused_inputs with file-level cross-TreeArtifact symlinks.
866+ #
867+ # cross_linking_tree produces a binary whose runfiles contain:
868+ # - *_whl_install: a real TreeArtifact with module.py
869+ # - *_venv: a TreeArtifact containing lib/module.py as a file-level symlink into whl_install
870+ #
871+ # The mtree content= paths reference files under *_venv/ (e.g. venv/lib/module.py).
872+ # The *_whl_install artifact path (whl_install/module.py) never appears in any mtree
873+ # content=, so the join marks whl_install files as unused. At runtime bsdtar
874+ # dereferences venv/lib/module.py → whl_install/module.py and fails if whl_install
875+ # has been pruned from the sandbox.
876+ #
877+ # This test is expected to FAIL until the pruner is fixed.
878+
879+ whl_install (name = "e23_whl_install" )
880+
881+ venv_binary (
882+ name = "e23_venv_binary" ,
883+ whl_install = ":e23_whl_install" ,
884+ )
885+
886+ # mtree is derived only from e23_venv_binary's runfiles. The venv contains
887+ # file-level symlinks into e23_whl_install, so the mtree content= paths are
888+ # e23_venv_binary_venv/lib/module.py (the symlink path), never the backing
889+ # e23_whl_install/module.py path. The pruner therefore marks e23_whl_install
890+ # files as unused even though bsdtar must dereference the symlinks at
891+ # archive-creation time.
892+
893+ mtree_spec (
894+ name = "mtree23" ,
895+ srcs = [":e23_venv_binary" ],
896+ )
897+
898+ tar (
899+ name = "tar23" ,
900+ srcs = [
901+ ":e23_venv_binary" ,
902+ ":e23_whl_install" , # needed by bsdtar via symlink dereference, not in mtree
903+ ],
904+ out = "23.tar" ,
905+ compute_unused_inputs = 1 ,
906+ mtree = ":mtree23" ,
907+ )
908+
909+ # e23_whl_install files must NOT appear in unused_inputs even though no mtree
910+ # content= directly names them: bsdtar dereferences
911+ # e23_venv_binary_venv/lib/module.py → e23_whl_install/module.py
912+ # at archive-creation time and fails if e23_whl_install is absent from the sandbox.
913+ assert_unused_listing (
914+ name = "test23_file_level_cross_treeartifact_not_pruned" ,
915+ actual = ":tar23" ,
916+ expected = [],
917+ )
918+
919+ #############
920+ # Theory 1 targets: used by the CI cache-invalidation test.
921+ #
922+ # whl_install_from_file reads whl_module.py so the CI step can change the file's
923+ # content between two `bazel build` invocations (using --disk_cache) and verify
924+ # that the archive is rebuilt. If whl_install is incorrectly pruned from the tar
925+ # action's cache key, the archive will be stale after the content change.
926+
927+ whl_install_from_file (
928+ name = "theory1_whl_install" ,
929+ srcs = ["whl_module.py" ],
930+ tags = ["manual" ],
931+ )
932+
933+ # venv_binary_independent: the venv action does NOT depend on whl_install at
934+ # the Bazel level, so changing whl_module.py leaves the venv digest unchanged.
935+ # Only theory1_whl_install changes, which is exactly what we need: if
936+ # whl_install is incorrectly in unused_inputs_list, the tar's reduced cache
937+ # key is identical to the previous build and Bazel serves a stale archive.
938+ venv_binary_independent (
939+ name = "theory1_venv_binary" ,
940+ whl_name = "theory1_whl_install" ,
941+ tags = ["manual" ],
942+ )
943+
944+ mtree_spec (
945+ name = "mtree_theory1" ,
946+ srcs = [":theory1_venv_binary" ],
947+ tags = ["manual" ],
948+ )
949+
950+ tar (
951+ name = "tar_theory1" ,
952+ srcs = [
953+ ":theory1_venv_binary" ,
954+ ":theory1_whl_install" , # needed by bsdtar via symlink dereference, not in mtree
955+ ],
956+ out = "theory1.tar" ,
957+ compute_unused_inputs = 1 ,
958+ mtree = ":mtree_theory1" ,
959+ tags = ["manual" ],
960+ )
0 commit comments