2023-10-19 22:57:48 +00:00
|
|
|
load("@aspect_bazel_lib//lib:copy_directory.bzl", "copy_directory")
|
2023-10-10 21:13:17 +00:00
|
|
|
load("@aspect_bazel_lib//lib:diff_test.bzl", "diff_test")
|
2024-05-01 19:36:39 +00:00
|
|
|
load("@aspect_bazel_lib//lib:tar.bzl", "mtree_mutate", "mtree_spec", "tar")
|
2023-10-03 20:00:58 +00:00
|
|
|
load("@aspect_bazel_lib//lib:testing.bzl", "assert_archive_contains")
|
|
|
|
load("@bazel_skylib//rules:write_file.bzl", "write_file")
|
|
|
|
load(":asserts.bzl", "assert_tar_listing")
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
# The examples below work with both source files and generated files.
|
|
|
|
# Here we generate a file to use in the examples.
|
2023-10-03 20:00:58 +00:00
|
|
|
write_file(
|
|
|
|
name = "fixture1",
|
2024-08-05 18:18:57 +00:00
|
|
|
out = "generated.txt",
|
2023-10-03 20:00:58 +00:00
|
|
|
content = ["hello a"],
|
|
|
|
)
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 1: Show that you can run any `tar` command you like, using a genrule.
|
|
|
|
# This is advanced, atypical usage where you need such a level of control and don't want to use the `tar` rule.
|
2023-10-03 20:00:58 +00:00
|
|
|
genrule(
|
|
|
|
name = "tar_genrule",
|
|
|
|
srcs = [
|
|
|
|
":fixture1",
|
|
|
|
"src_file",
|
|
|
|
],
|
|
|
|
outs = ["1.tar"],
|
|
|
|
cmd = "$(BSDTAR_BIN) --create --dereference --file $@ -s '#$(BINDIR)##' $(execpath :fixture1) $(execpath src_file)",
|
2023-10-10 21:13:17 +00:00
|
|
|
target_compatible_with = select({
|
|
|
|
# bsdtar.exe: -s is not supported by this version of bsdtar
|
|
|
|
"@platforms//os:windows": ["@platforms//:incompatible"],
|
|
|
|
"//conditions:default": [],
|
|
|
|
}),
|
2023-10-03 20:00:58 +00:00
|
|
|
toolchains = ["@bsd_tar_toolchains//:resolved_toolchain"],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_archive_contains(
|
|
|
|
name = "test_genrule",
|
|
|
|
archive = "1.tar",
|
|
|
|
expected = [
|
2024-08-05 18:18:57 +00:00
|
|
|
"lib/tests/tar/generated.txt",
|
2023-10-03 20:00:58 +00:00
|
|
|
"lib/tests/tar/src_file",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 2: exact control of the resulting tar file, using a custom specification in the "mtree" format.
|
2023-10-03 20:00:58 +00:00
|
|
|
# Copied from the output of `man tar`:
|
2024-08-05 18:18:57 +00:00
|
|
|
# An input file in mtree(5) format can be used to create an output
|
|
|
|
# archive with arbitrary ownership, permissions, or names that differ
|
2023-10-03 20:00:58 +00:00
|
|
|
# from existing data on disk:
|
|
|
|
# $ cat input.mtree
|
|
|
|
# #mtree
|
|
|
|
# usr/bin uid=0 gid=0 mode=0755 type=dir
|
|
|
|
# usr/bin/ls uid=0 gid=0 mode=0755 type=file content=myls
|
|
|
|
# $ tar -cvf output.tar @input.mtree
|
|
|
|
tar(
|
|
|
|
name = "tar_custom_mtree",
|
|
|
|
srcs = ["src_file"],
|
|
|
|
mtree = [
|
|
|
|
"usr/bin uid=0 gid=0 mode=0755 time=1672560000 type=dir",
|
|
|
|
"usr/bin/ls uid=0 gid=0 mode=0755 time=1672560000 type=file content={}/src_file".format(package_name()),
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_custom_mtree",
|
|
|
|
actual = "tar_custom_mtree",
|
|
|
|
expected = [
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 usr/bin/",
|
|
|
|
"-rwxr-xr-x 0 0 0 21 Jan 1 2023 usr/bin/ls",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 3: compression.
|
|
|
|
# This uses gzip, see the `compress` attribute documentation for other legal values.
|
2023-10-03 20:00:58 +00:00
|
|
|
tar(
|
|
|
|
name = "tar_compress",
|
2024-08-05 18:18:57 +00:00
|
|
|
srcs = ["generated.txt"],
|
2023-10-03 20:00:58 +00:00
|
|
|
out = "3.tgz",
|
|
|
|
compress = "gzip",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_archive_contains(
|
|
|
|
name = "test_compress",
|
|
|
|
archive = "3.tgz",
|
2024-08-05 18:18:57 +00:00
|
|
|
expected = ["lib/tests/tar/generated.txt"],
|
2023-10-03 20:00:58 +00:00
|
|
|
type = "tar",
|
|
|
|
)
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 4: you can pass arbitrary command-line flags to the bsdtar executable.
|
2023-10-03 20:00:58 +00:00
|
|
|
write_file(
|
|
|
|
name = "fixture4",
|
|
|
|
out = ".git",
|
|
|
|
content = ["it's a folder"],
|
|
|
|
)
|
|
|
|
|
|
|
|
tar(
|
|
|
|
name = "tar_flags",
|
|
|
|
srcs = [
|
|
|
|
".git",
|
|
|
|
"src_file",
|
2024-08-05 18:18:57 +00:00
|
|
|
":fixture1",
|
2023-10-03 20:00:58 +00:00
|
|
|
],
|
|
|
|
out = "4.tar",
|
|
|
|
# Due to this argument, .git should not appear in the resulting tar
|
|
|
|
args = ["--exclude-vcs"],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_flags",
|
|
|
|
actual = "tar_flags",
|
|
|
|
expected = [
|
2023-10-19 22:57:48 +00:00
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/",
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/",
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/",
|
2023-10-03 20:00:58 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 21 Jan 1 2023 lib/tests/tar/src_file",
|
2024-08-05 18:18:57 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 7 Jan 1 2023 lib/tests/tar/generated.txt",
|
2023-10-03 20:00:58 +00:00
|
|
|
],
|
|
|
|
)
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 5: features like `strip_prefix` are supported by `mtree_mutate`.
|
|
|
|
# This lets you port code that used the `pkg_tar` rule from bazelbuild/rules_pkg.
|
2023-10-03 20:00:58 +00:00
|
|
|
_SRCS5 = [
|
|
|
|
":fixture1",
|
|
|
|
"src_file",
|
|
|
|
]
|
|
|
|
|
|
|
|
mtree_spec(
|
|
|
|
name = "mtree5",
|
|
|
|
srcs = _SRCS5,
|
|
|
|
)
|
|
|
|
|
2024-05-01 19:36:39 +00:00
|
|
|
mtree_mutate(
|
2023-10-03 20:00:58 +00:00
|
|
|
name = "strip_prefix",
|
2024-05-01 19:36:39 +00:00
|
|
|
mtree = "mtree5",
|
|
|
|
strip_prefix = package_name(),
|
2023-10-03 20:00:58 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
tar(
|
|
|
|
name = "tar_strip_prefix",
|
|
|
|
srcs = _SRCS5,
|
|
|
|
out = "5.tar",
|
|
|
|
mtree = "strip_prefix",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_strip_prefix",
|
|
|
|
actual = "tar_strip_prefix",
|
|
|
|
expected = [
|
2024-08-05 18:18:57 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 7 Jan 1 2023 generated.txt",
|
2023-10-03 20:00:58 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 21 Jan 1 2023 src_file",
|
|
|
|
],
|
|
|
|
)
|
2023-10-05 21:22:38 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 6: When archiving a binary, the "runfiles" are included.
|
2023-10-09 22:57:52 +00:00
|
|
|
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"],
|
2023-10-19 22:57:48 +00:00
|
|
|
out = "6.tar",
|
2023-10-09 22:57:52 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
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
|
|
|
|
) > $@
|
|
|
|
""",
|
2023-10-10 21:13:17 +00:00
|
|
|
target_compatible_with = select({
|
|
|
|
# requires runfiles tree, otherwise get
|
|
|
|
# ERROR: cannot find bazel_tools/tools/bash/runfiles/runfiles.bash
|
|
|
|
"@platforms//os:windows": ["@platforms//:incompatible"],
|
2023-11-15 23:07:03 +00:00
|
|
|
# TODO(sahin): incompatible with bzlmod
|
|
|
|
"@aspect_bazel_lib//lib:bzlmod": ["@platforms//:incompatible"],
|
2023-10-10 21:13:17 +00:00
|
|
|
"//conditions:default": [],
|
|
|
|
}),
|
2023-10-09 22:57:52 +00:00
|
|
|
toolchains = ["@bsd_tar_toolchains//:resolved_toolchain"],
|
|
|
|
)
|
|
|
|
|
|
|
|
diff_test(
|
|
|
|
name = "test_runfiles",
|
|
|
|
timeout = "short",
|
|
|
|
file1 = "src_file",
|
|
|
|
file2 = "cat_src_file_output",
|
|
|
|
)
|
2023-10-19 22:57:48 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 7: You can archive directories,
|
|
|
|
# both those in the source tree and those produced by rules that understand "tree artifacts".
|
2023-10-19 22:57:48 +00:00
|
|
|
copy_directory(
|
|
|
|
name = "treeartifact",
|
|
|
|
src = "srcdir",
|
|
|
|
out = "treeartifact",
|
|
|
|
)
|
|
|
|
|
|
|
|
tar(
|
|
|
|
name = "dirs",
|
2023-12-13 23:21:02 +00:00
|
|
|
# Note, testonly should be propagated, proven by
|
2024-08-05 18:18:57 +00:00
|
|
|
# % bazel query --output=label_kind 'attr("testonly", 1, lib/tests/tar:all)'
|
2023-12-13 23:21:02 +00:00
|
|
|
# mtree_spec rule //lib/tests/tar:_dirs.mtree
|
|
|
|
# tar rule //lib/tests/tar:dirs
|
|
|
|
testonly = True,
|
2023-10-19 22:57:48 +00:00
|
|
|
srcs = glob(["srcdir/**"]) + [
|
|
|
|
"treeartifact",
|
|
|
|
],
|
|
|
|
out = "7.tar",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_dirs",
|
|
|
|
actual = "dirs",
|
|
|
|
expected = [
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/",
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/",
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/",
|
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/srcdir/",
|
|
|
|
"-rwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/srcdir/info",
|
|
|
|
"-rwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/srcdir/pkg",
|
2024-05-08 00:52:35 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 1 Jan 1 2023 lib/tests/tar/srcdir/space in name.txt",
|
2023-10-19 22:57:48 +00:00
|
|
|
"drwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/treeartifact/",
|
|
|
|
"-rwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/treeartifact/info",
|
|
|
|
"-rwxr-xr-x 0 0 0 0 Jan 1 2023 lib/tests/tar/treeartifact/pkg",
|
2024-05-08 00:52:35 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 1 Jan 1 2023 lib/tests/tar/treeartifact/space in name.txt",
|
2023-10-19 22:57:48 +00:00
|
|
|
],
|
|
|
|
)
|
2023-12-13 23:03:32 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 8: arbitrary mutations of the mtree spec can be performed.
|
|
|
|
# Typically use the `mtree_mutate` rule which supports specific mutations using a more ergonomic API,
|
|
|
|
# see Example 12 below.
|
2023-12-13 23:03:32 +00:00
|
|
|
_SRCS8 = [
|
|
|
|
":fixture1",
|
|
|
|
"src_file",
|
|
|
|
]
|
|
|
|
|
|
|
|
mtree_spec(
|
|
|
|
name = "mtree8",
|
|
|
|
srcs = _SRCS8,
|
|
|
|
)
|
|
|
|
|
|
|
|
# This is a very simple way to mutate the mtree specification, just using regex.
|
|
|
|
# See docs on tar about future directions for mtree mutation
|
|
|
|
genrule(
|
|
|
|
name = "change_owner",
|
|
|
|
srcs = ["mtree8"],
|
|
|
|
outs = ["mtree8.mutated"],
|
|
|
|
# Modify uid and gid, e.g.
|
|
|
|
# lib/tests/tar/a uid=0 gid=0 time=1672560000 mode=0755 type=file content=bazel-out/darwin_arm64-opt/bin/lib/tests/tar/a
|
|
|
|
# ->
|
|
|
|
# lib/tests/tar/a uid=1000 gid=500 time=1672560000 mode=0755 type=file content=bazel-out/darwin_arm64-opt/bin/lib/tests/tar/a
|
|
|
|
cmd = "sed 's/uid=0/uid=1000/;s/gid=0/gid=500/' <$< >$@",
|
|
|
|
)
|
|
|
|
|
|
|
|
tar(
|
|
|
|
name = "tar_change_owner",
|
|
|
|
srcs = _SRCS8,
|
|
|
|
out = "8.tar",
|
|
|
|
mtree = "change_owner",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_change_owner",
|
|
|
|
actual = "tar_change_owner",
|
|
|
|
expected = [
|
|
|
|
"drwxr-xr-x 0 1000 500 0 Jan 1 2023 lib/",
|
|
|
|
"drwxr-xr-x 0 1000 500 0 Jan 1 2023 lib/tests/",
|
|
|
|
"drwxr-xr-x 0 1000 500 0 Jan 1 2023 lib/tests/tar/",
|
2024-08-05 18:18:57 +00:00
|
|
|
"-rwxr-xr-x 0 1000 500 7 Jan 1 2023 lib/tests/tar/generated.txt",
|
2023-12-13 23:03:32 +00:00
|
|
|
"-rwxr-xr-x 0 1000 500 21 Jan 1 2023 lib/tests/tar/src_file",
|
|
|
|
],
|
|
|
|
)
|
2023-12-20 19:22:42 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 9: Files from a different repository (see #697)
|
2023-12-20 19:22:42 +00:00
|
|
|
# Note: This test uses an exported file from skylib, so we do not need to create
|
|
|
|
# an additional workspace just for this test.
|
|
|
|
tar(
|
|
|
|
name = "tar_different_repo",
|
|
|
|
srcs = ["@bazel_skylib//:LICENSE"],
|
|
|
|
out = "9.tar",
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_archive_contains(
|
|
|
|
name = "test_different_repo",
|
|
|
|
archive = "9.tar",
|
|
|
|
expected = [
|
|
|
|
"LICENSE",
|
|
|
|
],
|
|
|
|
)
|
2024-03-01 22:51:47 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 10: Similar to Example 9, you can reference generated files in the `mtree` attribute as well.
|
2024-03-01 22:51:47 +00:00
|
|
|
tar(
|
|
|
|
name = "tar_location_expansion",
|
|
|
|
srcs = ["@bazel_skylib//:LICENSE"],
|
|
|
|
out = "10.tar",
|
|
|
|
mtree = [
|
2024-08-05 18:18:57 +00:00
|
|
|
"license uid=0 gid=0 time=1672560000 mode=0755 type=file content=$(location @bazel_skylib//:LICENSE)",
|
2024-03-01 22:51:47 +00:00
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_tar_location_expansion",
|
|
|
|
actual = "tar_location_expansion",
|
|
|
|
expected = [
|
2024-08-05 18:18:57 +00:00
|
|
|
"-rwxr-xr-x 0 0 0 11358 Jan 1 2023 license",
|
2024-03-01 22:51:47 +00:00
|
|
|
],
|
|
|
|
)
|
2024-03-08 18:47:38 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 11: You can create a tar without srcs, only empty directories
|
2024-03-08 18:47:38 +00:00
|
|
|
tar(
|
|
|
|
name = "create_tmp",
|
|
|
|
mtree = ["./tmp time=1501783453.0 mode=1777 gid=0 uid=0 type=dir"],
|
|
|
|
)
|
|
|
|
|
|
|
|
assert_tar_listing(
|
|
|
|
name = "test_create_create_tmp",
|
|
|
|
actual = "create_tmp",
|
|
|
|
expected = [
|
|
|
|
"drwxrwxrwt 0 0 0 0 Aug 3 2017 ./tmp/",
|
|
|
|
],
|
|
|
|
)
|
2024-05-01 19:36:39 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 12: arbitrary mtree modifications
|
2024-05-01 19:36:39 +00:00
|
|
|
mtree_mutate(
|
|
|
|
name = "modified1",
|
|
|
|
mtree = "source-casync.mtree",
|
2024-07-02 10:29:24 +00:00
|
|
|
package_dir = "test",
|
2024-07-19 00:54:51 +00:00
|
|
|
strip_prefix = "xattr",
|
2024-05-01 19:36:39 +00:00
|
|
|
)
|
|
|
|
|
|
|
|
diff_test(
|
|
|
|
name = "test1",
|
|
|
|
file1 = "modified1.mtree",
|
|
|
|
file2 = "expected1.mtree",
|
|
|
|
)
|
|
|
|
|
|
|
|
mtree_mutate(
|
|
|
|
name = "modified2",
|
|
|
|
mtime = 946684740, # 1999-12-31, 23:59
|
|
|
|
mtree = "source-casync.mtree",
|
|
|
|
owner = "123",
|
|
|
|
ownername = "fred",
|
|
|
|
)
|
|
|
|
|
|
|
|
diff_test(
|
|
|
|
name = "test2",
|
|
|
|
file1 = "modified2.mtree",
|
|
|
|
file2 = "expected2.mtree",
|
|
|
|
)
|
2024-07-02 16:27:06 +00:00
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 13: Ensure that multiple entries at the root directory are handled correctly (bug #851)
|
2024-07-02 16:27:06 +00:00
|
|
|
# NOTE: The mtree_spec part of this test is placed at the root BUILD.bazel because
|
|
|
|
# that's the only way to ensure that the mtree_spec generates single-component
|
|
|
|
# entries (which would trigger the bug).
|
|
|
|
exports_files(["expected13.mtree"])
|
|
|
|
|
2024-08-05 18:18:57 +00:00
|
|
|
#############
|
|
|
|
# Example 14: Ensure mtree_mutate correctly handles prefix stripping for top-level directories (bug #851)
|
2024-07-02 16:27:06 +00:00
|
|
|
write_file(
|
|
|
|
name = "test14_main",
|
|
|
|
out = "14project/__main__.py",
|
|
|
|
content = ["__main__.py"],
|
|
|
|
)
|
|
|
|
|
|
|
|
write_file(
|
|
|
|
name = "test14_bin",
|
|
|
|
out = "14project_bin",
|
|
|
|
content = ["project_bin"],
|
|
|
|
)
|
|
|
|
|
|
|
|
mtree_spec(
|
|
|
|
name = "mtree14",
|
|
|
|
srcs = [
|
|
|
|
":test14_bin",
|
|
|
|
":test14_main",
|
|
|
|
],
|
|
|
|
)
|
|
|
|
|
|
|
|
mtree_mutate(
|
|
|
|
name = "strip_prefix14_unsorted",
|
|
|
|
mtree = "mtree14",
|
|
|
|
strip_prefix = "lib/tests/tar",
|
|
|
|
)
|
|
|
|
|
|
|
|
# NOTE: On some systems, the mtree_spec output can have a different order.
|
|
|
|
# To make the test less brittle, we sort the mtree output and replace the BINDIR with a constant placeholder
|
|
|
|
genrule(
|
|
|
|
name = "strip_prefix14",
|
|
|
|
srcs = [":strip_prefix14_unsorted"],
|
|
|
|
outs = ["actual14.mtree"],
|
|
|
|
cmd = "sort $< | sed 's#$(BINDIR)#{BINDIR}#' >$@",
|
|
|
|
)
|
|
|
|
|
|
|
|
diff_test(
|
|
|
|
name = "test14",
|
|
|
|
file1 = ":strip_prefix14",
|
|
|
|
file2 = "expected14.mtree",
|
|
|
|
)
|