blob: e48a191243f82cf850ff599c03f08d25aa287269 [file] [log] [blame]
# Copyright 2023 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.
"""Checks that source files are listed in build files, such as BUILD.bazel."""
import logging
from typing import Callable, Sequence
from pw_presubmit import build, format_code, git_repo, presubmit_context
from pw_presubmit.presubmit import (
Check,
FileFilter,
)
from pw_presubmit.presubmit_context import (
PresubmitContext,
PresubmitFailure,
)
_LOG: logging.Logger = logging.getLogger(__name__)
# The filter is used twice for each source_is_in_* check. First to decide
# whether the check should be run. Once it's running, we use ctx.all_paths
# instead of ctx.paths since we want to check that all files are in the build,
# not just changed files, but we need to run ctx.all_paths through the same
# filter within the check or we won't properly ignore files that the caller
# asked to be ignored.
_DEFAULT_BAZEL_EXTENSIONS = (*format_code.C_FORMAT.extensions,)
def bazel(
source_filter: FileFilter,
files_and_extensions_to_check: Sequence[str] = _DEFAULT_BAZEL_EXTENSIONS,
) -> Check:
"""Create a presubmit check that ensures source files are in Bazel files.
Args:
source_filter: filter that selects files that must be in the Bazel build
files_and_extensions_to_check: files and extensions to look for (the
source_filter might match build files that won't be in the build but
this should only match source files)
"""
@source_filter.apply_to_check()
def source_is_in_bazel_build(ctx: PresubmitContext):
"""Checks that source files are in the Bazel build."""
paths = source_filter.filter(ctx.all_paths)
paths = presubmit_context.apply_exclusions(ctx, paths)
missing = build.check_bazel_build_for_files(
files_and_extensions_to_check,
paths,
bazel_dirs=[ctx.root],
)
if missing:
with ctx.failure_summary_log.open('w') as outs:
print('Missing files:', file=outs)
for miss in missing:
print(miss, file=outs)
_LOG.warning('All source files must appear in BUILD.bazel files')
raise PresubmitFailure
return source_is_in_bazel_build
_DEFAULT_GN_EXTENSIONS = (
'setup.cfg',
'.toml',
'.rst',
'.py',
*format_code.C_FORMAT.extensions,
)
def gn( # pylint: disable=invalid-name
source_filter: FileFilter,
files_and_extensions_to_check: Sequence[str] = _DEFAULT_GN_EXTENSIONS,
) -> Check:
"""Create a presubmit check that ensures source files are in GN files.
Args:
source_filter: filter that selects files that must be in the GN build
files_and_extensions_to_check: files and extensions to look for (the
source_filter might match build files that won't be in the build but
this should only match source files)
"""
@source_filter.apply_to_check()
def source_is_in_gn_build(ctx: PresubmitContext):
"""Checks that source files are in the GN build."""
paths = source_filter.filter(ctx.all_paths)
paths = presubmit_context.apply_exclusions(ctx, paths)
missing = build.check_gn_build_for_files(
files_and_extensions_to_check,
paths,
gn_build_files=git_repo.list_files(
pathspecs=['BUILD.gn', '*BUILD.gn'], repo_path=ctx.root
),
)
if missing:
with ctx.failure_summary_log.open('w') as outs:
print('Missing files:', file=outs)
for miss in missing:
print(miss, file=outs)
_LOG.warning('All source files must appear in BUILD.gn files')
raise PresubmitFailure
return source_is_in_gn_build
_DEFAULT_CMAKE_EXTENSIONS = (*format_code.C_FORMAT.extensions,)
def cmake(
source_filter: FileFilter,
run_cmake: Callable[[PresubmitContext], None],
files_and_extensions_to_check: Sequence[str] = _DEFAULT_CMAKE_EXTENSIONS,
) -> Check:
"""Create a presubmit check that ensures source files are in CMake files.
Args:
source_filter: filter that selects files that must be in the CMake build
run_cmake: callable that takes a PresubmitContext and invokes CMake
files_and_extensions_to_check: files and extensions to look for (the
source_filter might match build files that won't be in the build but
this should only match source files)
"""
to_check = tuple(files_and_extensions_to_check)
@source_filter.apply_to_check()
def source_is_in_cmake_build(ctx: PresubmitContext):
"""Checks that source files are in the CMake build."""
paths = source_filter.filter(ctx.all_paths)
paths = presubmit_context.apply_exclusions(ctx, paths)
run_cmake(ctx)
missing = build.check_compile_commands_for_files(
ctx.output_dir / 'compile_commands.json',
(f for f in paths if str(f).endswith(to_check)),
)
if missing:
with ctx.failure_summary_log.open('w') as outs:
print('Missing files:', file=outs)
for miss in missing:
print(miss, file=outs)
_LOG.warning(
'Files missing from CMake:\n%s',
'\n'.join(str(f) for f in missing),
)
raise PresubmitFailure
return source_is_in_cmake_build