blob: 71982ae02b33b9429e9c110e49de7f5090c3b85b [file] [log] [blame]
# 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.
"""Runs `bb add`, with additional convenience features."""
import argparse
import logging
import os
from pathlib import Path
import shlex
import sys
from typing import Iterable, List
from llvm_tools import chroot
from llvm_tools import cros_cls
from llvm_tools import get_llvm_hash
from llvm_tools import llvm_next
DEFAULT_LLVM_NEXT_BUILDERS = ("chromeos/staging/staging-build-chromiumos-sdk",)
def generate_bb_add_command(
use_llvm_next: bool,
extra_cls: Iterable[cros_cls.ChangeListURL],
bots: Iterable[str],
tags: Iterable[str],
) -> List[str]:
"""Generates a `bb add` command.
Args:
use_llvm_next: if True, all current llvm-next CLs will be added to the
run.
extra_cls: A list of extra CLs to add to the run.
bots: Bots that should be spawned by this command, e.g.,
`chromeos/staging/staging-build-chromiumos-sdk`.
tags: Tags that should be applied to the bot invocation(s). This can
make searching for the invocations easier using tools like `bb ls`.
Returns:
A command that would spawn the requested builders in the requested
configuration.
"""
cls: List[cros_cls.ChangeListURL] = []
if use_llvm_next:
if not llvm_next.LLVM_NEXT_TESTING_CLS:
raise ValueError(
"llvm-next testing requested, but no llvm-next CLs exist."
)
cls += llvm_next.LLVM_NEXT_TESTING_CLS
if extra_cls:
cls += extra_cls
cmd = ["bb", "add"]
for cl in cls:
cmd += ("-cl", cl.crrev_url_without_http())
for tag in tags:
cmd += ("-t", tag)
cmd += bots
return cmd
def is_pointless_llvm_next_invocation(chromeos_tree: Path) -> bool:
"""Returns False if llvm-next testing is likely to be useful."""
if not llvm_next.LLVM_NEXT_TESTING_CLS:
logging.info(
"Tests seem pointless: no llvm-next testing CLs are registered."
)
return True
current_hash = get_llvm_hash.LLVMHash().GetCrOSCurrentLLVMHash(
chromeos_tree
)
if current_hash == llvm_next.LLVM_NEXT_HASH:
logging.info(
"Tests seem pointless: current LLVM hash (%s) is the same as "
"llvm-next",
current_hash,
)
return True
logging.info(
"Testing seems useful; llvm-next hash is %s", llvm_next.LLVM_NEXT_HASH
)
return False
def parse_opts(my_dir: Path, argv: List[str]) -> argparse.Namespace:
parser = argparse.ArgumentParser(
description=__doc__,
formatter_class=argparse.RawDescriptionHelpFormatter,
)
parser.add_argument(
"--add-llvm-next-verification-builders",
action="store_true",
help="""
Add the default series of builders used to help verify llvm-next. Does
not imply --llvm-next.
""",
)
parser.add_argument(
"--chromeos-tree",
type=Path,
help="""
ChromeOS tree to make modifications in. Will be inferred if none is
passed.
""",
)
parser.add_argument(
"--dry-run",
action="store_true",
help="Print the `bb` command, rather than running it.",
)
parser.add_argument(
"--llvm-next",
action="store_true",
help="Add the current llvm-next patch set.",
)
parser.add_argument(
"--cl",
action="append",
type=cros_cls.ChangeListURL.parse,
help="""
CL to add to the `bb add` run. May be specified multiple times. In the
form crrev.com/c/123456.
""",
)
parser.add_argument(
"--skip-if-pointless",
action="store_true",
help="""
If this is passed, the `bb add` will be skipped. It's an error to pass
this flag without `--llvm-next`.
""",
)
parser.add_argument(
"--tag",
action="append",
help="""
Tag to add to the `bb add` invocation. May be specified multiple times.
Tags are arbitrary text.
""",
)
parser.add_argument(
"bot", nargs="*", default=[], help="Bot(s) to run `bb add` with."
)
opts = parser.parse_args(argv)
if opts.skip_if_pointless and not opts.llvm_next:
parser.error("--skip-if-pointless may only be used with --llvm-next.")
if opts.add_llvm_next_verification_builders:
opts.bot += DEFAULT_LLVM_NEXT_BUILDERS
if not opts.bot:
parser.error("At least one bot must be specified.")
if not opts.chromeos_tree:
opts.chromeos_tree = chroot.FindChromeOSRootAbove(my_dir)
return opts
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()
opts = parse_opts(my_dir, argv)
if opts.skip_if_pointless and is_pointless_llvm_next_invocation(
opts.chromeos_tree
):
logging.info(
"--skip-if-pointless passed for pointless invocation; quit."
)
return
cmd = generate_bb_add_command(
use_llvm_next=opts.llvm_next,
extra_cls=opts.cl,
bots=opts.bot,
tags=opts.tag or (),
)
if opts.dry_run:
logging.info(
"--dry-run specified; would run: `%s` otherwise", shlex.join(cmd)
)
return
logging.info("Running `bb add` command: %s...", shlex.join(cmd))
# execvp raises if it fails, so no need to check.
os.execvp(cmd[0], cmd)