Push native libraries separately when gyp manages install

When an APK is installed, any native libraries it contains are
extracted to the APK's lib folder on the device. It is actually
possible to add, change, or remove libraries in that folder directly.

This means, that when gyp is managing the install of APKs, the APK does
not actually need to include the native library, and that can be pushed
separately.

This does several nice things:
libraries can be pushed while a .apk is building
.apk no longer needs to be rebuilt+reinstalled when a library changes
if native library isn't changed, it doesn't need to be zipped+installed
w/ component build, only changed libraries will need to be reinstalled

BUG=158821


Review URL: https://chromiumcodereview.appspot.com/13334003

git-svn-id: http://src.chromium.org/svn/trunk/src/build@191888 4ff67af0-8c30-449e-8e8b-ad334ec8d88c
This commit is contained in:
cjhopman@chromium.org 2013-04-02 20:36:13 +00:00
Родитель 8278ca35b0
Коммит 6bcaee7278
5 изменённых файлов: 200 добавлений и 9 удалений

Просмотреть файл

@ -0,0 +1,72 @@
#!/usr/bin/env python
#
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Creates symlinks to native libraries for an APK.
The native libraries should have previously been pushed to the device (in
options.target_dir). This script then creates links in an apk's lib/ folder to
those native libraries.
"""
import json
import optparse
import os
import sys
BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..')
sys.path.append(BUILD_ANDROID_DIR)
from pylib import android_commands
from pylib import build_utils
from pylib.utils import apk_helper
def CreateLinks(options):
libraries = build_utils.ReadJson(options.libraries_json)
apk_package = apk_helper.GetPackageName(options.apk)
# There is a large (~100ms) overhead for each call to adb.RunShellCommand. To
# avoid this overhead, craft a single command that creates all the links.
link = '/data/data/' + apk_package + '/lib/$f'
target = options.target_dir + '/$f'
names = ' '.join(libraries)
cmd = (
'for f in ' + names + '; do \n' +
'rm ' + link + ' > /dev/null 2>&1 \n' +
'ln -s ' + target + ' ' + link + '\n' +
'done'
)
adb = android_commands.AndroidCommands()
result = adb.RunShellCommand(cmd)
if result:
raise Exception(
'Unexpected output creating links on device.\n' +
'\n'.join(result))
def main(argv):
parser = optparse.OptionParser()
parser.add_option('--apk', help='Path to the apk.')
parser.add_option('--libraries-json',
help='Path to the json list of native libraries.')
parser.add_option('--target-dir',
help='Device directory that contains the target libraries for symlinks.')
parser.add_option('--stamp', help='Path to touch on success.')
options, _ = parser.parse_args()
required_options = ['apk', 'libraries_json', 'target_dir']
build_utils.CheckOptions(options, parser, required=required_options)
CreateLinks(options)
if options.stamp:
build_utils.Touch(options.stamp)
if __name__ == '__main__':
sys.exit(main(sys.argv))

55
android/gyp/push_libraries.py Executable file
Просмотреть файл

@ -0,0 +1,55 @@
#!/usr/bin/env python
#
# Copyright 2013 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Pushes native libraries to a device.
"""
import json
import optparse
import os
import sys
BUILD_ANDROID_DIR = os.path.join(os.path.dirname(__file__), '..')
sys.path.append(BUILD_ANDROID_DIR)
from pylib import android_commands
from pylib import build_utils
def DoPush(options):
libraries = build_utils.ReadJson(options.libraries_json)
adb = android_commands.AndroidCommands()
adb.RunShellCommand('mkdir ' + options.device_dir)
for lib in libraries:
device_path = os.path.join(options.device_dir, lib)
host_path = os.path.join(options.libraries_dir, lib)
adb.PushIfNeeded(host_path, device_path)
def main(argv):
parser = optparse.OptionParser()
parser.add_option('--libraries-dir',
help='Directory that contains stripped libraries.')
parser.add_option('--device-dir',
help='Device directory to push the libraries to.')
parser.add_option('--libraries-json',
help='Path to the json list of native libraries.')
parser.add_option('--stamp', help='Path to touch on success.')
options, _ = parser.parse_args()
required_options = ['libraries_dir', 'device_dir', 'libraries_json']
build_utils.CheckOptions(options, parser, required=required_options)
DoPush(options)
if options.stamp:
build_utils.Touch(options.stamp)
if __name__ == '__main__':
sys.exit(main(sys.argv))

Просмотреть файл

@ -45,9 +45,7 @@ def ParseArgs():
# Check that required options have been provided.
required_options = ('android_sdk', 'android_sdk_tools', 'R_dir', 'res_dirs',
'crunch_input_dir', 'crunch_output_dir')
for option_name in required_options:
if not getattr(options, option_name):
parser.error('--%s is required' % option_name.replace('_', '-'))
build_utils.CheckOptions(options, parser, required=required_options)
return options

