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