SkQP: split make_apk script into create_apk and make_apk
gn_to_bp: wrap defines in ifndef create_apk.py assumes you are either run from the aosp tree, or you are being run from make_universal_apk.py, which now defers to create_apk.py for all functionality, even argument parsing. tools/skqp/generate_gn_args moved some functionity into skqp_gn_args.py, which is now used by create_apk.py create_apk now accepts android sdk license for you. create_apk and make_universal_apk.py now are better about cleaning up after exceptions happen. Old script make_apk.sh now just points at make_universal_apk.py CQ_INCLUDE_TRYBOTS=skia.primary:Build-Debian9-Clang-x86-devrel-Android_SKQP,Test-Debian9-Clang-NUC7i5BNK-CPU-Emulator-x86-devrel-All-Android_SKQP Change-Id: I2dba20ef7017987cabb2bd49f070e2b1594785d5 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/235678 Reviewed-by: Ben Wagner <bungeman@google.com> Commit-Queue: Hal Canary <halcanary@google.com>
This commit is contained in:
parent
e53f7ff95f
commit
82e3afa99d
@ -116,4 +116,7 @@ def WriteUserConfig(userConfigPath, defines):
|
||||
print >>f, '#pragma once'
|
||||
print >>f, '#include "SkUserConfigManual.h"'
|
||||
for define in sorted(defines):
|
||||
print >>f, '#define', define.replace('=', ' ')
|
||||
print >>f, ''
|
||||
print >>f, '#ifndef', define.split('=')[0]
|
||||
print >>f, '#define', define.replace('=', ' ', 1)
|
||||
print >>f, '#endif'
|
||||
|
235
tools/skqp/create_apk.py
Executable file
235
tools/skqp/create_apk.py
Executable file
@ -0,0 +1,235 @@
|
||||
#! /usr/bin/env python
|
||||
# Copyright 2019 Google LLC.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
'''
|
||||
This script can be run with no arguments, in which case it will produce an
|
||||
APK with native libraries for all four architectures: arm, arm64, x86, and
|
||||
x64. You can instead list the architectures you want as arguments to this
|
||||
script. For example:
|
||||
|
||||
python create_apk.py arm x86
|
||||
|
||||
The environment variables ANDROID_NDK and ANDROID_HOME must be set to the
|
||||
locations of the Android NDK and SDK.
|
||||
|
||||
Additionally, `ninja` should be in your path.
|
||||
|
||||
It assumes that the source tree is in the desired state, e.g. by having
|
||||
run 'python tools/git-sync-deps' in the root of the skia checkout.
|
||||
|
||||
We also assume that the 'resources' directory has been copied to
|
||||
'platform_tools/android/apps/skqp/src/main/assets', and the
|
||||
'tools/skqp/download_model' script has been run.
|
||||
|
||||
Also:
|
||||
* If the environment variable SKQP_BUILD_DIR is set, many of the
|
||||
intermediate build objects will be placed here.
|
||||
* If the environment variable SKQP_OUTPUT_DIR is set, the final APK
|
||||
will be placed in this directory.
|
||||
* If the environment variable SKQP_DEBUG is set, Skia will be compiled
|
||||
in debug mode.
|
||||
'''
|
||||
|
||||
import os
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
import time
|
||||
|
||||
import skqp_gn_args
|
||||
|
||||
def print_cmd(cmd, o):
|
||||
m = re.compile('[^A-Za-z0-9_./-]')
|
||||
o.write('+ ')
|
||||
for c in cmd:
|
||||
if m.search(c) is not None:
|
||||
o.write(repr(c) + ' ')
|
||||
else:
|
||||
o.write(c + ' ')
|
||||
o.write('\n')
|
||||
o.flush()
|
||||
|
||||
def check_call(cmd, **kwargs):
|
||||
print_cmd(cmd, sys.stdout)
|
||||
return subprocess.check_call(cmd, **kwargs)
|
||||
|
||||
def find_name(searchpath, filename):
|
||||
for dirpath, _, filenames in os.walk(searchpath):
|
||||
if filename in filenames:
|
||||
yield os.path.join(dirpath, filename)
|
||||
|
||||
def check_ninja():
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
return subprocess.call(['ninja', '--version'],
|
||||
stdout=devnull, stderr=devnull) == 0
|
||||
|
||||
def remove(p):
|
||||
if not os.path.islink(p) and os.path.isdir(p):
|
||||
shutil.rmtree(p)
|
||||
elif os.path.lexists(p):
|
||||
os.remove(p)
|
||||
assert not os.path.exists(p)
|
||||
|
||||
def makedirs(dst):
|
||||
if not os.path.exists(dst):
|
||||
os.makedirs(dst)
|
||||
|
||||
class RemoveFiles(object):
|
||||
def __init__(self, *args):
|
||||
self.args = args
|
||||
def __enter__(self):
|
||||
pass
|
||||
def __exit__(self, a, b, c):
|
||||
for arg in self.args:
|
||||
remove(arg)
|
||||
|
||||
class ChDir(object):
|
||||
def __init__(self, d):
|
||||
self.orig = os.getcwd()
|
||||
os.chdir(d)
|
||||
def __enter__(self):
|
||||
pass
|
||||
def __exit__(self, a, b, c):
|
||||
os.chdir(self.orig)
|
||||
|
||||
def make_symlinked_subdir(target, working_dir):
|
||||
newdir = os.path.join(working_dir, os.path.basename(target))
|
||||
makedirs(newdir)
|
||||
os.symlink(os.path.relpath(newdir, os.path.dirname(target)), target)
|
||||
|
||||
def accept_android_license(android_home):
|
||||
proc = subprocess.Popen(
|
||||
[android_home + '/tools/bin/sdkmanager', '--licenses'],
|
||||
stdin=subprocess.PIPE)
|
||||
while proc.poll() is None:
|
||||
proc.stdin.write('y\n')
|
||||
time.sleep(1)
|
||||
|
||||
# pylint: disable=bad-whitespace
|
||||
skia_to_android_arch_name_map = {'arm' : 'armeabi-v7a',
|
||||
'arm64': 'arm64-v8a' ,
|
||||
'x86' : 'x86' ,
|
||||
'x64' : 'x86_64' }
|
||||
|
||||
def create_apk_impl(opts):
|
||||
build_dir, final_output_dir = opts.build_dir, opts.final_output_dir
|
||||
|
||||
assert os.path.exists('bin/gn') # Did you `tools/git-syc-deps`?
|
||||
|
||||
for d in [build_dir, final_output_dir]:
|
||||
makedirs(d)
|
||||
|
||||
apps_dir = 'platform_tools/android/apps'
|
||||
app = 'skqp'
|
||||
lib = 'lib%s_app.so' % app
|
||||
|
||||
# These are the locations in the tree where the gradle needs or will create
|
||||
# not-checked-in files. Treat them specially to keep the tree clean.
|
||||
remove(build_dir + '/libs')
|
||||
build_paths = [apps_dir + '/.gradle',
|
||||
apps_dir + '/' + app + '/build',
|
||||
apps_dir + '/' + app + '/src/main/libs']
|
||||
for path in build_paths:
|
||||
remove(path)
|
||||
try:
|
||||
make_symlinked_subdir(path, build_dir)
|
||||
except OSError:
|
||||
sys.stderr.write('failed to create symlink "%s"\n' % path)
|
||||
|
||||
lib_dir = '%s/%s/src/main/libs' % (apps_dir, app)
|
||||
apk_build_dir = '%s/%s/build/outputs/apk' % (apps_dir, app)
|
||||
for d in [lib_dir, apk_build_dir]:
|
||||
shutil.rmtree(d, True) # force rebuild
|
||||
|
||||
with RemoveFiles(*build_paths):
|
||||
for arch in opts.architectures:
|
||||
build = os.path.join(build_dir, arch)
|
||||
gn_args = opts.gn_args(arch)
|
||||
args = ' '.join('%s=%s' % (k, v) for k, v in gn_args.items())
|
||||
check_call(['bin/gn', 'gen', build, '--args=' + args])
|
||||
check_call(['ninja', '-C', build, lib])
|
||||
dst = '%s/%s' % (lib_dir, skia_to_android_arch_name_map[arch])
|
||||
makedirs(dst)
|
||||
shutil.copy(os.path.join(build, lib), dst)
|
||||
|
||||
accept_android_license(opts.android_home)
|
||||
env_copy = os.environ.copy()
|
||||
env_copy['ANDROID_HOME'] = opts.android_home
|
||||
env_copy['ANDROID_NDK_HOME'] = opts.android_ndk
|
||||
# Why does gradlew need to be called from this directory?
|
||||
check_call(['apps/gradlew', '-p' 'apps/' + app,
|
||||
'-P', 'suppressNativeBuild',
|
||||
':%s:assembleUniversalDebug' % app],
|
||||
env=env_copy, cwd='platform_tools/android')
|
||||
|
||||
apk_name = app + "-universal-debug.apk"
|
||||
|
||||
apk_list = list(find_name(apk_build_dir, apk_name))
|
||||
assert len(apk_list) == 1
|
||||
|
||||
out = os.path.join(final_output_dir, apk_name)
|
||||
shutil.move(apk_list[0], out)
|
||||
sys.stdout.write(out + '\n')
|
||||
|
||||
arches = '_'.join(sorted(opts.architectures))
|
||||
copy = os.path.join(final_output_dir, "%s-%s-debug.apk" % (app, arches))
|
||||
shutil.copyfile(out, copy)
|
||||
sys.stdout.write(copy + '\n')
|
||||
|
||||
sys.stdout.write('* * * COMPLETE * * *\n\n')
|
||||
|
||||
|
||||
def create_apk(opts):
|
||||
skia_dir = os.path.abspath(os.path.dirname(__file__) + '/../..')
|
||||
assert os.path.exists(skia_dir)
|
||||
with ChDir(skia_dir):
|
||||
create_apk_impl(opts)
|
||||
|
||||
class SkQP_Build_Options(object):
|
||||
def __init__(self):
|
||||
assert '/' in [os.sep, os.altsep] # 'a/b' over os.path.join('a', 'b')
|
||||
self.error = ''
|
||||
if not check_ninja():
|
||||
self.error += '`ninja` is not in the path.\n'
|
||||
for var in ['ANDROID_NDK', 'ANDROID_HOME']:
|
||||
if not os.path.exists(os.environ.get(var, '')):
|
||||
self.error += 'Environment variable `%s` is not set.\n' % var
|
||||
self.android_ndk = os.path.abspath(os.environ['ANDROID_NDK'])
|
||||
self.android_home = os.path.abspath(os.environ['ANDROID_HOME'])
|
||||
args = sys.argv[1:]
|
||||
for arg in args:
|
||||
if arg not in skia_to_android_arch_name_map:
|
||||
self.error += ('Argument %r is not in %r\n' %
|
||||
(arg, skia_to_android_arch_name_map.keys()))
|
||||
self.architectures = args if args else skia_to_android_arch_name_map.keys()
|
||||
default_build = os.path.dirname(__file__) + '/../../out/skqp'
|
||||
self.build_dir = os.path.abspath(os.environ.get('SKQP_BUILD_DIR', default_build))
|
||||
self.final_output_dir = os.path.abspath(os.environ.get('SKQP_OUTPUT_DIR', default_build))
|
||||
self.debug = bool(os.environ.get('SKQP_DEBUG', ''))
|
||||
|
||||
def gn_args(self, arch):
|
||||
return skqp_gn_args.GetGNArgs(arch, self.android_ndk, self.debug, 26)
|
||||
|
||||
def write(self, o):
|
||||
for k, v in [('ANDROID_NDK', self.android_ndk),
|
||||
('ANDROID_HOME', self.android_home),
|
||||
('SKQP_OUTPUT_DIR', self.final_output_dir),
|
||||
('SKQP_BUILD_DIR', self.build_dir),
|
||||
('SKQP_DEBUG', self.debug),
|
||||
('Architectures', self.architectures)]:
|
||||
o.write('%s = %r\n' % (k, v))
|
||||
o.flush()
|
||||
|
||||
def main():
|
||||
options = SkQP_Build_Options()
|
||||
if options.error:
|
||||
sys.stderr.write(options.error + __doc__)
|
||||
sys.exit(1)
|
||||
options.write(sys.stdout)
|
||||
create_apk(options)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -1,71 +1,6 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Copyright 2018 Google Inc.
|
||||
# Copyright 2019 Google LLC.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib2
|
||||
|
||||
def checksum(path):
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
m = hashlib.md5()
|
||||
with open(path, 'rb') as f:
|
||||
while True:
|
||||
buf = f.read(4096)
|
||||
if 0 == len(buf):
|
||||
return m.hexdigest()
|
||||
m.update(buf)
|
||||
|
||||
def download(md5, path):
|
||||
if not md5 == checksum(path):
|
||||
dirname = os.path.dirname(path)
|
||||
if dirname and not os.path.exists(dirname):
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except:
|
||||
# ignore race condition
|
||||
if not os.path.exists(dirname):
|
||||
raise
|
||||
url = 'https://storage.googleapis.com/skia-skqp-assets/' + md5
|
||||
with open(path, 'wb') as o:
|
||||
shutil.copyfileobj(urllib2.urlopen(url), o)
|
||||
|
||||
def tmp(prefix):
|
||||
fd, path = tempfile.mkstemp(prefix=prefix)
|
||||
os.close(fd)
|
||||
return path
|
||||
|
||||
def main():
|
||||
target_dir = os.path.join('platform_tools', 'android', 'apps', 'skqp', 'src', 'main', 'assets')
|
||||
os.chdir(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, target_dir))
|
||||
checksum_path = 'files.checksum'
|
||||
if not os.path.isfile(checksum_path):
|
||||
sys.stderr.write('Error: "%s" is missing.\n' % os.path.join(target_dir, checksum_path))
|
||||
sys.exit(1)
|
||||
file_list_file = tmp('files_')
|
||||
with open(checksum_path, 'r') as f:
|
||||
md5 = f.read().strip()
|
||||
assert(len(md5) == 32)
|
||||
download(md5, file_list_file)
|
||||
with open(file_list_file, 'r') as f:
|
||||
records = []
|
||||
for line in f:
|
||||
md5, path = line.strip().split(';', 1)
|
||||
records.append((md5, path))
|
||||
sys.stderr.write('Downloading %d files.\n' % len(records))
|
||||
pool = multiprocessing.Pool(processes=multiprocessing.cpu_count() * 2)
|
||||
for record in records:
|
||||
pool.apply_async(download, record, callback=lambda x: sys.stderr.write('.'))
|
||||
pool.close()
|
||||
pool.join()
|
||||
sys.stderr.write('\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
import download_model
|
||||
download_model.main()
|
||||
|
69
tools/skqp/download_model.py
Executable file
69
tools/skqp/download_model.py
Executable file
@ -0,0 +1,69 @@
|
||||
#! /usr/bin/env python
|
||||
|
||||
# Copyright 2018 Google Inc.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
import hashlib
|
||||
import multiprocessing
|
||||
import os
|
||||
import shutil
|
||||
import sys
|
||||
import tempfile
|
||||
import urllib2
|
||||
|
||||
def checksum(path):
|
||||
if not os.path.exists(path):
|
||||
return None
|
||||
m = hashlib.md5()
|
||||
with open(path, 'rb') as f:
|
||||
while True:
|
||||
buf = f.read(4096)
|
||||
if not buf:
|
||||
return m.hexdigest()
|
||||
m.update(buf)
|
||||
|
||||
def download(md5, path):
|
||||
if not md5 == checksum(path):
|
||||
dirname = os.path.dirname(path)
|
||||
if dirname and not os.path.exists(dirname):
|
||||
try:
|
||||
os.makedirs(dirname)
|
||||
except OSError:
|
||||
pass # ignore race condition
|
||||
url = 'https://storage.googleapis.com/skia-skqp-assets/' + md5
|
||||
with open(path, 'wb') as o:
|
||||
shutil.copyfileobj(urllib2.urlopen(url), o)
|
||||
|
||||
def tmp(prefix):
|
||||
fd, path = tempfile.mkstemp(prefix=prefix)
|
||||
os.close(fd)
|
||||
return path
|
||||
|
||||
def main():
|
||||
target_dir = os.path.join('platform_tools', 'android', 'apps', 'skqp', 'src', 'main', 'assets')
|
||||
os.chdir(os.path.join(os.path.dirname(__file__), os.pardir, os.pardir, target_dir))
|
||||
checksum_path = 'files.checksum'
|
||||
if not os.path.isfile(checksum_path):
|
||||
sys.stderr.write('Error: "%s" is missing.\n' % os.path.join(target_dir, checksum_path))
|
||||
sys.exit(1)
|
||||
file_list_file = tmp('files_')
|
||||
with open(checksum_path, 'r') as f:
|
||||
md5 = f.read().strip()
|
||||
assert(len(md5) == 32)
|
||||
download(md5, file_list_file)
|
||||
with open(file_list_file, 'r') as f:
|
||||
records = []
|
||||
for line in f:
|
||||
md5, path = line.strip().split(';', 1)
|
||||
records.append((md5, path))
|
||||
sys.stderr.write('Downloading %d files.\n' % len(records))
|
||||
pool = multiprocessing.Pool(processes=multiprocessing.cpu_count() * 2)
|
||||
for record in records:
|
||||
pool.apply_async(download, record, callback=lambda x: sys.stderr.write('.'))
|
||||
pool.close()
|
||||
pool.join()
|
||||
sys.stderr.write('\n')
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
@ -8,14 +8,7 @@ import argparse
|
||||
import os
|
||||
import sys
|
||||
|
||||
from skqp_gn_args import SkqpGnArgs
|
||||
|
||||
fmt = '''
|
||||
target_cpu = "{arch}"
|
||||
ndk = "{android_ndk_dir}"
|
||||
is_debug = {debug}
|
||||
ndk_api = {api_level}
|
||||
'''
|
||||
import skqp_gn_args
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Generate args.gn file.')
|
||||
@ -25,23 +18,19 @@ def parse_args():
|
||||
help='defaults to "arm", valid values: "arm" "arm64" "x86" "x64"')
|
||||
parser.add_argument('--api_level', type=int, metavar='api_level',
|
||||
default=26, help='android API level, defaults to 26')
|
||||
parser.add_argument('--enable_workarounds', default=False,
|
||||
action='store_true', help="enable GPU work-arounds, defaults to false")
|
||||
parser.add_argument('--debug', default=False, action='store_true',
|
||||
help='compile native code in debug mode, defaults to false')
|
||||
|
||||
# parse the args and convert bools to strings.
|
||||
args = parser.parse_args()
|
||||
gn_bool = lambda b : 'true' if b else 'false'
|
||||
args.enable_workarounds = gn_bool(args.enable_workarounds)
|
||||
args.debug = gn_bool(args.debug)
|
||||
args.android_ndk_dir = os.path.abspath(args.android_ndk_dir)
|
||||
return args
|
||||
result = skqp_gn_args.GetGNArgs(args.arch,
|
||||
os.path.abspath(args.android_ndk_dir),
|
||||
args.debug,
|
||||
args.api_level)
|
||||
return args.target_build_dir, result)
|
||||
|
||||
def write_gn(o, args):
|
||||
o.write(fmt.format(**args))
|
||||
for k, v in SkqpGnArgs.iteritems():
|
||||
o.write('%s = %s\n' % (k,v) )
|
||||
l = max(len(k) for k in args)
|
||||
for k, v in sorted(args.items()):
|
||||
o.write('%-*s = %s\n' % (l, k, v))
|
||||
|
||||
def make_args_gn(out_dir, args):
|
||||
if out_dir == '-':
|
||||
@ -53,5 +42,5 @@ def make_args_gn(out_dir, args):
|
||||
write_gn(o, args)
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = parse_args()
|
||||
make_args_gn(args.target_build_dir, vars(args))
|
||||
build_dir, args = parse_args()
|
||||
make_args_gn(build_dir, args)
|
||||
|
@ -1,49 +1,5 @@
|
||||
#! /bin/sh
|
||||
|
||||
# Copyright 2018 Google Inc.
|
||||
# Copyright 2019 Google Inc.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
set -e
|
||||
|
||||
ANDROID_NDK="$1"
|
||||
|
||||
if ! [ -d "$ANDROID_NDK" ] || ! [ -x "${ANDROID_NDK}/ndk-build" ]; then
|
||||
printf "\nUsage:\n %s ANDROID_NDK_PATH\n" "$0" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case ":${PATH}:" in
|
||||
*/depot_tools:*) ;;
|
||||
*)
|
||||
printf '\ndepot_tools should be in your $PATH.\n' >&2
|
||||
exit 1;;
|
||||
esac
|
||||
|
||||
if ! [ -d "$ANDROID_HOME" ] || ! [ -x "${ANDROID_HOME}/platform-tools/adb" ]; then
|
||||
printf '\n$ANDROID_HOME not set or is broken.\n' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
set -x
|
||||
|
||||
ARCH=${SKQP_ARCH:-arm}
|
||||
|
||||
cd "$(dirname "$0")/../.."
|
||||
|
||||
BUILD=out/skqp-${ARCH}
|
||||
|
||||
python tools/skqp/generate_gn_args $BUILD "$ANDROID_NDK" $ARCH
|
||||
|
||||
GIT_SYNC_DEPS_QUIET=Y tools/git-sync-deps
|
||||
|
||||
bin/gn gen $BUILD
|
||||
|
||||
rm -rf $BUILD/gen
|
||||
|
||||
platform_tools/android/bin/android_build_app -C $BUILD skqp
|
||||
|
||||
set +x
|
||||
|
||||
printf '\n\nAPK built: "%s/skqp.apk"\n\n' "$(pwd)/$BUILD"
|
||||
|
||||
exec python "$(dirname "$0")"/make_universal_apk.py "$@"
|
||||
|
@ -21,7 +21,7 @@ run 'python tools/git-sync-deps' in the root of the skia checkout.
|
||||
|
||||
Also:
|
||||
* If the environment variable SKQP_BUILD_DIR is set, many of the
|
||||
intermediate build objects will be places here.
|
||||
intermediate build objects will be placed here.
|
||||
* If the environment variable SKQP_OUTPUT_DIR is set, the final APK
|
||||
will be placed in this directory.
|
||||
* If the environment variable SKQP_DEBUG is set, Skia will be compiled
|
||||
@ -29,211 +29,41 @@ Also:
|
||||
'''
|
||||
|
||||
import os
|
||||
import glob
|
||||
import re
|
||||
import subprocess
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
def print_cmd(cmd, o):
|
||||
m = re.compile('[^A-Za-z0-9_./-]')
|
||||
o.write('+ ')
|
||||
for c in cmd:
|
||||
if m.search(c) is not None:
|
||||
o.write(repr(c) + ' ')
|
||||
else:
|
||||
o.write(c + ' ')
|
||||
o.write('\n')
|
||||
o.flush()
|
||||
import create_apk
|
||||
import download_model
|
||||
|
||||
def check_call(cmd, **kwargs):
|
||||
print_cmd(cmd, sys.stdout)
|
||||
return subprocess.check_call(cmd, **kwargs)
|
||||
|
||||
def find_name(searchpath, filename):
|
||||
for dirpath, _, filenames in os.walk(searchpath):
|
||||
if filename in filenames:
|
||||
yield os.path.join(dirpath, filename)
|
||||
|
||||
def check_ninja():
|
||||
with open(os.devnull, 'w') as devnull:
|
||||
return 0 == subprocess.call(['ninja', '--version'],
|
||||
stdout=devnull, stderr=devnull)
|
||||
|
||||
def remove(p):
|
||||
if not os.path.islink(p) and os.path.isdir(p):
|
||||
shutil.rmtree(p)
|
||||
elif os.path.lexists(p):
|
||||
os.remove(p)
|
||||
assert not os.path.exists(p)
|
||||
|
||||
skia_to_android_arch_name_map = {'arm' : 'armeabi-v7a',
|
||||
'arm64': 'arm64-v8a' ,
|
||||
'x86' : 'x86' ,
|
||||
'x64' : 'x86_64' }
|
||||
|
||||
def make_apk(architectures,
|
||||
android_ndk,
|
||||
android_home,
|
||||
build_dir,
|
||||
final_output_dir,
|
||||
debug,
|
||||
skia_dir):
|
||||
def make_apk(opts):
|
||||
assert '/' in [os.sep, os.altsep] # 'a/b' over os.path.join('a', 'b')
|
||||
assert check_ninja()
|
||||
assert os.path.exists(android_ndk)
|
||||
assert os.path.exists(android_home)
|
||||
assert os.path.exists(skia_dir)
|
||||
assert os.path.exists(skia_dir + '/bin/gn') # Did you `tools/git-syc-deps`?
|
||||
assert architectures
|
||||
assert all(arch in skia_to_android_arch_name_map
|
||||
for arch in architectures)
|
||||
|
||||
for d in [build_dir, final_output_dir]:
|
||||
if not os.path.exists(d):
|
||||
os.makedirs(d)
|
||||
skia_dir = os.path.dirname(__file__) + '/../..'
|
||||
create_apk.makedirs(opts.build_dir)
|
||||
assets_dir = skia_dir + '/platform_tools/android/apps/skqp/src/main/assets'
|
||||
gmkb = assets_dir + '/gmkb'
|
||||
resources_path = assets_dir + '/resources'
|
||||
|
||||
os.chdir(skia_dir)
|
||||
apps_dir = 'platform_tools/android/apps'
|
||||
with create_apk.RemoveFiles(resources_path, gmkb): # always clean up
|
||||
create_apk.remove(gmkb)
|
||||
create_apk.make_symlinked_subdir(gmkb, opts.build_dir)
|
||||
|
||||
# These are the locations in the tree where the gradle needs or will create
|
||||
# not-checked-in files. Treat them specially to keep the tree clean.
|
||||
aosp_mode = os.path.exists('MODULE_LICENSE_BSD')
|
||||
build_paths = [apps_dir + '/.gradle',
|
||||
apps_dir + '/skqp/build',
|
||||
apps_dir + '/skqp/src/main/libs']
|
||||
if not aosp_mode:
|
||||
build_paths.append(apps_dir + '/skqp/src/main/assets/gmkb')
|
||||
remove(build_dir + '/libs')
|
||||
for path in build_paths:
|
||||
remove(path)
|
||||
newdir = os.path.join(build_dir, os.path.basename(path))
|
||||
if not os.path.exists(newdir):
|
||||
os.makedirs(newdir)
|
||||
try:
|
||||
os.symlink(os.path.relpath(newdir, os.path.dirname(path)), path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if not aosp_mode:
|
||||
resources_path = apps_dir + '/skqp/src/main/assets/resources'
|
||||
remove(resources_path)
|
||||
create_apk.remove(resources_path)
|
||||
os.symlink('../../../../../../../resources', resources_path)
|
||||
build_paths.append(resources_path)
|
||||
|
||||
app = 'skqp'
|
||||
lib = 'libskqp_app.so'
|
||||
|
||||
shutil.rmtree(apps_dir + '/%s/src/main/libs' % app, True)
|
||||
|
||||
if not aosp_mode:
|
||||
if os.path.exists(apps_dir + '/skqp/src/main/assets/files.checksum'):
|
||||
check_call([sys.executable, 'tools/skqp/download_model'])
|
||||
if os.path.exists(assets_dir + '/files.checksum'):
|
||||
download_model.main()
|
||||
else:
|
||||
sys.stderr.write(
|
||||
'\n* * *\n\nNote: SkQP models are missing!!!!\n\n* * *\n\n')
|
||||
if aosp_mode:
|
||||
with open('include/config/SkUserConfig.h') as f:
|
||||
user_config = f.readlines()
|
||||
with open('include/config/SkUserConfig.h', 'w') as o:
|
||||
for line in user_config:
|
||||
m = re.match(r'^#define\s+([A-Za-z0-9_]+)(|\s.*)$', line)
|
||||
if m:
|
||||
o.write('#ifndef %s\n%s\n#endif\n' % (m.group(1), m.group(0).strip()))
|
||||
else:
|
||||
o.write(line)
|
||||
|
||||
for arch in architectures:
|
||||
build = os.path.join(build_dir, arch)
|
||||
gn_args = [android_ndk, '--arch', arch]
|
||||
if debug:
|
||||
build += '-debug'
|
||||
gn_args += ['--debug']
|
||||
check_call([sys.executable, 'tools/skqp/generate_gn_args', build]
|
||||
+ gn_args)
|
||||
check_call(['bin/gn', 'gen', build])
|
||||
check_call(['ninja', '-C', build, lib])
|
||||
dst = apps_dir + '/%s/src/main/libs/%s' % (
|
||||
app, skia_to_android_arch_name_map[arch])
|
||||
if not os.path.isdir(dst):
|
||||
os.makedirs(dst)
|
||||
shutil.copy(os.path.join(build, lib), dst)
|
||||
|
||||
if aosp_mode:
|
||||
subprocess.call('git', 'checkout', 'HEAD', 'include/config/SkUserConfig.h')
|
||||
|
||||
apk_build_dir = apps_dir + '/%s/build/outputs/apk' % app
|
||||
shutil.rmtree(apk_build_dir, True) # force rebuild
|
||||
|
||||
# Why does gradlew need to be called from this directory?
|
||||
os.chdir('platform_tools/android')
|
||||
env_copy = os.environ.copy()
|
||||
env_copy['ANDROID_HOME'] = android_home
|
||||
check_call(['apps/gradlew', '-p' 'apps/' + app, '-P', 'suppressNativeBuild',
|
||||
':%s:assembleUniversalDebug' % app], env=env_copy)
|
||||
os.chdir(skia_dir)
|
||||
|
||||
apk_name = app + "-universal-debug.apk"
|
||||
|
||||
apk_list = list(find_name(apk_build_dir, apk_name))
|
||||
assert len(apk_list) == 1
|
||||
|
||||
out = os.path.join(final_output_dir, apk_name)
|
||||
shutil.move(apk_list[0], out)
|
||||
sys.stdout.write(out + '\n')
|
||||
|
||||
for path in build_paths:
|
||||
remove(path)
|
||||
|
||||
arches = '_'.join(sorted(architectures))
|
||||
copy = os.path.join(final_output_dir, "%s-%s-debug.apk" % (app, arches))
|
||||
shutil.copyfile(out, copy)
|
||||
sys.stdout.write(copy + '\n')
|
||||
|
||||
sys.stdout.write('* * * COMPLETE * * *\n\n')
|
||||
'\n* * * Note: SkQP models are missing! * * *\n\n')
|
||||
create_apk.create_apk(opts)
|
||||
|
||||
def main():
|
||||
def error(s):
|
||||
sys.stderr.write(s + __doc__)
|
||||
options = create_apk.SkQP_Build_Options()
|
||||
if options.error:
|
||||
sys.stderr.write(options.error + __doc__)
|
||||
sys.exit(1)
|
||||
if not check_ninja():
|
||||
error('`ninja` is not in the path.\n')
|
||||
for var in ['ANDROID_NDK', 'ANDROID_HOME']:
|
||||
if not os.path.exists(os.environ.get(var, '')):
|
||||
error('Environment variable `%s` is not set.\n' % var)
|
||||
architectures = sys.argv[1:]
|
||||
for arg in sys.argv[1:]:
|
||||
if arg not in skia_to_android_arch_name_map:
|
||||
error('Argument %r is not in %r\n' %
|
||||
(arg, skia_to_android_arch_name_map.keys()))
|
||||
if not architectures:
|
||||
architectures = skia_to_android_arch_name_map.keys()
|
||||
skia_dir = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
|
||||
default_build = os.path.join(skia_dir, 'out', 'skqp')
|
||||
build_dir = os.path.abspath(os.environ.get('SKQP_BUILD_DIR', default_build))
|
||||
final_output_dir = os.path.abspath(
|
||||
os.environ.get('SKQP_OUTPUT_DIR', default_build))
|
||||
debug = bool(os.environ.get('SKQP_DEBUG', ''))
|
||||
android_ndk = os.path.abspath(os.environ['ANDROID_NDK'])
|
||||
android_home = os.path.abspath(os.environ['ANDROID_HOME'])
|
||||
|
||||
for k, v in [('ANDROID_NDK', android_ndk),
|
||||
('ANDROID_HOME', android_home),
|
||||
('skia root directory', skia_dir),
|
||||
('SKQP_OUTPUT_DIR', final_output_dir),
|
||||
('SKQP_BUILD_DIR', build_dir),
|
||||
('Architectures', architectures)]:
|
||||
sys.stdout.write('%s = %r\n' % (k, v))
|
||||
sys.stdout.flush()
|
||||
make_apk(architectures,
|
||||
android_ndk,
|
||||
android_home,
|
||||
build_dir,
|
||||
final_output_dir,
|
||||
debug,
|
||||
skia_dir)
|
||||
options.write(sys.stdout)
|
||||
make_apk(options)
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
# Copyright 2019 Google LLC.
|
||||
# Use of this source code is governed by a BSD-style license that can be
|
||||
# found in the LICENSE file.
|
||||
|
||||
SkqpGnArgs = {
|
||||
'extra_cflags': '[ "-DSK_ENABLE_DUMP_GPU", "-DSK_BUILD_FOR_SKQP" ]',
|
||||
'skia_enable_fontmgr_android': 'false',
|
||||
@ -17,3 +18,15 @@ SkqpGnArgs = {
|
||||
'skia_use_piex': 'false',
|
||||
'skia_use_vulkan': 'true',
|
||||
}
|
||||
|
||||
def GetGNArgs(arch, android_ndk, debug, api_level):
|
||||
def gn_quote(s):
|
||||
return '"%s"' % s
|
||||
gn_args = {
|
||||
'target_cpu': gn_quote(arch),
|
||||
'ndk': gn_quote(android_ndk),
|
||||
'is_debug': 'true' if debug else 'false',
|
||||
'ndk_api': api_level,
|
||||
}
|
||||
gn_args.update(SkqpGnArgs)
|
||||
return gn_args
|
||||
|
Loading…
Reference in New Issue
Block a user