Skip to content

Commit

Permalink
Switch tar verification to a manifest comparison. (#4458)
Browse files Browse the repository at this point in the history
This removes the install marker-relative path checks, and replaces it
with bidirectional verification: previously, files in the tar file had
to be in install data, but there was no check that files in install data
were all in the tar.
  • Loading branch information
jonmeow authored Oct 30, 2024
1 parent d944347 commit 85f6bf3
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 65 deletions.
14 changes: 7 additions & 7 deletions toolchain/install/BUILD
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

load("@rules_cc//cc:defs.bzl", "cc_binary", "cc_library", "cc_test")
load("//bazel/cc_toolchains:defs.bzl", "cc_env")
load("//bazel/manifest:defs.bzl", "manifest")
load("install_filegroups.bzl", "install_filegroup", "install_symlink", "install_target", "make_install_filegroups")
load("pkg_helpers.bzl", "pkg_naming_variables", "pkg_tar_and_test")
load("run_tool.bzl", "run_tool")
Expand Down Expand Up @@ -145,6 +146,11 @@ make_install_filegroups(
prefix = "prefix_root",
)

manifest(
name = "install_data_manifest.txt",
srcs = [":install_data"],
)

pkg_naming_variables(
name = "packaging_variables",
)
Expand All @@ -157,18 +163,12 @@ pkg_naming_variables(
# `carbon_toolchain_tar_gz_rule`.
pkg_tar_and_test(
srcs = [":pkg_data"],
install_data_manifest = ":install_data_manifest.txt",
name_base = "carbon_toolchain",
package_dir = "carbon_toolchain-$(version)",
package_file_name_base = "carbon_toolchain-$(version)",
package_variables = ":packaging_variables",
stamp = -1, # Allow `--stamp` builds to produce file timestamps.
test_data = [
":install_data",
],
# TODO: This is used to make sure that tar files are in install_data (one
# direction). Replace with a check that the files in install_data and tar
# match (bidirectional).
test_install_marker = "prefix_root/lib/carbon/carbon_install.txt",
)

# Support `bazel run` on specific binaries.
Expand Down
18 changes: 8 additions & 10 deletions toolchain/install/pkg_helpers.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ pkg_naming_variables = rule(
attrs = VERSION_ATTRS,
)

def pkg_tar_and_test(name_base, package_file_name_base, test_data, test_install_marker, **kwargs):
def pkg_tar_and_test(name_base, package_file_name_base, install_data_manifest, **kwargs):
"""Create a `pkg_tar` and a test for both `.tar` and `.tar.gz` extensions.
Args:
Expand All @@ -35,10 +35,8 @@ def pkg_tar_and_test(name_base, package_file_name_base, test_data, test_install_
package_file_name_base:
The base of the `package_file_name` attribute to `pkg_tar`. The file
extensions will be appended after a `.`.
test_data:
The test data to verify the tar file against.
test_install_marker:
The install marker within the test data to locate the installation.
install_data_manifest:
The install data manifest file to compare with.
**kwargs:
Passed to `pkg_tar` for all the rest of its attributes.
"""
Expand All @@ -58,11 +56,11 @@ def pkg_tar_and_test(name_base, package_file_name_base, test_data, test_install_
name = name_base + "_" + target_ext + "_test",
size = "small",
srcs = ["toolchain_tar_test.py"],
data = [":" + tar_target, test_install_marker] + test_data,
args = [
"$(location :{})".format(tar_target),
"$(location {})".format(test_install_marker),
],
data = [":" + tar_target, install_data_manifest],
env = {
"INSTALL_DATA_MANIFEST": "$(location {})".format(install_data_manifest),
"TAR_FILE": "$(location :{})".format(tar_target),
},
main = "toolchain_tar_test.py",
# The compressed tar is slow, exclude building and testing that.
tags = ["manual"] if file_ext == "tar.gz" else [],
Expand Down
82 changes: 34 additions & 48 deletions toolchain/install/toolchain_tar_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,57 +8,43 @@
SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
"""

import argparse
import sys
from pathlib import Path
import os
import re
import tarfile


def main() -> None:
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument(
"tar_file",
type=Path,
help="The tar file to test.",
)
parser.add_argument(
"install_marker",
type=Path,
help="The path of the install marker in a prefix root to test against.",
)
args = parser.parse_args()

# Locate the prefix root from the install marker.
if not args.install_marker.exists():
sys.exit("ERROR: No install marker: " + args.install_marker)
prefix_root_path = args.install_marker.parents[2]

# First check that every file and directory in the tar file exists in our
# prefix root, and build a set of those paths.
installed_paths = set()
with tarfile.open(args.tar_file) as tar:
for tarinfo in tar:
relative_path = Path(*Path(tarinfo.name).parts[1:])
installed_paths.add(relative_path)
if not prefix_root_path.joinpath(relative_path).exists():
sys.exit(
"ERROR: File `{0}` is not in prefix root: `{1}`".format(
tarinfo.name, prefix_root_path
)
)

# If we found an empty tar file, it's always an error.
if len(installed_paths) == 0:
sys.exit("ERROR: Tar file `{0}` was empty.".format(args.tar_file))

# Now check that every file and directory in the prefix root is in that set.
for prefix_path in prefix_root_path.glob("**/*"):
relative_path = prefix_path.relative_to(prefix_root_path)
if relative_path not in installed_paths:
sys.exit(
"ERROR: File `{0}` is not in tar file.".format(relative_path)
import unittest


class ToolchainTarTest(unittest.TestCase):
def test_tar(self) -> None:
install_data_manifest = Path(os.environ["INSTALL_DATA_MANIFEST"])
tar_file = Path(os.environ["TAR_FILE"])

# Gather install data files.
with open(install_data_manifest) as manifest:
# Remove everything up to and including `prefix_root`.
install_files = set(
[
re.sub("^.*/prefix_root/", "", entry.strip())
for entry in manifest.readlines()
]
)
self.assertTrue(install_files, f"`{install_data_manifest}` is empty.")

# Gather tar files.
with tarfile.open(tar_file) as tar:
# Remove the first path component.
tar_files = set(
[
str(Path(*Path(tarinfo.name).parts[1:]))
for tarinfo in tar
if not tarinfo.isdir()
]
)
self.assertTrue(tar_files, f"`{tar_file}` is empty.")

self.assertSetEqual(install_files, tar_files)


if __name__ == "__main__":
main()
unittest.main()

0 comments on commit 85f6bf3

Please sign in to comment.