|  | #!/usr/bin/env python3 | 
|  | # Copyright 2020 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 | 
|  |  | 
|  | 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['SAMPLE_PROJECT_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' | 
|  |  | 
|  | # 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 check_for_git_changes(ctx: PresubmitContext): | 
|  | """Checks that repositories have all changes committed.""" | 
|  | checked_repos = (PIGWEED_ROOT, *ctx.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,) | 
|  |  | 
|  | QUICK = ( | 
|  | default_build, | 
|  | format_code.presubmit_checks( | 
|  | code_formats=format_code.CODE_FORMATS_WITH_BLACK | 
|  | ), | 
|  | ) | 
|  |  | 
|  | LINTFORMAT = ( | 
|  | # keep-sorted: start | 
|  | cpp_checks.pragma_once, | 
|  | format_code.presubmit_checks(), | 
|  | inclusive_language.presubmit_check, | 
|  | keep_sorted.presubmit_check, | 
|  | python_checks.gn_python_lint, | 
|  | # keep-sorted: end | 
|  | ) | 
|  |  | 
|  | FULL = ( | 
|  | QUICK,  # Add all checks from the 'quick' program | 
|  | LINTFORMAT, | 
|  | # Use the upstream Python checks, with custom path filters applied. | 
|  | python_checks.gn_python_check, | 
|  | ) | 
|  |  | 
|  | PROGRAMS = pw_presubmit.Programs( | 
|  | # keep-sorted: start | 
|  | 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', | 
|  | 'sample_project_tools.presubmit_checks', | 
|  | '--base', | 
|  | 'origin/main..HEAD', | 
|  | '--program', | 
|  | 'quick', | 
|  | ], | 
|  | ) | 
|  | return 0 | 
|  |  | 
|  | exclude.extend(PATH_EXCLUSIONS) | 
|  | repos = git_repo.discover_submodules(superproject_dir=PROJECT_ROOT) | 
|  | return cli.run( | 
|  | root=PROJECT_ROOT, repositories=repos, 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()) |