diff --git a/java/google/registry/builddefs/zip_file.bzl b/java/google/registry/builddefs/zip_file.bzl index 53f9e7e6a..ee96c28f0 100644 --- a/java/google/registry/builddefs/zip_file.bzl +++ b/java/google/registry/builddefs/zip_file.bzl @@ -12,7 +12,103 @@ # See the License for the specific language governing permissions and # limitations under the License. +"""Zip file creator that allows arbitrary path renaming. +This rule takes two main inputs: a bunch of filesets and a dictionary of +hard-coded source to dest mappings. It then applies those mappings to the input +file paths, to create a zip file with the same name as the rule. + +The following preconditions must be met: + +- Sources and destinations can't begin or end with slash. +- Every file must be matched by a mapping. +- Every mapping must match something. + +The source can either be an exact match or a prefix. + +- If a match is exact, the destination replaces the entire path. If the + destination path is empty, then the path remains the same. + +- If the match is a prefix, then the destination replaces the source prefix in + the path. If the destination is empty, then the source prefix is removed. + +- If source is an empty string, it matches everything. In this case, + destination becomes the path prefix. + +Prefixes are matched with component granularity, not characters. Mappings with +more components take precedence. Mappings with equal components are sorted +asciibetically. + +Mappings apply to the "short path" of a file, which is relative to the root of +the repository, not the current directory. Short paths do not take into +consideration bazel-foo/ output directories. Furthermore, if a file is located +in an external repository, then its short path will begin with external/foo/. + +The deps attribute allows zip_file() rules to depend on other zip_file() rules. +In such cases, the contents of directly dependent zip files are unzipped and +then re-zipped. Mappings specified by the current rule do not apply to the +files extracted from dependent zips. However those files can be overridden. + +The simplest example of this rule, which simply zips up short paths, is as +follows: + + # //my/package/BUILD + zip_file( + name = "doodle", + srcs = ["hello.txt"], + mappings = {"": ""}, + ) + +The rule above would create a zip file name //my/package/doodle.zip which would +contain a single file named "my/package/hello.txt". + +If we wanted to strip the package path, we could do the following: + + # //my/package/BUILD + zip_file( + name = "doodle", + srcs = ["hello.txt"], + mappings = {"my/package": ""}, + ) + +In this case, doodle.zip would contain a single file: "hello.txt". + +If we wanted to rename hello.txt, we could do the following: + + # //my/package/BUILD + zip_file( + name = "doodle", + srcs = ["hello.txt"], + mappings = {"my/package/hello.txt": "my/package/world.txt"}, + ) + +A zip file can be assembled across many rules. For example: + + # //webapp/html/BUILD + zip_file( + name = "assets", + srcs = glob(["*.html"]), + mappings = {"webapp/html": ""}, + ) + + # //webapp/js/BUILD + zip_file( + name = "assets", + srcs = glob(["*.js"]), + mappings = {"webapp/js": "assets/js"}, + ) + + # //webapp/BUILD + zip_file( + name = "war", + deps = [ + "//webapp/html:assets", + "//webapp/js:assets", + ], + mappings = {"webapp/html": ""}, + ) + +""" load('//java/google/registry/builddefs:defs.bzl', 'ZIPPER', 'runpath') @@ -24,6 +120,7 @@ def _impl(ctx): fail('mappings should not begin or end with slash') mapped = _map_sources(ctx.files.srcs, ctx.attr.mappings) cmd = [ + '#!/bin/sh', 'set -e', 'repo="$(pwd)"', 'zipper="${repo}/%s"' % ctx.file._zipper.path, @@ -45,13 +142,15 @@ def _impl(ctx): 'cd "${repo}"', 'rm -rf "${tmp}"', ] + script = ctx.new_file(ctx.configuration.bin_dir, '%s.sh' % ctx.label.name) + ctx.file_action(output=script, content='\n'.join(cmd), executable=True) inputs = [ctx.file._zipper] inputs += [dep.zip_file for dep in ctx.attr.deps] inputs += ctx.files.srcs ctx.action( inputs=inputs, outputs=[ctx.outputs.out], - command='\n'.join(cmd), + executable=script, mnemonic='zip', progress_message='Creating zip with %d inputs %s' % ( len(inputs), ctx.label))