1 #!/usr/bin/env python 2 # 3 # Copyright (C) 2015 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 """Builds the Android Clang toolchain.""" 18 import argparse 19 import glob 20 import logging 21 import multiprocessing 22 import os 23 import pprint 24 import subprocess 25 import sys 26 27 import version 28 29 30 # Disable all the "too many/few methods/parameters" warnings and the like. 31 # pylint: disable=design 32 33 # Disable lint warnings for todo comments and the like. 34 # pylint: disable=fixme 35 36 # TODO: Add docstrings? 37 # pylint: disable=missing-docstring 38 39 40 THIS_DIR = os.path.realpath(os.path.dirname(__file__)) 41 ORIG_ENV = dict(os.environ) 42 43 44 class Config(object): 45 """Container for global configuration options.""" 46 47 # Set True to skip all actions (log only). Controlled by --dry-run. 48 dry_run = False 49 50 51 def logger(): 52 """Returns the default logger for the module.""" 53 return logging.getLogger(__name__) 54 55 56 def android_path(*args): 57 return os.path.realpath(os.path.join(THIS_DIR, '../..', *args)) 58 59 60 def build_path(*args): 61 # Our multistage build directories will be placed under OUT_DIR if it is in 62 # the environment. By default they will be placed under 63 # $ANDROID_BUILD_TOP/out. 64 top_out = ORIG_ENV.get('OUT_DIR', 'out') 65 return os.path.join(top_out, *args) 66 67 68 def short_version(): 69 return '.'.join([version.major, version.minor]) 70 71 72 def long_version(): 73 return '.'.join([version.major, version.minor, version.patch]) 74 75 76 def check_call(cmd, *args, **kwargs): 77 """Proxy for subprocess.check_call with logging and dry-run support.""" 78 import subprocess 79 logger().info('check_call: %s', ' '.join(cmd)) 80 if 'env' in kwargs: 81 # Rather than dump the whole environment to the terminal every time, 82 # just print the difference between this call and our environment. 83 # Note that this will not include environment that was *removed* from 84 # os.environ. 85 extra_env = dict(set(kwargs['env'].items()) - set(os.environ.items())) 86 if len(extra_env) > 0: 87 logger().info('check_call additional env:\n%s', 88 pprint.pformat(extra_env)) 89 if not Config.dry_run: 90 subprocess.check_call(cmd, *args, **kwargs) 91 92 93 def install_file(src, dst): 94 """Proxy for shutil.copy2 with logging and dry-run support.""" 95 import shutil 96 logger().info('copy %s %s', src, dst) 97 if not Config.dry_run: 98 shutil.copy2(src, dst) 99 100 101 def install_directory(src, dst): 102 """Proxy for shutil.copytree with logging and dry-run support.""" 103 import shutil 104 logger().info('copytree %s %s', src, dst) 105 if not Config.dry_run: 106 shutil.copytree(src, dst) 107 108 109 def rmtree(path): 110 """Proxy for shutil.rmtree with logging and dry-run support.""" 111 import shutil 112 logger().info('rmtree %s', path) 113 if not Config.dry_run: 114 shutil.rmtree(path) 115 116 117 def rename(src, dst): 118 """Proxy for os.rename with logging and dry-run support.""" 119 logger().info('rename %s %s', src, dst) 120 if not Config.dry_run: 121 os.rename(src, dst) 122 123 124 def makedirs(path): 125 """Proxy for os.makedirs with logging and dry-run support.""" 126 logger().info('makedirs %s', path) 127 if not Config.dry_run: 128 os.makedirs(path) 129 130 131 def symlink(src, dst): 132 """Proxy for os.symlink with logging and dry-run support.""" 133 logger().info('symlink %s %s', src, dst) 134 if not Config.dry_run: 135 os.symlink(src, dst) 136 137 138 def build(out_dir, prebuilts_path=None, prebuilts_version=None, 139 build_all_clang_tools=None, build_all_llvm_tools=None, 140 debug_clang=None, max_jobs=multiprocessing.cpu_count()): 141 products = ( 142 'aosp_arm', 143 'aosp_arm64', 144 'aosp_mips', 145 'aosp_mips64', 146 'aosp_x86', 147 'aosp_x86_64', 148 ) 149 for product in products: 150 build_product(out_dir, product, prebuilts_path, prebuilts_version, 151 build_all_clang_tools, build_all_llvm_tools, debug_clang, 152 max_jobs) 153 154 155 def build_product(out_dir, product, prebuilts_path, prebuilts_version, 156 build_all_clang_tools, build_all_llvm_tools, debug_clang, 157 max_jobs): 158 env = dict(ORIG_ENV) 159 env['DISABLE_LLVM_DEVICE_BUILDS'] = 'true' 160 env['DISABLE_RELOCATION_PACKER'] = 'true' 161 env['FORCE_BUILD_LLVM_COMPONENTS'] = 'true' 162 env['FORCE_BUILD_SANITIZER_SHARED_OBJECTS'] = 'true' 163 env['OUT_DIR'] = out_dir 164 env['SKIP_LLVM_TESTS'] = 'true' 165 env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true' 166 env['TARGET_BUILD_VARIANT'] = 'userdebug' 167 env['TARGET_PRODUCT'] = product 168 169 if debug_clang: 170 env['FORCE_BUILD_LLVM_DEBUG'] = 'true' 171 env['FORCE_BUILD_LLVM_DISABLE_NDEBUG'] = 'true' 172 173 overrides = [] 174 if prebuilts_path is not None: 175 overrides.append('LLVM_PREBUILTS_BASE={}'.format(prebuilts_path)) 176 if prebuilts_version is not None: 177 overrides.append('LLVM_PREBUILTS_VERSION={}'.format(prebuilts_version)) 178 179 # Use at least 1 and at most all available CPUs (sanitize the user input). 180 jobs_arg = '-j{}'.format( 181 max(1, min(max_jobs, multiprocessing.cpu_count()))) 182 183 targets = ['clang-toolchain-minimal'] 184 if build_all_clang_tools: 185 targets += ['clang-toolchain-full'] 186 if build_all_llvm_tools: 187 targets += ['llvm-tools'] 188 check_call(['make', jobs_arg] + overrides + targets, 189 cwd=android_path(), env=env) 190 191 192 def package_toolchain(build_dir, build_name, host, dist_dir): 193 package_name = 'clang-' + build_name 194 install_host_dir = build_path('install', host) 195 install_dir = os.path.join(install_host_dir, package_name) 196 197 # Remove any previously installed toolchain so it doesn't pollute the 198 # build. 199 if os.path.exists(install_host_dir): 200 rmtree(install_host_dir) 201 202 install_toolchain(build_dir, install_dir, host, True) 203 204 version_file_path = os.path.join(install_dir, 'AndroidVersion.txt') 205 with open(version_file_path, 'w') as version_file: 206 version_file.write('{}.{}.{}\n'.format( 207 version.major, version.minor, version.patch)) 208 209 tarball_name = package_name + '-' + host 210 package_path = os.path.join(dist_dir, tarball_name) + '.tar.bz2' 211 logger().info('Packaging %s', package_path) 212 args = [ 213 'tar', '-cjC', install_host_dir, '-f', package_path, package_name 214 ] 215 check_call(args) 216 217 218 def install_minimal_toolchain(build_dir, install_dir, host, strip): 219 install_built_host_files(build_dir, install_dir, host, strip, minimal=True) 220 install_headers(build_dir, install_dir, host) 221 install_sanitizers(build_dir, install_dir, host) 222 223 224 def install_toolchain(build_dir, install_dir, host, strip): 225 install_built_host_files(build_dir, install_dir, host, strip) 226 install_compiler_wrapper(install_dir, host) 227 install_sanitizer_scripts(install_dir) 228 install_scan_scripts(install_dir) 229 install_analyzer_scripts(install_dir) 230 install_headers(build_dir, install_dir, host) 231 install_profile_rt(build_dir, install_dir, host) 232 install_sanitizers(build_dir, install_dir, host) 233 install_sanitizer_tests(build_dir, install_dir, host) 234 install_libomp(build_dir, install_dir, host) 235 install_license_files(install_dir) 236 install_repo_prop(install_dir) 237 238 239 def get_built_host_files(host, minimal): 240 is_windows = host.startswith('windows') 241 is_darwin = host.startswith('darwin-x86') 242 bin_ext = '.exe' if is_windows else '' 243 244 if is_windows: 245 lib_ext = '.dll' 246 elif is_darwin: 247 lib_ext = '.dylib' 248 else: 249 lib_ext = '.so' 250 251 built_files = [ 252 'bin/clang' + bin_ext, 253 'bin/clang++' + bin_ext, 254 ] 255 if not is_windows: 256 built_files.extend(['lib64/libc++' + lib_ext]) 257 258 if minimal: 259 return built_files 260 261 built_files.extend([ 262 'bin/clang-format' + bin_ext, 263 'bin/clang-tidy' + bin_ext, 264 ]) 265 266 if is_windows: 267 built_files.extend([ 268 'bin/clang_32' + bin_ext, 269 ]) 270 else: 271 built_files.extend([ 272 'bin/FileCheck' + bin_ext, 273 'bin/llvm-as' + bin_ext, 274 'bin/llvm-dis' + bin_ext, 275 'bin/llvm-link' + bin_ext, 276 'bin/llvm-symbolizer' + bin_ext, 277 'lib64/libLLVM' + lib_ext, 278 'lib64/LLVMgold' + lib_ext, 279 ]) 280 return built_files 281 282 283 def install_built_host_files(build_dir, install_dir, host, strip, minimal=None): 284 built_files = get_built_host_files(host, minimal) 285 for built_file in built_files: 286 dirname = os.path.dirname(built_file) 287 install_path = os.path.join(install_dir, dirname) 288 if not os.path.exists(install_path): 289 makedirs(install_path) 290 291 built_path = os.path.join(build_dir, 'host', host, built_file) 292 install_file(built_path, install_path) 293 294 file_name = os.path.basename(built_file) 295 296 # Only strip bin files (not libs) on darwin. 297 is_darwin = host.startswith('darwin-x86') 298 if strip and (not is_darwin or built_file.startswith('bin/')): 299 check_call(['strip', os.path.join(install_path, file_name)]) 300 301 302 def install_sanitizer_scripts(install_dir): 303 script_path = android_path( 304 'external/compiler-rt/lib/asan/scripts/asan_device_setup') 305 install_file(script_path, os.path.join(install_dir, 'bin')) 306 307 308 def install_analyzer_scripts(install_dir): 309 """Create and install bash scripts for invoking Clang for analysis.""" 310 analyzer_text = ( 311 '#!/bin/bash\n' 312 'if [ "$1" != "-cc1" ]; then\n' 313 ' `dirname $0`/../clang{clang_suffix} -target {target} "$@"\n' 314 'else\n' 315 ' # target/triple already spelled out.\n' 316 ' `dirname $0`/../clang{clang_suffix} "$@"\n' 317 'fi\n' 318 ) 319 320 arch_target_pairs = ( 321 ('arm64-v8a', 'aarch64-none-linux-android'), 322 ('armeabi', 'armv5te-none-linux-androideabi'), 323 ('armeabi-v7a', 'armv7-none-linux-androideabi'), 324 ('armeabi-v7a-hard', 'armv7-none-linux-androideabi'), 325 ('mips', 'mipsel-none-linux-android'), 326 ('mips64', 'mips64el-none-linux-android'), 327 ('x86', 'i686-none-linux-android'), 328 ('x86_64', 'x86_64-none-linux-android'), 329 ) 330 331 for arch, target in arch_target_pairs: 332 arch_path = os.path.join(install_dir, 'bin', arch) 333 makedirs(arch_path) 334 335 analyzer_file_path = os.path.join(arch_path, 'analyzer') 336 logger().info('Creating %s', analyzer_file_path) 337 with open(analyzer_file_path, 'w') as analyzer_file: 338 analyzer_file.write( 339 analyzer_text.format(clang_suffix='', target=target)) 340 subprocess.check_call(['chmod', 'a+x', analyzer_file_path]) 341 342 analyzerpp_file_path = os.path.join(arch_path, 'analyzer++') 343 logger().info('Creating %s', analyzerpp_file_path) 344 with open(analyzerpp_file_path, 'w') as analyzerpp_file: 345 analyzerpp_file.write( 346 analyzer_text.format(clang_suffix='++', target=target)) 347 subprocess.check_call(['chmod', 'a+x', analyzerpp_file_path]) 348 349 350 def install_scan_scripts(install_dir): 351 tools_install_dir = os.path.join(install_dir, 'tools') 352 makedirs(tools_install_dir) 353 tools = ('scan-build', 'scan-view') 354 tools_dir = android_path('external/clang/tools') 355 for tool in tools: 356 tool_path = os.path.join(tools_dir, tool) 357 install_path = os.path.join(install_dir, 'tools', tool) 358 install_directory(tool_path, install_path) 359 360 361 def install_headers(build_dir, install_dir, host): 362 def should_copy(path): 363 if os.path.basename(path) in ('Makefile', 'CMakeLists.txt'): 364 return False 365 _, ext = os.path.splitext(path) 366 if ext == '.mk': 367 return False 368 return True 369 370 headers_src = android_path('external/clang/lib/Headers') 371 headers_dst = os.path.join( 372 install_dir, 'lib64/clang', short_version(), 'include') 373 makedirs(headers_dst) 374 for header in os.listdir(headers_src): 375 if not should_copy(header): 376 continue 377 install_file(os.path.join(headers_src, header), headers_dst) 378 379 install_file(android_path('bionic/libc/include/stdatomic.h'), headers_dst) 380 381 # arm_neon.h gets produced as part of external/clang/Android.bp. 382 # We must bundle the resulting file as part of the official Clang headers. 383 arm_neon_h = os.path.join( 384 build_dir, 'soong/.intermediates/external/clang/clang-gen-arm-neon/gen/clang/Basic/arm_neon.h') 385 install_file(arm_neon_h, headers_dst) 386 387 symlink(short_version(), 388 os.path.join(install_dir, 'lib64/clang', long_version())) 389 390 391 def install_profile_rt(build_dir, install_dir, host): 392 lib_dir = os.path.join( 393 install_dir, 'lib64/clang', short_version(), 'lib/linux') 394 makedirs(lib_dir) 395 396 install_target_profile_rt(build_dir, lib_dir) 397 398 # We only support profiling libs for Linux and Android. 399 if host == 'linux-x86': 400 install_host_profile_rt(build_dir, host, lib_dir) 401 402 403 def install_target_profile_rt(build_dir, lib_dir): 404 product_to_arch = { 405 'generic': 'arm', 406 'generic_arm64': 'aarch64', 407 'generic_mips': 'mipsel', 408 'generic_mips64': 'mips64el', 409 'generic_x86': 'i686', 410 'generic_x86_64': 'x86_64', 411 } 412 413 for product, arch in product_to_arch.items(): 414 product_dir = os.path.join(build_dir, 'target/product', product) 415 static_libs = os.path.join(product_dir, 'obj/STATIC_LIBRARIES') 416 built_lib = os.path.join( 417 static_libs, 'libprofile_rt_intermediates/libprofile_rt.a') 418 lib_name = 'libclang_rt.profile-{}-android.a'.format(arch) 419 install_file(built_lib, os.path.join(lib_dir, lib_name)) 420 421 422 def install_host_profile_rt(build_dir, host, lib_dir): 423 arch_to_obj_dir = { 424 'i686': 'obj32', 425 'x86_64': 'obj', 426 } 427 428 for arch, obj_dir in arch_to_obj_dir.items(): 429 static_libs = os.path.join( 430 build_dir, 'host', host, obj_dir, 'STATIC_LIBRARIES') 431 built_lib = os.path.join( 432 static_libs, 'libprofile_rt_intermediates/libprofile_rt.a') 433 lib_name = 'libclang_rt.profile-{}.a'.format(arch) 434 install_file(built_lib, os.path.join(lib_dir, lib_name)) 435 436 437 def install_libomp(build_dir, install_dir, host): 438 # libomp is not built for Darwin 439 if host == 'darwin-x86': 440 return 441 442 lib_dir = os.path.join( 443 install_dir, 'lib64/clang', short_version(), 'lib/linux') 444 if not os.path.isdir(lib_dir): 445 makedirs(lib_dir) 446 447 product_to_arch = { 448 'generic': 'arm', 449 'generic_arm64': 'arm64', 450 'generic_x86': 'x86', 451 'generic_x86_64': 'x86_64', 452 } 453 454 for product, arch in product_to_arch.items(): 455 module = 'libomp-' + arch 456 product_dir = os.path.join(build_dir, 'target/product', product) 457 shared_libs = os.path.join(product_dir, 'obj/SHARED_LIBRARIES') 458 built_lib = os.path.join( 459 shared_libs, 460 '{}_intermediates/PACKED/{}.so'.format(module, module)) 461 install_file(built_lib, os.path.join(lib_dir, module + '.so')) 462 463 464 def install_sanitizers(build_dir, install_dir, host): 465 headers_src = android_path('external/compiler-rt/include/sanitizer') 466 clang_lib = os.path.join(install_dir, 'lib64/clang', short_version()) 467 headers_dst = os.path.join(clang_lib, 'include/sanitizer') 468 lib_dst = os.path.join(clang_lib, 'lib/linux') 469 install_directory(headers_src, headers_dst) 470 471 if not os.path.exists(lib_dst): 472 makedirs(lib_dst) 473 474 if host == 'linux-x86': 475 install_host_sanitizers(build_dir, host, lib_dst) 476 477 # Tuples of (product, arch) 478 product_to_arch = ( 479 ('generic', 'arm'), 480 ('generic_arm64', 'aarch64'), 481 ('generic_x86', 'i686'), 482 ('generic_mips', 'mips'), 483 ('generic_mips64', 'mips64'), 484 ) 485 486 sanitizers = ('asan', 'ubsan_standalone') 487 488 for product, arch in product_to_arch: 489 for sanitizer in sanitizers: 490 module = 'libclang_rt.{}-{}-android'.format(sanitizer, arch) 491 product_dir = os.path.join(build_dir, 'target/product', product) 492 lib_dir = os.path.join(product_dir, 'obj/SHARED_LIBRARIES', 493 '{}_intermediates'.format(module)) 494 lib_name = '{}.so'.format(module) 495 built_lib = os.path.join(lib_dir, 'PACKED', lib_name) 496 install_file(built_lib, lib_dst) 497 498 499 # Also install the asan_test binaries. We need to do this because the 500 # platform sources for compiler-rt are potentially different from our 501 # toolchain sources. The only way to ensure that this test builds 502 # correctly is to make it a prebuilt based on our latest toolchain 503 # sources. Note that this is only created/compiled by the previous 504 # stage (usually stage1) compiler. We are not doing a subsequent 505 # compile with our stage2 binaries to construct any further 506 # device-targeted objects. 507 def install_sanitizer_tests(build_dir, install_dir, host): 508 # Tuples of (product, arch) 509 product_to_arch = ( 510 ('generic', 'arm'), 511 ('generic_arm64', 'aarch64'), 512 ('generic_x86', 'i686'), 513 ('generic_mips', 'mips'), 514 ('generic_mips64', 'mips64'), 515 ) 516 517 for product, arch in product_to_arch: 518 product_dir = os.path.join(build_dir, 'target/product', product) 519 test_module = 'asan_test' 520 test_dir = os.path.join(product_dir, 'obj/EXECUTABLES', 521 '{}_intermediates'.format(test_module)) 522 built_test = os.path.join(test_dir, 'PACKED', test_module) 523 test_dst = os.path.join(install_dir, 'test', arch, 'bin') 524 makedirs(test_dst) 525 install_file(built_test, test_dst) 526 527 528 def install_host_sanitizers(build_dir, host, lib_dst): 529 # Tuples of (name, multilib). 530 libs = ( 531 ('asan', True), 532 ('asan_cxx', True), 533 ('ubsan_standalone', True), 534 ('ubsan_standalone_cxx', True), 535 ('tsan', False), 536 ('tsan_cxx', False), 537 ) 538 539 obj32 = os.path.join(build_dir, 'host', host, 'obj32/STATIC_LIBRARIES') 540 obj64 = os.path.join(build_dir, 'host', host, 'obj/STATIC_LIBRARIES') 541 for lib, is_multilib in libs: 542 built_lib_name = 'lib{}.a'.format(lib) 543 544 obj64_dir = os.path.join(obj64, 'lib{}_intermediates'.format(lib)) 545 lib64_name = 'libclang_rt.{}-x86_64.a'.format(lib) 546 built_lib64 = os.path.join(obj64_dir, built_lib_name) 547 install_file(built_lib64, os.path.join(lib_dst, lib64_name)) 548 if is_multilib: 549 obj32_dir = os.path.join(obj32, 'lib{}_intermediates'.format(lib)) 550 lib32_name = 'libclang_rt.{}-i686.a'.format(lib) 551 built_lib32 = os.path.join(obj32_dir, built_lib_name) 552 install_file(built_lib32, os.path.join(lib_dst, lib32_name)) 553 554 555 def install_license_files(install_dir): 556 projects = ( 557 'clang', 558 'clang-tools-extra', 559 'compiler-rt', 560 'libcxx', 561 'libcxxabi', 562 'libunwind_llvm', 563 'llvm', 564 'openmp_llvm' 565 ) 566 567 notices = [] 568 for project in projects: 569 project_path = android_path('external', project) 570 license_pattern = os.path.join(project_path, 'MODULE_LICENSE_*') 571 for license_file in glob.glob(license_pattern): 572 install_file(license_file, install_dir) 573 with open(os.path.join(project_path, 'NOTICE')) as notice_file: 574 notices.append(notice_file.read()) 575 with open(os.path.join(install_dir, 'NOTICE'), 'w') as notice_file: 576 notice_file.write('\n'.join(notices)) 577 578 579 def install_repo_prop(install_dir): 580 file_name = 'repo.prop' 581 582 dist_dir = os.environ.get('DIST_DIR') 583 if dist_dir is not None: 584 dist_repo_prop = os.path.join(dist_dir, file_name) 585 install_file(dist_repo_prop, install_dir) 586 else: 587 out_file = os.path.join(install_dir, file_name) 588 with open(out_file, 'w') as prop_file: 589 cmd = [ 590 'repo', 'forall', '-c', 591 'echo $REPO_PROJECT $(git rev-parse HEAD)', 592 ] 593 check_call(cmd, stdout=prop_file) 594 595 596 def install_compiler_wrapper(install_dir, host): 597 is_windows = host.startswith('windows') 598 bin_ext = '.exe' if is_windows else '' 599 600 built_files = [ 601 'bin/clang' + bin_ext, 602 'bin/clang++' + bin_ext, 603 ] 604 605 if is_windows: 606 built_files.extend([ 607 'bin/clang_32' + bin_ext, 608 ]) 609 610 wrapper_dir = android_path('external/clang') 611 wrapper = os.path.join(wrapper_dir, 'compiler_wrapper') 612 613 for built_file in built_files: 614 old_file = os.path.join(install_dir, built_file) 615 new_file = os.path.join(install_dir, built_file + ".real") 616 rename(old_file, new_file) 617 install_file(wrapper, old_file) 618 619 620 def parse_args(): 621 parser = argparse.ArgumentParser() 622 623 parser.add_argument('-j', action='store', dest='jobs', type=int, 624 default=multiprocessing.cpu_count(), 625 help='Specify number of executed jobs') 626 627 parser.add_argument( 628 '--build-name', default='dev', help='Release name for the package.') 629 parser.add_argument( 630 '--dry-run', action='store_true', default=False, 631 help='Skip running commands; just print.') 632 parser.add_argument( 633 '-v', '--verbose', action='store_true', default=False, 634 help='Print debug output.') 635 636 multi_stage_group = parser.add_mutually_exclusive_group() 637 multi_stage_group.add_argument( 638 '--multi-stage', action='store_true', default=True, 639 help='Perform multi-stage build (enabled by default).') 640 multi_stage_group.add_argument( 641 '--no-multi-stage', action='store_false', dest='multi_stage', 642 help='Do not perform multi-stage build.') 643 644 build_all_llvm_tools_group = parser.add_mutually_exclusive_group() 645 build_all_llvm_tools_group.add_argument( 646 '--build-all-llvm-tools', action='store_true', default=True, 647 help='Build all the LLVM tools/utilities.') 648 build_all_llvm_tools_group.add_argument( 649 '--no-build-all-llvm-tools', action='store_false', 650 dest='build_all_llvm_tools', 651 help='Do not build all the LLVM tools/utilities.') 652 653 build_debug_clang_group = parser.add_mutually_exclusive_group() 654 build_debug_clang_group.add_argument( 655 '--debug-clang', action='store_true', default=True, 656 help='Also generate a debug version of clang (enabled by default).') 657 build_debug_clang_group.add_argument( 658 '--no-debug-clang', action='store_false', 659 dest='debug_clang', 660 help='Skip generating a debug version of clang.') 661 662 return parser.parse_args() 663 664 665 def main(): 666 args = parse_args() 667 log_level = logging.INFO 668 if args.verbose: 669 log_level = logging.DEBUG 670 logging.basicConfig(level=log_level) 671 672 logger().info('chdir %s', android_path()) 673 os.chdir(android_path()) 674 675 Config.dry_run = args.dry_run 676 677 if sys.platform.startswith('linux'): 678 hosts = ['linux-x86', 'windows-x86'] 679 elif sys.platform == 'darwin': 680 hosts = ['darwin-x86'] 681 else: 682 raise RuntimeError('Unsupported host: {}'.format(sys.platform)) 683 684 stage_1_out_dir = build_path('stage1') 685 686 # For a multi-stage build, build a minimum clang for the first stage that is 687 # just enough to build the second stage. 688 is_stage1_final = not args.multi_stage 689 build(out_dir=stage_1_out_dir, 690 build_all_clang_tools=is_stage1_final, 691 build_all_llvm_tools=(is_stage1_final and args.build_all_llvm_tools), 692 max_jobs=args.jobs) 693 final_out_dir = stage_1_out_dir 694 if args.multi_stage: 695 stage_1_install_dir = build_path('stage1-install') 696 for host in hosts: 697 package_name = 'clang-' + args.build_name 698 install_host_dir = os.path.join(stage_1_install_dir, host) 699 install_dir = os.path.join(install_host_dir, package_name) 700 701 # Remove any previously installed toolchain so it doesn't pollute 702 # the build. 703 if os.path.exists(install_host_dir): 704 rmtree(install_host_dir) 705 706 install_minimal_toolchain(stage_1_out_dir, install_dir, host, True) 707 708 stage_2_out_dir = build_path('stage2') 709 build(out_dir=stage_2_out_dir, prebuilts_path=stage_1_install_dir, 710 prebuilts_version=package_name, 711 build_all_clang_tools=True, 712 build_all_llvm_tools=args.build_all_llvm_tools, 713 max_jobs=args.jobs) 714 final_out_dir = stage_2_out_dir 715 716 if args.debug_clang: 717 debug_clang_out_dir = build_path('debug') 718 build(out_dir=debug_clang_out_dir, 719 prebuilts_path=stage_1_install_dir, 720 prebuilts_version=package_name, 721 build_all_clang_tools=True, 722 build_all_llvm_tools=args.build_all_llvm_tools, 723 debug_clang=args.debug_clang, 724 max_jobs=args.jobs) 725 # Install the actual debug toolchain somewhere, so it is easier to 726 # use. 727 debug_package_name = 'clang-debug' 728 base_debug_install_dir = build_path('debug-install') 729 for host in hosts: 730 debug_install_host_dir = os.path.join( 731 base_debug_install_dir, host) 732 debug_install_dir = os.path.join( 733 debug_install_host_dir, debug_package_name) 734 if os.path.exists(debug_install_host_dir): 735 rmtree(debug_install_host_dir) 736 install_toolchain( 737 debug_clang_out_dir, debug_install_dir, host, False) 738 739 dist_dir = ORIG_ENV.get('DIST_DIR', final_out_dir) 740 for host in hosts: 741 package_toolchain(final_out_dir, args.build_name, host, dist_dir) 742 743 744 if __name__ == '__main__': 745 main() 746