xref: /aosp_15_r20/external/crosvm/tools/presubmit (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker#!/usr/bin/env python3
2*bb4ee6a4SAndroid Build Coastguard Worker# Copyright 2022 The ChromiumOS Authors
3*bb4ee6a4SAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be
4*bb4ee6a4SAndroid Build Coastguard Worker# found in the LICENSE file.
5*bb4ee6a4SAndroid Build Coastguard Worker
6*bb4ee6a4SAndroid Build Coastguard Workerimport os
7*bb4ee6a4SAndroid Build Coastguard Workerimport typing
8*bb4ee6a4SAndroid Build Coastguard Workerfrom typing import Generator, List, Literal, Optional, Tuple, Union
9*bb4ee6a4SAndroid Build Coastguard Worker
10*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.common import (
11*bb4ee6a4SAndroid Build Coastguard Worker    CROSVM_ROOT,
12*bb4ee6a4SAndroid Build Coastguard Worker    TOOLS_ROOT,
13*bb4ee6a4SAndroid Build Coastguard Worker    Triple,
14*bb4ee6a4SAndroid Build Coastguard Worker    argh,
15*bb4ee6a4SAndroid Build Coastguard Worker    chdir,
16*bb4ee6a4SAndroid Build Coastguard Worker    cmd,
17*bb4ee6a4SAndroid Build Coastguard Worker    run_main,
18*bb4ee6a4SAndroid Build Coastguard Worker)
19*bb4ee6a4SAndroid Build Coastguard Workerfrom impl.presubmit import Check, CheckContext, run_checks, Group
20*bb4ee6a4SAndroid Build Coastguard Worker
21*bb4ee6a4SAndroid Build Coastguard Workerpython = cmd("python3")
22*bb4ee6a4SAndroid Build Coastguard Workermypy = cmd("mypy").with_color_env("MYPY_FORCE_COLOR")
23*bb4ee6a4SAndroid Build Coastguard Workerblack = cmd("black").with_color_arg(always="--color", never="--no-color")
24*bb4ee6a4SAndroid Build Coastguard Workermdformat = cmd("mdformat")
25*bb4ee6a4SAndroid Build Coastguard Workerlucicfg = cmd("third_party/depot_tools/lucicfg")
26*bb4ee6a4SAndroid Build Coastguard Worker
27*bb4ee6a4SAndroid Build Coastguard Worker# All supported platforms as a type and a list.
28*bb4ee6a4SAndroid Build Coastguard WorkerPlatform = Literal["x86_64", "aarch64", "mingw64", "armhf"]
29*bb4ee6a4SAndroid Build Coastguard WorkerPLATFORMS: Tuple[Platform, ...] = typing.get_args(Platform)
30*bb4ee6a4SAndroid Build Coastguard Worker
31*bb4ee6a4SAndroid Build Coastguard WorkerClippyOnlyPlatform = Literal["android"]
32*bb4ee6a4SAndroid Build Coastguard WorkerCLIPPY_ONLY_PLATFORMS: Tuple[ClippyOnlyPlatform, ...] = typing.get_args(ClippyOnlyPlatform)
33*bb4ee6a4SAndroid Build Coastguard Worker
34*bb4ee6a4SAndroid Build Coastguard Worker
35*bb4ee6a4SAndroid Build Coastguard Workerdef platform_is_supported(platform: Union[Platform, ClippyOnlyPlatform]):
36*bb4ee6a4SAndroid Build Coastguard Worker    "Returns true if the platform is available as a target in rustup."
37*bb4ee6a4SAndroid Build Coastguard Worker    triple = Triple.from_shorthand(platform)
38*bb4ee6a4SAndroid Build Coastguard Worker    installed_toolchains = cmd("rustup target list --installed").lines()
39*bb4ee6a4SAndroid Build Coastguard Worker    return str(triple) in installed_toolchains
40*bb4ee6a4SAndroid Build Coastguard Worker
41*bb4ee6a4SAndroid Build Coastguard Worker
42*bb4ee6a4SAndroid Build Coastguard Worker####################################################################################################
43*bb4ee6a4SAndroid Build Coastguard Worker# Check methods
44*bb4ee6a4SAndroid Build Coastguard Worker#
45*bb4ee6a4SAndroid Build Coastguard Worker# Each check returns a Command (or list of Commands) to be run to execute the check. They are
46*bb4ee6a4SAndroid Build Coastguard Worker# registered and configured in the CHECKS list below.
47*bb4ee6a4SAndroid Build Coastguard Worker#
48*bb4ee6a4SAndroid Build Coastguard Worker# Some check functions are factory functions that return a check command for all supported
49*bb4ee6a4SAndroid Build Coastguard Worker# platforms.
50*bb4ee6a4SAndroid Build Coastguard Worker
51*bb4ee6a4SAndroid Build Coastguard Worker
52*bb4ee6a4SAndroid Build Coastguard Workerdef check_python_tests(_: CheckContext):
53*bb4ee6a4SAndroid Build Coastguard Worker    "Runs unit tests for python dev tooling."
54*bb4ee6a4SAndroid Build Coastguard Worker    PYTHON_TESTS = [
55*bb4ee6a4SAndroid Build Coastguard Worker        # Disabled due to b/309148074
56*bb4ee6a4SAndroid Build Coastguard Worker        # "tests.cl_tests",
57*bb4ee6a4SAndroid Build Coastguard Worker        "impl.common",
58*bb4ee6a4SAndroid Build Coastguard Worker    ]
59*bb4ee6a4SAndroid Build Coastguard Worker    return [python.with_cwd(TOOLS_ROOT).with_args("-m", file) for file in PYTHON_TESTS]
60*bb4ee6a4SAndroid Build Coastguard Worker
61*bb4ee6a4SAndroid Build Coastguard Worker
62*bb4ee6a4SAndroid Build Coastguard Workerdef check_python_types(context: CheckContext):
63*bb4ee6a4SAndroid Build Coastguard Worker    "Run mypy type checks on python dev tooling."
64*bb4ee6a4SAndroid Build Coastguard Worker    return [mypy("--pretty", file) for file in context.all_files]
65*bb4ee6a4SAndroid Build Coastguard Worker
66*bb4ee6a4SAndroid Build Coastguard Worker
67*bb4ee6a4SAndroid Build Coastguard Workerdef check_python_format(context: CheckContext):
68*bb4ee6a4SAndroid Build Coastguard Worker    "Runs the black formatter on python dev tooling."
69*bb4ee6a4SAndroid Build Coastguard Worker    return black.with_args(
70*bb4ee6a4SAndroid Build Coastguard Worker        "--check" if not context.fix else None,
71*bb4ee6a4SAndroid Build Coastguard Worker        *context.modified_files,
72*bb4ee6a4SAndroid Build Coastguard Worker    )
73*bb4ee6a4SAndroid Build Coastguard Worker
74*bb4ee6a4SAndroid Build Coastguard Worker
75*bb4ee6a4SAndroid Build Coastguard Workerdef check_markdown_format(context: CheckContext):
76*bb4ee6a4SAndroid Build Coastguard Worker    "Runs mdformat on all markdown files."
77*bb4ee6a4SAndroid Build Coastguard Worker    if "blaze" in mdformat("--version").stdout():
78*bb4ee6a4SAndroid Build Coastguard Worker        raise Exception(
79*bb4ee6a4SAndroid Build Coastguard Worker            "You are using google's mdformat. "
80*bb4ee6a4SAndroid Build Coastguard Worker            + "Please update your PATH to ensure the pip installed mdformat is available."
81*bb4ee6a4SAndroid Build Coastguard Worker        )
82*bb4ee6a4SAndroid Build Coastguard Worker    return mdformat.with_args(
83*bb4ee6a4SAndroid Build Coastguard Worker        "--wrap 100",
84*bb4ee6a4SAndroid Build Coastguard Worker        "--check" if not context.fix else "",
85*bb4ee6a4SAndroid Build Coastguard Worker        *context.modified_files,
86*bb4ee6a4SAndroid Build Coastguard Worker    )
87*bb4ee6a4SAndroid Build Coastguard Worker
88*bb4ee6a4SAndroid Build Coastguard Worker
89*bb4ee6a4SAndroid Build Coastguard Workerdef check_rust_format(context: CheckContext):
90*bb4ee6a4SAndroid Build Coastguard Worker    "Runs rustfmt on all modified files."
91*bb4ee6a4SAndroid Build Coastguard Worker    rustfmt = cmd(cmd("rustup +nightly which rustfmt").stdout())
92*bb4ee6a4SAndroid Build Coastguard Worker    # Windows doesn't accept very long arguments: https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-createprocessa#:~:text=The%20maximum%20length%20of%20this%20string%20is%2032%2C767%20characters%2C%20including%20the%20Unicode%20terminating%20null%20character.%20If%20lpApplicationName%20is%20NULL%2C%20the%20module%20name%20portion%20of%20lpCommandLine%20is%20limited%20to%20MAX_PATH%20characters.
93*bb4ee6a4SAndroid Build Coastguard Worker    return list(
94*bb4ee6a4SAndroid Build Coastguard Worker        rustfmt.with_color_flag()
95*bb4ee6a4SAndroid Build Coastguard Worker        .with_args("--check" if not context.fix else "")
96*bb4ee6a4SAndroid Build Coastguard Worker        .foreach(context.modified_files, batch_size=10)
97*bb4ee6a4SAndroid Build Coastguard Worker    )
98*bb4ee6a4SAndroid Build Coastguard Worker
99*bb4ee6a4SAndroid Build Coastguard Worker
100*bb4ee6a4SAndroid Build Coastguard Workerdef check_cargo_doc(_: CheckContext):
101*bb4ee6a4SAndroid Build Coastguard Worker    "Runs cargo-doc and verifies that no warnings are emitted."
102*bb4ee6a4SAndroid Build Coastguard Worker    return cmd("./tools/cargo-doc").with_env("RUSTDOCFLAGS", "-D warnings").with_color_flag()
103*bb4ee6a4SAndroid Build Coastguard Worker
104*bb4ee6a4SAndroid Build Coastguard Worker
105*bb4ee6a4SAndroid Build Coastguard Workerdef check_doc_tests(_: CheckContext):
106*bb4ee6a4SAndroid Build Coastguard Worker    "Runs cargo doc tests. These cannot be run via nextest and run_tests."
107*bb4ee6a4SAndroid Build Coastguard Worker    return cmd(
108*bb4ee6a4SAndroid Build Coastguard Worker        "cargo test",
109*bb4ee6a4SAndroid Build Coastguard Worker        "--doc",
110*bb4ee6a4SAndroid Build Coastguard Worker        "--workspace",
111*bb4ee6a4SAndroid Build Coastguard Worker        "--features=all-x86_64",
112*bb4ee6a4SAndroid Build Coastguard Worker    ).with_color_flag()
113*bb4ee6a4SAndroid Build Coastguard Worker
114*bb4ee6a4SAndroid Build Coastguard Worker
115*bb4ee6a4SAndroid Build Coastguard Workerdef check_mdbook(_: CheckContext):
116*bb4ee6a4SAndroid Build Coastguard Worker    "Runs cargo-doc and verifies that no warnings are emitted."
117*bb4ee6a4SAndroid Build Coastguard Worker    return cmd("mdbook build docs/book/")
118*bb4ee6a4SAndroid Build Coastguard Worker
119*bb4ee6a4SAndroid Build Coastguard Worker
120*bb4ee6a4SAndroid Build Coastguard Workerdef check_crosvm_tests(platform: Platform):
121*bb4ee6a4SAndroid Build Coastguard Worker    def check(_: CheckContext):
122*bb4ee6a4SAndroid Build Coastguard Worker        if not platform_is_supported(platform):
123*bb4ee6a4SAndroid Build Coastguard Worker            return None
124*bb4ee6a4SAndroid Build Coastguard Worker        dut = None
125*bb4ee6a4SAndroid Build Coastguard Worker        if os.access("/dev/kvm", os.W_OK):
126*bb4ee6a4SAndroid Build Coastguard Worker            if platform == "x86_64":
127*bb4ee6a4SAndroid Build Coastguard Worker                dut = "--dut=vm"
128*bb4ee6a4SAndroid Build Coastguard Worker            elif platform == "aarch64":
129*bb4ee6a4SAndroid Build Coastguard Worker                dut = "--dut=vm"
130*bb4ee6a4SAndroid Build Coastguard Worker        return cmd("./tools/run_tests --verbose --platform", platform, dut).with_color_flag()
131*bb4ee6a4SAndroid Build Coastguard Worker
132*bb4ee6a4SAndroid Build Coastguard Worker    check.__doc__ = f"Runs all crosvm tests for {platform}."
133*bb4ee6a4SAndroid Build Coastguard Worker
134*bb4ee6a4SAndroid Build Coastguard Worker    return check
135*bb4ee6a4SAndroid Build Coastguard Worker
136*bb4ee6a4SAndroid Build Coastguard Worker
137*bb4ee6a4SAndroid Build Coastguard Workerdef check_crosvm_unit_tests(platform: Platform):
138*bb4ee6a4SAndroid Build Coastguard Worker    def check(_: CheckContext):
139*bb4ee6a4SAndroid Build Coastguard Worker        if not platform_is_supported(platform):
140*bb4ee6a4SAndroid Build Coastguard Worker            return None
141*bb4ee6a4SAndroid Build Coastguard Worker        return cmd("./tools/run_tests --verbose --platform", platform).with_color_flag()
142*bb4ee6a4SAndroid Build Coastguard Worker
143*bb4ee6a4SAndroid Build Coastguard Worker    check.__doc__ = f"Runs crosvm unit tests for {platform}."
144*bb4ee6a4SAndroid Build Coastguard Worker
145*bb4ee6a4SAndroid Build Coastguard Worker    return check
146*bb4ee6a4SAndroid Build Coastguard Worker
147*bb4ee6a4SAndroid Build Coastguard Worker
148*bb4ee6a4SAndroid Build Coastguard Workerdef check_crosvm_build(
149*bb4ee6a4SAndroid Build Coastguard Worker    platform: Platform, features: Optional[str] = None, no_default_features: bool = False
150*bb4ee6a4SAndroid Build Coastguard Worker):
151*bb4ee6a4SAndroid Build Coastguard Worker    def check(_: CheckContext):
152*bb4ee6a4SAndroid Build Coastguard Worker        return cmd(
153*bb4ee6a4SAndroid Build Coastguard Worker            "./tools/run_tests --no-run --verbose --platform",
154*bb4ee6a4SAndroid Build Coastguard Worker            platform,
155*bb4ee6a4SAndroid Build Coastguard Worker            f"--features={features}" if features is not None else None,
156*bb4ee6a4SAndroid Build Coastguard Worker            "--no-default-features" if no_default_features else None,
157*bb4ee6a4SAndroid Build Coastguard Worker        ).with_color_flag()
158*bb4ee6a4SAndroid Build Coastguard Worker
159*bb4ee6a4SAndroid Build Coastguard Worker    check.__doc__ = f"Builds crosvm for {platform} with features {features}."
160*bb4ee6a4SAndroid Build Coastguard Worker
161*bb4ee6a4SAndroid Build Coastguard Worker    return check
162*bb4ee6a4SAndroid Build Coastguard Worker
163*bb4ee6a4SAndroid Build Coastguard Worker
164*bb4ee6a4SAndroid Build Coastguard Workerdef check_clippy(platform: Union[Platform, ClippyOnlyPlatform]):
165*bb4ee6a4SAndroid Build Coastguard Worker    def check(context: CheckContext):
166*bb4ee6a4SAndroid Build Coastguard Worker        if not platform_is_supported(platform):
167*bb4ee6a4SAndroid Build Coastguard Worker            return None
168*bb4ee6a4SAndroid Build Coastguard Worker        return cmd(
169*bb4ee6a4SAndroid Build Coastguard Worker            "./tools/clippy --platform",
170*bb4ee6a4SAndroid Build Coastguard Worker            platform,
171*bb4ee6a4SAndroid Build Coastguard Worker            "--fix" if context.fix else None,
172*bb4ee6a4SAndroid Build Coastguard Worker        ).with_color_flag()
173*bb4ee6a4SAndroid Build Coastguard Worker
174*bb4ee6a4SAndroid Build Coastguard Worker    check.__doc__ = f"Runs clippy for {platform}."
175*bb4ee6a4SAndroid Build Coastguard Worker
176*bb4ee6a4SAndroid Build Coastguard Worker    return check
177*bb4ee6a4SAndroid Build Coastguard Worker
178*bb4ee6a4SAndroid Build Coastguard Worker
179*bb4ee6a4SAndroid Build Coastguard Workerdef custom_check(name: str, can_fix: bool = False):
180*bb4ee6a4SAndroid Build Coastguard Worker    "Custom checks are written in python in tools/custom_checks. This is a wrapper to call them."
181*bb4ee6a4SAndroid Build Coastguard Worker
182*bb4ee6a4SAndroid Build Coastguard Worker    def check(context: CheckContext):
183*bb4ee6a4SAndroid Build Coastguard Worker        return cmd(
184*bb4ee6a4SAndroid Build Coastguard Worker            TOOLS_ROOT / "custom_checks",
185*bb4ee6a4SAndroid Build Coastguard Worker            name,
186*bb4ee6a4SAndroid Build Coastguard Worker            *context.modified_files,
187*bb4ee6a4SAndroid Build Coastguard Worker            "--fix" if can_fix and context.fix else None,
188*bb4ee6a4SAndroid Build Coastguard Worker        )
189*bb4ee6a4SAndroid Build Coastguard Worker
190*bb4ee6a4SAndroid Build Coastguard Worker    check.__name__ = name.replace("-", "_")
191*bb4ee6a4SAndroid Build Coastguard Worker    check.__doc__ = f"Runs tools/custom_check {name}"
192*bb4ee6a4SAndroid Build Coastguard Worker    return check
193*bb4ee6a4SAndroid Build Coastguard Worker
194*bb4ee6a4SAndroid Build Coastguard Worker
195*bb4ee6a4SAndroid Build Coastguard Worker####################################################################################################
196*bb4ee6a4SAndroid Build Coastguard Worker# Checks configuration
197*bb4ee6a4SAndroid Build Coastguard Worker#
198*bb4ee6a4SAndroid Build Coastguard Worker# Configures which checks are available and on which files they are run.
199*bb4ee6a4SAndroid Build Coastguard Worker# Check names default to the function name minus the check_ prefix
200*bb4ee6a4SAndroid Build Coastguard Worker
201*bb4ee6a4SAndroid Build Coastguard WorkerCHECKS: List[Check] = [
202*bb4ee6a4SAndroid Build Coastguard Worker    Check(
203*bb4ee6a4SAndroid Build Coastguard Worker        check_rust_format,
204*bb4ee6a4SAndroid Build Coastguard Worker        files=["**.rs"],
205*bb4ee6a4SAndroid Build Coastguard Worker        exclude=["system_api/src/bindings/*"],
206*bb4ee6a4SAndroid Build Coastguard Worker        can_fix=True,
207*bb4ee6a4SAndroid Build Coastguard Worker    ),
208*bb4ee6a4SAndroid Build Coastguard Worker    Check(
209*bb4ee6a4SAndroid Build Coastguard Worker        check_mdbook,
210*bb4ee6a4SAndroid Build Coastguard Worker        files=["docs/**/*"],
211*bb4ee6a4SAndroid Build Coastguard Worker    ),
212*bb4ee6a4SAndroid Build Coastguard Worker    Check(
213*bb4ee6a4SAndroid Build Coastguard Worker        check_cargo_doc,
214*bb4ee6a4SAndroid Build Coastguard Worker        files=["**.rs", "**Cargo.toml"],
215*bb4ee6a4SAndroid Build Coastguard Worker        priority=True,
216*bb4ee6a4SAndroid Build Coastguard Worker    ),
217*bb4ee6a4SAndroid Build Coastguard Worker    Check(
218*bb4ee6a4SAndroid Build Coastguard Worker        check_doc_tests,
219*bb4ee6a4SAndroid Build Coastguard Worker        files=["**.rs", "**Cargo.toml"],
220*bb4ee6a4SAndroid Build Coastguard Worker        priority=True,
221*bb4ee6a4SAndroid Build Coastguard Worker    ),
222*bb4ee6a4SAndroid Build Coastguard Worker    Check(
223*bb4ee6a4SAndroid Build Coastguard Worker        check_python_tests,
224*bb4ee6a4SAndroid Build Coastguard Worker        files=["tools/**.py"],
225*bb4ee6a4SAndroid Build Coastguard Worker        python_tools=True,
226*bb4ee6a4SAndroid Build Coastguard Worker        priority=True,
227*bb4ee6a4SAndroid Build Coastguard Worker    ),
228*bb4ee6a4SAndroid Build Coastguard Worker    Check(
229*bb4ee6a4SAndroid Build Coastguard Worker        check_python_types,
230*bb4ee6a4SAndroid Build Coastguard Worker        files=["tools/**.py"],
231*bb4ee6a4SAndroid Build Coastguard Worker        exclude=[
232*bb4ee6a4SAndroid Build Coastguard Worker            "tools/windows/*",
233*bb4ee6a4SAndroid Build Coastguard Worker            "tools/contrib/memstats_chart/*",
234*bb4ee6a4SAndroid Build Coastguard Worker            "tools/contrib/cros_tracing_analyser/*",
235*bb4ee6a4SAndroid Build Coastguard Worker        ],
236*bb4ee6a4SAndroid Build Coastguard Worker        python_tools=True,
237*bb4ee6a4SAndroid Build Coastguard Worker    ),
238*bb4ee6a4SAndroid Build Coastguard Worker    Check(
239*bb4ee6a4SAndroid Build Coastguard Worker        check_python_format,
240*bb4ee6a4SAndroid Build Coastguard Worker        files=["**.py"],
241*bb4ee6a4SAndroid Build Coastguard Worker        python_tools=True,
242*bb4ee6a4SAndroid Build Coastguard Worker        exclude=["infra/recipes.py"],
243*bb4ee6a4SAndroid Build Coastguard Worker        can_fix=True,
244*bb4ee6a4SAndroid Build Coastguard Worker    ),
245*bb4ee6a4SAndroid Build Coastguard Worker    Check(
246*bb4ee6a4SAndroid Build Coastguard Worker        check_markdown_format,
247*bb4ee6a4SAndroid Build Coastguard Worker        files=["**.md"],
248*bb4ee6a4SAndroid Build Coastguard Worker        exclude=[
249*bb4ee6a4SAndroid Build Coastguard Worker            "infra/README.recipes.md",
250*bb4ee6a4SAndroid Build Coastguard Worker            "docs/book/src/appendix/memory_layout.md",
251*bb4ee6a4SAndroid Build Coastguard Worker        ],
252*bb4ee6a4SAndroid Build Coastguard Worker        can_fix=True,
253*bb4ee6a4SAndroid Build Coastguard Worker    ),
254*bb4ee6a4SAndroid Build Coastguard Worker    *(
255*bb4ee6a4SAndroid Build Coastguard Worker        Check(
256*bb4ee6a4SAndroid Build Coastguard Worker            check_crosvm_build(platform, features="default"),
257*bb4ee6a4SAndroid Build Coastguard Worker            custom_name=f"crosvm_build_default_{platform}",
258*bb4ee6a4SAndroid Build Coastguard Worker            files=["**.rs"],
259*bb4ee6a4SAndroid Build Coastguard Worker            priority=True,
260*bb4ee6a4SAndroid Build Coastguard Worker        )
261*bb4ee6a4SAndroid Build Coastguard Worker        for platform in PLATFORMS
262*bb4ee6a4SAndroid Build Coastguard Worker    ),
263*bb4ee6a4SAndroid Build Coastguard Worker    *(
264*bb4ee6a4SAndroid Build Coastguard Worker        Check(
265*bb4ee6a4SAndroid Build Coastguard Worker            check_crosvm_build(platform, features="", no_default_features=True),
266*bb4ee6a4SAndroid Build Coastguard Worker            custom_name=f"crosvm_build_no_default_{platform}",
267*bb4ee6a4SAndroid Build Coastguard Worker            files=["**.rs"],
268*bb4ee6a4SAndroid Build Coastguard Worker            priority=True,
269*bb4ee6a4SAndroid Build Coastguard Worker        )
270*bb4ee6a4SAndroid Build Coastguard Worker        # TODO: b/260607247 crosvm does not compile with no-default-features on mingw64
271*bb4ee6a4SAndroid Build Coastguard Worker        for platform in PLATFORMS
272*bb4ee6a4SAndroid Build Coastguard Worker        if platform != "mingw64"
273*bb4ee6a4SAndroid Build Coastguard Worker    ),
274*bb4ee6a4SAndroid Build Coastguard Worker    *(
275*bb4ee6a4SAndroid Build Coastguard Worker        Check(
276*bb4ee6a4SAndroid Build Coastguard Worker            check_crosvm_tests(platform),
277*bb4ee6a4SAndroid Build Coastguard Worker            custom_name=f"crosvm_tests_{platform}",
278*bb4ee6a4SAndroid Build Coastguard Worker            files=["**.rs"],
279*bb4ee6a4SAndroid Build Coastguard Worker            priority=True,
280*bb4ee6a4SAndroid Build Coastguard Worker        )
281*bb4ee6a4SAndroid Build Coastguard Worker        for platform in PLATFORMS
282*bb4ee6a4SAndroid Build Coastguard Worker    ),
283*bb4ee6a4SAndroid Build Coastguard Worker    *(
284*bb4ee6a4SAndroid Build Coastguard Worker        Check(
285*bb4ee6a4SAndroid Build Coastguard Worker            check_crosvm_unit_tests(platform),
286*bb4ee6a4SAndroid Build Coastguard Worker            custom_name=f"crosvm_unit_tests_{platform}",
287*bb4ee6a4SAndroid Build Coastguard Worker            files=["**.rs"],
288*bb4ee6a4SAndroid Build Coastguard Worker            priority=True,
289*bb4ee6a4SAndroid Build Coastguard Worker        )
290*bb4ee6a4SAndroid Build Coastguard Worker        for platform in PLATFORMS
291*bb4ee6a4SAndroid Build Coastguard Worker    ),
292*bb4ee6a4SAndroid Build Coastguard Worker    *(
293*bb4ee6a4SAndroid Build Coastguard Worker        Check(
294*bb4ee6a4SAndroid Build Coastguard Worker            check_clippy(platform),
295*bb4ee6a4SAndroid Build Coastguard Worker            custom_name=f"clippy_{platform}",
296*bb4ee6a4SAndroid Build Coastguard Worker            files=["**.rs"],
297*bb4ee6a4SAndroid Build Coastguard Worker            can_fix=True,
298*bb4ee6a4SAndroid Build Coastguard Worker            priority=True,
299*bb4ee6a4SAndroid Build Coastguard Worker        )
300*bb4ee6a4SAndroid Build Coastguard Worker        for platform in (*PLATFORMS, *CLIPPY_ONLY_PLATFORMS)
301*bb4ee6a4SAndroid Build Coastguard Worker    ),
302*bb4ee6a4SAndroid Build Coastguard Worker    Check(
303*bb4ee6a4SAndroid Build Coastguard Worker        custom_check("check-copyright-header"),
304*bb4ee6a4SAndroid Build Coastguard Worker        files=["**.rs", "**.py", "**.c", "**.h", "**.policy", "**.sh"],
305*bb4ee6a4SAndroid Build Coastguard Worker        exclude=[
306*bb4ee6a4SAndroid Build Coastguard Worker            "infra/recipes.py",
307*bb4ee6a4SAndroid Build Coastguard Worker            "hypervisor/src/whpx/whpx_sys/*.h",
308*bb4ee6a4SAndroid Build Coastguard Worker            "third_party/vmm_vhost/*",
309*bb4ee6a4SAndroid Build Coastguard Worker            "net_sys/src/lib.rs",
310*bb4ee6a4SAndroid Build Coastguard Worker            "system_api/src/bindings/*",
311*bb4ee6a4SAndroid Build Coastguard Worker        ],
312*bb4ee6a4SAndroid Build Coastguard Worker        python_tools=True,
313*bb4ee6a4SAndroid Build Coastguard Worker        can_fix=True,
314*bb4ee6a4SAndroid Build Coastguard Worker    ),
315*bb4ee6a4SAndroid Build Coastguard Worker    Check(
316*bb4ee6a4SAndroid Build Coastguard Worker        custom_check("check-rust-features"),
317*bb4ee6a4SAndroid Build Coastguard Worker        files=["**Cargo.toml"],
318*bb4ee6a4SAndroid Build Coastguard Worker    ),
319*bb4ee6a4SAndroid Build Coastguard Worker    Check(
320*bb4ee6a4SAndroid Build Coastguard Worker        custom_check("check-rust-lockfiles"),
321*bb4ee6a4SAndroid Build Coastguard Worker        files=["**Cargo.toml"],
322*bb4ee6a4SAndroid Build Coastguard Worker    ),
323*bb4ee6a4SAndroid Build Coastguard Worker    Check(
324*bb4ee6a4SAndroid Build Coastguard Worker        custom_check("check-line-endings"),
325*bb4ee6a4SAndroid Build Coastguard Worker    ),
326*bb4ee6a4SAndroid Build Coastguard Worker    Check(
327*bb4ee6a4SAndroid Build Coastguard Worker        custom_check("check-file-ends-with-newline"),
328*bb4ee6a4SAndroid Build Coastguard Worker        exclude=[
329*bb4ee6a4SAndroid Build Coastguard Worker            "**.h264",
330*bb4ee6a4SAndroid Build Coastguard Worker            "**.vp8",
331*bb4ee6a4SAndroid Build Coastguard Worker            "**.vp9",
332*bb4ee6a4SAndroid Build Coastguard Worker            "**.ivf",
333*bb4ee6a4SAndroid Build Coastguard Worker            "**.bin",
334*bb4ee6a4SAndroid Build Coastguard Worker            "**.png",
335*bb4ee6a4SAndroid Build Coastguard Worker            "**.min.js",
336*bb4ee6a4SAndroid Build Coastguard Worker            "**.drawio",
337*bb4ee6a4SAndroid Build Coastguard Worker            "**.json",
338*bb4ee6a4SAndroid Build Coastguard Worker            "**.dtb",
339*bb4ee6a4SAndroid Build Coastguard Worker            "**.dtbo",
340*bb4ee6a4SAndroid Build Coastguard Worker        ],
341*bb4ee6a4SAndroid Build Coastguard Worker    ),
342*bb4ee6a4SAndroid Build Coastguard Worker]
343*bb4ee6a4SAndroid Build Coastguard Worker
344*bb4ee6a4SAndroid Build Coastguard Worker####################################################################################################
345*bb4ee6a4SAndroid Build Coastguard Worker# Group configuration
346*bb4ee6a4SAndroid Build Coastguard Worker#
347*bb4ee6a4SAndroid Build Coastguard Worker# Configures pre-defined groups of checks. Some are configured for CI builders and others
348*bb4ee6a4SAndroid Build Coastguard Worker# are configured for convenience during local development.
349*bb4ee6a4SAndroid Build Coastguard Worker
350*bb4ee6a4SAndroid Build Coastguard WorkerGROUPS: List[Group] = [
351*bb4ee6a4SAndroid Build Coastguard Worker    # The default group is run if no check or group is explicitly set
352*bb4ee6a4SAndroid Build Coastguard Worker    Group(
353*bb4ee6a4SAndroid Build Coastguard Worker        name="default",
354*bb4ee6a4SAndroid Build Coastguard Worker        doc="Checks run by default",
355*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
356*bb4ee6a4SAndroid Build Coastguard Worker            "default_health_checks",
357*bb4ee6a4SAndroid Build Coastguard Worker            # Run only one task per platform to prevent blocking on the build cache.
358*bb4ee6a4SAndroid Build Coastguard Worker            "crosvm_tests_x86_64",
359*bb4ee6a4SAndroid Build Coastguard Worker            "crosvm_unit_tests_aarch64",
360*bb4ee6a4SAndroid Build Coastguard Worker            "crosvm_unit_tests_mingw64",
361*bb4ee6a4SAndroid Build Coastguard Worker            "clippy_armhf",
362*bb4ee6a4SAndroid Build Coastguard Worker        ],
363*bb4ee6a4SAndroid Build Coastguard Worker    ),
364*bb4ee6a4SAndroid Build Coastguard Worker    Group(
365*bb4ee6a4SAndroid Build Coastguard Worker        name="quick",
366*bb4ee6a4SAndroid Build Coastguard Worker        doc="Runs a quick subset of presubmit checks.",
367*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
368*bb4ee6a4SAndroid Build Coastguard Worker            "default_health_checks",
369*bb4ee6a4SAndroid Build Coastguard Worker            "crosvm_unit_tests_x86_64",
370*bb4ee6a4SAndroid Build Coastguard Worker            "clippy_aarch64",
371*bb4ee6a4SAndroid Build Coastguard Worker        ],
372*bb4ee6a4SAndroid Build Coastguard Worker    ),
373*bb4ee6a4SAndroid Build Coastguard Worker    Group(
374*bb4ee6a4SAndroid Build Coastguard Worker        name="all",
375*bb4ee6a4SAndroid Build Coastguard Worker        doc="Run checks of all builders.",
376*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
377*bb4ee6a4SAndroid Build Coastguard Worker            "health_checks",
378*bb4ee6a4SAndroid Build Coastguard Worker            *(f"linux_{platform}" for platform in PLATFORMS),
379*bb4ee6a4SAndroid Build Coastguard Worker            *(f"clippy_{platform}" for platform in CLIPPY_ONLY_PLATFORMS),
380*bb4ee6a4SAndroid Build Coastguard Worker        ],
381*bb4ee6a4SAndroid Build Coastguard Worker    ),
382*bb4ee6a4SAndroid Build Coastguard Worker    # Convenience groups for local usage:
383*bb4ee6a4SAndroid Build Coastguard Worker    Group(
384*bb4ee6a4SAndroid Build Coastguard Worker        name="clippy",
385*bb4ee6a4SAndroid Build Coastguard Worker        doc="Runs clippy for all platforms",
386*bb4ee6a4SAndroid Build Coastguard Worker        checks=[f"clippy_{platform}" for platform in PLATFORMS + CLIPPY_ONLY_PLATFORMS],
387*bb4ee6a4SAndroid Build Coastguard Worker    ),
388*bb4ee6a4SAndroid Build Coastguard Worker    Group(
389*bb4ee6a4SAndroid Build Coastguard Worker        name="unit_tests",
390*bb4ee6a4SAndroid Build Coastguard Worker        doc="Runs unit tests for all platforms",
391*bb4ee6a4SAndroid Build Coastguard Worker        checks=[f"crosvm_unit_tests_{platform}" for platform in PLATFORMS],
392*bb4ee6a4SAndroid Build Coastguard Worker    ),
393*bb4ee6a4SAndroid Build Coastguard Worker    Group(
394*bb4ee6a4SAndroid Build Coastguard Worker        name="format",
395*bb4ee6a4SAndroid Build Coastguard Worker        doc="Runs all formatting checks (or fixes)",
396*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
397*bb4ee6a4SAndroid Build Coastguard Worker            "rust_format",
398*bb4ee6a4SAndroid Build Coastguard Worker            "markdown_format",
399*bb4ee6a4SAndroid Build Coastguard Worker            "python_format",
400*bb4ee6a4SAndroid Build Coastguard Worker        ],
401*bb4ee6a4SAndroid Build Coastguard Worker    ),
402*bb4ee6a4SAndroid Build Coastguard Worker    Group(
403*bb4ee6a4SAndroid Build Coastguard Worker        name="default_health_checks",
404*bb4ee6a4SAndroid Build Coastguard Worker        doc="Health checks to run by default",
405*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
406*bb4ee6a4SAndroid Build Coastguard Worker            # Check if lockfiles need updating first. Otherwise another step may do the update.
407*bb4ee6a4SAndroid Build Coastguard Worker            "rust_lockfiles",
408*bb4ee6a4SAndroid Build Coastguard Worker            "copyright_header",
409*bb4ee6a4SAndroid Build Coastguard Worker            "file_ends_with_newline",
410*bb4ee6a4SAndroid Build Coastguard Worker            "line_endings",
411*bb4ee6a4SAndroid Build Coastguard Worker            "markdown_format",
412*bb4ee6a4SAndroid Build Coastguard Worker            "mdbook",
413*bb4ee6a4SAndroid Build Coastguard Worker            "cargo_doc",
414*bb4ee6a4SAndroid Build Coastguard Worker            "python_format",
415*bb4ee6a4SAndroid Build Coastguard Worker            "python_types",
416*bb4ee6a4SAndroid Build Coastguard Worker            "rust_features",
417*bb4ee6a4SAndroid Build Coastguard Worker            "rust_format",
418*bb4ee6a4SAndroid Build Coastguard Worker        ],
419*bb4ee6a4SAndroid Build Coastguard Worker    ),
420*bb4ee6a4SAndroid Build Coastguard Worker    # The groups below are used by builders in CI:
421*bb4ee6a4SAndroid Build Coastguard Worker    Group(
422*bb4ee6a4SAndroid Build Coastguard Worker        name="health_checks",
423*bb4ee6a4SAndroid Build Coastguard Worker        doc="Checks run on the health_check builder",
424*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
425*bb4ee6a4SAndroid Build Coastguard Worker            "default_health_checks",
426*bb4ee6a4SAndroid Build Coastguard Worker            "doc_tests",
427*bb4ee6a4SAndroid Build Coastguard Worker            "python_tests",
428*bb4ee6a4SAndroid Build Coastguard Worker        ],
429*bb4ee6a4SAndroid Build Coastguard Worker    ),
430*bb4ee6a4SAndroid Build Coastguard Worker    Group(
431*bb4ee6a4SAndroid Build Coastguard Worker        name="android-aarch64",
432*bb4ee6a4SAndroid Build Coastguard Worker        doc="Checks run on the android-aarch64 builder",
433*bb4ee6a4SAndroid Build Coastguard Worker        checks=[
434*bb4ee6a4SAndroid Build Coastguard Worker            "clippy_android",
435*bb4ee6a4SAndroid Build Coastguard Worker        ],
436*bb4ee6a4SAndroid Build Coastguard Worker    ),
437*bb4ee6a4SAndroid Build Coastguard Worker    *(
438*bb4ee6a4SAndroid Build Coastguard Worker        Group(
439*bb4ee6a4SAndroid Build Coastguard Worker            name=f"linux_{platform}",
440*bb4ee6a4SAndroid Build Coastguard Worker            doc=f"Checks run on the linux-{platform} builder",
441*bb4ee6a4SAndroid Build Coastguard Worker            checks=[
442*bb4ee6a4SAndroid Build Coastguard Worker                f"crosvm_tests_{platform}",
443*bb4ee6a4SAndroid Build Coastguard Worker                f"clippy_{platform}",
444*bb4ee6a4SAndroid Build Coastguard Worker                f"crosvm_build_default_{platform}",
445*bb4ee6a4SAndroid Build Coastguard Worker            ]
446*bb4ee6a4SAndroid Build Coastguard Worker            # TODO: b/260607247 crosvm does not compile with no-default-features on mingw64
447*bb4ee6a4SAndroid Build Coastguard Worker            + ([f"crosvm_build_no_default_{platform}"] if platform != "mingw64" else []),
448*bb4ee6a4SAndroid Build Coastguard Worker        )
449*bb4ee6a4SAndroid Build Coastguard Worker        for platform in PLATFORMS
450*bb4ee6a4SAndroid Build Coastguard Worker    ),
451*bb4ee6a4SAndroid Build Coastguard Worker]
452*bb4ee6a4SAndroid Build Coastguard Worker
453*bb4ee6a4SAndroid Build Coastguard Worker# Turn both lists into dicts for convenience
454*bb4ee6a4SAndroid Build Coastguard WorkerCHECKS_DICT = dict((c.name, c) for c in CHECKS)
455*bb4ee6a4SAndroid Build Coastguard WorkerGROUPS_DICT = dict((c.name, c) for c in GROUPS)
456*bb4ee6a4SAndroid Build Coastguard Worker
457*bb4ee6a4SAndroid Build Coastguard Worker
458*bb4ee6a4SAndroid Build Coastguard Workerdef validate_config():
459*bb4ee6a4SAndroid Build Coastguard Worker    "Validates the CHECKS and GROUPS configuration."
460*bb4ee6a4SAndroid Build Coastguard Worker    for group in GROUPS:
461*bb4ee6a4SAndroid Build Coastguard Worker        for check in group.checks:
462*bb4ee6a4SAndroid Build Coastguard Worker            if check not in CHECKS_DICT and check not in GROUPS_DICT:
463*bb4ee6a4SAndroid Build Coastguard Worker                raise Exception(f"Group {group.name} includes non-existing item {check}.")
464*bb4ee6a4SAndroid Build Coastguard Worker
465*bb4ee6a4SAndroid Build Coastguard Worker    def find_in_group(check: Check):
466*bb4ee6a4SAndroid Build Coastguard Worker        for group in GROUPS:
467*bb4ee6a4SAndroid Build Coastguard Worker            if check.name in group.checks:
468*bb4ee6a4SAndroid Build Coastguard Worker                return True
469*bb4ee6a4SAndroid Build Coastguard Worker        return False
470*bb4ee6a4SAndroid Build Coastguard Worker
471*bb4ee6a4SAndroid Build Coastguard Worker    for check in CHECKS:
472*bb4ee6a4SAndroid Build Coastguard Worker        if not find_in_group(check):
473*bb4ee6a4SAndroid Build Coastguard Worker            raise Exception(f"Check {check.name} is not included in any group.")
474*bb4ee6a4SAndroid Build Coastguard Worker
475*bb4ee6a4SAndroid Build Coastguard Worker    all_names = [c.name for c in CHECKS] + [g.name for g in GROUPS]
476*bb4ee6a4SAndroid Build Coastguard Worker    for name in all_names:
477*bb4ee6a4SAndroid Build Coastguard Worker        if all_names.count(name) > 1:
478*bb4ee6a4SAndroid Build Coastguard Worker            raise Exception(f"Check or group {name} is defined multiple times.")
479*bb4ee6a4SAndroid Build Coastguard Worker
480*bb4ee6a4SAndroid Build Coastguard Worker
481*bb4ee6a4SAndroid Build Coastguard Workerdef get_check_names_in_group(group: Group) -> Generator[str, None, None]:
482*bb4ee6a4SAndroid Build Coastguard Worker    for name in group.checks:
483*bb4ee6a4SAndroid Build Coastguard Worker        if name in GROUPS_DICT:
484*bb4ee6a4SAndroid Build Coastguard Worker            yield from get_check_names_in_group(GROUPS_DICT[name])
485*bb4ee6a4SAndroid Build Coastguard Worker        else:
486*bb4ee6a4SAndroid Build Coastguard Worker            yield name
487*bb4ee6a4SAndroid Build Coastguard Worker
488*bb4ee6a4SAndroid Build Coastguard Worker
489*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--list-checks", default=False, help="List names of available checks and exit.")
490*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--fix", default=False, help="Asks checks to fix problems where possible.")
491*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-delta", default=False, help="Run on all files instead of just modified files.")
492*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg("--no-parallel", default=False, help="Do not run checks in parallel.")
493*bb4ee6a4SAndroid Build Coastguard Worker@argh.arg(
494*bb4ee6a4SAndroid Build Coastguard Worker    "checks_or_groups",
495*bb4ee6a4SAndroid Build Coastguard Worker    help="List of checks or groups to run. Defaults to run the `default` group.",
496*bb4ee6a4SAndroid Build Coastguard Worker)
497*bb4ee6a4SAndroid Build Coastguard Workerdef main(
498*bb4ee6a4SAndroid Build Coastguard Worker    list_checks: bool = False,
499*bb4ee6a4SAndroid Build Coastguard Worker    fix: bool = False,
500*bb4ee6a4SAndroid Build Coastguard Worker    no_delta: bool = False,
501*bb4ee6a4SAndroid Build Coastguard Worker    no_parallel: bool = False,
502*bb4ee6a4SAndroid Build Coastguard Worker    *checks_or_groups: str,
503*bb4ee6a4SAndroid Build Coastguard Worker):
504*bb4ee6a4SAndroid Build Coastguard Worker    chdir(CROSVM_ROOT)
505*bb4ee6a4SAndroid Build Coastguard Worker    validate_config()
506*bb4ee6a4SAndroid Build Coastguard Worker
507*bb4ee6a4SAndroid Build Coastguard Worker    if not checks_or_groups:
508*bb4ee6a4SAndroid Build Coastguard Worker        checks_or_groups = ("default",)
509*bb4ee6a4SAndroid Build Coastguard Worker
510*bb4ee6a4SAndroid Build Coastguard Worker    # Resolve and validate the groups and checks provided
511*bb4ee6a4SAndroid Build Coastguard Worker    check_names: List[str] = []
512*bb4ee6a4SAndroid Build Coastguard Worker    for check_or_group in checks_or_groups:
513*bb4ee6a4SAndroid Build Coastguard Worker        if check_or_group in CHECKS_DICT:
514*bb4ee6a4SAndroid Build Coastguard Worker            check_names.append(check_or_group)
515*bb4ee6a4SAndroid Build Coastguard Worker        elif check_or_group in GROUPS_DICT:
516*bb4ee6a4SAndroid Build Coastguard Worker            check_names += list(get_check_names_in_group(GROUPS_DICT[check_or_group]))
517*bb4ee6a4SAndroid Build Coastguard Worker        else:
518*bb4ee6a4SAndroid Build Coastguard Worker            raise Exception(f"No such check or group: {check_or_group}")
519*bb4ee6a4SAndroid Build Coastguard Worker
520*bb4ee6a4SAndroid Build Coastguard Worker    # Remove duplicates while preserving order
521*bb4ee6a4SAndroid Build Coastguard Worker    check_names = list(dict.fromkeys(check_names))
522*bb4ee6a4SAndroid Build Coastguard Worker
523*bb4ee6a4SAndroid Build Coastguard Worker    if list_checks:
524*bb4ee6a4SAndroid Build Coastguard Worker        for check in check_names:
525*bb4ee6a4SAndroid Build Coastguard Worker            print(check)
526*bb4ee6a4SAndroid Build Coastguard Worker        return
527*bb4ee6a4SAndroid Build Coastguard Worker
528*bb4ee6a4SAndroid Build Coastguard Worker    check_list = [CHECKS_DICT[name] for name in check_names]
529*bb4ee6a4SAndroid Build Coastguard Worker
530*bb4ee6a4SAndroid Build Coastguard Worker    run_checks(
531*bb4ee6a4SAndroid Build Coastguard Worker        check_list,
532*bb4ee6a4SAndroid Build Coastguard Worker        fix=fix,
533*bb4ee6a4SAndroid Build Coastguard Worker        run_on_all_files=no_delta,
534*bb4ee6a4SAndroid Build Coastguard Worker        parallel=not no_parallel,
535*bb4ee6a4SAndroid Build Coastguard Worker    )
536*bb4ee6a4SAndroid Build Coastguard Worker
537*bb4ee6a4SAndroid Build Coastguard Worker
538*bb4ee6a4SAndroid Build Coastguard Workerdef usage():
539*bb4ee6a4SAndroid Build Coastguard Worker    groups = "\n".join(f"  {group.name}: {group.doc}" for group in GROUPS)
540*bb4ee6a4SAndroid Build Coastguard Worker    checks = "\n".join(f"  {check.name}: {check.doc}" for check in CHECKS)
541*bb4ee6a4SAndroid Build Coastguard Worker    return f"""\
542*bb4ee6a4SAndroid Build Coastguard WorkerRuns checks on the crosvm codebase.
543*bb4ee6a4SAndroid Build Coastguard Worker
544*bb4ee6a4SAndroid Build Coastguard WorkerBasic usage, to run a default selection of checks:
545*bb4ee6a4SAndroid Build Coastguard Worker
546*bb4ee6a4SAndroid Build Coastguard Worker    ./tools/presubmit
547*bb4ee6a4SAndroid Build Coastguard Worker
548*bb4ee6a4SAndroid Build Coastguard WorkerSome checkers can fix issues they find (e.g. formatters, clippy, etc):
549*bb4ee6a4SAndroid Build Coastguard Worker
550*bb4ee6a4SAndroid Build Coastguard Worker    ./tools/presubmit --fix
551*bb4ee6a4SAndroid Build Coastguard Worker
552*bb4ee6a4SAndroid Build Coastguard Worker
553*bb4ee6a4SAndroid Build Coastguard WorkerVarious groups of presubmit checks can be run via:
554*bb4ee6a4SAndroid Build Coastguard Worker
555*bb4ee6a4SAndroid Build Coastguard Worker    ./tools/presubmit group_name
556*bb4ee6a4SAndroid Build Coastguard Worker
557*bb4ee6a4SAndroid Build Coastguard WorkerAvailable groups are:
558*bb4ee6a4SAndroid Build Coastguard Worker{groups}
559*bb4ee6a4SAndroid Build Coastguard Worker
560*bb4ee6a4SAndroid Build Coastguard WorkerYou can also provide the names of specific checks to run:
561*bb4ee6a4SAndroid Build Coastguard Worker
562*bb4ee6a4SAndroid Build Coastguard Worker    ./tools/presubmit check1 check2
563*bb4ee6a4SAndroid Build Coastguard Worker
564*bb4ee6a4SAndroid Build Coastguard WorkerAvailable checks are:
565*bb4ee6a4SAndroid Build Coastguard Worker{checks}
566*bb4ee6a4SAndroid Build Coastguard Worker"""
567*bb4ee6a4SAndroid Build Coastguard Worker
568*bb4ee6a4SAndroid Build Coastguard Worker
569*bb4ee6a4SAndroid Build Coastguard Workerif __name__ == "__main__":
570*bb4ee6a4SAndroid Build Coastguard Worker    run_main(main, usage=usage())
571