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