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