blob: 973302ab5c3f2d8f09d52edd65865e7e03004098 [file] [log] [blame]
#!/usr/bin/env fuchsia-vendored-python
# Copyright 2022 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""
Process build "force clean fences". If it detects that the build should be clobbered,
it will run `gn clean`.
A fence is crossed when a machine checks out a different revision and
there is a diff between $BUILD_DIR/.force_clean_fences and the outputs of the main
get_fences.py file and get_fences.py files under vendor/*/.
"""
import argparse
import pathlib
import subprocess
import sys
def main():
parser = argparse.ArgumentParser(
description="Process build force-clean fences."
)
parser.add_argument(
"--gn-bin",
type=pathlib.Path,
required=True,
help="Path to prebuilt GN binary.",
)
parser.add_argument(
"--checkout-dir",
type=pathlib.Path,
required=True,
help="Path to $FUCHSIA_DIR.",
)
parser.add_argument(
"--build-dir",
type=pathlib.Path,
required=True,
help="Path to the root build dir, e.g. $FUCHSIA_DIR/out/default.",
)
parser.add_argument("--verbose", action="store_true")
args = parser.parse_args()
def _log(message):
if args.verbose:
print(message)
# check that inputs are valid
if not args.gn_bin.is_file():
raise RuntimeError(f"{args.gn_bin} is not a file")
if not args.checkout_dir.is_dir():
raise RuntimeError(f"{args.checkout_dir} is not a directory")
if not args.build_dir.is_dir():
raise RuntimeError(f"{args.build_dir} is not a directory")
# Find the main get_fences.py script and all those under vendor/*/
get_fences_scripts = []
subdirs = [args.checkout_dir]
vendor_dir = args.checkout_dir / "vendor"
if vendor_dir.exists():
subdirs.extend(vendor_dir.iterdir())
for subdir in subdirs:
get_fences_script = subdir / "build" / "force_clean" / "get_fences.py"
if get_fences_script.exists():
get_fences_scripts.append(get_fences_script)
# execute fences scripts using the same interpreter as us
current_fences = []
for script in sorted(get_fences_scripts):
_log(f"generating clean-build fences from {script}")
current_fences.append(
subprocess.run(
[sys.executable, script],
stdout=subprocess.PIPE,
text=True,
check=True,
).stdout
)
current_fences = "\n".join(current_fences)
existing_fences = None
existing_fences_path = args.build_dir / ".force_clean_fences"
if existing_fences_path.exists():
_log("reading existing build dir's force-clean fences")
with open(existing_fences_path, "r") as f:
existing_fences = f.read()
def _write_fences():
_log(
f"writing new fences:\n=============\n{current_fences}\n============="
)
with open(existing_fences_path, "w") as f:
f.write(current_fences)
# clobber if needed
if existing_fences == None:
_log("no fences file found, assuming nothing to clean")
_write_fences()
elif existing_fences != current_fences:
_log(f"new fences found, clobbering build...")
subprocess.run([args.gn_bin, "clean", args.build_dir])
_write_fences()
else:
_log("force_clean fences up-to-date, not clobbering")
return 0
if __name__ == "__main__":
sys.exit(main())