Create a helper rule for selecting a file from outputs of another rul… (#233)

* Create a helper rule for selecting a file from outputs of another rule or a filegroup by subpath

* Add tests

* Address code review comments

* + formatting

Co-authored-by: c-parsons <cparsons@google.com>
diff --git a/rules/select_file.bzl b/rules/select_file.bzl
new file mode 100644
index 0000000..0ae08fa
--- /dev/null
+++ b/rules/select_file.bzl
@@ -0,0 +1,40 @@
+"""
+select_file() build rule implementation.
+
+Selects a single file from the outputs of some target by given relative path.
+"""
+
+def _impl(ctx):
+    if ctx.attr.subpath and len(ctx.attr.subpath) == 0:
+        fail("Subpath can not be empty.")
+
+    out = None
+    canonical = ctx.attr.subpath.replace("\\", "/")
+    for file_ in ctx.attr.srcs.files.to_list():
+        if file_.path.replace("\\", "/").endswith(canonical):
+            out = file_
+            break
+    if not out:
+        files_str = ",\n".join([
+            str(f.path)
+            for f in ctx.attr.srcs.files.to_list()
+        ])
+        fail("Can not find specified file in [%s]" % files_str)
+    return [DefaultInfo(files = depset([out]))]
+
+select_file = rule(
+    implementation = _impl,
+    doc = "Selects a single file from the outputs of some target \
+by given relative path",
+    attrs = {
+        "srcs": attr.label(
+            allow_files = True,
+            mandatory = True,
+            doc = "The target producing the file among other outputs",
+        ),
+        "subpath": attr.string(
+            mandatory = True,
+            doc = "Relative path to the file",
+        ),
+    },
+)
diff --git a/tests/select_file/BUILD b/tests/select_file/BUILD
new file mode 100644
index 0000000..e795502
--- /dev/null
+++ b/tests/select_file/BUILD
@@ -0,0 +1,34 @@
+load("//rules:select_file.bzl", "select_file")
+load("//rules:diff_test.bzl", "diff_test")
+
+filegroup(
+    name = "fg",
+    srcs = [
+        "subdir/inner.txt",
+        ":select_me.txt",
+    ],
+)
+
+select_file(
+    name = "select_me",
+    srcs = ":fg",
+    subpath = "select_me.txt",
+)
+
+select_file(
+    name = "select_inner",
+    srcs = ":fg",
+    subpath = "subdir/inner.txt",
+)
+
+diff_test(
+    name = "selected_me",
+    file1 = ":select_me",
+    file2 = ":select_me.txt",
+)
+
+diff_test(
+    name = "selected_inner",
+    file1 = ":select_inner",
+    file2 = "subdir/inner.txt",
+)
diff --git a/tests/select_file/select_me.txt b/tests/select_file/select_me.txt
new file mode 100644
index 0000000..4221a1e
--- /dev/null
+++ b/tests/select_file/select_me.txt
@@ -0,0 +1 @@
+Outer
diff --git a/tests/select_file/subdir/inner.txt b/tests/select_file/subdir/inner.txt
new file mode 100644
index 0000000..d87e45a
--- /dev/null
+++ b/tests/select_file/subdir/inner.txt
@@ -0,0 +1 @@
+Inner