xref: /aosp_15_r20/external/open-dice/tools/open_dice_tools/presubmit.py (revision 60b67249c2e226f42f35cc6cfe66c6048e0bae6b)
1# Copyright 2023 Google LLC
2#
3# Licensed under the Apache License, Version 2.0 (the "License"); you may not
4# use this file except in compliance with the License. You may obtain a copy of
5# the License at
6#
7#     https://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12# License for the specific language governing permissions and limitations under
13# the License.
14"""Presubmit script."""
15
16import argparse
17import logging
18import os
19from pathlib import Path
20import re
21import sys
22
23import pw_cli.log
24import pw_presubmit
25from pw_presubmit import (
26    build,
27    cli,
28    cpp_checks,
29    format_code,
30    git_repo,
31    inclusive_language,
32    install_hook,
33    keep_sorted,
34    presubmit_context,
35    python_checks,
36)
37
38_LOG = logging.getLogger(__name__)
39
40# Set up variables for key project paths.
41try:
42    PROJECT_ROOT = Path(os.environ["PW_PROJECT_ROOT"])
43except KeyError:
44    print(
45        "ERROR: The presubmit checks must be run in the Open Dice project's "
46        "root directory",
47        file=sys.stderr,
48    )
49    sys.exit(2)
50
51PIGWEED_ROOT = PROJECT_ROOT / "third_party" / "pigweed" / "src"
52
53# Rerun the build if files with these extensions change.
54_BUILD_EXTENSIONS = frozenset(
55    [".rst", ".gn", ".gni", *format_code.C_FORMAT.extensions]
56)
57
58default_build = build.GnGenNinja(name="default_build")
59
60EXCLUSIONS = presubmit_context.FormatOptions.load().exclude
61
62OTHER_CHECKS = (build.gn_gen_check,)
63
64_FORMAT = (format_code.presubmit_checks(exclude=EXCLUSIONS),)
65
66QUICK = (
67    default_build,
68    _FORMAT,
69)
70
71
72def include_guard(path: Path) -> str:
73    path = path.relative_to(PROJECT_ROOT)
74    exclude = ("include",)
75    transform = {"ulib": "lib"}
76    parts = [transform.get(x, x) for x in path.parts if x not in exclude]
77    return re.sub(r"[.-]", "_", "".join(f"{x}_" for x in parts).upper())
78
79
80LINTFORMAT = (
81    _FORMAT,
82    cpp_checks.include_guard_check(include_guard).with_filter(
83        exclude=EXCLUSIONS
84    ),
85    inclusive_language.presubmit_check.with_filter(exclude=EXCLUSIONS),
86    keep_sorted.presubmit_check.with_filter(exclude=EXCLUSIONS),
87    python_checks.gn_python_lint,
88)
89
90FULL = (
91    QUICK,  # Add all checks from the 'quick' program
92    LINTFORMAT,
93    # Use the upstream Python checks, with custom path filters applied.
94    python_checks.gn_python_check,
95)
96
97PROGRAMS = pw_presubmit.Programs(
98    # keep-sorted: start
99    full=FULL,
100    lintformat=LINTFORMAT,
101    other_checks=OTHER_CHECKS,
102    quick=QUICK,
103    # keep-sorted: end
104)
105
106
107def run(install: bool, exclude: list, **presubmit_args) -> int:
108    """Process the --install argument then invoke pw_presubmit."""
109
110    # Install the presubmit Git pre-push hook, if requested.
111    if install:
112        install_hook.install_git_hook(
113            "pre-push",
114            [
115                "python",
116                "-m",
117                "open_dice_tools.presubmit",
118                "--base",
119                "origin/main..HEAD",
120                "--program",
121                "quick",
122            ],
123        )
124        return 0
125
126    repos = git_repo.discover_submodules(superproject_dir=PROJECT_ROOT)
127    return cli.run(
128        root=PROJECT_ROOT, repositories=repos, exclude=exclude, **presubmit_args
129    )
130
131
132def main() -> int:
133    """Run the presubmit checks for this repository."""
134    parser = argparse.ArgumentParser(description=__doc__)
135    cli.add_arguments(parser, PROGRAMS, "quick")
136
137    # Define an option for installing a Git pre-push hook for this script.
138    parser.add_argument(
139        "--install",
140        action="store_true",
141        help="Install the presubmit as a Git pre-push hook and exit.",
142    )
143
144    return run(**vars(parser.parse_args()))
145
146
147if __name__ == "__main__":
148    pw_cli.log.install(logging.INFO)
149    sys.exit(main())
150