Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2072)

Unified Diff: android_webview/tools/apk_merger.py

Issue 1027593004: Repack 64-bit and 32-bit APKs into the same APK. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Remove superfluous code Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: android_webview/tools/apk_merger.py
diff --git a/android_webview/tools/apk_merger.py b/android_webview/tools/apk_merger.py
new file mode 100755
index 0000000000000000000000000000000000000000..a8aa44d015159e9458af98bae24c54661b22030a
--- /dev/null
+++ b/android_webview/tools/apk_merger.py
@@ -0,0 +1,202 @@
+#!/usr/bin/python
+# Copyright 2014 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.
+
+""" Merges a 64-bit and a 32-bit APK into a single APK
+
+"""
+
+import os
+import sys
+import shutil
+import zipfile
+import filecmp
+import tempfile
+import argparse
+import subprocess
+
+SRC_DIR = os.path.join(os.path.dirname(__file__), '..', '..')
+SRC_DIR = os.path.abspath(SRC_DIR)
+BUILD_ANDROID_GYP_DIR = os.path.join(SRC_DIR, 'build/android/gyp')
+sys.path.append(BUILD_ANDROID_GYP_DIR)
+
+import finalize_apk
+from util import build_utils
+
+class ApkMergeFailure(Exception):
+ pass
+
+
+def UnpackApk(file_name, dst):
+ zippy = zipfile.ZipFile(file_name)
+ zippy.extractall(dst)
+
+
+def GetNonDirFiles(top, base_dir):
+ """ Return a list containing all (non-directory) files in tree with top as
+ root.
+
+ Each file is represented by the relative path from base_dir to that file.
+ If top is a file (not a directory) then a list containing only top is
+ returned.
+ """
+ if os.path.isdir(top):
+ ret = []
+ for dirpath, _, filenames in os.walk(top):
+ for filename in filenames:
+ ret.append(
+ os.path.relpath(os.path.join(dirpath, filename), base_dir))
+ return ret
+ else:
+ return [os.path.relpath(top, base_dir)]
+
+
+def GetDiffFiles(dcmp, base_dir):
+ """ Return the list of files contained only in the right directory of dcmp.
+
+ The files returned are represented by relative paths from base_dir.
+ """
+ copy_files = []
+ for file_name in dcmp.right_only:
+ copy_files.extend(
+ GetNonDirFiles(os.path.join(dcmp.right, file_name), base_dir))
+
+ # we cannot merge APKs with files with similar names but different contents
+ if len(dcmp.diff_files) > 0:
+ raise ApkMergeFailure('found differing files: %s in %s and %s' %
+ (dcmp.diff_files, dcmp.left, dcmp.right))
+
+ if len(dcmp.funny_files) > 0:
+ ApkMergeFailure('found uncomparable files: %s in %s and %s' %
+ (dcmp.funny_files, dcmp.left, dcmp.right))
+
+ for sub_dcmp in dcmp.subdirs.itervalues():
+ copy_files.extend(GetDiffFiles(sub_dcmp, base_dir))
+ return copy_files
+
+
+def CheckFilesExpected(actual_files, expected_files):
+ """ Check that the lists of actual and expected files are the same. """
+ file_set = set()
+ for file_name in actual_files:
+ base_name = os.path.basename(file_name)
+ if base_name not in expected_files:
+ raise ApkMergeFailure('Found unexpected file named %s.' %
+ file_name)
+ if base_name in file_set:
+ raise ApkMergeFailure('Duplicate file %s to add to APK!' %
+ file_name)
+ file_set.add(base_name)
+
+ if len(file_set) != len(expected_files):
+ raise ApkMergeFailure('Missing expected files to add to APK!')
+
+
+def AddDiffFiles(diff_files, tmp_dir_32, tmp_apk, expected_files):
+ """ Insert files only present in 32-bit APK into 64-bit APK (tmp_apk). """
+ old_dir = os.getcwd()
+ # Move into 32-bit directory to make sure the files we insert have correct
+ # relative paths.
+ os.chdir(tmp_dir_32)
+ try:
+ for diff_file in diff_files:
+ extra_flags = expected_files[os.path.basename(diff_file)]
+ build_utils.CheckOutput(['zip', '-r', '-X', '--no-dir-entries',
+ tmp_apk, diff_file] + extra_flags)
+ except build_utils.CalledProcessError as e:
+ raise ApkMergeFailure(
+ 'Failed to add file %s to APK: %s' % (diff_file, e.output))
+ finally:
+ # Move out of 32-bit directory when done
+ os.chdir(old_dir)
+
+
+def RemoveMetafiles(tmp_apk):
+ """ Remove all meta info to avoid certificate clashes """
+ try:
+ build_utils.CheckOutput(['zip', '-d', tmp_apk, 'META-INF/*'])
+ except build_utils.CalledProcessError as e:
+ raise ApkMergeFailure('Failed to delete Meta folder: ' + e.output)
+
+
+def SignAndAlignApk(tmp_apk, signed_tmp_apk, new_apk, zipalign_path,
+ keystore_path, key_name, key_password):
+ try:
+ finalize_apk.JarSigner(
+ keystore_path,
+ key_name,
+ key_password,
+ tmp_apk,
+ signed_tmp_apk)
+ except build_utils.CalledProcessError as e:
+ raise ApkMergeFailure('Failed to sign APK: ' + e.output)
+
+ try:
+ finalize_apk.AlignApk(zipalign_path, signed_tmp_apk, new_apk)
+ except build_utils.CalledProcessError as e:
+ raise ApkMergeFailure('Failed to align APK: ' + e.output)
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description='Merge a 32-bit APK into a 64-bit APK')
+ # Using type=os.path.abspath converts file paths to absolute paths so that
+ # we can change working directory without affecting these paths
+ parser.add_argument('--apk_32bit', required=True, type=os.path.abspath)
+ parser.add_argument('--apk_64bit', required=True, type=os.path.abspath)
+ parser.add_argument('--out_apk', required=True, type=os.path.abspath)
+ parser.add_argument('--zipalign_path', required=True, type=os.path.abspath)
+ parser.add_argument('--keystore_path', required=True, type=os.path.abspath)
+ parser.add_argument('--key_name', required=True)
+ parser.add_argument('--key_password', required=True)
+ args = parser.parse_args()
+
+ tmp_dir = tempfile.mkdtemp()
+ tmp_dir_64 = os.path.join(tmp_dir, '64_bit')
+ tmp_dir_32 = os.path.join(tmp_dir, '32_bit')
+ tmp_apk = os.path.join(tmp_dir, 'tmp.apk')
+ signed_tmp_apk = os.path.join(tmp_dir, 'signed.apk')
+ new_apk = args.out_apk
+
+ # Expected files to copy from 32- to 64-bit APK together with an extra flag
+ # setting the compression level of the file
+ expected_files = {'snapshot_blob_32.bin': ['-0'],
+ 'natives_blob_32.bin': ['-0'],
+ 'libwebviewchromium.so': []}
+
+ try:
+ shutil.copyfile(args.apk_64bit, tmp_apk)
+
+ # need to unpack APKs to compare their contents
+ UnpackApk(args.apk_64bit, tmp_dir_64)
+ UnpackApk(args.apk_32bit, tmp_dir_32)
+
+ dcmp = filecmp.dircmp(
+ tmp_dir_64,
+ tmp_dir_32,
+ ignore=['META-INF', 'AndroidManifest.xml'])
+
+ diff_files = GetDiffFiles(dcmp, tmp_dir_32)
+
+ # Check that diff_files match exactly those files we want to insert into
+ # the 64-bit APK.
+ CheckFilesExpected(diff_files, expected_files)
+
+ RemoveMetafiles(tmp_apk)
+
+ AddDiffFiles(diff_files, tmp_dir_32, tmp_apk, expected_files)
+
+ SignAndAlignApk(tmp_apk, signed_tmp_apk, new_apk, args.zipalign_path,
+ args.keystore_path, args.key_name, args.key_password)
+
+ except ApkMergeFailure as e:
+ print e
+ return 1
+ finally:
+ shutil.rmtree(tmp_dir)
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698