xref: /aosp_15_r20/external/pigweed/pw_build/py/pw_build/pigweed_upstream_build.py (revision 61c4878ac05f98d0ceed94b57d316916de578985)
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"""Default pw build script for upstream Pigweed.
15
16Usage:
17
18   pw build --list
19   pw build --recipe default_gn
20   pw build --recipe default_* --watch
21   pw build --step gn_combined_build_check --step gn_python_*
22   pw build -C out/gn --watch
23"""
24
25import argparse
26import logging
27from pathlib import Path
28import shutil
29import sys
30
31import pw_cli.env
32import pw_presubmit.pigweed_presubmit
33from pw_presubmit.build import gn_args
34
35from pw_build.build_recipe import (
36    BuildCommand,
37    BuildRecipe,
38    should_gn_gen,
39    should_regenerate_cmake,
40)
41from pw_build.project_builder_presubmit_runner import (
42    get_parser,
43    main,
44)
45
46
47_LOG = logging.getLogger('pw_build')
48
49_PW_ENV = pw_cli.env.pigweed_environment()
50_REPO_ROOT = pw_cli.env.project_root()
51_PACKAGE_ROOT = _PW_ENV.PW_PACKAGE_ROOT
52if not _PACKAGE_ROOT:
53    _PACKAGE_ROOT = _REPO_ROOT / 'environment/packages'
54
55
56def gn_recipe() -> BuildRecipe:
57    """Return the default_gn recipe."""
58    default_gn_gen_command = [
59        'gn',
60        'gen',
61        # NOTE: Not an f-string. BuildRecipe will replace with the out dir.
62        '{build_dir}',
63        '--export-compile-commands',
64    ]
65
66    if shutil.which('ccache'):
67        default_gn_gen_command.append(gn_args(pw_command_launcher='ccache'))
68
69    return BuildRecipe(
70        build_dir=Path('out/gn'),
71        title='default_gn',
72        steps=[
73            BuildCommand(
74                run_if=should_gn_gen,
75                command=default_gn_gen_command,
76            ),
77            BuildCommand(
78                build_system_command='ninja',
79                targets=['default'],
80            ),
81        ],
82    )
83
84
85def bazel_recipe() -> BuildRecipe:
86    """Return the default_bazel recipe."""
87    default_bazel_targets = ['//...:all']
88
89    return BuildRecipe(
90        build_dir=Path('out/bazel'),
91        title='default_bazel',
92        steps=[
93            BuildCommand(
94                build_system_command='bazel',
95                build_system_extra_args=[
96                    'build',
97                    '--verbose_failures',
98                    '--worker_verbose',
99                ],
100                targets=default_bazel_targets,
101            ),
102            BuildCommand(
103                build_system_command='bazel',
104                build_system_extra_args=[
105                    'test',
106                    '--test_output=errors',
107                ],
108                targets=default_bazel_targets,
109            ),
110        ],
111    )
112
113
114def cmake_recipe() -> BuildRecipe:
115    """Construct the default_cmake recipe."""
116    toolchain_path = (
117        _REPO_ROOT / 'pw_toolchain' / 'host_clang' / 'toolchain.cmake'
118    )
119
120    cmake_generate_command = [
121        'cmake',
122        '--fresh',
123        '--debug-output',
124        '-DCMAKE_MESSAGE_LOG_LEVEL=WARNING',
125        '-S',
126        str(_REPO_ROOT),
127        '-B',
128        # NOTE: Not an f-string. BuildRecipe will replace with the out dir.
129        '{build_dir}',
130        '-G',
131        'Ninja',
132        f'-DCMAKE_TOOLCHAIN_FILE={toolchain_path}',
133        '-DCMAKE_EXPORT_COMPILE_COMMANDS=1',
134        f'-Ddir_pw_third_party_nanopb={_PACKAGE_ROOT / "nanopb"}',
135        '-Dpw_third_party_nanopb_ADD_SUBDIRECTORY=ON',
136        f'-Ddir_pw_third_party_emboss={_PACKAGE_ROOT / "emboss"}',
137        f'-Ddir_pw_third_party_boringssl={_PACKAGE_ROOT / "boringssl"}',
138    ]
139
140    if shutil.which('ccache'):
141        cmake_generate_command.append("-DCMAKE_C_COMPILER_LAUNCHER=ccache")
142        cmake_generate_command.append("-DCMAKE_CXX_COMPILER_LAUNCHER=ccache")
143
144    pw_package_install_steps = [
145        BuildCommand(
146            command=['pw', '--no-banner', 'package', 'install', package],
147        )
148        for package in ['emboss', 'nanopb', 'boringssl']
149    ]
150
151    return BuildRecipe(
152        build_dir=Path('out/cmake'),
153        title='default_cmake',
154        steps=pw_package_install_steps
155        + [
156            BuildCommand(
157                run_if=should_regenerate_cmake(cmake_generate_command),
158                command=cmake_generate_command,
159            ),
160            BuildCommand(
161                build_system_command='ninja',
162                targets=pw_presubmit.pigweed_presubmit.CMAKE_TARGETS,
163            ),
164        ],
165    )
166
167
168def pigweed_upstream_main() -> int:
169    """Entry point for Pigweed upstream ``pw build`` command.
170
171    Defines one or more BuildRecipes and passes that along with all of Pigweed
172    upstream presubmit programs to the project_builder_presubmit_runner.main to
173    start a pw build invocation.
174
175    Returns:
176      An int representing the success or failure status of the build; 0 if
177      successful, 1 if failed.
178
179    Command line usage examples:
180
181    .. code-block:: bash
182
183       pw build --list
184       pw build --recipe default_gn
185       pw build --recipe default_* --watch
186       pw build --step gn_combined_build_check --step gn_python_*
187       pw build -C out/gn --watch
188    """
189
190    return main(
191        presubmit_programs=pw_presubmit.pigweed_presubmit.PROGRAMS,
192        build_recipes=[
193            gn_recipe(),
194            bazel_recipe(),
195            cmake_recipe(),
196        ],
197        default_root_logfile=Path('out/build.txt'),
198    )
199
200
201def _build_argument_parser() -> argparse.ArgumentParser:
202    return get_parser(
203        pw_presubmit.pigweed_presubmit.PROGRAMS,
204        [
205            gn_recipe(),
206            bazel_recipe(),
207            cmake_recipe(),
208        ],
209    )
210
211
212if __name__ == '__main__':
213    sys.exit(pigweed_upstream_main())
214