• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2023 The Pigweed Authors
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"""Checks that source files are listed in build files, such as BUILD.bazel."""
15
16import logging
17from typing import Callable, Sequence
18
19from pw_presubmit import build, format_code, git_repo
20from pw_presubmit.presubmit import (
21    Check,
22    FileFilter,
23    PresubmitContext,
24    PresubmitFailure,
25)
26
27_LOG: logging.Logger = logging.getLogger(__name__)
28
29# The filter is used twice for each source_is_in_* check.  First to decide
30# whether the check should be run. Once it's running, we use ctx.all_paths
31# instead of ctx.paths since we want to check that all files are in the build,
32# not just changed files, but we need to run ctx.all_paths through the same
33# filter within the check or we won't properly ignore files that the caller
34# asked to be ignored.
35
36_DEFAULT_BAZEL_EXTENSIONS = (*format_code.C_FORMAT.extensions,)
37
38
39def bazel(
40    source_filter: FileFilter,
41    files_and_extensions_to_check: Sequence[str] = _DEFAULT_BAZEL_EXTENSIONS,
42) -> Check:
43    """Create a presubmit check that ensures source files are in Bazel files.
44
45    Args:
46        source_filter: filter that selects files that must be in the Bazel build
47        files_and_extensions_to_check: files and extensions to look for (the
48            source_filter might match build files that won't be in the build but
49            this should only match source files)
50    """
51
52    @source_filter.apply_to_check()
53    def source_is_in_bazel_build(ctx: PresubmitContext):
54        """Checks that source files are in the Bazel build."""
55
56        paths = source_filter.filter(ctx.all_paths)
57
58        missing = build.check_bazel_build_for_files(
59            files_and_extensions_to_check,
60            paths,
61            bazel_dirs=[ctx.root],
62        )
63
64        if missing:
65            with ctx.failure_summary_log.open('w') as outs:
66                print('Missing files:', file=outs)
67                for miss in missing:
68                    print(miss, file=outs)
69
70            _LOG.warning('All source files must appear in BUILD.bazel files')
71            raise PresubmitFailure
72
73    return source_is_in_bazel_build
74
75
76_DEFAULT_GN_EXTENSIONS = (
77    'setup.cfg',
78    '.toml',
79    '.rst',
80    '.py',
81    *format_code.C_FORMAT.extensions,
82)
83
84
85def gn(  # pylint: disable=invalid-name
86    source_filter: FileFilter,
87    files_and_extensions_to_check: Sequence[str] = _DEFAULT_GN_EXTENSIONS,
88) -> Check:
89    """Create a presubmit check that ensures source files are in GN files.
90
91    Args:
92        source_filter: filter that selects files that must be in the GN build
93        files_and_extensions_to_check: files and extensions to look for (the
94            source_filter might match build files that won't be in the build but
95            this should only match source files)
96    """
97
98    @source_filter.apply_to_check()
99    def source_is_in_gn_build(ctx: PresubmitContext):
100        """Checks that source files are in the GN build."""
101
102        paths = source_filter.filter(ctx.all_paths)
103
104        missing = build.check_gn_build_for_files(
105            files_and_extensions_to_check,
106            paths,
107            gn_build_files=git_repo.list_files(
108                pathspecs=['BUILD.gn', '*BUILD.gn'], repo_path=ctx.root
109            ),
110        )
111
112        if missing:
113            with ctx.failure_summary_log.open('w') as outs:
114                print('Missing files:', file=outs)
115                for miss in missing:
116                    print(miss, file=outs)
117
118            _LOG.warning('All source files must appear in BUILD.gn files')
119            raise PresubmitFailure
120
121    return source_is_in_gn_build
122
123
124_DEFAULT_CMAKE_EXTENSIONS = (*format_code.C_FORMAT.extensions,)
125
126
127def cmake(
128    source_filter: FileFilter,
129    run_cmake: Callable[[PresubmitContext], None],
130    files_and_extensions_to_check: Sequence[str] = _DEFAULT_CMAKE_EXTENSIONS,
131) -> Check:
132    """Create a presubmit check that ensures source files are in CMake files.
133
134    Args:
135        source_filter: filter that selects files that must be in the CMake build
136        run_cmake: callable that takes a PresubmitContext and invokes CMake
137        files_and_extensions_to_check: files and extensions to look for (the
138            source_filter might match build files that won't be in the build but
139            this should only match source files)
140    """
141
142    to_check = tuple(files_and_extensions_to_check)
143
144    @source_filter.apply_to_check()
145    def source_is_in_cmake_build(ctx: PresubmitContext):
146        """Checks that source files are in the CMake build."""
147
148        paths = source_filter.filter(ctx.all_paths)
149
150        run_cmake(ctx)
151        missing = build.check_compile_commands_for_files(
152            ctx.output_dir / 'compile_commands.json',
153            (f for f in paths if str(f).endswith(to_check)),
154        )
155
156        if missing:
157            with ctx.failure_summary_log.open('w') as outs:
158                print('Missing files:', file=outs)
159                for miss in missing:
160                    print(miss, file=outs)
161
162            _LOG.warning(
163                'Files missing from CMake:\n%s',
164                '\n'.join(str(f) for f in missing),
165            )
166            raise PresubmitFailure
167
168    return source_is_in_cmake_build
169