1# Copyright (c) 2018 Foundries.io 2# 3# SPDX-License-Identifier: Apache-2.0 4 5import argparse 6import os 7import pathlib 8import shlex 9import sys 10import yaml 11 12from west import log 13from west.configuration import config 14from zcmake import DEFAULT_CMAKE_GENERATOR, run_cmake, run_build, CMakeCache 15from build_helpers import is_zephyr_build, find_build_dir, load_domains, \ 16 FIND_BUILD_DIR_DESCRIPTION 17 18from zephyr_ext_common import Forceable 19 20_ARG_SEPARATOR = '--' 21 22SYSBUILD_PROJ_DIR = pathlib.Path(__file__).resolve().parent.parent.parent \ 23 / pathlib.Path('share/sysbuild') 24 25BUILD_USAGE = '''\ 26west build [-h] [-b BOARD[@REV]]] [-d BUILD_DIR] 27 [-S SNIPPET] [--shield SHIELD] 28 [-t TARGET] [-p {auto, always, never}] [-c] [--cmake-only] 29 [-n] [-o BUILD_OPT] [-f] 30 [--sysbuild | --no-sysbuild] [--domain DOMAIN] 31 [source_dir] -- [cmake_opt [cmake_opt ...]] 32''' 33 34BUILD_DESCRIPTION = f'''\ 35Convenience wrapper for building Zephyr applications. 36 37{FIND_BUILD_DIR_DESCRIPTION} 38 39positional arguments: 40 source_dir application source directory 41 cmake_opt extra options to pass to cmake; implies -c 42 (these must come after "--" as shown above) 43''' 44 45PRISTINE_DESCRIPTION = """\ 46A "pristine" build directory is empty. The -p option controls 47whether the build directory is made pristine before the build 48is done. A bare '--pristine' with no value is the same as 49--pristine=always. Setting --pristine=auto uses heuristics to 50guess if a pristine build may be necessary.""" 51 52def _banner(msg): 53 log.inf('-- west build: ' + msg, colorize=True) 54 55def config_get(option, fallback): 56 return config.get('build', option, fallback=fallback) 57 58def config_getboolean(option, fallback): 59 return config.getboolean('build', option, fallback=fallback) 60 61class AlwaysIfMissing(argparse.Action): 62 63 def __call__(self, parser, namespace, values, option_string=None): 64 setattr(namespace, self.dest, values or 'always') 65 66class Build(Forceable): 67 68 def __init__(self): 69 super(Build, self).__init__( 70 'build', 71 # Keep this in sync with the string in west-commands.yml. 72 'compile a Zephyr application', 73 BUILD_DESCRIPTION, 74 accepts_unknown_args=True) 75 76 self.source_dir = None 77 '''Source directory for the build, or None on error.''' 78 79 self.build_dir = None 80 '''Final build directory used to run the build, or None on error.''' 81 82 self.created_build_dir = False 83 '''True if the build directory was created; False otherwise.''' 84 85 self.run_cmake = False 86 '''True if CMake was run; False otherwise. 87 88 Note: this only describes CMake runs done by this command. The 89 build system generated by CMake may also update itself due to 90 internal logic.''' 91 92 self.cmake_cache = None 93 '''Final parsed CMake cache for the build, or None on error.''' 94 95 def do_add_parser(self, parser_adder): 96 parser = parser_adder.add_parser( 97 self.name, 98 help=self.help, 99 formatter_class=argparse.RawDescriptionHelpFormatter, 100 description=self.description, 101 usage=BUILD_USAGE) 102 103 # Remember to update west-completion.bash if you add or remove 104 # flags 105 106 parser.add_argument('-b', '--board', 107 help='board to build for with optional board revision') 108 # Hidden option for backwards compatibility 109 parser.add_argument('-s', '--source-dir', help=argparse.SUPPRESS) 110 parser.add_argument('-d', '--build-dir', 111 help='build directory to create or use') 112 self.add_force_arg(parser) 113 114 group = parser.add_argument_group('cmake and build tool') 115 group.add_argument('-c', '--cmake', action='store_true', 116 help='force a cmake run') 117 group.add_argument('--cmake-only', action='store_true', 118 help="just run cmake; don't build (implies -c)") 119 group.add_argument('--domain', action='append', 120 help='''execute build tool (make or ninja) only for 121 given domain''') 122 group.add_argument('-t', '--target', 123 help='''run build system target TARGET 124 (try "-t usage")''') 125 group.add_argument('-T', '--test-item', 126 help='''Build based on test data in testcase.yaml 127 or sample.yaml. If source directory is not used 128 an argument has to be defined as 129 SOURCE_PATH/TEST_NAME. 130 E.g. samples/hello_world/sample.basic.helloworld. 131 If source directory is passed 132 then "TEST_NAME" is enough.''') 133 group.add_argument('-o', '--build-opt', default=[], action='append', 134 help='''options to pass to the build tool 135 (make or ninja); may be given more than once''') 136 group.add_argument('-n', '--just-print', '--dry-run', '--recon', 137 dest='dry_run', action='store_true', 138 help="just print build commands; don't run them") 139 group.add_argument('-S', '--snippet', dest='snippets', metavar='SNIPPET', 140 action='append', default=[], 141 help='''add the argument to SNIPPET; may be given 142 multiple times. Forces CMake to run again if given. 143 Do not use this option with manually specified 144 -DSNIPPET... cmake arguments: the results are 145 undefined''') 146 group.add_argument('--shield', dest='shields', metavar='SHIELD', 147 action='append', default=[], 148 help='''add the argument to SHIELD; may be given 149 multiple times. Forces CMake to run again if given. 150 Do not use this option with manually specified 151 -DSHIELD... cmake arguments: the results are 152 undefined''') 153 154 group = parser.add_mutually_exclusive_group() 155 group.add_argument('--sysbuild', action='store_true', 156 help='''create multi domain build system''') 157 group.add_argument('--no-sysbuild', action='store_true', 158 help='''do not create multi domain build system 159 (default)''') 160 161 group = parser.add_argument_group('pristine builds', 162 PRISTINE_DESCRIPTION) 163 group.add_argument('-p', '--pristine', choices=['auto', 'always', 164 'never'], action=AlwaysIfMissing, nargs='?', 165 help='pristine build folder setting') 166 167 return parser 168 169 def do_run(self, args, remainder): 170 self.args = args # Avoid having to pass them around 171 self.config_board = config_get('board', None) 172 log.dbg('args: {} remainder: {}'.format(args, remainder), 173 level=log.VERBOSE_EXTREME) 174 # Store legacy -s option locally 175 source_dir = self.args.source_dir 176 self._parse_remainder(remainder) 177 # Parse testcase.yaml or sample.yaml files for additional options. 178 if self.args.test_item: 179 # we get path + testitem 180 item = os.path.basename(self.args.test_item) 181 if self.args.source_dir: 182 test_path = self.args.source_dir 183 else: 184 test_path = os.path.dirname(self.args.test_item) 185 if test_path and os.path.exists(test_path): 186 self.args.source_dir = test_path 187 if not self._parse_test_item(item): 188 log.die("No test metadata found") 189 else: 190 log.die("test item path does not exist") 191 192 if source_dir: 193 if self.args.source_dir: 194 log.die("source directory specified twice:({} and {})".format( 195 source_dir, self.args.source_dir)) 196 self.args.source_dir = source_dir 197 log.dbg('source_dir: {} cmake_opts: {}'.format(self.args.source_dir, 198 self.args.cmake_opts), 199 level=log.VERBOSE_EXTREME) 200 self._sanity_precheck() 201 self._setup_build_dir() 202 203 if args.pristine is not None: 204 pristine = args.pristine 205 else: 206 # Load the pristine={auto, always, never} configuration value 207 pristine = config_get('pristine', 'never') 208 if pristine not in ['auto', 'always', 'never']: 209 log.wrn( 210 'treating unknown build.pristine value "{}" as "never"'. 211 format(pristine)) 212 pristine = 'never' 213 self.auto_pristine = pristine == 'auto' 214 215 log.dbg('pristine: {} auto_pristine: {}'.format(pristine, 216 self.auto_pristine), 217 level=log.VERBOSE_VERY) 218 if is_zephyr_build(self.build_dir): 219 if pristine == 'always': 220 self._run_pristine() 221 self.run_cmake = True 222 else: 223 self._update_cache() 224 if (self.args.cmake or self.args.cmake_opts or 225 self.args.cmake_only or self.args.snippets or 226 self.args.shields): 227 self.run_cmake = True 228 else: 229 self.run_cmake = True 230 self.source_dir = self._find_source_dir() 231 self._sanity_check() 232 233 board, origin = self._find_board() 234 self._run_cmake(board, origin, self.args.cmake_opts) 235 if args.cmake_only: 236 return 237 238 self._sanity_check() 239 self._update_cache() 240 self.domains = load_domains(self.build_dir) 241 242 self._run_build(args.target, args.domain) 243 244 def _find_board(self): 245 board, origin = None, None 246 if self.cmake_cache: 247 board, origin = (self.cmake_cache.get('CACHED_BOARD'), 248 'CMakeCache.txt') 249 250 # A malformed CMake cache may exist, but not have a board. 251 # This happens if there's a build error from a previous run. 252 if board is not None: 253 return (board, origin) 254 255 if self.args.board: 256 board, origin = self.args.board, 'command line' 257 elif 'BOARD' in os.environ: 258 board, origin = os.environ['BOARD'], 'env' 259 elif self.config_board is not None: 260 board, origin = self.config_board, 'configfile' 261 return board, origin 262 263 def _parse_remainder(self, remainder): 264 self.args.source_dir = None 265 self.args.cmake_opts = None 266 267 try: 268 # Only one source_dir is allowed, as the first positional arg 269 if remainder[0] != _ARG_SEPARATOR: 270 self.args.source_dir = remainder[0] 271 remainder = remainder[1:] 272 # Only the first argument separator is consumed, the rest are 273 # passed on to CMake 274 if remainder[0] == _ARG_SEPARATOR: 275 remainder = remainder[1:] 276 if remainder: 277 self.args.cmake_opts = remainder 278 except IndexError: 279 pass 280 281 def _parse_test_item(self, test_item): 282 found_test_metadata = False 283 for yp in ['sample.yaml', 'testcase.yaml']: 284 yf = os.path.join(self.args.source_dir, yp) 285 if not os.path.exists(yf): 286 continue 287 found_test_metadata = True 288 with open(yf, 'r') as stream: 289 try: 290 y = yaml.safe_load(stream) 291 except yaml.YAMLError as exc: 292 log.die(exc) 293 common = y.get('common') 294 tests = y.get('tests') 295 if not tests: 296 log.die(f"No tests found in {yf}") 297 if test_item not in tests: 298 log.die(f"Test item {test_item} not found in {yf}") 299 item = tests.get(test_item) 300 301 sysbuild = False 302 extra_dtc_overlay_files = [] 303 extra_overlay_confs = [] 304 extra_conf_files = [] 305 required_snippets = [] 306 for section in [common, item]: 307 if not section: 308 continue 309 sysbuild = section.get('sysbuild', sysbuild) 310 for data in [ 311 'extra_args', 312 'extra_configs', 313 'extra_conf_files', 314 'extra_overlay_confs', 315 'extra_dtc_overlay_files', 316 'required_snippets' 317 ]: 318 extra = section.get(data) 319 if not extra: 320 continue 321 if isinstance(extra, str): 322 arg_list = extra.split(" ") 323 else: 324 arg_list = extra 325 326 if data == 'extra_configs': 327 args = ["-D{}".format(arg.replace('"', '\"')) for arg in arg_list] 328 elif data == 'extra_args': 329 # Retain quotes around config options 330 config_options = [arg for arg in arg_list if arg.startswith("CONFIG_")] 331 non_config_options = [arg for arg in arg_list if not arg.startswith("CONFIG_")] 332 args = ["-D{}".format(a.replace('"', '\"')) for a in config_options] 333 args.extend(["-D{}".format(arg.replace('"', '')) for arg in non_config_options]) 334 elif data == 'extra_conf_files': 335 extra_conf_files.extend(arg_list) 336 continue 337 elif data == 'extra_overlay_confs': 338 extra_overlay_confs.extend(arg_list) 339 continue 340 elif data == 'extra_dtc_overlay_files': 341 extra_dtc_overlay_files.extend(arg_list) 342 continue 343 elif data == 'required_snippets': 344 required_snippets.extend(arg_list) 345 continue 346 347 if self.args.cmake_opts: 348 self.args.cmake_opts.extend(args) 349 else: 350 self.args.cmake_opts = args 351 352 self.args.sysbuild = sysbuild 353 354 if found_test_metadata: 355 args = [] 356 if extra_conf_files: 357 args.append(f"CONF_FILE=\"{';'.join(extra_conf_files)}\"") 358 359 if extra_dtc_overlay_files: 360 args.append(f"DTC_OVERLAY_FILE=\"{';'.join(extra_dtc_overlay_files)}\"") 361 362 if extra_overlay_confs: 363 args.append(f"OVERLAY_CONFIG=\"{';'.join(extra_overlay_confs)}\"") 364 365 if required_snippets: 366 args.append(f"SNIPPET=\"{';'.join(required_snippets)}\"") 367 368 # Build the final argument list 369 args_expanded = ["-D{}".format(a.replace('"', '')) for a in args] 370 371 if self.args.cmake_opts: 372 self.args.cmake_opts.extend(args_expanded) 373 else: 374 self.args.cmake_opts = args_expanded 375 376 return found_test_metadata 377 378 def _sanity_precheck(self): 379 app = self.args.source_dir 380 if app: 381 self.check_force( 382 os.path.isdir(app), 383 'source directory {} does not exist'.format(app)) 384 self.check_force( 385 'CMakeLists.txt' in os.listdir(app), 386 "{} doesn't contain a CMakeLists.txt".format(app)) 387 388 def _update_cache(self): 389 try: 390 self.cmake_cache = CMakeCache.from_build_dir(self.build_dir) 391 except FileNotFoundError: 392 pass 393 394 def _setup_build_dir(self): 395 # Initialize build_dir and created_build_dir attributes. 396 # If we created the build directory, we must run CMake. 397 log.dbg('setting up build directory', level=log.VERBOSE_EXTREME) 398 # The CMake Cache has not been loaded yet, so this is safe 399 board, _ = self._find_board() 400 source_dir = self._find_source_dir() 401 app = os.path.split(source_dir)[1] 402 build_dir = find_build_dir(self.args.build_dir, board=board, 403 source_dir=source_dir, app=app) 404 if not build_dir: 405 log.die('Unable to determine a default build folder. Check ' 406 'your build.dir-fmt configuration option') 407 408 if os.path.exists(build_dir): 409 if not os.path.isdir(build_dir): 410 log.die('build directory {} exists and is not a directory'. 411 format(build_dir)) 412 else: 413 os.makedirs(build_dir, exist_ok=False) 414 self.created_build_dir = True 415 self.run_cmake = True 416 417 self.build_dir = build_dir 418 419 def _find_source_dir(self): 420 # Initialize source_dir attribute, either from command line argument, 421 # implicitly from the build directory's CMake cache, or using the 422 # default (current working directory). 423 log.dbg('setting up source directory', level=log.VERBOSE_EXTREME) 424 if self.args.source_dir: 425 source_dir = self.args.source_dir 426 elif self.cmake_cache: 427 source_dir = self.cmake_cache.get('APP_DIR') 428 429 if not source_dir: 430 source_dir = self.cmake_cache.get('APPLICATION_SOURCE_DIR') 431 432 if not source_dir: 433 source_dir = self.cmake_cache.get('CMAKE_HOME_DIRECTORY') 434 435 if not source_dir: 436 # This really ought to be there. The build directory 437 # must be corrupted somehow. Let's see what we can do. 438 log.die('build directory', self.build_dir, 439 'CMake cache has no CMAKE_HOME_DIRECTORY;', 440 'please give a source_dir') 441 else: 442 source_dir = os.getcwd() 443 return os.path.abspath(source_dir) 444 445 def _sanity_check_source_dir(self): 446 if self.source_dir == self.build_dir: 447 # There's no forcing this. 448 log.die('source and build directory {} cannot be the same; ' 449 'use --build-dir {} to specify a build directory'. 450 format(self.source_dir, self.build_dir)) 451 452 srcrel = os.path.relpath(self.source_dir) 453 self.check_force( 454 not is_zephyr_build(self.source_dir), 455 'it looks like {srcrel} is a build directory: ' 456 'did you mean --build-dir {srcrel} instead?'. 457 format(srcrel=srcrel)) 458 self.check_force( 459 'CMakeLists.txt' in os.listdir(self.source_dir), 460 'source directory "{srcrel}" does not contain ' 461 'a CMakeLists.txt; is this really what you ' 462 'want to build? (Use -s SOURCE_DIR to specify ' 463 'the application source directory)'. 464 format(srcrel=srcrel)) 465 466 def _sanity_check(self): 467 # Sanity check the build configuration. 468 # Side effect: may update cmake_cache attribute. 469 log.dbg('sanity checking the build', level=log.VERBOSE_EXTREME) 470 self._sanity_check_source_dir() 471 472 if not self.cmake_cache: 473 return # That's all we can check without a cache. 474 475 if "CMAKE_PROJECT_NAME" not in self.cmake_cache: 476 # This happens sometimes when a build system is not 477 # completely generated due to an error during the 478 # CMake configuration phase. 479 self.run_cmake = True 480 481 cached_proj = self.cmake_cache.get('APPLICATION_SOURCE_DIR') 482 cached_app = self.cmake_cache.get('APP_DIR') 483 # if APP_DIR is None but APPLICATION_SOURCE_DIR is set, that indicates 484 # an older build folder, this still requires pristine. 485 if cached_app is None and cached_proj: 486 cached_app = cached_proj 487 488 log.dbg('APP_DIR:', cached_app, level=log.VERBOSE_EXTREME) 489 source_abs = (os.path.abspath(self.args.source_dir) 490 if self.args.source_dir else None) 491 cached_abs = os.path.abspath(cached_app) if cached_app else None 492 493 log.dbg('pristine:', self.auto_pristine, level=log.VERBOSE_EXTREME) 494 495 # If the build directory specifies a source app, make sure it's 496 # consistent with --source-dir. 497 apps_mismatched = (source_abs and cached_abs and 498 pathlib.Path(source_abs).resolve() != pathlib.Path(cached_abs).resolve()) 499 500 self.check_force( 501 not apps_mismatched or self.auto_pristine, 502 'Build directory "{}" is for application "{}", but source ' 503 'directory "{}" was specified; please clean it, use --pristine, ' 504 'or use --build-dir to set another build directory'. 505 format(self.build_dir, cached_abs, source_abs)) 506 507 if apps_mismatched: 508 self.run_cmake = True # If they insist, we need to re-run cmake. 509 510 # If CACHED_BOARD is not defined, we need some other way to 511 # find the board. 512 cached_board = self.cmake_cache.get('CACHED_BOARD') 513 log.dbg('CACHED_BOARD:', cached_board, level=log.VERBOSE_EXTREME) 514 # If apps_mismatched and self.auto_pristine are true, we will 515 # run pristine on the build, invalidating the cached 516 # board. In that case, we need some way of getting the board. 517 self.check_force((cached_board and 518 not (apps_mismatched and self.auto_pristine)) 519 or self.args.board or self.config_board or 520 os.environ.get('BOARD'), 521 'Cached board not defined, please provide it ' 522 '(provide --board, set default with ' 523 '"west config build.board <BOARD>", or set ' 524 'BOARD in the environment)') 525 526 # Check consistency between cached board and --board. 527 boards_mismatched = (self.args.board and cached_board and 528 self.args.board != cached_board) 529 self.check_force( 530 not boards_mismatched or self.auto_pristine, 531 'Build directory {} targets board {}, but board {} was specified. ' 532 '(Clean the directory, use --pristine, or use --build-dir to ' 533 'specify a different one.)'. 534 format(self.build_dir, cached_board, self.args.board)) 535 536 if self.auto_pristine and (apps_mismatched or boards_mismatched): 537 self._run_pristine() 538 self.cmake_cache = None 539 log.dbg('run_cmake:', True, level=log.VERBOSE_EXTREME) 540 self.run_cmake = True 541 542 # Tricky corner-case: The user has not specified a build folder but 543 # there was one in the CMake cache. Since this is going to be 544 # invalidated, reset to CWD and re-run the basic tests. 545 if ((boards_mismatched and not apps_mismatched) and 546 (not source_abs and cached_abs)): 547 self.source_dir = self._find_source_dir() 548 self._sanity_check_source_dir() 549 550 def _run_cmake(self, board, origin, cmake_opts): 551 if board is None and config_getboolean('board_warn', True): 552 log.wrn('This looks like a fresh build and BOARD is unknown;', 553 "so it probably won't work. To fix, use", 554 '--board=<your-board>.') 555 log.inf('Note: to silence the above message, run', 556 "'west config build.board_warn false'") 557 558 if not self.run_cmake: 559 return 560 561 _banner('generating a build system') 562 563 if board is not None and origin != 'CMakeCache.txt': 564 cmake_opts = ['-DBOARD={}'.format(board)] 565 else: 566 cmake_opts = [] 567 if self.args.cmake_opts: 568 cmake_opts.extend(self.args.cmake_opts) 569 if self.args.snippets: 570 cmake_opts.append(f'-DSNIPPET={";".join(self.args.snippets)}') 571 if self.args.shields: 572 cmake_opts.append(f'-DSHIELD={";".join(self.args.shields)}') 573 574 user_args = config_get('cmake-args', None) 575 if user_args: 576 cmake_opts.extend(shlex.split(user_args)) 577 578 config_sysbuild = config_getboolean('sysbuild', False) 579 if self.args.sysbuild or (config_sysbuild and not self.args.no_sysbuild): 580 cmake_opts.extend(['-S{}'.format(SYSBUILD_PROJ_DIR), 581 '-DAPP_DIR:PATH={}'.format(self.source_dir)]) 582 else: 583 # self.args.no_sysbuild == True or config sysbuild False 584 cmake_opts.extend(['-S{}'.format(self.source_dir)]) 585 586 # Invoke CMake from the current working directory using the 587 # -S and -B options (officially introduced in CMake 3.13.0). 588 # This is important because users expect invocations like this 589 # to Just Work: 590 # 591 # west build -- -DOVERLAY_CONFIG=relative-path.conf 592 final_cmake_args = ['-DWEST_PYTHON={}'.format(pathlib.Path(sys.executable).as_posix()), 593 '-B{}'.format(self.build_dir), 594 '-G{}'.format(config_get('generator', 595 DEFAULT_CMAKE_GENERATOR))] 596 if cmake_opts: 597 final_cmake_args.extend(cmake_opts) 598 run_cmake(final_cmake_args, dry_run=self.args.dry_run) 599 600 def _run_pristine(self): 601 _banner('making build dir {} pristine'.format(self.build_dir)) 602 if not is_zephyr_build(self.build_dir): 603 log.die('Refusing to run pristine on a folder that is not a ' 604 'Zephyr build system') 605 606 cache = CMakeCache.from_build_dir(self.build_dir) 607 608 app_src_dir = cache.get('APPLICATION_SOURCE_DIR') 609 app_bin_dir = cache.get('APPLICATION_BINARY_DIR') 610 611 cmake_args = [f'-DBINARY_DIR={app_bin_dir}', 612 f'-DSOURCE_DIR={app_src_dir}', 613 '-P', cache['ZEPHYR_BASE'] + '/cmake/pristine.cmake'] 614 run_cmake(cmake_args, cwd=self.build_dir, dry_run=self.args.dry_run) 615 616 def _run_build(self, target, domain): 617 if target: 618 _banner('running target {}'.format(target)) 619 elif self.run_cmake: 620 _banner('building application') 621 extra_args = ['--target', target] if target else [] 622 if self.args.build_opt: 623 extra_args.append('--') 624 extra_args.extend(self.args.build_opt) 625 if self.args.verbose: 626 self._append_verbose_args(extra_args, 627 not bool(self.args.build_opt)) 628 629 domains = load_domains(self.build_dir) 630 build_dir_list = [] 631 632 if domain is None: 633 # If no domain is specified, we just build top build dir as that 634 # will build all domains. 635 build_dir_list = [domains.get_top_build_dir()] 636 else: 637 _banner('building domain(s): {}'.format(' '.join(domain))) 638 domain_list = domains.get_domains(domain) 639 for d in domain_list: 640 build_dir_list.append(d.build_dir) 641 642 for b in build_dir_list: 643 run_build(b, extra_args=extra_args, 644 dry_run=self.args.dry_run) 645 646 def _append_verbose_args(self, extra_args, add_dashes): 647 # These hacks are only needed for CMake versions earlier than 648 # 3.14. When Zephyr's minimum version is at least that, we can 649 # drop this nonsense and just run "cmake --build BUILD -v". 650 self._update_cache() 651 if not self.cmake_cache: 652 return 653 generator = self.cmake_cache.get('CMAKE_GENERATOR') 654 if not generator: 655 return 656 # Substring matching is for things like "Eclipse CDT4 - Ninja". 657 if 'Ninja' in generator: 658 if add_dashes: 659 extra_args.append('--') 660 extra_args.append('-v') 661 elif generator == 'Unix Makefiles': 662 if add_dashes: 663 extra_args.append('--') 664 extra_args.append('VERBOSE=1') 665