Home | History | Annotate | Download | only in clang
      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