feat: implement path transforms
diff --git a/docs/tar.md b/docs/tar.md index d7f94ba..58d5eb1 100644 --- a/docs/tar.md +++ b/docs/tar.md
@@ -25,11 +25,31 @@ ## mtree_spec <pre> -mtree_spec(<a href="#mtree_spec-name">name</a>, <a href="#mtree_spec-out">out</a>, <a href="#mtree_spec-srcs">srcs</a>) +mtree_spec(<a href="#mtree_spec-name">name</a>, <a href="#mtree_spec-out">out</a>, <a href="#mtree_spec-srcs">srcs</a>, <a href="#mtree_spec-transform">transform</a>) </pre> Create an mtree specification to map a directory hierarchy. See https://man.freebsd.org/cgi/man.cgi?mtree(8) +Supports `$` and `^` RegExp tokens, which may be used together. + +* for stripping prefix, use `^path/to/strip` +* for stripping suffix, use `path/to/strip$` +* for exact match and replace, use `^path/to/strip$` +* for partial match and replace, use `replace_anywhere` + + +An example of stripping package path relative to the workspace + +```starlark +tar( + srcs = ["PKGINFO"], + transform = { + "^{}".format(package_name()): "" + } +) +``` + + **ATTRIBUTES** @@ -38,6 +58,7 @@ | <a id="mtree_spec-name"></a>name | A unique name for this target. | <a href="https://bazel.build/docs/build-ref.html#name">Name</a> | required | | | <a id="mtree_spec-out"></a>out | Resulting specification file to write | <a href="https://bazel.build/docs/build-ref.html#labels">Label</a> | optional | | | <a id="mtree_spec-srcs"></a>srcs | Files that are placed into the tar | <a href="https://bazel.build/docs/build-ref.html#labels">List of labels</a> | required | | +| <a id="mtree_spec-transform"></a>transform | A dict for path transforming. These are applied serially in respect to their orders. | <a href="https://bazel.build/docs/skylark/lib/dict.html">Dictionary: String -> String</a> | optional | {} | <a id="tar_rule"></a>
diff --git a/lib/private/tar.bzl b/lib/private/tar.bzl index 4b301e5..29f0dec 100644 --- a/lib/private/tar.bzl +++ b/lib/private/tar.bzl
@@ -51,6 +51,7 @@ _mtree_attrs = { "srcs": attr.label_list(doc = "Files that are placed into the tar", mandatory = True, allow_files = True), + "transform": attr.string_dict(doc = """A dict for path transforming. These are applied serially in respect to their orders."""), "out": attr.output(doc = "Resulting specification file to write"), } @@ -124,11 +125,7 @@ return DefaultInfo(files = depset([out]), runfiles = ctx.runfiles([out])) -def _default_mtree_line(file): - # Functions passed to map_each cannot take optional arguments. - return _mtree_line(file.short_path, file.path, "dir" if file.is_directory else "file") - -def _mtree_line(file, content, type, uid = "0", gid = "0", time = "1672560000", mode = "0755"): +def _mtree_line(file, content, type, uid = "0", gid = "0", time = "1672560000.000000", mode = "0755"): return " ".join([ file, "uid=" + uid, @@ -139,12 +136,32 @@ "content=" + content, ]) +def _transform(path, transforms): + for (match, replace) in transforms.items(): + # full match + if match.startswith("^") and match.endswith("$"): + if match.removeprefix("^").removesuffix("$") == path: + path = replace + elif match.startswith("^"): + if path.startswith(match.removeprefix("^")): + path = "".join([replace, path.removeprefix(match.removeprefix("^"))]) + elif match.endswith("$"): + if path.endswith(match.removesuffix("$")): + path = "".join([path.removesuffix(match.removesuffix("$")), replace]) + else: + path = path.replace(match, replace) + + return path + def _mtree_impl(ctx): out = ctx.outputs.out or ctx.actions.declare_file(ctx.attr.name + ".spec") 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.files.srcs: + path = _transform(s.short_path, ctx.attr.transform) + content.add(_mtree_line(path, s.path, "dir" if s.is_directory else "file")) for s in ctx.attr.srcs: default_info = s[DefaultInfo] @@ -153,7 +170,7 @@ 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) + destination = _transform(_runfile_path(ctx, file, runfiles_dir), ctx.attr.transform) content.add(_mtree_line(destination, file.path, "file")) ctx.actions.write(out, content = content)
diff --git a/lib/tar.bzl b/lib/tar.bzl index 172c07e..98d3ec6 100644 --- a/lib/tar.bzl +++ b/lib/tar.bzl
@@ -23,7 +23,27 @@ load("//lib/private:tar.bzl", "tar_lib", _tar = "tar") mtree_spec = rule( - doc = "Create an mtree specification to map a directory hierarchy. See https://man.freebsd.org/cgi/man.cgi?mtree(8)", + doc = """Create an mtree specification to map a directory hierarchy. See https://man.freebsd.org/cgi/man.cgi?mtree(8) + +Supports `$` and `^` RegExp tokens, which may be used together. + +* for stripping prefix, use `^path/to/strip` +* for stripping suffix, use `path/to/strip$` +* for exact match and replace, use `^path/to/strip$` +* for partial match and replace, use `replace_anywhere` + + +An example of stripping package path relative to the workspace + +```starlark +tar( + srcs = ["PKGINFO"], + transform = { + "^{}".format(package_name()): "" + } +) +``` +""", implementation = tar_lib.mtree_implementation, attrs = tar_lib.mtree_attrs, ) @@ -69,8 +89,11 @@ name = mtree_target, srcs = kwargs["srcs"], out = "{}.txt".format(mtree_target), + transform = kwargs.pop("transform", {}), ) elif types.is_list(mtree): + if kwargs.pop("transform", None): + fail("transform shall be provided only when mtree=auto") write_file( name = mtree_target, out = "{}.txt".format(mtree_target),
diff --git a/lib/tests/tar/BUILD.bazel b/lib/tests/tar/BUILD.bazel index 2851bae..c781111 100644 --- a/lib/tests/tar/BUILD.bazel +++ b/lib/tests/tar/BUILD.bazel
@@ -199,3 +199,30 @@ file1 = "src_file", file2 = "cat_src_file_output", ) + +tar( + name = "transform", + srcs = [ + "pkg/debian-binary", + "src_file", + ], + out = "transform.tar", + transform = { + "^lib/tests/": "", + "src_file$": "PKGINFO", + "^tar": "package", + # will flatten pkg/debian-binary to be inside package + "pkg/debian-binary": "debian-binary", + }, +) + +assert_tar_listing( + name = "test_transform", + actual = "transform", + expected = [ + # + # TODO: https://github.com/aspect-build/bazel-lib/issues/625 + "-rwxr-xr-x 0 0 0 0 Jan 1 2023 package/debian-binary", + "-rwxr-xr-x 0 0 0 21 Jan 1 2023 package/PKGINFO", + ], +)
diff --git a/lib/tests/tar/pkg/debian-binary b/lib/tests/tar/pkg/debian-binary new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/lib/tests/tar/pkg/debian-binary