[gn] use action() instead of copy() for libcxx headers (#195948)
copy() doesn't handle file deletions. Use an action() that syncs the
output directory with the input list via a response file, removing files
that are no longer in the list.
This works because if files are added or removed, ninja's command line
tracking re-runs the script, and if contents of existing files change,
ninja's input mtime checking reruns it.
This also makes the remove_float_h workaround unnecessary.
Motivated by all the recent header removals in libc++.
diff --git a/llvm/utils/gn/build/sync_source_dir.py b/llvm/utils/gn/build/sync_source_dir.py
new file mode 100644
index 0000000..7f6f9a2
--- /dev/null
+++ b/llvm/utils/gn/build/sync_source_dir.py
@@ -0,0 +1,59 @@
+#!/usr/bin/env python3
+
+"""Syncs files from a source dir to an output dir.
+
+Reads a list of files from a response file, copies them from the source dir
+to the output dir, and removes files in the output dir that are not in the
+list (except for files passed via --except)."""
+
+import argparse
+import os
+import shlex
+import sys
+
+
+def read(filename):
+ with open(filename) as f:
+ return f.read()
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description=__doc__,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument("--stamp", required=True,
+ help="name of a file whose mtime is updated on run")
+ parser.add_argument("--source-dir", required=True)
+ parser.add_argument("--output-dir", required=True)
+ parser.add_argument("--except", dest="exceptions", action="append",
+ default=[])
+ parser.add_argument("rspfile")
+ args = parser.parse_args()
+
+ files = shlex.split(read(args.rspfile))
+
+ # Copy files from source dir to output dir.
+ for f in files:
+ src = os.path.join(args.source_dir, f)
+ dst = os.path.join(args.output_dir, f)
+ os.makedirs(os.path.dirname(dst), exist_ok=True)
+ data = read(src)
+ if not os.path.exists(dst) or read(dst) != data:
+ with open(dst, "w") as dst_file:
+ dst_file.write(data)
+
+ # Remove files in output dir that are not in the list.
+ want = set(files)
+ exceptions = set(args.exceptions)
+ for root, dirs, filenames in os.walk(args.output_dir):
+ for filename in filenames:
+ filepath = os.path.join(root, filename)
+ relpath = os.path.relpath(filepath, args.output_dir)
+ if relpath not in want and relpath not in exceptions:
+ os.remove(filepath)
+
+ open(args.stamp, "w") # Update mtime on stamp file.
+
+
+if __name__ == "__main__":
+ sys.exit(main())
diff --git a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
index be2a650..797cdcc 100644
--- a/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
+++ b/llvm/utils/gn/secondary/libcxx/include/BUILD.gn
@@ -75,20 +75,9 @@
[ "LIBCXX_CONFIG_SITE_MODULE_ENTRY=textual header \"__config_site\"" ]
}
- # FIXME: Remove this after a while.
- action("remove_float_h") {
- stamp = "$target_gen_dir/remove_float_h.stamp"
- outputs = [ stamp ]
- script = "//llvm/utils/gn/build/remove_if_exists.py"
- args = [
- "--stamp",
- rebase_path(stamp, root_build_dir),
- rebase_path("$root_build_dir/include/c++/v1/float.h", root_build_dir),
- ]
- }
-
- copy("copy_headers") {
- sources = [
+ action("copy_headers") {
+ script = "//llvm/utils/gn/build/sync_source_dir.py"
+ inputs = [
"__algorithm/adjacent_find.h",
"__algorithm/all_of.h",
"__algorithm/any_of.h",
@@ -1771,8 +1760,24 @@
"wchar.h",
"wctype.h",
]
+ response_file_contents = inputs
+ outputs = [ "$target_gen_dir/copy_headers.stamp" ]
+ args = [
+ "--stamp",
+ rebase_path(outputs[0], root_build_dir),
+ "--source-dir",
+ rebase_path("//libcxx/include", root_build_dir),
+ "--output-dir",
+ rebase_path(libcxx_generated_include_dir, root_build_dir),
+ "--except",
+ "__assertion_handler",
+ "--except",
+ "__config_site",
+ "--except",
+ "module.modulemap",
+ "{{response_file_name}}",
+ ]
deps = [
- ":remove_float_h",
":write_assertion_handler",
":write_config_site",
":write_modulemap",
@@ -1788,7 +1793,6 @@
# don't get copied on macOS due to that.
deps += [ "//libcxxabi/include" ]
}
- outputs = [ "$root_build_dir/include/c++/v1/{{source_target_relative}}" ]
}
}