Просмотреть файл

@ -3,6 +3,7 @@
# found in the LICENSE file.
import fnmatch
import json
import os
import pipes
import shlex
@ -55,6 +56,17 @@ def ParseGypList(gyp_string):
return shlex.split(gyp_string)
def CheckOptions(options, parser, required=[]):
for option_name in required:
if not getattr(options, option_name):
parser.error('--%s is required' % option_name.replace('_', '-'))
def ReadJson(path):
with open(path, 'r') as jsonfile:
return json.load(jsonfile)
# This can be used in most cases like subprocess.check_call. The output,
# particularly when the command fails, better highlights the command's failure.
# This call will directly exit on a failure in the subprocess so that no python

Просмотреть файл

@ -73,8 +73,8 @@
'intermediate_dir': '<(PRODUCT_DIR)/<(_target_name)',
'asset_location%': '<(intermediate_dir)/assets',
'codegen_stamp': '<(intermediate_dir)/codegen.stamp',
'compile_input_paths': [ ],
'package_input_paths': [ ],
'compile_input_paths': [],
'package_input_paths': [],
'ordered_libraries_file': '<(intermediate_dir)/native_libraries.json',
# TODO(cjhopman): build/ shouldn't refer to content/. The libraryloader and
# nativelibraries template should be moved out of content/ (to base/?).
@ -97,6 +97,8 @@
'obfuscated_jar_path': '<(intermediate_dir)/obfuscated.jar',
'dex_path': '<(intermediate_dir)/classes.dex',
'android_manifest': '<(java_in_dir)/AndroidManifest.xml',
'push_stamp': '<(intermediate_dir)/push.stamp',
'link_stamp': '<(intermediate_dir)/link.stamp',
'codegen_input_paths': [],
'final_apk_path': '<(PRODUCT_DIR)/apks/<(apk_name).apk',
'apk_install_stamp': '<(intermediate_dir)/apk_install.stamp',
@ -128,7 +130,6 @@
'variables': {
'compile_input_paths': [ '<(native_libraries_java_stamp)' ],
'generated_src_dirs': [ '<(native_libraries_java_dir)' ],
'package_input_paths': [ '<(strip_stamp)' ],
},
'actions': [
{
@ -189,9 +190,6 @@
{
'action_name': 'strip_native_libraries',
'message': 'Stripping libraries for <(_target_name)',
'variables': {
'apk_libraries_dir': '<(intermediate_dir)/libs/<(android_app_abi)',
},
'inputs': [
'<(DEPTH)/build/android/pylib/build_utils.py',
'<(DEPTH)/build/android/strip_library_for_apk.py',
@ -211,6 +209,62 @@
],
},
],
'conditions': [
['gyp_managed_install == 1', {
'variables': {
'apk_libraries_dir': '<(intermediate_dir)/lib.stripped/',
'device_library_dir': '/data/local/tmp/chromium/lib.stripped/<(_target_name)',
},
'dependencies': [
'<(DEPTH)/tools/android/md5sum/md5sum.gyp:md5sum',
],
'actions': [
{
'action_name': 'push_libraries_<(_target_name)',
'message': 'Pushing libraries to device for <(_target_name)',
'inputs': [
'<(DEPTH)/build/android/pylib/build_utils.py',
'<(DEPTH)/build/android/gyp/push_libraries.py',
'<(strip_stamp)',
],
'outputs': [
'<(push_stamp)'
],
'action': [
'python', '<(DEPTH)/build/android/gyp/push_libraries.py',
'--libraries-dir=<(apk_libraries_dir)',
'--device-dir=<(device_library_dir)',
'--libraries-json=<(ordered_libraries_file)',
'--stamp=<(push_stamp)',
],
},
{
'action_name': 'create_library_links',
'message': 'Creating links on device for <(_target_name).',
'inputs': [
'<(DEPTH)/build/android/gyp/create_device_library_links.py',
'<(apk_install_stamp)',
'<(push_stamp)'
],
'outputs': [
'<(link_stamp)'
],
'action': [
'python', '<(DEPTH)/build/android/gyp/create_device_library_links.py',
'--apk=<(final_apk_path)',
'--libraries-json=<(ordered_libraries_file)',
'--target-dir=<(device_library_dir)',
'--stamp=<(link_stamp)',
],
},
],
}, {
'variables': {
'apk_libraries_dir': '<(intermediate_dir)/libs/<(android_app_abi)',
'package_input_paths': [ '<(strip_stamp)' ],
},
}],
],
}], # native_libs_paths != []
['java_strings_grd != ""', {
'variables': {