summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjbudorick <jbudorick@chromium.org>2015-08-25 14:36:59 -0700
committerCommit bot <commit-bot@chromium.org>2015-08-25 21:38:00 +0000
commitf091dcb5146ce071e183303bd316f06e92b6ee3e (patch)
tree352bd2a373ac3423cd3604c7ed3d9ef799cc6a9f
parentd088d16429595fd6702e5e8542089f1b4fc34b7d (diff)
downloadchromium_src-f091dcb5146ce071e183303bd316f06e92b6ee3e.zip
chromium_src-f091dcb5146ce071e183303bd316f06e92b6ee3e.tar.gz
chromium_src-f091dcb5146ce071e183303bd316f06e92b6ee3e.tar.bz2
[Android] Add gyp support for multidex. (RELAND)
Reland of https://codereview.chromium.org/1278573002 BUG=272790 TBR=thakis@chromium.org,mmenke@chromium.org Review URL: https://codereview.chromium.org/1318513003 Cr-Commit-Position: refs/heads/master@{#345443}
-rw-r--r--base/BUILD.gn20
-rw-r--r--base/android/java/src/org/chromium/base/BaseChromiumApplication.java11
-rw-r--r--base/android/java/src/org/chromium/base/annotations/MainDex.java21
-rw-r--r--base/android/java/src/org/chromium/base/multidex/ChromiumMultiDex.java58
-rw-r--r--base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java7
-rw-r--r--base/base.gyp19
-rw-r--r--base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java8
-rw-r--r--base/test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java21
-rw-r--r--build/android/ant/apk-package.xml33
-rw-r--r--build/android/apkbuilder_action.gypi5
-rwxr-xr-xbuild/android/gyp/dex.py52
-rwxr-xr-xbuild/android/gyp/main_dex_list.py84
-rw-r--r--build/android/main_dex_action.gypi44
-rw-r--r--build/android/main_dex_classes.flags3
-rw-r--r--build/android/pylib/constants/__init__.py3
-rw-r--r--build/config/android/rules.gni3
-rw-r--r--build/java.gypi10
-rw-r--r--build/java_apk.gypi52
-rw-r--r--build/secondary/third_party/android_tools/BUILD.gn5
-rw-r--r--chrome/android/BUILD.gn1
-rw-r--r--chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java5
-rw-r--r--chrome/chrome_tests.gypi1
-rw-r--r--net/android/BUILD.gn1
-rw-r--r--net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java7
-rw-r--r--net/net.gyp1
25 files changed, 459 insertions, 16 deletions
diff --git a/base/BUILD.gn b/base/BUILD.gn
index b6fb63e..a4e5a5c 100644
--- a/base/BUILD.gn
+++ b/base/BUILD.gn
@@ -1591,6 +1591,7 @@ if (is_android) {
]
deps = [
+ "//third_party/android_tools:android_support_multidex_java",
"//third_party/jsr-305:jsr_305_javalib",
]
@@ -1622,12 +1623,29 @@ if (is_android) {
DEPRECATED_java_in_dir = "test/android/javatests/src"
}
+ # TODO(jbudorick): Remove this once we roll to robolectric 3.0 and pull
+ # in the multidex shadow library. crbug.com/522043
+ # GYP: //base.gyp:base_junit_test_support
+ java_library("base_junit_test_support") {
+ testonly = true
+ java_files = [ "test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java" ]
+ deps = [
+ "//third_party/android_tools:android_support_multidex_java",
+ "//third_party/robolectric:android-all-4.3_r2-robolectric-0",
+ "//third_party/robolectric:robolectric_java",
+ ]
+ }
+
# GYP: //base.gyp:base_junit_tests
junit_binary("base_junit_tests") {
- java_files = [ "android/junit/src/org/chromium/base/LogTest.java" ]
+ java_files = [
+ "android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java",
+ "android/junit/src/org/chromium/base/LogTest.java",
+ ]
deps = [
":base_java",
":base_java_test_support",
+ ":base_junit_test_support",
]
}
diff --git a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java
index d7c7b05..6d73c8fe 100644
--- a/base/android/java/src/org/chromium/base/BaseChromiumApplication.java
+++ b/base/android/java/src/org/chromium/base/BaseChromiumApplication.java
@@ -11,6 +11,8 @@ import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Window;
+import org.chromium.base.multidex.ChromiumMultiDex;
+
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
@@ -20,6 +22,15 @@ import java.lang.reflect.Proxy;
* Basic application functionality that should be shared among all browser applications.
*/
public class BaseChromiumApplication extends Application {
+
+ private static final String TAG = "cr.base";
+
+ @Override
+ protected void attachBaseContext(Context base) {
+ super.attachBaseContext(base);
+ ChromiumMultiDex.install(this);
+ }
+
/**
* Interface to be implemented by listeners for window focus events.
*/
diff --git a/base/android/java/src/org/chromium/base/annotations/MainDex.java b/base/android/java/src/org/chromium/base/annotations/MainDex.java
new file mode 100644
index 0000000..0b35ade
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/annotations/MainDex.java
@@ -0,0 +1,21 @@
+// Copyright 2015 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.
+
+package org.chromium.base.annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * An annotation that signals that a class should be kept in the main dex file.
+ *
+ * This generally means it's used by renderer processes, which can't load secondary dexes
+ * on K and below.
+ */
+@Target(ElementType.TYPE)
+@Retention(RetentionPolicy.CLASS)
+public @interface MainDex {
+}
diff --git a/base/android/java/src/org/chromium/base/multidex/ChromiumMultiDex.java b/base/android/java/src/org/chromium/base/multidex/ChromiumMultiDex.java
new file mode 100644
index 0000000..2c28673
--- /dev/null
+++ b/base/android/java/src/org/chromium/base/multidex/ChromiumMultiDex.java
@@ -0,0 +1,58 @@
+// Copyright 2015 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.
+
+package org.chromium.base.multidex;
+
+import android.content.Context;
+import android.os.Build;
+import android.os.Process;
+import android.support.multidex.MultiDex;
+
+import org.chromium.base.Log;
+import org.chromium.base.VisibleForTesting;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Performs multidex installation for non-isolated processes.
+ */
+public class ChromiumMultiDex {
+
+ private static final String TAG = "cr.base.multidex";
+
+ /**
+ * Installs secondary dexes if possible.
+ *
+ * Isolated processes (e.g. renderer processes) can't load secondary dex files on
+ * K and below, so we don't even try in that case.
+ *
+ * @param context The application context.
+ */
+ @VisibleForTesting
+ public static void install(Context context) {
+ try {
+ // TODO(jbudorick): Back out this version check once support for K & below works.
+ // http://crbug.com/512357
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP && processIsIsolated()) {
+ Log.i(TAG, "Skipping multidex installation: inside isolated process.");
+ } else {
+ MultiDex.install(context);
+ Log.i(TAG, "Completed multidex installation.");
+ }
+ } catch (NoSuchMethodException e) {
+ Log.wtf(TAG, "Failed multidex installation", e);
+ } catch (IllegalAccessException e) {
+ Log.wtf(TAG, "Failed multidex installation", e);
+ } catch (InvocationTargetException e) {
+ Log.wtf(TAG, "Failed multidex installation", e);
+ }
+ }
+
+ // Calls Process.isIsolated, a private Android API.
+ private static boolean processIsIsolated()
+ throws NoSuchMethodException, IllegalAccessException, InvocationTargetException {
+ return (boolean) Process.class.getMethod("isIsolated").invoke(null);
+ }
+
+}
diff --git a/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java b/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java
index d3441f7..239de67 100644
--- a/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java
+++ b/base/android/junit/src/org/chromium/base/BaseChromiumApplicationTest.java
@@ -13,6 +13,7 @@ import android.view.KeyEvent;
import junit.framework.Assert;
import org.chromium.base.BaseChromiumApplication.WindowFocusChangedListener;
+import org.chromium.base.test.shadows.ShadowMultiDex;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -25,10 +26,12 @@ import org.robolectric.util.ActivityController;
/** Unit tests for {@link BaseChromiumApplication}. */
@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, application = BaseChromiumApplication.class,
- shadows = {BaseChromiumApplicationTest.TrackingShadowActivity.class})
+@Config(manifest = Config.NONE,
+ application = BaseChromiumApplication.class,
+ shadows = {BaseChromiumApplicationTest.TrackingShadowActivity.class, ShadowMultiDex.class})
public class BaseChromiumApplicationTest {
+ /** Shadow that tracks calls to onWindowFocusChanged and dispatchKeyEvent. */
@Implements(Activity.class)
public static class TrackingShadowActivity extends ShadowActivity {
private int mWindowFocusCalls;
diff --git a/base/base.gyp b/base/base.gyp
index a9038cd..d57ec6e 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -1450,6 +1450,7 @@
'base_java_library_process_type',
'base_java_memory_pressure_level',
'base_native_libraries_gen',
+ '../third_party/android_tools/android_tools.gyp:android_support_multidex_javalib',
'../third_party/jsr-305/jsr-305.gyp:jsr_305_javalib',
],
'includes': [ '../build/java.gypi' ],
@@ -1507,12 +1508,30 @@
'includes': [ '../build/java.gypi' ],
},
{
+ # TODO(jbudorick): Remove this once we roll to robolectric 3.0 and pull
+ # in the multidex shadow library. crbug.com/522043
+ # GN: //base:base_junit_test_support
+ 'target_name': 'base_junit_test_support',
+ 'type': 'none',
+ 'dependencies': [
+ '../testing/android/junit/junit_test.gyp:junit_test_support',
+ '../third_party/android_tools/android_tools.gyp:android_support_multidex_javalib',
+ ],
+ 'variables': {
+ 'src_paths': [
+ '../base/test/android/junit/',
+ ],
+ },
+ 'includes': [ '../build/host_jar.gypi' ]
+ },
+ {
# GN: //base:base_junit_tests
'target_name': 'base_junit_tests',
'type': 'none',
'dependencies': [
'base_java',
'base_java_test_support',
+ 'base_junit_test_support',
'../testing/android/junit/junit_test.gyp:junit_test_support',
],
'variables': {
diff --git a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
index 35fe9b9..0eab231 100644
--- a/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
+++ b/base/test/android/javatests/src/org/chromium/base/test/BaseInstrumentationTestRunner.java
@@ -8,6 +8,7 @@ import android.content.Context;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.os.Build;
+import android.os.Bundle;
import android.test.AndroidTestRunner;
import android.test.InstrumentationTestRunner;
import android.text.TextUtils;
@@ -17,6 +18,7 @@ import junit.framework.TestResult;
import org.chromium.base.Log;
import org.chromium.base.SysUtils;
+import org.chromium.base.multidex.ChromiumMultiDex;
import org.chromium.base.test.BaseTestResult.SkipCheck;
import org.chromium.base.test.util.MinAndroidSdkLevel;
import org.chromium.base.test.util.Restriction;
@@ -32,6 +34,12 @@ public class BaseInstrumentationTestRunner extends InstrumentationTestRunner {
private static final String TAG = "cr.base.test";
@Override
+ public void onCreate(Bundle arguments) {
+ ChromiumMultiDex.install(getTargetContext());
+ super.onCreate(arguments);
+ }
+
+ @Override
protected AndroidTestRunner getAndroidTestRunner() {
AndroidTestRunner runner = new AndroidTestRunner() {
@Override
diff --git a/base/test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java b/base/test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java
new file mode 100644
index 0000000..b3ff57e
--- /dev/null
+++ b/base/test/android/junit/src/org/chromium/base/test/shadows/ShadowMultiDex.java
@@ -0,0 +1,21 @@
+// Copyright 2015 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.
+
+package org.chromium.base.test.shadows;
+
+import android.content.Context;
+import android.support.multidex.MultiDex;
+
+import org.robolectric.annotation.Implementation;
+import org.robolectric.annotation.Implements;
+
+/** Do-nothing shadow for {@link android.support.multidex.MultiDex}. */
+@Implements(MultiDex.class)
+public class ShadowMultiDex {
+
+ @Implementation
+ public static void install(Context context) {
+ }
+
+}
diff --git a/build/android/ant/apk-package.xml b/build/android/ant/apk-package.xml
index e8b76f7e..cb79560 100644
--- a/build/android/ant/apk-package.xml
+++ b/build/android/ant/apk-package.xml
@@ -54,6 +54,9 @@
<property name="resource.package.file.name" value="${RESOURCE_PACKAGED_APK_NAME}" />
<property name="intermediate.dex.file" location="${DEX_FILE_PATH}" />
+ <condition property="multidex.enabled" value="true">
+ <equals arg1="${MULTIDEX_ENABLED}" arg2="1"/>
+ </condition>
<!-- Macro that enables passing a variable list of external jar files
to ApkBuilder. -->
@@ -70,13 +73,32 @@
hascode="${HAS_CODE}"
previousBuildType="/"
buildType="${build.is.packaging.debug}/${build.is.signing.debug}">
- <dex path="${intermediate.dex.file}"/>
+ <dex path="${intermediate.dex.file}" />
<nativefolder path="${native.libs.absolute.dir}" />
<extra-jars/>
</apkbuilder>
</sequential>
</macrodef>
+ <macrodef name="multidex-package-helper">
+ <element name="extra-jars" optional="yes" />
+ <sequential>
+ <apkbuilder
+ outfolder="${out.absolute.dir}"
+ resourcefile="${resource.package.file.name}"
+ apkfilepath="${out.packaged.file}"
+ debugpackaging="${build.is.packaging.debug}"
+ debugsigning="${build.is.signing.debug}"
+ verbose="${verbose}"
+ hascode="false"
+ previousBuildType="/"
+ buildType="${build.is.packaging.debug}/${build.is.signing.debug}">
+ <zip path="${intermediate.dex.file}" />
+ <nativefolder path="${native.libs.absolute.dir}" />
+ <extra-jars/>
+ </apkbuilder>
+ </sequential>
+ </macrodef>
<!-- Packages the application. -->
<target name="-package">
@@ -89,7 +111,14 @@
</package-helper>
</then>
<else>
- <package-helper />
+ <if condition="${multidex.enabled}">
+ <then>
+ <multidex-package-helper />
+ </then>
+ <else>
+ <package-helper />
+ </else>
+ </if>
</else>
</if>
</target>
diff --git a/build/android/apkbuilder_action.gypi b/build/android/apkbuilder_action.gypi
index 27807d8..e073e9b 100644
--- a/build/android/apkbuilder_action.gypi
+++ b/build/android/apkbuilder_action.gypi
@@ -56,6 +56,11 @@
'-DDEX_FILE_PATH=<(dex_path)',
]
}],
+ ['enable_multidex == 1', {
+ 'action': [
+ '-DMULTIDEX_ENABLED=1',
+ ]
+ }]
],
'action': [
'python', '<(DEPTH)/build/android/gyp/ant.py',
diff --git a/build/android/gyp/dex.py b/build/android/gyp/dex.py
index c26d23a..3b4141f 100755
--- a/build/android/gyp/dex.py
+++ b/build/android/gyp/dex.py
@@ -4,21 +4,57 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
+import logging
import optparse
import os
import sys
+import tempfile
+import zipfile
from util import build_utils
from util import md5_check
-def DoDex(options, paths):
+def DoMultiDex(options, paths):
+ main_dex_list = []
+ main_dex_list_files = build_utils.ParseGypList(options.main_dex_list_paths)
+ for m in main_dex_list_files:
+ with open(m) as main_dex_list_file:
+ main_dex_list.extend(l for l in main_dex_list_file if l)
+
+ with tempfile.NamedTemporaryFile(suffix='.txt') as combined_main_dex_list:
+ combined_main_dex_list.write('\n'.join(main_dex_list))
+ combined_main_dex_list.flush()
+
+ dex_args = [
+ '--multi-dex',
+ '--minimal-main-dex',
+ '--main-dex-list=%s' % combined_main_dex_list.name
+ ]
+
+ DoDex(options, paths, dex_args=dex_args)
+
+ if options.dex_path.endswith('.zip'):
+ iz = zipfile.ZipFile(options.dex_path, 'r')
+ tmp_dex_path = '%s.tmp.zip' % options.dex_path
+ oz = zipfile.ZipFile(tmp_dex_path, 'w', zipfile.ZIP_DEFLATED)
+ for i in iz.namelist():
+ if i.endswith('.dex'):
+ oz.writestr(i, iz.read(i))
+ os.remove(options.dex_path)
+ os.rename(tmp_dex_path, options.dex_path)
+
+
+def DoDex(options, paths, dex_args=None):
dx_binary = os.path.join(options.android_sdk_tools, 'dx')
# See http://crbug.com/272064 for context on --force-jumbo.
dex_cmd = [dx_binary, '--dex', '--force-jumbo', '--output', options.dex_path]
if options.no_locals != '0':
dex_cmd.append('--no-locals')
+ if dex_args:
+ dex_cmd += dex_args
+
dex_cmd += paths
record_path = '%s.md5.stamp' % options.dex_path
@@ -54,9 +90,14 @@ def main():
'is enabled.'))
parser.add_option('--no-locals',
help='Exclude locals list from the dex file.')
+ parser.add_option('--multi-dex', default=False, action='store_true',
+ help='Create multiple dex files.')
parser.add_option('--inputs', help='A list of additional input paths.')
parser.add_option('--excluded-paths',
help='A list of paths to exclude from the dex file.')
+ parser.add_option('--main-dex-list-paths',
+ help='A list of paths containing a list of the classes to '
+ 'include in the main dex.')
options, paths = parser.parse_args(args)
@@ -76,7 +117,14 @@ def main():
paths = [p for p in paths if not
os.path.relpath(p, options.output_directory) in exclude_paths]
- DoDex(options, paths)
+ if options.multi_dex and options.main_dex_list_paths:
+ DoMultiDex(options, paths)
+ else:
+ if options.multi_dex:
+ logging.warning('--multi-dex is unused without --main-dex-list-paths')
+ elif options.main_dex_list_paths:
+ logging.warning('--main-dex-list-paths is unused without --multi-dex')
+ DoDex(options, paths)
if options.depfile:
build_utils.WriteDepfile(
diff --git a/build/android/gyp/main_dex_list.py b/build/android/gyp/main_dex_list.py
new file mode 100755
index 0000000..6442001
--- /dev/null
+++ b/build/android/gyp/main_dex_list.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python
+#
+# Copyright 2015 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.
+
+import argparse
+import os
+import sys
+import tempfile
+
+from util import build_utils
+
+sys.path.append(os.path.abspath(os.path.join(
+ os.path.dirname(__file__), os.pardir)))
+from pylib import constants
+
+
+def main():
+ parser = argparse.ArgumentParser()
+ parser.add_argument('--android-sdk-tools', required=True,
+ help='Android sdk build tools directory.')
+ parser.add_argument('--main-dex-rules-path', action='append', default=[],
+ dest='main_dex_rules_paths',
+ help='A file containing a list of proguard rules to use '
+ 'in determining the class to include in the '
+ 'main dex.')
+ parser.add_argument('--main-dex-list-path', required=True,
+ help='The main dex list file to generate.')
+ parser.add_argument('paths', nargs='+',
+ help='JARs for which a main dex list should be '
+ 'generated.')
+
+ args = parser.parse_args()
+
+ with open(args.main_dex_list_path, 'w') as main_dex_list_file:
+
+ shrinked_android_jar = os.path.abspath(
+ os.path.join(args.android_sdk_tools, 'lib', 'shrinkedAndroid.jar'))
+ dx_jar = os.path.abspath(
+ os.path.join(args.android_sdk_tools, 'lib', 'dx.jar'))
+ paths_arg = ':'.join(args.paths)
+ rules_file = os.path.abspath(
+ os.path.join(args.android_sdk_tools, 'mainDexClasses.rules'))
+
+ with tempfile.NamedTemporaryFile(suffix='.jar') as temp_jar:
+ proguard_cmd = [
+ constants.PROGUARD_SCRIPT_PATH,
+ '-forceprocessing',
+ '-dontwarn', '-dontoptimize', '-dontobfuscate', '-dontpreverify',
+ '-injars', paths_arg,
+ '-outjars', temp_jar.name,
+ '-libraryjars', shrinked_android_jar,
+ '-include', rules_file,
+ ]
+ for m in args.main_dex_rules_paths:
+ proguard_cmd.extend(['-include', m])
+
+ main_dex_list = ''
+ try:
+ build_utils.CheckOutput(proguard_cmd)
+
+ java_cmd = [
+ 'java', '-cp', dx_jar,
+ 'com.android.multidex.MainDexListBuilder',
+ temp_jar.name, paths_arg
+ ]
+ main_dex_list = build_utils.CheckOutput(java_cmd)
+ except build_utils.CalledProcessError as e:
+ if 'output jar is empty' in e.output:
+ pass
+ elif "input doesn't contain any classes" in e.output:
+ pass
+ else:
+ raise
+
+ main_dex_list_file.write(main_dex_list)
+
+ return 0
+
+
+if __name__ == '__main__':
+ sys.exit(main())
+
diff --git a/build/android/main_dex_action.gypi b/build/android/main_dex_action.gypi
new file mode 100644
index 0000000..06717dd
--- /dev/null
+++ b/build/android/main_dex_action.gypi
@@ -0,0 +1,44 @@
+# Copyright 2015 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.
+
+# This file is meant to be included into an action to provide a rule that
+# generates a list of classes that must be kept in the main dex file.
+#
+# To use this, create a gyp target with the following form:
+# {
+# 'action_name': 'some name for the action'
+# 'actions': [
+# 'variables': {
+# 'jar_path': 'path to jar',
+# 'output_path': 'output path'
+# },
+# 'includes': [ 'relative/path/to/main_dex_action.gypi' ],
+# ],
+# },
+#
+
+{
+ 'message': 'Generating main dex classes list for <(jar_path)',
+ 'variables': {
+ 'jar_path%': '',
+ 'output_path%': '',
+ 'main_dex_list_script': '<(DEPTH)/build/android/gyp/main_dex_list.py',
+ 'main_dex_rules_path': '<(DEPTH)/build/android/main_dex_classes.flags',
+ },
+ 'inputs': [
+ '<(jar_path)',
+ '<(main_dex_list_script)',
+ '<(main_dex_rules_path)',
+ ],
+ 'outputs': [
+ '<(output_path)',
+ ],
+ 'action': [
+ 'python', '<(main_dex_list_script)',
+ '--main-dex-list-path', '<(output_path)',
+ '--android-sdk-tools', '<(android_sdk_tools)',
+ '--main-dex-rules-path', '<(main_dex_rules_path)',
+ '<(jar_path)',
+ ]
+}
diff --git a/build/android/main_dex_classes.flags b/build/android/main_dex_classes.flags
new file mode 100644
index 0000000..d9689b2
--- /dev/null
+++ b/build/android/main_dex_classes.flags
@@ -0,0 +1,3 @@
+-keep @**.MainDex class * {
+ *;
+}
diff --git a/build/android/pylib/constants/__init__.py b/build/android/pylib/constants/__init__.py
index 4617119..43c0a6d 100644
--- a/build/android/pylib/constants/__init__.py
+++ b/build/android/pylib/constants/__init__.py
@@ -189,6 +189,9 @@ ANDROID_SDK_TOOLS = os.path.join(ANDROID_SDK_ROOT,
ANDROID_NDK_ROOT = os.path.join(DIR_SOURCE_ROOT,
'third_party/android_tools/ndk')
+PROGUARD_SCRIPT_PATH = os.path.join(
+ ANDROID_SDK_ROOT, 'tools', 'proguard', 'bin', 'proguard.sh')
+
EMULATOR_SDK_ROOT = os.environ.get('ANDROID_EMULATOR_SDK_ROOT',
os.path.join(DIR_SOURCE_ROOT,
'android_emulator_sdk'))
diff --git a/build/config/android/rules.gni b/build/config/android/rules.gni
index 3f14b6762..88276bf 100644
--- a/build/config/android/rules.gni
+++ b/build/config/android/rules.gni
@@ -940,6 +940,9 @@ template("java_library") {
# proguard_preprocess: If true, proguard preprocessing will be run. This can
# be used to remove unwanted parts of the library.
# proguard_config: Path to the proguard config for preprocessing.
+# supports_android: If true, Android targets (android_library, android_apk)
+# may depend on this target. Note: if true, this target must only use the
+# subset of Java available on Android.
#
# Example
# java_prebuilt("foo_java") {
diff --git a/build/java.gypi b/build/java.gypi
index 614d50a..dc642b9 100644
--- a/build/java.gypi
+++ b/build/java.gypi
@@ -64,6 +64,7 @@
'instr_stamp': '<(intermediate_dir)/instr.stamp',
'additional_input_paths': [],
'dex_path': '<(PRODUCT_DIR)/lib.java/<(_target_name).dex.jar',
+ 'main_dex_list_path': '<(intermediate_dir)/main_dex_list.txt',
'generated_src_dirs': ['>@(generated_R_dirs)'],
'generated_R_dirs': [],
'has_java_resources%': 0,
@@ -113,6 +114,7 @@
'variables': {
'input_jars_paths': ['<(jar_final_path)'],
'library_dexed_jars_paths': ['<(dex_path)'],
+ 'main_dex_list_paths': ['<(main_dex_list_path)'],
},
},
}],
@@ -309,6 +311,14 @@
]
},
{
+ 'action_name': 'main_dex_list_for_<(_target_name)',
+ 'variables': {
+ 'jar_path': '<(javac_jar_path)',
+ 'output_path': '<(main_dex_list_path)',
+ },
+ 'includes': [ 'android/main_dex_action.gypi' ],
+ },
+ {
'action_name': 'instr_jar_<(_target_name)',
'message': 'Instrumenting <(_target_name) jar',
'variables': {
diff --git a/build/java_apk.gypi b/build/java_apk.gypi
index 6dbb858..52902a0 100644
--- a/build/java_apk.gypi
+++ b/build/java_apk.gypi
@@ -70,11 +70,14 @@
'variables': {
'tested_apk_obfuscated_jar_path%': '/',
'tested_apk_dex_path%': '/',
+ 'tested_apk_is_multidex%': 0,
'additional_input_paths': [],
'create_density_splits%': 0,
'language_splits': [],
'input_jars_paths': [],
'library_dexed_jars_paths': [],
+ 'main_dex_list_path': '<(intermediate_dir)/main_dex_list.txt',
+ 'main_dex_list_paths': ['<(main_dex_list_path)'],
'additional_src_dirs': [],
'generated_src_dirs': [],
'app_manifest_version_name%': '<(android_app_version_name)',
@@ -131,7 +134,7 @@
'jar_path': '<(PRODUCT_DIR)/lib.java/<(jar_name)',
'obfuscated_jar_path': '<(intermediate_dir)/obfuscated.jar',
'test_jar_path': '<(PRODUCT_DIR)/test.lib.java/<(apk_name).jar',
- 'dex_path': '<(intermediate_dir)/classes.dex',
+ 'enable_multidex%': 0,
'emma_device_jar': '<(android_sdk_root)/tools/lib/emma_device.jar',
'android_manifest_path%': '<(java_in_dir)/AndroidManifest.xml',
'split_android_manifest_path': '<(intermediate_dir)/split-manifests/<(android_app_abi)/AndroidManifest.xml',
@@ -160,6 +163,7 @@
'unsigned_apk_path': '<(intermediate_dir)/<(apk_name)-unsigned.apk',
'unsigned_abi_split_apk_path': '<(intermediate_dir)/<(apk_name)-abi-<(android_app_abi)-unsigned.apk',
'create_abi_split%': 0,
+ 'enable_multidex%': 0,
},
'unsigned_apk_path': '<(unsigned_apk_path)',
'unsigned_abi_split_apk_path': '<(unsigned_abi_split_apk_path)',
@@ -193,6 +197,11 @@
}, {
'managed_input_apk_path': '<(unsigned_apk_path)',
}],
+ ['enable_multidex == 1', {
+ 'dex_path': '<(intermediate_dir)/classes.dex.zip',
+ }, {
+ 'dex_path': '<(intermediate_dir)/classes.dex',
+ }],
],
},
'native_lib_target%': '',
@@ -213,6 +222,7 @@
'native_lib_placeholder_stamp': '<(apk_package_native_libs_dir)/<(android_app_abi)/native_lib_placeholder.stamp',
'native_lib_placeholders': [],
'main_apk_name': '<(apk_name)',
+ 'dex_path': '<(dex_path)',
'enable_errorprone%': '0',
'errorprone_exe_path': '<(PRODUCT_DIR)/bin.java/chromium_errorprone',
},
@@ -231,6 +241,7 @@
'apk_output_jar_path': '<(jar_path)',
'tested_apk_obfuscated_jar_path': '<(obfuscated_jar_path)',
'tested_apk_dex_path': '<(dex_path)',
+ 'tested_apk_is_multidex': '<(enable_multidex)',
},
},
'conditions': [
@@ -752,8 +763,7 @@
],
},
],
- },
- ]
+ }],
],
'dependencies': [
'<(DEPTH)/tools/android/md5sum/md5sum.gyp:md5sum',
@@ -885,6 +895,14 @@
],
},
{
+ 'action_name': 'main_dex_list_for_<(_target_name)',
+ 'variables': {
+ 'jar_path': '<(javac_jar_path)',
+ 'output_path': '<(main_dex_list_path)',
+ },
+ 'includes': [ 'android/main_dex_action.gypi' ],
+ },
+ {
'action_name': 'instr_jar_<(_target_name)',
'message': 'Instrumenting <(_target_name) jar',
'variables': {
@@ -1005,14 +1023,40 @@
{
'action_name': 'dex_<(_target_name)',
'variables': {
+ 'dex_additional_options': [],
'dex_input_paths': [
- '>@(library_dexed_jars_paths)',
'<(jar_path)',
],
'output_path': '<(dex_path)',
'proguard_enabled_input_path': '<(obfuscated_jar_path)',
},
+ 'conditions': [
+ ['enable_multidex == 1', {
+ 'variables': {
+ 'dex_additional_options': [
+ '--multi-dex',
+ '--main-dex-list-paths', '>@(main_dex_list_paths)',
+ ],
+ },
+ 'inputs': [
+ '>@(main_dex_list_paths)',
+ ],
+ }]
+ ],
'target_conditions': [
+ ['enable_multidex == 1 or tested_apk_is_multidex == 1', {
+ 'variables': {
+ 'dex_input_paths': [
+ '>@(input_jars_paths)',
+ ],
+ },
+ }, {
+ 'variables': {
+ 'dex_input_paths': [
+ '>@(library_dexed_jars_paths)',
+ ],
+ },
+ }],
['emma_instrument != 0', {
'variables': {
'dex_no_locals': 1,
diff --git a/build/secondary/third_party/android_tools/BUILD.gn b/build/secondary/third_party/android_tools/BUILD.gn
index a6b3758..175409b 100644
--- a/build/secondary/third_party/android_tools/BUILD.gn
+++ b/build/secondary/third_party/android_tools/BUILD.gn
@@ -49,6 +49,11 @@ android_java_prebuilt("android_support_design_java") {
jar_path = "$android_sdk_root/extras/android/support/design/libs/android-support-design.jar"
}
+java_prebuilt("android_support_multidex_java") {
+ supports_android = true
+ jar_path = "$android_sdk_root/extras/android/support/multidex/library/libs/android-support-multidex.jar"
+}
+
android_java_prebuilt("android_support_v13_java") {
jar_path =
"$android_sdk_root/extras/android/support/v13/android-support-v13.jar"
diff --git a/chrome/android/BUILD.gn b/chrome/android/BUILD.gn
index 67e7710..49e3598 100644
--- a/chrome/android/BUILD.gn
+++ b/chrome/android/BUILD.gn
@@ -474,6 +474,7 @@ junit_binary("chrome_junit_tests") {
":chrome_java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:base_junit_test_support",
"//sync:sync_java_test_support",
"//sync/android:sync_java",
"//third_party/junit:hamcrest",
diff --git a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java
index 54522d8..d8809d2 100644
--- a/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java
+++ b/chrome/android/junit/src/org/chromium/chrome/browser/media/remote/AbstractMediaRouteControllerTest.java
@@ -32,6 +32,7 @@ import android.support.v7.media.MediaRouter.RouteInfo;
import org.chromium.base.ApplicationStatus;
import org.chromium.base.BaseChromiumApplication;
import org.chromium.base.CommandLine;
+import org.chromium.base.test.shadows.ShadowMultiDex;
import org.chromium.base.test.util.Feature;
import org.chromium.chrome.browser.media.remote.MediaRouteController.MediaStateListener;
import org.chromium.chrome.browser.media.remote.MediaRouteController.UiListener;
@@ -51,8 +52,8 @@ import org.robolectric.internal.ReflectionHelpers;
/** Tests for {@link AbstractMediaRouteController}. */
@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE, shadows = AbstractMediaRouteControllerTest.ShadowMediaRouter.class,
- application = BaseChromiumApplication.class)
+@Config(manifest = Config.NONE, application = BaseChromiumApplication.class,
+ shadows = {AbstractMediaRouteControllerTest.ShadowMediaRouter.class, ShadowMultiDex.class})
public class AbstractMediaRouteControllerTest {
/** Reset the environment before each test. */
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 9348234..68ed2e1 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -3045,6 +3045,7 @@
'chrome_java',
'../base/base.gyp:base',
'../base/base.gyp:base_java_test_support',
+ '../base/base.gyp:base_junit_test_support',
'../sync/sync.gyp:sync_java_test_support',
'../testing/android/junit/junit_test.gyp:junit_test_support',
],
diff --git a/net/android/BUILD.gn b/net/android/BUILD.gn
index 86b9c86..c891da7 100644
--- a/net/android/BUILD.gn
+++ b/net/android/BUILD.gn
@@ -87,6 +87,7 @@ junit_binary("net_junit_tests") {
":net_java",
"//base:base_java",
"//base:base_java_test_support",
+ "//base:base_junit_test_support",
"//third_party/junit:hamcrest",
]
}
diff --git a/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java b/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java
index f6a97c0..7edc4e3 100644
--- a/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java
+++ b/net/android/junit/src/org/chromium/net/HttpNegotiateAuthenticatorTest.java
@@ -29,6 +29,7 @@ import android.os.Bundle;
import android.os.Handler;
import org.chromium.base.BaseChromiumApplication;
+import org.chromium.base.test.shadows.ShadowMultiDex;
import org.chromium.testing.local.LocalRobolectricTestRunner;
import org.junit.Before;
import org.junit.Test;
@@ -45,9 +46,9 @@ import java.io.IOException;
* Robolectric tests for HttpNegotiateAuthenticator
*/
@RunWith(LocalRobolectricTestRunner.class)
-@Config(manifest = Config.NONE,
- shadows = HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.class,
- application = BaseChromiumApplication.class)
+@Config(manifest = Config.NONE, application = BaseChromiumApplication.class,
+ shadows = {HttpNegotiateAuthenticatorTest.ExtendedShadowAccountManager.class,
+ ShadowMultiDex.class})
public class HttpNegotiateAuthenticatorTest {
// Since the account manager is an SDK singleton (it is fetched using AccountManager.get()) we
// can't validate its method calls with Mockito, so do so using our shadow method. Since the
diff --git a/net/net.gyp b/net/net.gyp
index 66d8ae3..d4fe51b 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -1545,6 +1545,7 @@
'net_java',
'../base/base.gyp:base',
'../base/base.gyp:base_java_test_support',
+ '../base/base.gyp:base_junit_test_support',
'../testing/android/junit/junit_test.gyp:junit_test_support',
],
'variables': {