blob: 4256d4033a317386cbe636f422646c27bd2444e3 [file] [log] [blame]
#!/usr/bin/env python
#
# Copyright (C) 2015 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
"""Makes an old style monolithic NDK package out of individual modules."""
from __future__ import print_function
import argparse
import datetime
import os
import shutil
import site
import stat
import subprocess
import sys
import tempfile
site.addsitedir(os.path.join(os.path.dirname(__file__), '../lib'))
import build_support # pylint: disable=import-error
THIS_DIR = os.path.dirname(__file__)
ANDROID_TOP = os.path.realpath(os.path.join(THIS_DIR, '../../..'))
def expand_packages(package, host, arches):
"""Expands package definition tuple into list of full package names.
>>> expand_packages('gcc-{arch}-{host}', 'linux', ['arm64', 'x86_64'])
['gcc-arm64-linux-x86_64', 'gcc-x86_64-linux-x86_64']
>>> expand_packages('gcclibs-{arch}', 'linux', ['arm64', 'x86_64'])
['gcclibs-arm64', 'gcclibs-x86_64']
>>> expand_packages('llvm-{host}', 'linux', ['arm'])
['llvm-linux-x86_64']
>>> expand_packages('platforms', 'linux', ['arm'])
['platforms']
>>> expand_packages('libc++-{abi}', 'linux', ['arm'])
['libc++-armeabi', 'libc++-armeabi-v7a', 'libc++-armeabi-v7a-hard']
>>> expand_packages('{triple}-4.9-{host}', 'linux', ['arm', 'x86_64'])
['arm-linux-androideabi-4.9-linux-x86_64', 'x86_64-4.9-linux-x86_64']
"""
host_tag = build_support.host_to_tag(host)
packages = set()
for arch in arches:
triple = build_support.arch_to_toolchain(arch)
for abi in build_support.arch_to_abis(arch):
packages.add(package.format(
abi=abi, arch=arch, host=host_tag, triple=triple))
# Sorting isn't really necessary; it's just for the benefit of the
# doctests. These are small sets, so it's basically free.
return sorted(packages)
def get_all_packages(host, arches):
packages = [
'binutils-{arch}-{host}',
'build',
'cpufeatures',
'gcc-{arch}-{host}',
'gcclibs-{arch}',
'gdbserver-{arch}',
'gnustl-4.9',
'gtest',
'host-tools-{host}',
'libcxx',
'llvm-{host}',
'native_app_glue',
'ndk_helper',
'platforms',
'python-packages',
'stlport',
'tests',
]
expanded = []
for package in packages:
expanded.extend(expand_packages(package, host, arches))
return expanded
def check_packages(path, packages):
for package in packages:
print('Checking ' + package)
package_path = os.path.join(path, package + '.tar.bz2')
if not os.path.exists(package_path):
raise RuntimeError('Missing package: ' + package_path)
output = subprocess.check_output(['tar', 'tf', package_path])
for f in output.splitlines():
file_name = os.path.basename(f)
if file_name == 'repo.prop':
break
else:
msg = 'Package does not contain a repo.prop: ' + package_path
raise RuntimeError(msg)
def extract_all(path, packages, out_dir):
os.makedirs(out_dir)
for package in packages:
print('Unpacking ' + package)
package_path = os.path.join(path, package + '.tar.bz2')
subprocess.check_call(['tar', 'xf', package_path, '-C', out_dir])
def make_ndk_build_shortcut(out_dir, host):
if host.startswith('windows'):
make_ndk_build_cmd_helper(out_dir)
else:
make_ndk_build_sh_helper(out_dir)
def make_ndk_build_cmd_helper(out_dir):
with open(os.path.join(out_dir, 'ndk-build.cmd'), 'w') as helper:
helper.writelines([
'@echo off\n',
r'%~dp0\build\ndk-build.cmd %*',
])
def make_ndk_build_sh_helper(out_dir):
file_path = os.path.join(out_dir, 'ndk-build')
with open(file_path, 'w') as helper:
helper.writelines([
'#!/bin/sh\n',
'DIR="$(cd "$(dirname "$0")" && pwd)"\n',
'$DIR/build/ndk-build $*',
])
mode = os.stat(file_path).st_mode
os.chmod(file_path, mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
def make_package(release, package_dir, packages, host, out_dir, temp_dir):
release_name = 'android-ndk-{}'.format(release)
extract_dir = os.path.join(temp_dir, release_name)
extract_all(package_dir, packages, extract_dir)
make_ndk_build_shortcut(extract_dir, host)
host_tag = build_support.host_to_tag(host)
package_name = '{}-{}'.format(release_name, host_tag)
package_path = os.path.join(out_dir, package_name)
print('Packaging ' + package_name)
files = os.path.relpath(extract_dir, temp_dir)
if host.startswith('windows'):
_make_zip_package(package_path, temp_dir, files)
else:
_make_tar_package(package_path, temp_dir, files)
def _make_tar_package(package_path, base_dir, files):
subprocess.check_call(
['tar', 'cjf', package_path + '.tar.bz2', '-C', base_dir, files])
def _make_zip_package(package_path, base_dir, files):
cwd = os.getcwd()
package_path = os.path.realpath(package_path)
os.chdir(base_dir)
try:
subprocess.check_call(['zip', '-9qr', package_path + '.zip', files])
finally:
os.chdir(cwd)
class ArgParser(argparse.ArgumentParser):
def __init__(self):
super(ArgParser, self).__init__(
description='Repackages NDK modules as a monolithic package. If '
'--unpack is used, instead installs the monolithic '
'package to a directory.')
self.add_argument(
'--arch', choices=build_support.ALL_ARCHITECTURES,
help='Bundle only the given architecture.')
self.add_argument(
'--host', choices=('darwin', 'linux', 'windows', 'windows64'),
default=build_support.get_default_host(),
help='Package binaries for given OS (e.g. linux).')
self.add_argument(
'--release', default=datetime.date.today().strftime('%Y%m%d'),
help='Release name for the package.')
self.add_argument(
'-f', '--force', dest='force', action='store_true',
help='Clobber out directory if it exists.')
self.add_argument(
'--package-dir', type=os.path.realpath,
default=build_support.get_default_package_dir(),
help='Directory containing NDK modules.')
self.add_argument(
'--unpack', action='store_true',
help='Unpack the NDK to a directory rather than make a tarball.')
self.add_argument(
'out_dir', metavar='OUT_DIR',
help='Directory to install bundle or tarball to.')
def main():
if 'ANDROID_BUILD_TOP' not in os.environ:
os.environ['ANDROID_BUILD_TOP'] = ANDROID_TOP
args = ArgParser().parse_args()
arches = build_support.ALL_ARCHITECTURES
if args.arch is not None:
arches = [args.arch]
if os.path.exists(args.out_dir) and args.unpack:
if args.force:
shutil.rmtree(args.out_dir)
else:
sys.exit(args.out_dir + ' already exists. Use -f to overwrite.')
packages = get_all_packages(args.host, arches)
check_packages(args.package_dir, packages)
if args.unpack:
extract_all(args.package_dir, packages, args.out_dir)
make_ndk_build_shortcut(args.out_dir, args.host)
else:
package_dir = tempfile.mkdtemp()
try:
make_package(args.release, args.package_dir, packages, args.host,
args.out_dir, package_dir)
finally:
shutil.rmtree(package_dir)
if __name__ == '__main__':
main()