pw_build: pw_mirror_tree target

The pw_mirror_tree target makes source files available in the output
directory. This can be used to efficiently add a prefix to a source
tree.

Change-Id: I9f5458e2b786ab8ca0132b6e85f4df57a1c267a5
Reviewed-on: https://pigweed-review.googlesource.com/c/pigweed/pigweed/+/34980
Commit-Queue: Wyatt Hepler <hepler@google.com>
Pigweed-Auto-Submit: Wyatt Hepler <hepler@google.com>
Reviewed-by: Alexei Frolov <frolv@google.com>
diff --git a/pw_build/mirror_tree.gni b/pw_build/mirror_tree.gni
new file mode 100644
index 0000000..0551d58
--- /dev/null
+++ b/pw_build/mirror_tree.gni
@@ -0,0 +1,61 @@
+# Copyright 2021 The Pigweed Authors
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may not
+# use this file except in compliance with the License. You may obtain a copy of
+# the License at
+#
+#     https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations under
+# the License.
+
+import("//build_overrides/pigweed.gni")
+
+# Mirrors a directory structure to the output directory.
+#
+# This is similar to a GN copy target, with some differences:
+#
+#   - The outputs list is generated by the template based on the source_root and
+#     directory arguments, rather than using source expansion.
+#   - The source_root argument can be used to trim prefixes from source files.
+#   - pw_mirror_tree uses hard links instead of copies for efficiency.
+#
+# Args:
+#
+#   directory: Output directory for the files.
+#   sources: List of files to mirror to the output directory.
+#   source_root: Root path for sources; defaults to ".".
+#
+template("pw_mirror_tree") {
+  assert(defined(invoker.sources) && invoker.sources != [],
+         "At least one source file must be provided in 'sources'")
+  assert(defined(invoker.directory) && invoker.directory != "",
+         "The output path must be specified as 'directory'")
+
+  if (defined(invoker.source_root)) {
+    _root = invoker.source_root
+  } else {
+    _root = "."
+  }
+
+  action(target_name) {
+    script = "$dir_pw_build/py/pw_build/mirror_tree.py"
+
+    outputs = []
+    foreach(path, rebase_path(invoker.sources, _root)) {
+      outputs += [ "${invoker.directory}/$path" ]
+    }
+
+    args = [
+             "--source-root",
+             rebase_path(_root),
+             "--directory",
+             rebase_path(invoker.directory),
+           ] + rebase_path(invoker.sources)
+
+    forward_variables_from(invoker, "*", [ "script" ])
+  }
+}