1#!/bin/sh 2# 3# Copyright (C) 2018 The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16# 17""":" # Shell script (in docstring to appease pylint) 18# Find and invoke hermetic python3 interpreter 19. "`dirname $0`/envsetup.sh"; exec "$PY3" "$0" "$@" 20# Shell script end 21 22Invoke trusty build system and run tests. 23""" 24 25import argparse 26import getpass 27import json 28import multiprocessing 29import os 30import pathlib 31import re 32import shutil 33import stat 34import subprocess 35import sys 36from zipfile import ZipFile, ZipInfo, ZIP_DEFLATED 37 38import run_tests 39import trusty_build_config 40from trusty_build_config import ( 41 TrustyAndroidTest, 42 TrustyBuildConfig, 43 TrustyPortTest, 44 TrustyCompositeTest, 45) 46 47from log_processor import LogEngine 48 49script_dir = os.path.dirname(os.path.abspath(__file__)) 50 51SDK_README_PATH = "trusty/user/base/sdk/README.md" 52TRUSTED_APP_MAKEFILE_PATH = "trusty/user/base/make/trusted_app.mk" 53TRUSTED_LOADABLE_APP_MAKEFILE_PATH = "trusty/kernel/make/loadable_app.mk" 54GEN_MANIFEST_MAKEFILE_PATH = "trusty/user/base/make/gen_manifest.mk" 55 56ZIP_CREATE_SYSTEM_UNIX = 3 57SYMLINK_MODE = stat.S_IFLNK | stat.S_IRWXU | stat.S_IRWXG | stat.S_IRWXO 58 59 60def get_new_build_id(build_root): 61 """Increment build-id file and return new build-id number.""" 62 path = os.path.join(build_root, "BUILDID") 63 try: 64 with open(path, "r", encoding="utf-8") as f: 65 num = int(f.read()) + 1 66 except IOError: 67 num = 1 68 with open(path, "w", encoding="utf-8") as f: 69 f.write(str(num)) 70 f.truncate() 71 # Return buildid string: <user>@<hostname>-<num> 72 # Use getpass.getuser() to avoid non-portability/failure of 73 # os.getlogin() 74 return getpass.getuser() + "@" + os.uname()[1] + "-" + str(num) 75 76 77def mkdir(path): 78 """Create directory including parents if it does not already exist.""" 79 try: 80 os.makedirs(path) 81 except OSError: 82 if not os.path.isdir(path): 83 raise 84 85 86def copy_file(src, dest, optional=False): 87 """Copy a file. 88 89 Copy a file or exit if the file cannot be copied. 90 91 Args: 92 src: Path of file to copy. 93 dest: Path to copy file to. 94 optional: Optional boolean argument. If True don't exit if source file 95 does not exist. 96 """ 97 if not os.path.exists(src) and optional: 98 return 99 print("Copy:", repr(src), "->", repr(dest)) 100 shutil.copy(src, dest) 101 102 103def archive_build_file(args, project, src, dest=None, optional=False): 104 """Copy a file to build archive directory. 105 106 Construct src and dest path and call copy_file. 107 108 Args: 109 args: Program arguments. 110 project: Project name. 111 src: Source path relative to project build dir. 112 dest: Optional dest path relative to archive dir. Can be omitted if src 113 is a simple filename. 114 optional: Optional boolean argument. If True don't exit if source file 115 does not exist. 116 """ 117 if not dest: 118 dest = src 119 src = os.path.join(args.build_root, "build-" + project, src) 120 # dest must be a fixed path for repeated builds of the same artifact 121 # for compatibility with prebuilt update scripts. 122 # Project is fine because that specifies what artifact is being looked 123 # for - LK for a specific target. 124 # BUILD_ID or feature selections that may change are not, because the 125 # prebuilt update script cannot predict the path at which the artifact 126 # will live. 127 dest = os.path.join(args.archive, project + "." + dest) 128 copy_file(src, dest, optional=optional) 129 130 131def archive_symlink(zip_archive, arcname, target): 132 """Add a symbolic link to the archive 133 134 Args: 135 zip_archive: Archive to update 136 arcname: Filename in the archive to be added 137 target: Symbolic link target 138 """ 139 zinfo = ZipInfo(arcname) 140 zinfo.create_system = ZIP_CREATE_SYSTEM_UNIX 141 zinfo.external_attr = SYMLINK_MODE << 16 142 zip_archive.writestr(zinfo, target) 143 144 145def is_child_of_any(path, possible_parents): 146 for possible_parent in possible_parents: 147 if path.startswith(possible_parent): 148 return True 149 return False 150 151 152def archive_dir(zip_archive, src, dest, omit=()): 153 """Recursively add a directory to a ZIP file. 154 155 Recursively add the src directory to the ZIP with dest path inside the 156 archive. 157 158 Args: 159 zip_archive: A ZipFile opened for append or write. 160 src: Source directory to add to the archive. 161 dest: Destination path inside the archive (must be a relative path). 162 omit: List of directorys to omit from the archive. Specified as relative 163 paths from `src`. 164 """ 165 for root, dirs, files in os.walk(src): 166 rel_root = os.path.relpath(root, start=src) 167 if is_child_of_any(rel_root, omit): 168 continue 169 170 for d in dirs: 171 dir_path = os.path.join(root, d) 172 173 if os.path.islink(dir_path): 174 archive_dest = os.path.join( 175 dest, os.path.relpath(dir_path, start=src) 176 ) 177 archive_symlink( 178 zip_archive, archive_dest, os.readlink(dir_path) 179 ) 180 181 for f in files: 182 file_path = os.path.join(root, f) 183 archive_dest = os.path.join( 184 dest, os.path.relpath(file_path, start=src) 185 ) 186 if os.path.islink(file_path): 187 archive_symlink( 188 zip_archive, archive_dest, os.readlink(file_path) 189 ) 190 else: 191 zip_archive.write(file_path, archive_dest) 192 193 194def archive_file(zip_archive, src_file, dest_dir="", optional=False): 195 """Add a file to a ZIP file. 196 197 Adds src_file to archive in the directory dest_dir, relative to the root of 198 the archive. 199 200 Args: 201 zip_archive: A ZipFile opened for append or write. 202 src_file: Source file to add to the archive. 203 dest_dir: Relative destination path in the archive for this file. 204 optional: Optional boolean argument. If True don't exit if source file 205 does not exist. 206 """ 207 if not os.path.exists(src_file) and optional: 208 return 209 zip_archive.write( 210 src_file, os.path.join(dest_dir, os.path.basename(src_file)) 211 ) 212 213 214def assemble_sdk(build_config, args): 215 """Assemble Trusty SDK archive""" 216 filename = os.path.join(args.archive, "trusty_sdk-" + args.buildid + ".zip") 217 with ZipFile(filename, "a", compression=ZIP_DEFLATED) as sdk_archive: 218 print("Building SDK archive ZIP...") 219 for project in args.project: 220 print(f"Adding SDK project... ({project})") 221 project_buildroot = os.path.join( 222 args.build_root, "build-" + project 223 ) 224 225 project_sysroot_dir = os.path.join("sysroots", project, "usr") 226 src = os.path.join(project_buildroot, "sdk", "sysroot", "usr") 227 archive_dir(sdk_archive, src, project_sysroot_dir, omit=["lib/doc"]) 228 229 src = os.path.join(project_buildroot, "sdk", "LICENSE") 230 archive_file(sdk_archive, src) 231 232 project_makefile_dir = os.path.join("make", project) 233 src = os.path.join(project_buildroot, "sdk", "make") 234 archive_dir(sdk_archive, src, project_makefile_dir) 235 236 project_tools_dir = os.path.join("sysroots", project, "tools") 237 src = os.path.join( 238 project_buildroot, "host_tools", "apploader_package_tool" 239 ) 240 archive_file(sdk_archive, src, project_tools_dir, optional=True) 241 242 src = os.path.join( 243 project_buildroot, "sdk", "tools", "manifest_compiler.py" 244 ) 245 archive_file(sdk_archive, src, project_tools_dir) 246 247 project_keys = build_config.signing_keys(project) 248 for filename in project_keys: 249 archive_file(sdk_archive, filename, project_tools_dir) 250 251 print("Adding SDK sundries...") 252 253 # Copy the app makefile 254 archive_file(sdk_archive, TRUSTED_APP_MAKEFILE_PATH, "make") 255 archive_file(sdk_archive, TRUSTED_LOADABLE_APP_MAKEFILE_PATH, "make") 256 archive_file(sdk_archive, GEN_MANIFEST_MAKEFILE_PATH, "make") 257 258 # Copy SDK README 259 archive_file(sdk_archive, SDK_README_PATH) 260 261 # Add clang version info 262 envsetup = os.path.join(script_dir, "envsetup.sh") 263 cmd = f"source {envsetup} && echo $CLANG_BINDIR" 264 clang_bindir = ( 265 subprocess.check_output(cmd, shell=True, executable="/bin/bash") 266 .decode() 267 .strip() 268 ) 269 clang_dir = os.path.join(clang_bindir, "../") 270 271 cmd = f"cd {clang_dir}; git rev-parse HEAD" 272 clang_prebuilt_commit = ( 273 subprocess.check_output(cmd, shell=True, executable="/bin/bash") 274 .decode() 275 .strip() 276 ) 277 278 archive_file( 279 sdk_archive, 280 os.path.join(clang_dir, "AndroidVersion.txt"), 281 "clang-version", 282 ) 283 archive_file( 284 sdk_archive, 285 os.path.join(clang_dir, "clang_source_info.md"), 286 "clang-version", 287 ) 288 sdk_archive.writestr( 289 os.path.join("clang-version", "PrebuiltCommitId.txt"), 290 clang_prebuilt_commit, 291 ) 292 293 # Add trusty version info 294 sdk_archive.writestr("Version.txt", args.buildid) 295 296 # Add the toolchain if requested 297 if args.archive_toolchain: 298 _head, clang_ver = os.path.split(os.path.realpath(clang_dir)) 299 print(f"Adding SDK toolchain... ({clang_ver})") 300 archive_dir( 301 sdk_archive, clang_dir, os.path.join("toolchain", clang_ver) 302 ) 303 archive_symlink( 304 sdk_archive, os.path.join("toolchain", "clang"), clang_ver 305 ) 306 307 308def build(args): 309 """Call build system and copy build files to archive dir.""" 310 mkdir(args.build_root) 311 312 if args.buildid is None: 313 args.buildid = get_new_build_id(args.build_root) 314 print("BuildID", args.buildid) 315 316 nice = "" if args.no_nice else "nice" 317 318 # build projects 319 failed = [] 320 321 for project in args.project: 322 cmd = ( 323 f"export BUILDROOT={args.build_root};" 324 f"export BUILDID={args.buildid};" 325 f"{nice} $BUILDTOOLS_BINDIR/make {project} " 326 f"-f $LKROOT/makefile -j {args.jobs}" 327 ) 328 # Call envsetup. If it fails, abort. 329 envsetup = os.path.join(script_dir, "envsetup.sh") 330 cmd = f"source {envsetup:s} && ({cmd:s})" 331 332 # check if we are attached to a real terminal 333 terminal_output = sys.stdout.isatty() 334 335 if args.color_log and terminal_output: 336 # postprocess output with custom log processor 337 338 # define additional env variable for make to generate log markers 339 cmd = f"export LOG_POSTPROCESSING=1; {cmd:s}" 340 341 with ( 342 open(project + ".log", "wt", encoding="utf-8") as log_file, 343 LogEngine(log_file) as log_engine, 344 ): 345 status = subprocess.call( 346 cmd, 347 shell=True, 348 executable="/bin/bash", 349 stdout=log_engine.stdout, 350 stderr=log_engine.stderr, 351 ) 352 else: # no output intercepting 353 status = subprocess.call(cmd, shell=True, executable="/bin/bash") 354 355 print("cmd: '" + cmd + "' returned", status) 356 if status: 357 failed.append(project) 358 359 if failed: 360 print() 361 print("some projects have failed to build:") 362 print(str(failed)) 363 sys.exit(1) 364 365 366def zip_dir(zip_archive, src, dest, filterfunc=lambda _: True): 367 """Recursively add a directory to a ZIP file. 368 369 Recursively add the src directory to the ZIP with dest path inside the 370 archive. 371 372 Args: 373 zip_archive: A ZipFile opened for append or write. 374 src: Source directory to add to the archive. 375 dest: Destination path inside the archive (must be a relative path). 376 """ 377 for root, _dirs, files in os.walk(src): 378 for f in files: 379 if not filterfunc(f): 380 continue 381 file_path = os.path.join(root, f) 382 archive_dest = os.path.join( 383 dest, os.path.relpath(file_path, start=src) 384 ) 385 zip_archive.write(file_path, archive_dest) 386 387 388def zip_file(zip_archive, src_file, dest_dir=""): 389 """Add a file to a ZIP file. 390 391 Adds src_file to archive in the directory dest_dir, relative to the root of 392 the archive. 393 394 Args: 395 zip_archive: A ZipFile opened for append or write. 396 src_file: Source file to add to the archive. 397 dest_dir: Relative destination path in the archive for this file. 398 """ 399 zip_archive.write( 400 src_file, os.path.join(dest_dir, os.path.basename(src_file)) 401 ) 402 403 404def archive_symbols(args, project): 405 """Archive symbol files for the kernel and each trusted app""" 406 proj_buildroot = os.path.join(args.build_root, "build-" + project) 407 filename = os.path.join(args.archive, f"{project}-{args.buildid}.syms.zip") 408 409 with ZipFile(filename, "a", compression=ZIP_DEFLATED) as zip_archive: 410 print("Archiving symbols in " + os.path.relpath(filename, args.archive)) 411 412 # archive the kernel elf file 413 zip_file(zip_archive, os.path.join(proj_buildroot, "lk.elf")) 414 415 # archive the kernel symbols 416 zip_file(zip_archive, os.path.join(proj_buildroot, "lk.elf.sym")) 417 zip_file(zip_archive, os.path.join(proj_buildroot, "lk.elf.sym.sorted")) 418 419 # archive path/to/app.syms.elf for each trusted app 420 zip_dir( 421 zip_archive, proj_buildroot, "", lambda f: f.endswith("syms.elf") 422 ) 423 424 425def archive_listings(args, project): 426 """Archive lst files for the kernel and each trusted app""" 427 proj_buildroot = os.path.join(args.build_root, "build-" + project) 428 filename = os.path.join(args.archive, f"{project}-{args.buildid}.lst.zip") 429 430 with ZipFile(filename, "a", compression=ZIP_DEFLATED) as zip_archive: 431 print("Archiving .lst in " + os.path.relpath(filename, args.archive)) 432 433 # archive all .lst files under the buildroot 434 zip_dir( 435 zip_archive, proj_buildroot, "", lambda f: f.endswith(".lst") 436 ) 437 438 439def create_uuid_map(args, project): 440 """Creating a mapping txt file for uuid and symbol files""" 441 442 def time_from_bytes(f, n: int) -> str: 443 """Read n bytes from f as an int, and convert that int to a string.""" 444 rtime = int.from_bytes(f.read(n), byteorder="little") 445 width = 2 * n 446 return f"{rtime:0{width}x}" 447 448 proj_buildroot = os.path.join(args.build_root, "build-" + project) 449 uuidmapfile = os.path.join(args.archive, "uuid-map.txt") 450 zipfile = os.path.join(args.archive, f"{project}-{args.buildid}.syms.zip") 451 sym_files = list(pathlib.Path(proj_buildroot).rglob("*.syms.elf")) 452 453 for file in sym_files: 454 folder = file.parents[0] 455 manifest_files = list(pathlib.Path(folder).glob("*.manifest")) 456 if len(manifest_files) == 1: 457 manifest = manifest_files[0] 458 with open(manifest, "rb") as f: 459 time_low = time_from_bytes(f, 4) 460 time_mid = time_from_bytes(f, 2) 461 time_hi_and_version = time_from_bytes(f, 2) 462 clock_seq_and_node = [time_from_bytes(f, 1) for _ in range(8)] 463 uuid_str = ( 464 f"{time_low}-{time_mid}-{time_hi_and_version}-" 465 f"{clock_seq_and_node[0]}{clock_seq_and_node[1]}-" 466 f"{clock_seq_and_node[2]}{clock_seq_and_node[3]}" 467 f"{clock_seq_and_node[4]}{clock_seq_and_node[5]}" 468 f"{clock_seq_and_node[6]}{clock_seq_and_node[7]}" 469 ) 470 with open(uuidmapfile, "a", encoding="utf-8") as f: 471 f.write(f"{uuid_str}, {file.relative_to(proj_buildroot)}\n") 472 473 if os.path.exists(uuidmapfile): 474 with ZipFile(zipfile, "a", compression=ZIP_DEFLATED) as zip_archive: 475 zip_file(zip_archive, uuidmapfile) 476 os.remove(uuidmapfile) 477 478 479def create_scripts_archive(args, project): 480 """Create an archive for the scripts""" 481 coverage_script = os.path.join(script_dir, "genReport.py") 482 scripts_zip = os.path.join( 483 args.archive, f"{project}-{args.buildid}.scripts.zip" 484 ) 485 if not os.path.exists(coverage_script): 486 print("Coverage script does not exist!") 487 return 488 489 with ZipFile(scripts_zip, "a", compression=ZIP_DEFLATED) as zip_archive: 490 zip_file(zip_archive, coverage_script) 491 492 493def archive(build_config, args): 494 if args.archive is None: 495 return 496 497 mkdir(args.archive) 498 499 # Copy the files we care about to the archive directory 500 for project in args.project: 501 # config-driven archiving 502 for item in build_config.dist: 503 archive_build_file( 504 args, project, item.src, item.dest, optional=item.optional 505 ) 506 507 # copy out tos.img if it exists 508 archive_build_file(args, project, "tos.img", optional=True) 509 510 # copy out monitor if it exists 511 archive_build_file( 512 args, project, "monitor/monitor.bin", "monitor.bin", optional=True 513 ) 514 515 # copy out trusty.padded if it exists 516 archive_build_file(args, project, "trusty.padded", optional=True) 517 518 # copy out trusty.signed if it exists 519 archive_build_file(args, project, "trusty.signed", optional=True) 520 521 # copy out trusty_usb.signed if it exists 522 archive_build_file(args, project, "trusty_usb.signed", optional=True) 523 524 # copy out lk image 525 archive_build_file(args, project, "lk.bin") 526 archive_build_file(args, project, "lk.elf") 527 528 # copy out qemu package if it exists 529 archive_build_file( 530 args, project, "trusty_qemu_package.zip", optional=True 531 ) 532 533 # copy out test package if it exists 534 archive_build_file( 535 args, project, "trusty_test_package.zip", optional=True 536 ) 537 538 # export the app package tool for use in the SDK. This can go away once 539 # all the SDK patches have landed, as the tool will be packaged in the 540 # SDK zip. 541 archive_build_file( 542 args, 543 project, 544 "host_tools/apploader_package_tool", 545 "apploader_package_tool", 546 optional=True, 547 ) 548 549 # copy out symbol files for kernel and apps 550 archive_symbols(args, project) 551 552 # copy out listings files for kernel and apps 553 archive_listings(args, project) 554 555 # create map between UUID and symbolic files 556 create_uuid_map(args, project) 557 558 # create zip file containing scripts 559 create_scripts_archive(args, project) 560 561 # create sdk zip 562 assemble_sdk(build_config, args) 563 564 565def get_build_deps(project_name, project, project_names, already_built): 566 if project_name not in already_built: 567 already_built.add(project_name) 568 for dep_project_name, dep_project in project.also_build.items(): 569 get_build_deps( 570 dep_project_name, dep_project, project_names, already_built 571 ) 572 project_names.append(project_name) 573 574 575def create_test_map(args, build_config, projects): 576 for project_name in projects: 577 test_map = {} 578 test_map["port_tests"] = [] 579 test_map["commands"] = [] 580 test_names = set() 581 duplicates = set() 582 project = build_config.get_project(project_name) 583 584 if not project or not project.tests: 585 return 586 587 port_test_prefix = "android-port-test:" 588 project_type_prefix = re.compile("([^:]+:)+") 589 590 for test in project.tests: 591 test_type = None 592 match test: 593 case TrustyCompositeTest() if any( 594 s 595 for s in test.sequence 596 if s.name.startswith(port_test_prefix) 597 ): 598 test_type = TrustyCompositeTest 599 case TrustyAndroidTest() if test.name.startswith( 600 port_test_prefix 601 ): 602 test_type = TrustyPortTest 603 case TrustyAndroidTest(): 604 test_type = TrustyAndroidTest 605 case _: 606 pass 607 608 if test_type: 609 test_obj = {"needs": []} 610 test_name = re.sub(project_type_prefix, "", test.name) 611 612 if test_name in test_names: 613 duplicates.add(test_name) 614 continue 615 test_names.add(test_name) 616 617 if hasattr(test, "need") and hasattr(test.need, "flags"): 618 test_obj["needs"] = list(test.need.flags) 619 if hasattr(test, "port_type"): 620 test_obj["type"] = str(test.port_type) 621 622 match test_type: 623 case trusty_build_config.TrustyPortTest: 624 test_obj["port_name"] = test_name 625 test_map["port_tests"].append(test_obj) 626 case trusty_build_config.TrustyAndroidTest: 627 test_obj["command_name"] = test_name 628 test_obj["command"] = test.command 629 test_map["commands"].append(test_obj) 630 case trusty_build_config.TrustyCompositeTest: 631 test_obj["port_name"] = test_name 632 test_obj["sequence"] = [] 633 634 for subtest in test.sequence: 635 subtest_name = re.sub( 636 project_type_prefix, "", subtest.name 637 ) 638 test_obj["sequence"].append(subtest_name) 639 if hasattr(subtest, "need") and hasattr( 640 subtest.need, "flags" 641 ): 642 test_obj["needs"] += list(subtest.need.flags) 643 644 test_obj["needs"] += list(set(test_obj["needs"])) 645 646 test_map["port_tests"].append(test_obj) 647 648 if duplicates: 649 print("ERROR: The following port tests are included multiple times") 650 for port in duplicates: 651 print(port) 652 sys.exit(-1) 653 654 project_buildroot = os.path.join( 655 args.build_root, "build-" + project_name 656 ) 657 zip_path = os.path.join(project_buildroot, "trusty_test_package.zip") 658 with ZipFile(zip_path, "a", compression=ZIP_DEFLATED) as zipf: 659 zipf.writestr( 660 project_name + "-test-map.json", json.dumps(test_map, indent=4) 661 ) 662 663 664def main(default_config=None, emulator=True): 665 top = os.path.abspath(os.path.join(script_dir, "../../../../..")) 666 667 parser = argparse.ArgumentParser() 668 669 parser.add_argument( 670 "project", 671 type=str, 672 nargs="*", 673 default=[".test.all"], 674 help="Project to build and/or test.", 675 ) 676 parser.add_argument( 677 "--build-root", 678 type=os.path.abspath, 679 default=os.path.join(top, "build-root"), 680 help="Root of intermediate build directory.", 681 ) 682 parser.add_argument( 683 "--archive", 684 type=str, 685 default=None, 686 help="Location of build artifacts directory. If " 687 "omitted, no artifacts will be produced.", 688 ) 689 parser.add_argument( 690 "--archive-toolchain", 691 action="store_true", 692 help="Include the clang toolchain in the archive.", 693 ) 694 parser.add_argument("--buildid", type=str, help="Server build id") 695 parser.add_argument( 696 "--jobs", 697 type=str, 698 default=multiprocessing.cpu_count(), 699 help="Max number of build jobs.", 700 ) 701 parser.add_argument( 702 "--test", 703 type=str, 704 action="append", 705 help="Manually specify test(s) to run. " 706 "Only build projects that have test(s) enabled that " 707 "matches a listed regex.", 708 ) 709 parser.add_argument( 710 "--verbose", 711 action="store_true", 712 help="Verbose debug output from test(s).", 713 ) 714 parser.add_argument( 715 "--debug-on-error", 716 action="store_true", 717 help="Wait for debugger connection if test fails.", 718 ) 719 parser.add_argument( 720 "--clang", action="store_true", default=None, help="Build with clang." 721 ) 722 parser.add_argument("--skip-build", action="store_true", help="Skip build.") 723 parser.add_argument( 724 "--skip-tests", action="store_true", help="Skip running tests." 725 ) 726 parser.add_argument( 727 "--run-disabled-tests", 728 action="store_true", 729 help="Also run disabled tests.", 730 ) 731 parser.add_argument( 732 "--skip-project", 733 action="append", 734 default=[], 735 help="Remove project from projects being built.", 736 ) 737 parser.add_argument( 738 "--config", 739 type=str, 740 help="Path to an alternate " "build-config file.", 741 default=default_config, 742 ) 743 parser.add_argument( 744 "--android", 745 type=str, 746 help="Path to an Android build to run tests against.", 747 ) 748 parser.add_argument( 749 "--color-log", 750 action="store_true", 751 help="Use colored build logs with pinned status lines.", 752 ) 753 parser.add_argument( 754 "--no-nice", 755 action="store_true", 756 help="Do not use nice to run the build.", 757 ) 758 args = parser.parse_args() 759 760 # Change the current directory to the Trusty root 761 # We do this after parsing all the arguments because 762 # some of the paths, e.g., build-root, might be relative 763 # to the directory that the script was called from, not 764 # to the Trusty root directory 765 os.chdir(top) 766 767 build_config = TrustyBuildConfig( 768 config_file=args.config, android=args.android 769 ) 770 771 projects = [] 772 for project in args.project: 773 if project == ".test.all": 774 projects += build_config.get_projects(build=True) 775 elif project == ".test": 776 projects += build_config.get_projects(build=True, have_tests=True) 777 else: 778 projects.append(project) 779 780 # skip specific projects 781 ok = True 782 for skip in args.skip_project: 783 if skip in projects: 784 projects.remove(skip) 785 else: 786 sys.stderr.write(f"ERROR unknown project --skip-project={skip}\n") 787 ok = False 788 if not ok: 789 sys.exit(1) 790 791 # If there's any test filters, ignore projects that don't have 792 # any tests that match those filters. 793 test_filters = ( 794 [re.compile(test) for test in args.test] if args.test else None 795 ) 796 if test_filters: 797 projects = run_tests.projects_to_test( 798 build_config, 799 projects, 800 test_filters, 801 run_disabled_tests=args.run_disabled_tests, 802 ) 803 804 # find build dependencies 805 projects_old = projects 806 projects = [] 807 built_projects = set() 808 for project_name in projects_old: 809 get_build_deps( 810 project_name, 811 build_config.get_project(project_name), 812 projects, 813 built_projects, 814 ) 815 args.project = projects 816 817 print("Projects", str(projects)) 818 819 if args.skip_build: 820 print("Skip build for", args.project) 821 else: 822 build(args) 823 create_test_map(args, build_config, projects) 824 archive(build_config, args) 825 826 # Run tests 827 if not args.skip_tests: 828 test_result = run_tests.test_projects( 829 build_config, 830 args.build_root, 831 projects, 832 run_disabled_tests=args.run_disabled_tests, 833 test_filters=test_filters, 834 verbose=args.verbose, 835 debug_on_error=args.debug_on_error, 836 emulator=emulator, 837 ) 838 839 test_result.print_results() 840 if test_result.failed_projects: 841 sys.exit(1) 842 843 844if __name__ == "__main__": 845 main() 846