Home | History | Annotate | Download | only in build-android
      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 
     18 import argparse
     19 import multiprocessing
     20 import os
     21 import shutil
     22 import subprocess
     23 import sys
     24 import time
     25 
     26 from subprocess import PIPE, STDOUT
     27 
     28 def install_file(file_name, src_dir, dst_dir):
     29     src_file = os.path.join(src_dir, file_name)
     30     dst_file = os.path.join(dst_dir, file_name)
     31 
     32     print('Copying {} to {}...'.format(src_file, dst_file))
     33     if os.path.isdir(src_file):
     34         _install_dir(src_file, dst_file)
     35     elif os.path.islink(src_file):
     36         _install_symlink(src_file, dst_file)
     37     else:
     38         _install_file(src_file, dst_file)
     39 
     40 
     41 def _install_dir(src_dir, dst_dir):
     42     parent_dir = os.path.normpath(os.path.join(dst_dir, '..'))
     43     if not os.path.exists(parent_dir):
     44         os.makedirs(parent_dir)
     45     shutil.copytree(src_dir, dst_dir, symlinks=True)
     46 
     47 
     48 def _install_symlink(src_file, dst_file):
     49     dirname = os.path.dirname(dst_file)
     50     if not os.path.exists(dirname):
     51         os.makedirs(dirname)
     52     link_target = os.readlink(src_file)
     53     os.symlink(link_target, dst_file)
     54 
     55 
     56 def _install_file(src_file, dst_file):
     57     dirname = os.path.dirname(dst_file)
     58     if not os.path.exists(dirname):
     59         os.makedirs(dirname)
     60     # copy2 is just copy followed by copystat (preserves file metadata).
     61     shutil.copy2(src_file, dst_file)
     62 
     63 THIS_DIR = os.path.realpath(os.path.dirname(__file__))
     64 
     65 ALL_ARCHITECTURES = (
     66   'arm',
     67   'arm64',
     68   'mips',
     69   'mips64',
     70   'x86',
     71   'x86_64',
     72 )
     73 
     74 # According to vk_platform.h, armeabi is not supported for Vulkan
     75 # so remove it from the abis list.
     76 ALL_ABIS = (
     77   'armeabi-v7a',
     78   'arm64-v8a',
     79   'mips',
     80   'mips64',
     81   'x86',
     82   'x86_64',
     83 )
     84 
     85 def jobs_arg():
     86   return '-j{}'.format(multiprocessing.cpu_count() * 2)
     87 
     88 def arch_to_abis(arch):
     89   return {
     90     'arm': ['armeabi-v7a'],
     91     'arm64': ['arm64-v8a'],
     92     'mips': ['mips'],
     93     'mips64': ['mips64'],
     94     'x86': ['x86'],
     95     'x86_64': ['x86_64'],
     96   }[arch]
     97 
     98 class ArgParser(argparse.ArgumentParser):
     99   def __init__(self):
    100     super(ArgParser, self).__init__()
    101 
    102     self.add_argument(
    103       '--out-dir', help='Directory to place temporary build files.',
    104       type=os.path.realpath, default=os.path.join(THIS_DIR, 'out'))
    105 
    106     self.add_argument(
    107       '--arch', choices=ALL_ARCHITECTURES,
    108       help='Architectures to build. Builds all if not present.')
    109 
    110     self.add_argument('--installdir', dest='installdir', required=True,
    111       help='Installation directory. Required.')
    112 
    113     # The default for --dist-dir has to be handled after parsing all
    114     # arguments because the default is derived from --out-dir. This is
    115     # handled in run().
    116     self.add_argument(
    117       '--dist-dir', help='Directory to place the packaged artifact.',
    118       type=os.path.realpath)
    119 
    120 
    121 def main():
    122   print('THIS_DIR: %s' % THIS_DIR)
    123   parser = ArgParser()
    124   args = parser.parse_args()
    125 
    126   arches = ALL_ARCHITECTURES
    127   if args.arch is not None:
    128     arches = [args.arch]
    129 
    130   # Make paths absolute, and ensure directories exist.
    131   installdir = os.path.abspath(args.installdir)
    132 
    133   abis = []
    134   for arch in arches:
    135     abis.extend(arch_to_abis(arch))
    136 
    137   shaderc_path = installdir + '/shaderc/android_test'
    138   print('shaderc_path = %s' % shaderc_path)
    139 
    140   ndk_dir = os.path.join(THIS_DIR, '../../../prebuilts/toolchain')
    141 
    142   ndk_build = os.path.join(ndk_dir, 'ndk-build')
    143   platforms_root = os.path.join(ndk_dir, 'platforms')
    144   toolchains_root = os.path.join(ndk_dir, 'toolchains')
    145   build_dir = installdir
    146 
    147   print('installdir: %s' % installdir)
    148   print('ndk_dir: %s' % ndk_dir)
    149   print('ndk_build: %s' % ndk_build)
    150   print('platforms_root: %s' % platforms_root)
    151 
    152   compiler = 'clang'
    153   stl = 'c++_static'
    154   obj_out = os.path.join(THIS_DIR, stl, 'obj')
    155   lib_out = os.path.join(THIS_DIR, 'jniLibs')
    156 
    157   print('obj_out: %s' % obj_out)
    158   print('lib_out: %s' % lib_out)
    159 
    160   print('Constructing shaderc build tree...')
    161   shaderc_root_dir = os.path.join(THIS_DIR, '../../shaderc')
    162 
    163   print('Pulling in vulkan headers and layers...')
    164   vulkan_root_dir = os.path.join(THIS_DIR, '../../vulkan-validation-layers')
    165   vulkan_headers_root_dir = os.path.join(THIS_DIR, '../../vulkan-headers')
    166 
    167   copies = [
    168       {
    169           'source_dir': os.path.join(shaderc_root_dir, 'shaderc'),
    170           'dest_dir': 'third_party/shaderc',
    171           'files': [
    172               'Android.mk', 'libshaderc/Android.mk',
    173               'libshaderc_util/Android.mk',
    174               'third_party/Android.mk',
    175               'utils/update_build_version.py',
    176               'CHANGES',
    177           ],
    178           'dirs': [
    179               'libshaderc/include', 'libshaderc/src',
    180               'libshaderc_util/include', 'libshaderc_util/src',
    181               'android_test'
    182           ],
    183       },
    184       {
    185           'source_dir': os.path.join(shaderc_root_dir, 'spirv-tools'),
    186           'dest_dir': 'third_party/shaderc/third_party/spirv-tools',
    187           'files': [
    188               'utils/generate_grammar_tables.py',
    189               'utils/generate_language_headers.py',
    190               'utils/generate_registry_tables.py',
    191               'utils/update_build_version.py',
    192               'Android.mk',
    193               'CHANGES',
    194           ],
    195           'dirs': ['include', 'source'],
    196       },
    197       {
    198           'source_dir': os.path.join(shaderc_root_dir, 'spirv-headers'),
    199           'dest_dir':
    200               'third_party/shaderc/third_party/spirv-tools/external/spirv-headers',
    201           'dirs': ['include',],
    202           'files': [
    203               'include/spirv/1.0/spirv.py',
    204               'include/spirv/1.1/spirv.py',
    205               'include/spirv/1.2/spirv.py',
    206           ],
    207       },
    208       {
    209           'source_dir': os.path.join(shaderc_root_dir, 'glslang'),
    210           'dest_dir': 'third_party/shaderc/third_party/glslang',
    211           'files': ['glslang/OSDependent/osinclude.h',
    212                     'Android.mk',
    213                    ],
    214           'dirs': [
    215               'SPIRV',
    216               'OGLCompilersDLL',
    217               'glslang/GenericCodeGen',
    218               'hlsl',
    219               'glslang/Include',
    220               'glslang/MachineIndependent',
    221               'glslang/OSDependent/Unix',
    222               'glslang/Public',
    223           ],
    224       },
    225       {
    226           'source_dir': vulkan_root_dir,
    227           'dest_dir': 'vulkan/src',
    228           'files': [
    229           ],
    230           'dirs': [
    231               'layers', 'scripts', 'build-android'
    232           ],
    233       },
    234 
    235       {
    236           'source_dir': vulkan_headers_root_dir,
    237           'dest_dir': 'vulkan/src',
    238           'files': [
    239           ],
    240           'dirs': [
    241               'include', 'registry'
    242           ],
    243       },
    244   ]
    245 
    246   default_ignore_patterns = shutil.ignore_patterns(
    247       "*CMakeLists.txt",
    248       "*test.h",
    249       "*test.cc")
    250 
    251   for properties in copies:
    252       source_dir = properties['source_dir']
    253       dest_dir = os.path.join(installdir, properties['dest_dir'])
    254       for d in properties['dirs']:
    255           src = os.path.join(source_dir, d)
    256           dst = os.path.join(dest_dir, d)
    257           print(src, " -> ", dst)
    258           shutil.copytree(src, dst,
    259                           ignore=default_ignore_patterns)
    260       for f in properties['files']:
    261           print(source_dir, ':', dest_dir, ":", f)
    262           # Only copy if the source file exists.  That way
    263           # we can update this script in anticipation of
    264           # source files yet-to-come.
    265           if os.path.exists(os.path.join(source_dir, f)):
    266               install_file(f, source_dir, dest_dir)
    267           else:
    268               print(source_dir, ':', dest_dir, ":", f, "SKIPPED")
    269 
    270   print('Constructing Vulkan validation layer source...')
    271 
    272   build_cmd = [
    273     'bash', build_dir + '/vulkan/src/build-android/android-generate.sh',
    274             build_dir + '/vulkan/src/registry'
    275   ]
    276   print('Generating generated layers...')
    277   subprocess.check_call(build_cmd)
    278   print('Generation finished')
    279 
    280   build_cmd = [
    281     'bash', ndk_build, '-C', build_dir + '/vulkan/src/build-android',
    282     jobs_arg(),
    283     'APP_ABI=' + ' '.join(abis),
    284     # Use the prebuilt platforms and toolchains.
    285     'NDK_PLATFORMS_ROOT=' + platforms_root,
    286     'NDK_TOOLCHAINS_ROOT=' + toolchains_root,
    287     'NDK_MODULE_PATH=' + installdir,
    288     'GNUSTL_PREFIX=',
    289     'APP_STL=' + stl,
    290     'NDK_TOOLCHAIN_VERSION=' + compiler,
    291 
    292     # Tell ndk-build where to put the results
    293     'NDK_OUT=' + obj_out,
    294     'NDK_LIBS_OUT=' + lib_out,
    295   ]
    296 
    297   print('Building Vulkan validation layers for ABIs:' +
    298     ' {}'.format(', '.join(abis)) + "...")
    299   print(' '.join(build_cmd))
    300 
    301   subprocess.check_call(build_cmd)
    302 
    303   print('Finished building Vulkan validation layers')
    304   out_package = os.path.join(installdir, 'vulkan_validation_layers.zip')
    305   os.chdir(lib_out)
    306   build_cmd = [
    307       'zip', '-9qr', out_package, "."
    308   ]
    309 
    310   print('Packaging Vulkan validation layers')
    311   subprocess.check_call(build_cmd)
    312   print('Finished Packaging Vulkan validation layers')
    313 
    314   for properties in copies:
    315       dest_dir = os.path.join(installdir, properties['dest_dir'])
    316       for d in properties['dirs']:
    317           dst = os.path.join(dest_dir, d)
    318           print('Remove: %s' % dst)
    319           shutil.rmtree(dst)
    320 
    321   return 0
    322 
    323 
    324 if __name__ == '__main__':
    325   main()
    326