diff options
author | George Burgess IV <gbiv@google.com> | 2024-04-16 08:46:20 -0600 |
---|---|---|
committer | Chromeos LUCI <chromeos-scoped@luci-project-accounts.iam.gserviceaccount.com> | 2024-04-17 15:40:14 +0000 |
commit | 9e2e613c5ac8d748ee2e64afa5780691ca3dac4a (patch) | |
tree | 577ebd06b18737aaaa52d18254b76aa20f413f48 | |
parent | d581fd07d429da6ff84cd793722e07df46fcb0ff (diff) | |
download | toolchain-utils-9e2e613c5ac8d748ee2e64afa5780691ca3dac4a.tar.gz |
llvm_tools: add upload_llvm_testing_helper_cl.py
This script provides a simple way to upload isolated, 'all-in-one' CLs
that make LLVM easier to test.
BUG=b:333462347
TEST=Ran the script: crrev.com/c/5457603
Change-Id: I2a107798ab4b2171b98b57634df6081de9dba485
Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/third_party/toolchain-utils/+/5458715
Reviewed-by: Ryan Beltran <ryanbeltran@chromium.org>
Commit-Queue: George Burgess <gbiv@chromium.org>
Tested-by: George Burgess <gbiv@chromium.org>
-rwxr-xr-x | llvm_tools/upload_llvm_testing_helper_cl.py | 186 | ||||
-rwxr-xr-x | llvm_tools/upload_llvm_testing_helper_cl_test.py | 69 |
2 files changed, 255 insertions, 0 deletions
diff --git a/llvm_tools/upload_llvm_testing_helper_cl.py b/llvm_tools/upload_llvm_testing_helper_cl.py new file mode 100755 index 00000000..d0587044 --- /dev/null +++ b/llvm_tools/upload_llvm_testing_helper_cl.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python3 +# Copyright 2024 The ChromiumOS Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Uploads an LLVM 'testing helper' CL. + +These CLs make the validation of LLVM easier, and do things like: +- allowing patches to be disabled if they no longer apply +- disabling warnings +""" + +import argparse +import logging +from pathlib import Path +import subprocess +import sys +from typing import List + +import chroot +from cros_utils import git_utils + + +# Text to add to the bottom of ebuild hooks. +DISABLE_WARNINGS_BLOCK = r""" + +# Disable -Werror where possible, so more serious issues (e.g., compiler +# crashes) can be more easily surfaced. +cros_pre_src_configure_disable_werror() { + # Add the special env var to toolchain/fatal_clang_warnings. There's logic + # in Chromite to search for & upload these directories on all builds, + # including failing ones. + local d="${CROS_ARTIFACTS_TMP_DIR}/toolchain/fatal_clang_warnings" + export CFLAGS+=" -D_CROSTC_FORCE_DISABLE_WERROR=${d} " + export CXXFLAGS+=" -D_CROSTC_FORCE_DISABLE_WERROR=${d} " + # Set these for ec ebuilds, since those ignore CFLAGS/CXXFLAGS + [[ -n "${_ECLASS_CROS_EC:-}" ]] && export EXTRA_CFLAGS+=" -D_CROSTC_FORCE_DISABLE_WERROR=${d} " + + # Also export an env var, since some build systems will ignore our CFLAGS + # but not filter the environment. + export FORCE_DISABLE_WERROR=1 +} +""" + +# Text to add to the bottom of `profiles/base/use.force`. +USE_FORCE_BLOCK = r""" + +# Force patch disabling, rather than failing to build LLVM and its subpackages. +# Without this, we'll lose signal on builders where any patch fails to apply. +continue-on-patch-failure +""" + +COMMIT_MESSAGE = """\ +DO NOT COMMIT: llvm-testing helper CL + +This CL was automatically generated to facilitate LLVM testing. +The script that generated this is located at +src/third_party/toolchain-utils/llvm_tools/upload_llvm_testing_helper_cl.py. + +BUG=None +TEST=None +""" + + +def add_force_rebuild_marker(chromiumos_overlay: Path): + """Adds a marker to force this change to appear as a toolchain change.""" + # `touch`ing anything in `sys-devel/llvm/files` causes an LLVM revbump, and + # causes all packages to be rebuilt. + force_rebuild_file = ( + chromiumos_overlay / "sys-devel" / "llvm" / "files" / "force_rebuild" + ) + force_rebuild_file.touch() + + +def add_use_force_block(chromiumos_overlay: Path): + use_force = chromiumos_overlay / "profiles" / "base" / "use.force" + # If this doesn't exist, that _can_ be worked with, but it's a smoke signal + # (since e.g., maybe the file no longer takes effect). Have someone + # investigate. + if not use_force.exists(): + raise ValueError(f"No file found at {use_force}; refusing to patch") + with use_force.open("a", encoding="utf-8") as f: + f.write(USE_FORCE_BLOCK) + + +def add_disable_warnings_block(chromiumos_overlay: Path): + ebuild_hooks = chromiumos_overlay / "profiles" / "base" / "profile.bashrc" + # If this doesn't exist, that _can_ be worked with, but it's a smoke signal + # (since e.g., maybe the file no longer takes effect). Have someone + # investigate. + if not ebuild_hooks.exists(): + raise ValueError(f"No file found at {ebuild_hooks}; refusing to patch") + with ebuild_hooks.open("a", encoding="utf-8") as f: + f.write(DISABLE_WARNINGS_BLOCK) + + +def commit_all_changes(git_dir: Path, message: str) -> str: + """Commits all changes in `git_dir`, with the given commit message.""" + # Explicitly add using `git add -A`, since that stages all unstaged changes + # & adds any files that aren't tracked. `git commit -a` skips adding + # untracked files. + subprocess.run( + ["git", "add", "-A"], + check=True, + cwd=git_dir, + stdin=subprocess.DEVNULL, + ) + subprocess.run( + ["git", "commit", "-m", message], + check=True, + cwd=git_dir, + stdin=subprocess.DEVNULL, + ) + return subprocess.run( + ["git", "rev-parse", "HEAD"], + check=True, + cwd=git_dir, + stdin=subprocess.DEVNULL, + stdout=subprocess.PIPE, + encoding="utf-8", + ).stdout.strip() + + +def create_helper_cl_commit_in_worktree_of(chromiumos_overlay: Path) -> str: + """Creates a commit containing the helper CL diff. Returns the SHA.commit""" + with git_utils.create_worktree(chromiumos_overlay) as worktree: + logging.info("Adding helper changes to CL in %s...", worktree) + add_force_rebuild_marker(worktree) + add_use_force_block(worktree) + add_disable_warnings_block(worktree) + return commit_all_changes(worktree, COMMIT_MESSAGE) + + +def main(argv: List[str]) -> None: + logging.basicConfig( + format=">> %(asctime)s: %(levelname)s: %(filename)s:%(lineno)d: " + "%(message)s", + level=logging.INFO, + ) + + my_dir = Path(__file__).parent.resolve() + parser = argparse.ArgumentParser( + description=__doc__, + formatter_class=argparse.RawDescriptionHelpFormatter, + ) + parser.add_argument( + "--chromeos-tree", + type=Path, + help=""" + The ChromeOS tree to update in. The `llvm-project` directory of this + may also be consulted. Will try to autodetect if none is specified. + """, + ) + parser.add_argument( + "--dry-run", + action="store_true", + help="Commit changes, but don't actually upload them.", + ) + opts = parser.parse_args(argv) + + chromeos_tree = opts.chromeos_tree + if not chromeos_tree: + chromeos_tree = chroot.FindChromeOSRootAbove(my_dir) + + chromiumos_overlay = ( + chromeos_tree / "src" / "third_party" / "chromiumos-overlay" + ) + helper_sha = create_helper_cl_commit_in_worktree_of(chromiumos_overlay) + if opts.dry_run: + logging.info( + "--dry-run specified; not uploading new commit (%s).", + helper_sha, + ) + return + + # This logs the CL information, so no need to print anything after this. + git_utils.upload_to_gerrit( + git_repo=chromiumos_overlay, + remote="cros", + branch="main", + ref=helper_sha, + ) + + +if __name__ == "__main__": + main(sys.argv[1:]) diff --git a/llvm_tools/upload_llvm_testing_helper_cl_test.py b/llvm_tools/upload_llvm_testing_helper_cl_test.py new file mode 100755 index 00000000..63951a59 --- /dev/null +++ b/llvm_tools/upload_llvm_testing_helper_cl_test.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +# Copyright 2024 The ChromiumOS Authors +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Tests for upload_llvm_testing_helper_cl""" + +from pathlib import Path +import shutil +import tempfile +import unittest + +import upload_llvm_testing_helper_cl + + +class Test(unittest.TestCase): + """Tests for upload_llvm_testing_helper_cl""" + + def make_tempdir(self) -> Path: + tempdir = tempfile.mkdtemp( + os.path.basename("upload_llvm_testing_helper_cl_test") + ) + self.addCleanup(shutil.rmtree, tempdir) + return Path(tempdir) + + def test_force_rebuild_marker_addition(self): + chromiumos_overlay = self.make_tempdir() + filesdir = chromiumos_overlay / "sys-devel" / "llvm" / "files" + filesdir.mkdir(parents=True) + upload_llvm_testing_helper_cl.add_force_rebuild_marker( + chromiumos_overlay + ) + self.assertTrue((filesdir / "force_rebuild").exists()) + + def test_use_force_block_addition(self): + chromiumos_overlay = self.make_tempdir() + use_force_file = chromiumos_overlay / "profiles" / "base" / "use.force" + use_force_file.parent.mkdir(parents=True) + use_force_file.write_text("# Whee", encoding="utf-8") + + upload_llvm_testing_helper_cl.add_use_force_block(chromiumos_overlay) + new_contents = use_force_file.read_text(encoding="utf-8") + + self.assertIn("# Whee\n", new_contents) + self.assertIn( + upload_llvm_testing_helper_cl.USE_FORCE_BLOCK, new_contents + ) + + def test_warning_disable_block_addition(self): + chromiumos_overlay = self.make_tempdir() + profile_bashrc = ( + chromiumos_overlay / "profiles" / "base" / "profile.bashrc" + ) + profile_bashrc.parent.mkdir(parents=True) + profile_bashrc.write_text("# Whee", encoding="utf-8") + + upload_llvm_testing_helper_cl.add_disable_warnings_block( + chromiumos_overlay + ) + new_contents = profile_bashrc.read_text(encoding="utf-8") + + self.assertIn("# Whee\n", new_contents) + self.assertIn( + upload_llvm_testing_helper_cl.DISABLE_WARNINGS_BLOCK, new_contents + ) + + +if __name__ == "__main__": + unittest.main() |