7This script can be run with no arguments, in which case it will produce an
8APK with native libraries for all four architectures: arm, arm64, x86, and
9x64. You can instead list the architectures you want as arguments to this
12 python create_apk.py arm x86
14The environment variables ANDROID_NDK_HOME
and ANDROID_HOME must be set to
15the locations of the Android NDK
and SDK.
17Additionally, `ninja` should be
in your path.
19It assumes that the source tree
is in the desired state, e.g. by having
20run
'python tools/git-sync-deps' in the root of the skia checkout.
22We also assume that the
'resources' directory has been copied to
23'platform_tools/android/apps/skqp/src/main/assets',
and the
24'tools/skqp/download_model' script has been run.
27 * If the environment variable SKQP_BUILD_DIR
is set, many of the
28 intermediate build objects will be placed here.
29 * If the environment variable SKQP_OUTPUT_DIR
is set, the final APK
30 will be placed
in this directory.
31 * If the environment variable SKQP_DEBUG
is set, Skia will be compiled
42sys.path.append(os.path.abspath(os.path.dirname(os.path.abspath(__file__)) + "../../../gn"))
46 m = re.compile('[^A-Za-z0-9_./-]')
49 if m.search(c)
is not None:
50 o.write(repr(c) +
' ')
58 return subprocess.check_call(cmd, **kwargs)
61 for dirpath, _, filenames
in os.walk(searchpath):
62 if filename
in filenames:
63 yield os.path.join(dirpath, filename)
66 with open(os.devnull,
'w')
as devnull:
67 return subprocess.call([
'ninja',
'--version'],
68 stdout=devnull, stderr=devnull) == 0
71 if not os.path.islink(p)
and os.path.isdir(p):
73 elif os.path.lexists(p):
75 assert not os.path.exists(p)
78 if not os.path.exists(dst):
100 newdir = os.path.join(working_dir, os.path.basename(target))
102 os.symlink(os.path.relpath(newdir, os.path.dirname(target)), target)
105 proc = subprocess.Popen(
106 [android_home +
'/tools/bin/sdkmanager',
'--licenses'],
107 stdin=subprocess.PIPE)
108 while proc.poll()
is None:
109 proc.stdin.write(
'y\n')
113skia_to_android_arch_name_map = {
'arm' :
'armeabi-v7a',
114 'arm64':
'arm64-v8a' ,
119 build_dir, final_output_dir = opts.build_dir, opts.final_output_dir
121 assert os.path.exists(
'bin/gn')
123 for d
in [build_dir, final_output_dir]:
126 apps_dir =
'platform_tools/android/apps'
128 lib =
'lib%s_jni.so' % app
132 remove(build_dir +
'/libs')
133 build_paths = [apps_dir +
'/.gradle',
134 apps_dir +
'/' + app +
'/build',
135 apps_dir +
'/' + app +
'/src/main/libs']
136 for path
in build_paths:
141 sys.stderr.write(
'failed to create symlink "%s"\n' % path)
143 lib_dir =
'%s/%s/src/main/libs' % (apps_dir, app)
144 apk_build_dir =
'%s/%s/build/outputs/apk' % (apps_dir, app)
145 for d
in [lib_dir, apk_build_dir]:
146 shutil.rmtree(d,
True)
149 for arch
in opts.architectures:
150 build = os.path.join(build_dir, arch)
151 gn_args = opts.gn_args(arch)
152 args =
' '.
join(
'%s=%s' % (k, v)
for k, v
in gn_args.items())
153 check_call([
'bin/gn',
'gen', build,
'--args=' + args])
156 except subprocess.CalledProcessError:
157 check_call([
'ninja',
'-C', build,
'-t',
'clean'])
159 dst =
'%s/%s' % (lib_dir, skia_to_android_arch_name_map[arch])
161 shutil.copy(os.path.join(build, lib), dst)
164 env_copy = os.environ.copy()
165 env_copy[
'ANDROID_HOME'] = opts.android_home
166 env_copy[
'ANDROID_NDK_HOME'] = opts.android_ndk
168 check_call([
'apps/gradlew',
'-p' 'apps/' + app,
169 '-P',
'suppressNativeBuild',
170 ':%s:assembleUniversalDebug' % app],
171 env=env_copy, cwd=
'platform_tools/android')
173 apk_name = app +
"-universal-debug.apk"
175 apk_list = list(
find_name(apk_build_dir, apk_name))
176 assert len(apk_list) == 1
178 out = os.path.join(final_output_dir, apk_name)
179 shutil.move(apk_list[0], out)
180 sys.stdout.write(out +
'\n')
182 arches =
'_'.
join(sorted(opts.architectures))
183 copy = os.path.join(final_output_dir,
"%s-%s-debug.apk" % (app, arches))
184 shutil.copyfile(out, copy)
185 sys.stdout.write(copy +
'\n')
187 sys.stdout.write(
'* * * COMPLETE * * *\n\n')
191 skia_dir = os.path.abspath(os.path.dirname(__file__) +
'/../..')
192 assert os.path.exists(skia_dir)
193 with ChDir(skia_dir):
198 assert '/' in [os.sep, os.altsep]
201 self.
error +=
'`ninja` is not in the path.\n'
202 for var
in [
'ANDROID_NDK_HOME',
'ANDROID_HOME']:
203 if not os.path.exists(os.environ.get(var,
'')):
204 self.
error +=
'Environment variable `%s` is not set.\n' % var
205 self.
android_ndk = os.path.abspath(os.environ[
'ANDROID_NDK_HOME'])
209 if arg
not in skia_to_android_arch_name_map:
210 self.
error += (
'Argument %r is not in %r\n' %
211 (arg, skia_to_android_arch_name_map.keys()))
213 default_build = os.path.dirname(__file__) +
'/../../out/skqp'
214 self.
build_dir = os.path.abspath(os.environ.get(
'SKQP_BUILD_DIR', default_build))
216 self.
debug = bool(os.environ.get(
'SKQP_DEBUG',
''))
219 return skqp_gn_args.GetGNArgs(arch=arch, ndk=self.
android_ndk, debug=self.
debug,
223 for k, v
in [(
'ANDROID_NDK_HOME', self.
android_ndk),
227 (
'SKQP_DEBUG', self.
debug),
229 o.write(
'%s = %r\n' % (k, v))
235 sys.stderr.write(options.error + __doc__)
237 options.write(sys.stdout)
240if __name__ ==
'__main__':
def __exit__(self, a, b, c)
def __exit__(self, a, b, c)
def __init__(self, *args)
def accept_android_license(android_home)
def create_apk_impl(opts)
def find_name(searchpath, filename)
def check_call(cmd, **kwargs)
def make_symlinked_subdir(target, working_dir)
static SkString join(const CommandLineFlags::StringArray &)