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 from __future__ import print_function
     18 
     19 import argparse
     20 import glob
     21 import multiprocessing
     22 import os
     23 import shutil
     24 import subprocess
     25 import sys
     26 
     27 import version
     28 
     29 
     30 THIS_DIR = os.path.realpath(os.path.dirname(__file__))
     31 ORIG_ENV = dict(os.environ)
     32 
     33 
     34 def android_path(*args):
     35     return os.path.realpath(os.path.join(THIS_DIR, '../..', *args))
     36 
     37 
     38 def build_path(*args):
     39     # Our multistage build directories will be placed under OUT_DIR if it is in
     40     # the environment. By default they will be placed under
     41     # $ANDROID_BUILD_TOP/out.
     42     top_out = ORIG_ENV.get('OUT_DIR', android_path('out'))
     43     if not os.path.isabs(top_out):
     44         top_out = os.path.realpath(top_out)
     45     return os.path.join(top_out, *args)
     46 
     47 
     48 def short_version():
     49     return '.'.join([version.major, version.minor])
     50 
     51 
     52 def long_version():
     53     return '.'.join([version.major, version.minor, version.patch])
     54 
     55 
     56 def install_file(src, dst):
     57     print('Copying ' + src)
     58     shutil.copy2(src, dst)
     59 
     60 
     61 def install_directory(src, dst):
     62     print('Copying ' + src)
     63     shutil.copytree(src, dst)
     64 
     65 
     66 def build(out_dir, prebuilts_path=None, prebuilts_version=None,
     67           build_all_llvm_tools=None):
     68     products = (
     69         'aosp_arm',
     70         'aosp_arm64',
     71         'aosp_mips',
     72         'aosp_mips64',
     73         'aosp_x86',
     74         'aosp_x86_64',
     75     )
     76     for product in products:
     77         build_product(out_dir, product, prebuilts_path, prebuilts_version,
     78                       build_all_llvm_tools)
     79 
     80 
     81 def build_product(out_dir, product, prebuilts_path, prebuilts_version,
     82                   build_all_llvm_tools):
     83     env = dict(ORIG_ENV)
     84     env['DISABLE_LLVM_DEVICE_BUILDS'] = 'true'
     85     env['DISABLE_RELOCATION_PACKER'] = 'true'
     86     env['FORCE_BUILD_LLVM_COMPONENTS'] = 'true'
     87     env['FORCE_BUILD_SANITIZER_SHARED_OBJECTS'] = 'true'
     88     env['OUT_DIR'] = out_dir
     89     env['SKIP_LLVM_TESTS'] = 'true'
     90     env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true'
     91     env['TARGET_BUILD_VARIANT'] = 'userdebug'
     92     env['TARGET_PRODUCT'] = product
     93 
     94     overrides = []
     95     if prebuilts_path is not None:
     96         overrides.append('LLVM_PREBUILTS_BASE={}'.format(prebuilts_path))
     97     if prebuilts_version is not None:
     98         overrides.append('LLVM_PREBUILTS_VERSION={}'.format(prebuilts_version))
     99 
    100     jobs_arg = '-j{}'.format(multiprocessing.cpu_count())
    101     targets = ['clang-toolchain']
    102     if build_all_llvm_tools:
    103         targets += ['llvm-tools']
    104     subprocess.check_call(
    105         ['make', jobs_arg] + overrides + targets, cwd=android_path(), env=env)
    106 
    107 
    108 def package_toolchain(build_dir, build_name, host, dist_dir):
    109     package_name = 'clang-' + build_name
    110     install_host_dir = build_path('install', host)
    111     install_dir = os.path.join(install_host_dir, package_name)
    112 
    113     # Remove any previously installed toolchain so it doesn't pollute the
    114     # build.
    115     if os.path.exists(install_host_dir):
    116         shutil.rmtree(install_host_dir)
    117 
    118     install_toolchain(build_dir, install_dir, host)
    119 
    120     version_file_path = os.path.join(install_dir, 'AndroidVersion.txt')
    121     with open(version_file_path, 'w') as version_file:
    122         version_file.write('{}.{}.{}\n'.format(
    123             version.major, version.minor, version.patch))
    124 
    125     tarball_name = package_name + '-' + host
    126     package_path = os.path.join(dist_dir, tarball_name) + '.tar.bz2'
    127     print('Packaging ' + package_path)
    128     args = [
    129         'tar', '-cjC', install_host_dir, '-f', package_path, package_name
    130     ]
    131     subprocess.check_call(args)
    132 
    133 
    134 def install_toolchain(build_dir, install_dir, host):
    135     install_built_host_files(build_dir, install_dir, host)
    136     install_sanitizer_scripts(install_dir)
    137     install_scan_scripts(install_dir)
    138     install_analyzer_scripts(install_dir)
    139     install_headers(build_dir, install_dir, host)
    140     install_profile_rt(build_dir, install_dir, host)
    141     install_sanitizers(build_dir, install_dir, host)
    142     install_license_files(install_dir)
    143     install_repo_prop(install_dir)
    144 
    145 
    146 def install_built_host_files(build_dir, install_dir, host):
    147     is_windows = host.startswith('windows')
    148     is_darwin = host.startswith('darwin-x86')
    149     bin_ext = '.exe' if is_windows else ''
    150 
    151     if is_windows:
    152         lib_ext = '.dll'
    153     elif is_darwin:
    154         lib_ext = '.dylib'
    155     else:
    156         lib_ext = '.so'
    157 
    158     built_files = [
    159         'bin/clang' + bin_ext,
    160         'bin/clang++' + bin_ext,
    161     ]
    162     if host != 'windows-x86':
    163         built_files.extend([
    164             'bin/FileCheck' + bin_ext,
    165             'bin/llvm-as' + bin_ext,
    166             'bin/llvm-dis' + bin_ext,
    167             'bin/llvm-link' + bin_ext,
    168             'lib64/libc++' + lib_ext,
    169             'lib64/libLLVM' + lib_ext,
    170             'lib64/LLVMgold' + lib_ext,
    171         ])
    172 
    173     for built_file in built_files:
    174         dirname = os.path.dirname(built_file)
    175         install_path = os.path.join(install_dir, dirname)
    176         if not os.path.exists(install_path):
    177             os.makedirs(install_path)
    178 
    179         built_path = os.path.join(build_dir, 'host', host, built_file)
    180         install_file(built_path, install_path)
    181 
    182         file_name = os.path.basename(built_file)
    183 
    184         # Only strip bin files (not libs) on darwin.
    185         if not is_darwin or built_file.startswith('bin/'):
    186             subprocess.check_call(
    187                 ['strip', os.path.join(install_path, file_name)])
    188 
    189 
    190 def install_sanitizer_scripts(install_dir):
    191     script_path = android_path(
    192         'external/compiler-rt/lib/asan/scripts/asan_device_setup')
    193     shutil.copy2(script_path, os.path.join(install_dir, 'bin'))
    194 
    195 
    196 def install_analyzer_scripts(install_dir):
    197     """Create and install bash scripts for invoking Clang for analysis."""
    198     analyzer_text = (
    199         '#!/bin/bash\n'
    200         'if [ "$1" != "-cc1" ]; then\n'
    201         '    `dirname $0`/../clang{clang_suffix} -target {target} "$@"\n'
    202         'else\n'
    203         '    # target/triple already spelled out.\n'
    204         '    `dirname $0`/../clang{clang_suffix} "$@"\n'
    205         'fi\n'
    206     )
    207 
    208     arch_target_pairs = (
    209         ('arm64-v8a', 'aarch64-none-linux-android'),
    210         ('armeabi', 'armv5te-none-linux-androideabi'),
    211         ('armeabi-v7a', 'armv7-none-linux-androideabi'),
    212         ('armeabi-v7a-hard', 'armv7-none-linux-androideabi'),
    213         ('mips', 'mipsel-none-linux-android'),
    214         ('mips64', 'mips64el-none-linux-android'),
    215         ('x86', 'i686-none-linux-android'),
    216         ('x86_64', 'x86_64-none-linux-android'),
    217     )
    218 
    219     for arch, target in arch_target_pairs:
    220         arch_path = os.path.join(install_dir, 'bin', arch)
    221         os.makedirs(arch_path)
    222 
    223         analyzer_file_path = os.path.join(arch_path, 'analyzer')
    224         print('Creating ' + analyzer_file_path)
    225         with open(analyzer_file_path, 'w') as analyzer_file:
    226             analyzer_file.write(
    227                 analyzer_text.format(clang_suffix='', target=target))
    228 
    229         analyzerpp_file_path = os.path.join(arch_path, 'analyzer++')
    230         print('Creating ' + analyzerpp_file_path)
    231         with open(analyzerpp_file_path, 'w') as analyzerpp_file:
    232             analyzerpp_file.write(
    233                 analyzer_text.format(clang_suffix='++', target=target))
    234 
    235 
    236 def install_scan_scripts(install_dir):
    237     tools_install_dir = os.path.join(install_dir, 'tools')
    238     os.makedirs(tools_install_dir)
    239     tools = ('scan-build', 'scan-view')
    240     tools_dir = android_path('external/clang/tools')
    241     for tool in tools:
    242         tool_path = os.path.join(tools_dir, tool)
    243         install_path = os.path.join(install_dir, 'tools', tool)
    244         install_directory(tool_path, install_path)
    245 
    246 
    247 def install_headers(build_dir, install_dir, host):
    248     def should_copy(path):
    249         if os.path.basename(path) in ('Makefile', 'CMakeLists.txt'):
    250             return False
    251         _, ext = os.path.splitext(path)
    252         if ext == '.mk':
    253             return False
    254         return True
    255 
    256     headers_src = android_path('external/clang/lib/Headers')
    257     headers_dst = os.path.join(
    258         install_dir, 'lib64/clang', short_version(), 'include')
    259     os.makedirs(headers_dst)
    260     for header in os.listdir(headers_src):
    261         if not should_copy(header):
    262             continue
    263         install_file(os.path.join(headers_src, header), headers_dst)
    264 
    265     install_file(android_path('bionic/libc/include/stdatomic.h'), headers_dst)
    266 
    267     # arm_neon.h gets produced as part of external/clang/lib/Basic/Android.mk.
    268     # We must bundle the resulting file as part of the official Clang headers.
    269     arm_neon_h = os.path.join(
    270         build_dir, 'host', host, 'obj/STATIC_LIBRARIES/'
    271         'libclangBasic_intermediates/include/clang/Basic/arm_neon.h')
    272     install_file(arm_neon_h, headers_dst)
    273 
    274     os.symlink(short_version(),
    275                os.path.join(install_dir, 'lib64/clang', long_version()))
    276 
    277 
    278 def install_profile_rt(build_dir, install_dir, host):
    279     lib_dir = os.path.join(
    280         install_dir, 'lib64/clang', short_version(), 'lib/linux')
    281     os.makedirs(lib_dir)
    282 
    283     install_target_profile_rt(build_dir, lib_dir)
    284 
    285     # We only support profiling libs for Linux and Android.
    286     if host == 'linux-x86':
    287         install_host_profile_rt(build_dir, host, lib_dir)
    288 
    289 
    290 def install_target_profile_rt(build_dir, lib_dir):
    291     product_to_arch = {
    292         'generic': 'arm',
    293         'generic_arm64': 'aarch64',
    294         'generic_mips': 'mipsel',
    295         'generic_mips64': 'mips64el',
    296         'generic_x86': 'i686',
    297         'generic_x86_64': 'x86_64',
    298     }
    299 
    300     for product, arch in product_to_arch.items():
    301         product_dir = os.path.join(build_dir, 'target/product', product)
    302         static_libs = os.path.join(product_dir, 'obj/STATIC_LIBRARIES')
    303         built_lib = os.path.join(
    304             static_libs, 'libprofile_rt_intermediates/libprofile_rt.a')
    305         lib_name = 'libclang_rt.profile-{}-android.a'.format(arch)
    306         install_file(built_lib, os.path.join(lib_dir, lib_name))
    307 
    308 
    309 def install_host_profile_rt(build_dir, host, lib_dir):
    310     arch_to_obj_dir = {
    311         'i686': 'obj32',
    312         'x86_64': 'obj',
    313     }
    314 
    315     for arch, obj_dir in arch_to_obj_dir.items():
    316         static_libs = os.path.join(
    317             build_dir, 'host', host, obj_dir, 'STATIC_LIBRARIES')
    318         built_lib = os.path.join(
    319             static_libs, 'libprofile_rt_intermediates/libprofile_rt.a')
    320         lib_name = 'libclang_rt.profile-{}.a'.format(arch)
    321         install_file(built_lib, os.path.join(lib_dir, lib_name))
    322 
    323 
    324 def install_sanitizers(build_dir, install_dir, host):
    325     headers_src = android_path('external/compiler-rt/include/sanitizer')
    326     clang_lib = os.path.join(install_dir, 'lib64/clang', short_version())
    327     headers_dst = os.path.join(clang_lib, 'include/sanitizer')
    328     lib_dst = os.path.join(clang_lib, 'lib/linux')
    329     install_directory(headers_src, headers_dst)
    330 
    331     if host == 'linux-x86':
    332         install_host_sanitizers(build_dir, host, lib_dst)
    333 
    334     # Tuples of (product, arch, libdir)
    335     product_to_arch = (
    336         ('generic', 'arm', 'lib'),
    337         ('generic_arm64', 'aarch64', 'lib64'),
    338         ('generic_x86', 'i686', 'lib'),
    339     )
    340 
    341     for product, arch, libdir in product_to_arch:
    342         product_dir = os.path.join(build_dir, 'target/product', product)
    343         system_dir = os.path.join(product_dir, 'system')
    344         system_lib_dir = os.path.join(system_dir, libdir)
    345         lib_name = 'libclang_rt.asan-{}-android.so'.format(arch)
    346         built_lib = os.path.join(system_lib_dir, lib_name)
    347         install_file(built_lib, lib_dst)
    348 
    349 
    350 def install_host_sanitizers(build_dir, host, lib_dst):
    351     # Tuples of (name, multilib).
    352     libs = (
    353         ('asan', True),
    354         ('asan_cxx', True),
    355         ('ubsan_standalone', True),
    356         ('ubsan_standalone_cxx', True),
    357         ('tsan', False),
    358         ('tsan_cxx', False),
    359     )
    360 
    361     obj32 = os.path.join(build_dir, 'host', host, 'obj32/STATIC_LIBRARIES')
    362     obj64 = os.path.join(build_dir, 'host', host, 'obj/STATIC_LIBRARIES')
    363     for lib, is_multilib in libs:
    364         built_lib_name = 'lib{}.a'.format(lib)
    365 
    366         obj64_dir = os.path.join(obj64, 'lib{}_intermediates'.format(lib))
    367         lib64_name = 'libclang_rt.{}-x86_64.a'.format(lib)
    368         built_lib64 = os.path.join(obj64_dir, built_lib_name)
    369         install_file(built_lib64, os.path.join(lib_dst, lib64_name))
    370         if is_multilib:
    371             obj32_dir = os.path.join(obj32, 'lib{}_intermediates'.format(lib))
    372             lib32_name = 'libclang_rt.{}-i686.a'.format(lib)
    373             built_lib32 = os.path.join(obj32_dir, built_lib_name)
    374             install_file(built_lib32, os.path.join(lib_dst, lib32_name))
    375 
    376 
    377 def install_license_files(install_dir):
    378     projects = (
    379         'clang',
    380         'compiler-rt',
    381         'libcxx',
    382         'libcxxabi',
    383         'libunwind_llvm',
    384         'llvm',
    385     )
    386 
    387     notices = []
    388     for project in projects:
    389         project_path = android_path('external', project)
    390         license_pattern = os.path.join(project_path, 'MODULE_LICENSE_*')
    391         for license_file in glob.glob(license_pattern):
    392             install_file(license_file, install_dir)
    393         with open(os.path.join(project_path, 'NOTICE')) as notice_file:
    394             notices.append(notice_file.read())
    395     with open(os.path.join(install_dir, 'NOTICE'), 'w') as notice_file:
    396         notice_file.write('\n'.join(notices))
    397 
    398 
    399 def install_repo_prop(install_dir):
    400     file_name = 'repo.prop'
    401 
    402     dist_dir = os.environ.get('DIST_DIR')
    403     if dist_dir is not None:
    404         dist_repo_prop = os.path.join(dist_dir, file_name)
    405         shutil.copy(dist_repo_prop, install_dir)
    406     else:
    407         out_file = os.path.join(install_dir, file_name)
    408         with open(out_file, 'w') as prop_file:
    409             cmd = [
    410                 'repo', 'forall', '-c',
    411                 'echo $REPO_PROJECT $(git rev-parse HEAD)',
    412             ]
    413             subprocess.check_call(cmd, stdout=prop_file)
    414 
    415 
    416 def parse_args():
    417     parser = argparse.ArgumentParser()
    418 
    419     parser.add_argument(
    420         '--build-name', default='dev', help='Release name for the package.')
    421 
    422     multi_stage_group = parser.add_mutually_exclusive_group()
    423     multi_stage_group.add_argument(
    424         '--multi-stage', action='store_true', default=True,
    425         help='Perform multi-stage build (enabled by default).')
    426     multi_stage_group.add_argument(
    427         '--no-multi-stage', action='store_false', dest='multi_stage',
    428         help='Do not perform multi-stage build.')
    429 
    430     parser.add_argument(
    431         '--build-all-llvm-tools', action='store_true', default=True,
    432         help='Build all the LLVM tools/utilities.')
    433 
    434     parser.add_argument(
    435         '--no-build-all-llvm-tools', action='store_false',
    436         dest='build_all_llvm_tools',
    437         help='Build all the LLVM tools/utilities.')
    438 
    439     return parser.parse_args()
    440 
    441 
    442 def main():
    443     args = parse_args()
    444 
    445     if sys.platform.startswith('linux'):
    446         hosts = ['linux-x86', 'windows-x86']
    447     elif sys.platform == 'darwin':
    448         hosts = ['darwin-x86']
    449     else:
    450         raise RuntimeError('Unsupported host: {}'.format(sys.platform))
    451 
    452     stage_1_out_dir = build_path('stage1')
    453     build(out_dir=stage_1_out_dir)
    454     final_out_dir = stage_1_out_dir
    455     if args.multi_stage:
    456         stage_1_install_dir = build_path('stage1-install')
    457         for host in hosts:
    458             package_name = 'clang-' + args.build_name
    459             install_host_dir = os.path.join(stage_1_install_dir, host)
    460             install_dir = os.path.join(install_host_dir, package_name)
    461 
    462             # Remove any previously installed toolchain so it doesn't pollute
    463             # the build.
    464             if os.path.exists(install_host_dir):
    465                 shutil.rmtree(install_host_dir)
    466 
    467             install_toolchain(stage_1_out_dir, install_dir, host)
    468 
    469         stage_2_out_dir = build_path('stage2')
    470         build(out_dir=stage_2_out_dir, prebuilts_path=stage_1_install_dir,
    471               prebuilts_version=package_name,
    472               build_all_llvm_tools=args.build_all_llvm_tools)
    473         final_out_dir = stage_2_out_dir
    474 
    475     dist_dir = ORIG_ENV.get('DIST_DIR', final_out_dir)
    476     for host in hosts:
    477         package_toolchain(final_out_dir, args.build_name, host, dist_dir)
    478 
    479 
    480 if __name__ == '__main__':
    481     main()
    482