| #!/usr/bin/env python3 |
| # Copyright 2021 The Pigweed Authors |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); you may not |
| # use this file except in compliance with the License. You may obtain a copy of |
| # the License at |
| # |
| # https://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT |
| # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the |
| # License for the specific language governing permissions and limitations under |
| # the License. |
| """Example presubmit check script.""" |
| |
| import argparse |
| import logging |
| import os |
| from pathlib import Path |
| import re |
| import sys |
| from typing import Callable |
| |
| try: |
| import pw_cli.log |
| except ImportError: |
| print('ERROR: Activate the environment before running presubmits!', |
| file=sys.stderr) |
| sys.exit(2) |
| |
| import pw_presubmit |
| from pw_presubmit import ( |
| build, |
| cli, |
| cpp_checks, |
| format_code, |
| git_repo, |
| inclusive_language, |
| install_hook, |
| keep_sorted, |
| python_checks, |
| ) |
| from pw_presubmit.presubmit_context import PresubmitContext, PresubmitFailure |
| |
| _LOG = logging.getLogger(__name__) |
| |
| # Set up variables for key project paths. |
| try: |
| PROJECT_ROOT = Path(os.environ['PIGWEED_EXPERIMENTAL_ROOT']) |
| except KeyError: |
| print( |
| 'ERROR: The presubmit checks must be run in the sample project\'s root' |
| ' directory', |
| file=sys.stderr) |
| sys.exit(2) |
| |
| PIGWEED_ROOT = PROJECT_ROOT / 'third_party' / 'pigweed' |
| REPOS = ( |
| PROJECT_ROOT, |
| PIGWEED_ROOT, |
| PROJECT_ROOT / 'third_party' / 'nanopb', |
| ) |
| |
| # Rerun the build if files with these extensions change. |
| _BUILD_EXTENSIONS = frozenset( |
| ['.rst', '.gn', '.gni', *format_code.C_FORMAT.extensions]) |
| |
| |
| # |
| # Presubmit checks |
| # |
| def default_build(ctx: PresubmitContext): |
| """Creates a default build.""" |
| build.gn_gen(ctx) |
| build.ninja(ctx) |
| |
| |
| def _package_root_arg(name: str) -> Callable[[PresubmitContext], str]: |
| def _format(ctx: PresubmitContext) -> str: |
| return '"{}"'.format(ctx.package_root / name) |
| |
| return _format |
| |
| |
| teensy_build = build.GnGenNinja( |
| name='teensy_build', |
| packages=('teensy', ), |
| gn_args=dict( |
| pw_arduino_build_CORE_PATH=lambda ctx: '"{}"'.format( |
| str(ctx.package_root)), |
| pw_arduino_build_CORE_NAME='"teensy"', |
| pw_arduino_build_PACKAGE_NAME='"avr/1.58.1"', |
| pw_arduino_build_BOARD='"teensy41"', |
| pw_arduino_build_MENU_OPTIONS=( |
| '["menu.usb.serial", "menu.keys.en-us", "menu.opt.o2std"]'), |
| ), |
| ) |
| |
| pico_build = build.GnGenNinja( |
| name='pico_build', |
| packages=('pico_sdk', ), |
| gn_args=dict( |
| PICO_SRC_DIR=_package_root_arg('pico_sdk'), |
| dir_pw_third_party_freertos='"//third_party/freertos/Source"', |
| ), |
| ) |
| |
| stm32cube_f4_build = build.GnGenNinja( |
| name='stm32cube_f4_build', |
| packages=('stm32cube_f4', ), |
| gn_args=dict( |
| dir_pw_third_party_stm32cube_f4=_package_root_arg('stm32cube_f4'), |
| dir_pw_third_party_freertos='"//third_party/freertos/Source"', |
| ), |
| ) |
| |
| stm32cube_f7_build = build.GnGenNinja( |
| name='stm32cube_f7_build', |
| packages=('stm32cube_f7', ), |
| gn_args=dict( |
| dir_pw_third_party_stm32cube_f7=_package_root_arg('stm32cube_f7'), |
| dir_pw_third_party_freertos='"//third_party/freertos/Source"', |
| ), |
| ) |
| |
| mimxrt595_evk_build = build.GnGenNinja( |
| name='mimxrt595_evk_build', |
| gn_args=dict( |
| dir_pw_third_party_freertos='"//third_party/freertos/Source"', |
| pw_MIMXRT595_EVK_SDK=_package_root_arg("SDK_2_12_1_EVK-MIMXRT595"), |
| pw_target_mimxrt595_evk_MANIFEST=_package_root_arg( |
| "SDK_2_12_1_EVK-MIMXRT595/EVK-MIMXRT595_manifest_v3_10.xml"), |
| pw_third_party_mcuxpresso_SDK="//targets/mimxrt595_evk:mimxrt595_sdk", |
| ), |
| ) |
| |
| pw_graphics_host = build.GnGenNinja( |
| name='pw_graphics_host', |
| packages=('imgui', 'glfw'), |
| gn_args=dict( |
| dir_pw_third_party_glfw=_package_root_arg('glfw'), |
| dir_pw_third_party_imgui=_package_root_arg('imgui'), |
| ), |
| ) |
| |
| |
| def check_for_git_changes(_: PresubmitContext): |
| """Checks that repositories have all changes commited.""" |
| checked_repos = (PIGWEED_ROOT, *REPOS) |
| changes = [r for r in checked_repos if git_repo.has_uncommitted_changes(r)] |
| for repo in changes: |
| _LOG.error('There are uncommitted changes in the %s repo!', repo.name) |
| if changes: |
| _LOG.warning( |
| 'Commit or stash pending changes before running the presubmit.') |
| raise PresubmitFailure |
| |
| |
| # Avoid running some checks on certain paths. |
| PATH_EXCLUSIONS = ( |
| re.compile(r'^external/'), |
| re.compile(r'^third_party/'), |
| re.compile(r'^vendor/'), |
| ) |
| |
| # |
| # Presubmit check programs |
| # |
| OTHER_CHECKS = ( |
| build.gn_gen_check, |
| inclusive_language.presubmit_check.with_filter(exclude=PATH_EXCLUSIONS), |
| pw_graphics_host, |
| mimxrt595_evk_build, |
| teensy_build, |
| stm32cube_f7_build, |
| ) |
| |
| QUICK = ( |
| # List some presubmit checks to run |
| default_build, |
| # Use the upstream formatting checks, with custom path filters applied. |
| format_code.presubmit_checks(), |
| ) |
| |
| LINTFORMAT = ( |
| # keep-sorted: start |
| cpp_checks.pragma_once, |
| format_code.presubmit_checks(), |
| keep_sorted.presubmit_check, |
| python_checks.gn_python_lint, |
| # keep-sorted: end |
| ) |
| |
| FULL = ( |
| cpp_checks.pragma_once, |
| QUICK, # Add all checks from the 'quick' program |
| # Use the upstream Python checks, with custom path filters applied. |
| python_checks.gn_python_check, |
| pico_build, |
| stm32cube_f4_build, |
| ) |
| |
| CI_CQ = (default_build, pico_build, stm32cube_f4_build) |
| |
| PROGRAMS = pw_presubmit.Programs( |
| # keep-sorted: start |
| ci_cq=CI_CQ, |
| full=FULL, |
| lintformat=LINTFORMAT, |
| other_checks=OTHER_CHECKS, |
| quick=QUICK, |
| # keep-sorted: end |
| ) |
| |
| |
| def run(install: bool, exclude: list, **presubmit_args) -> int: |
| """Process the --install argument then invoke pw_presubmit.""" |
| |
| # Install the presubmit Git pre-push hook, if requested. |
| if install: |
| install_hook.install_git_hook( |
| 'pre-push', |
| [ |
| 'python', |
| '-m', |
| 'pigweed_experimental_tools.presubmit_checks', |
| '--base', |
| 'origin/main..HEAD', |
| '--program', |
| 'quick', |
| ], |
| ) |
| return 0 |
| |
| exclude.extend(PATH_EXCLUSIONS) |
| return cli.run(root=PROJECT_ROOT, exclude=exclude, **presubmit_args) |
| |
| |
| def main() -> int: |
| """Run the presubmit checks for this repository.""" |
| parser = argparse.ArgumentParser(description=__doc__) |
| cli.add_arguments(parser, PROGRAMS, 'quick') |
| |
| # Define an option for installing a Git pre-push hook for this script. |
| parser.add_argument( |
| '--install', |
| action='store_true', |
| help='Install the presubmit as a Git pre-push hook and exit.') |
| |
| return run(**vars(parser.parse_args())) |
| |
| |
| if __name__ == '__main__': |
| pw_cli.log.install(logging.INFO) |
| sys.exit(main()) |