Adds a step before creating the resource apk (ap_) to swap out the application
tag's name attribute to the Instant Run application class.
--
MOS_MIGRATED_REVID=124606107
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
index e36b28d..135192f 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidBinary.java
@@ -180,12 +180,12 @@
: NativeLibs.fromPrecompiledObjects(ruleContext, depsByArchitecture);
// TODO(bazel-team): Resolve all the different cases of resource handling so this conditional
- // can go away: recompile from android_resources, and recompile from
- // android_binary attributes.
+ // can go away: recompile from android_resources, and recompile from android_binary attributes.
ApplicationManifest applicationManifest;
- ResourceApk splitResourceApk;
- ResourceApk incrementalResourceApk;
ResourceApk resourceApk;
+ ResourceApk incrementalResourceApk;
+ ResourceApk instantRunResourceApk;
+ ResourceApk splitResourceApk;
if (LocalResourceContainer.definesAndroidResources(ruleContext.attributes())) {
// Retrieve and compile the resources defined on the android_binary rule.
LocalResourceContainer.validateRuleContext(ruleContext);
@@ -211,6 +211,7 @@
}
applicationManifest = ruleManifest.mergeWith(ruleContext, resourceDeps);
+
resourceApk = applicationManifest.packWithDataAndResources(
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_APK),
ruleContext,
@@ -231,8 +232,11 @@
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_PROCESSED_MANIFEST),
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_ZIP));
ruleContext.assertNoErrors();
- incrementalResourceApk = applicationManifest.addStubApplication(ruleContext)
- .packWithDataAndResources(ruleContext
+
+ incrementalResourceApk = applicationManifest
+ .addMobileInstallStubApplication(ruleContext)
+ .packWithDataAndResources(
+ ruleContext
.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_INCREMENTAL_RESOURCES_APK),
ruleContext,
false, /* isLibrary */
@@ -252,9 +256,34 @@
null, /* manifestOut */
null /* mergedResourcesOut */);
ruleContext.assertNoErrors();
+
+ instantRunResourceApk = applicationManifest
+ .addInstantRunStubApplication(ruleContext)
+ .packWithDataAndResources(
+ getDxArtifact(ruleContext, "android_instant_run.ap_"),
+ ruleContext,
+ false, /* isLibrary */
+ resourceDeps,
+ null, /* Artifact rTxt */
+ null, /* Artifact symbolsTxt */
+ ruleContext.getTokenizedStringListAttr("resource_configuration_filters"),
+ ruleContext.getTokenizedStringListAttr("nocompress_extensions"),
+ ruleContext.attributes().get("crunch_png", Type.BOOLEAN),
+ ruleContext.getTokenizedStringListAttr("densities"),
+ applicationId,
+ versionCode,
+ versionName,
+ true, /* incremental */
+ ProguardHelper.getProguardConfigArtifact(ruleContext, "instant_run"),
+ null, /* mainDexProguardCfg */
+ null, /* manifestOut */
+ null /* mergedResourcesOut */);
+ ruleContext.assertNoErrors();
+
splitResourceApk = applicationManifest
.createSplitManifest(ruleContext, "android_resources", false)
- .packWithDataAndResources(getDxArtifact(ruleContext, "android_resources.ap_"),
+ .packWithDataAndResources(
+ getDxArtifact(ruleContext, "android_resources.ap_"),
ruleContext,
false, /* isLibrary */
resourceDeps,
@@ -273,7 +302,9 @@
null, /* manifestOut */
null /* mergedResourcesOut */);
ruleContext.assertNoErrors();
+
} else {
+
if (!ruleContext.attributes().get("crunch_png", Type.BOOLEAN)) {
ruleContext.throwWithRuleError("Setting crunch_png = 0 is not supported for android_binary"
+ " rules which depend on android_resources rules.");
@@ -286,38 +317,54 @@
throw new RuleErrorException();
}
applicationManifest = resourcesManifest.mergeWith(ruleContext, resourceDeps);
+
// Always recompiling resources causes AndroidTest to fail in certain circumstances.
if (shouldRegenerate(ruleContext, resourceDeps)) {
resourceApk = applicationManifest.packWithResources(
ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.ANDROID_RESOURCES_APK),
ruleContext,
resourceDeps,
- true,
+ true, /* createSource */
ProguardHelper.getProguardConfigArtifact(ruleContext, ""),
createMainDexProguardSpec(ruleContext));
+ ruleContext.assertNoErrors();
} else {
- resourceApk = applicationManifest.useCurrentResources(ruleContext,
+ resourceApk = applicationManifest.useCurrentResources(
+ ruleContext,
ProguardHelper.getProguardConfigArtifact(ruleContext, ""),
createMainDexProguardSpec(ruleContext));
+ ruleContext.assertNoErrors();
}
+
incrementalResourceApk = applicationManifest
- .addStubApplication(ruleContext)
+ .addMobileInstallStubApplication(ruleContext)
.packWithResources(
ruleContext.getImplicitOutputArtifact(
AndroidRuleClasses.ANDROID_INCREMENTAL_RESOURCES_APK),
ruleContext,
resourceDeps,
- false,
+ false, /* createSource */
ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental"),
null /* mainDexProguardConfig */);
ruleContext.assertNoErrors();
+ instantRunResourceApk = applicationManifest
+ .addInstantRunStubApplication(ruleContext)
+ .packWithResources(
+ getDxArtifact(ruleContext, "android_instant_run.ap_"),
+ ruleContext,
+ resourceDeps,
+ false, /* createSource */
+ ProguardHelper.getProguardConfigArtifact(ruleContext, "instant_run"),
+ null /* mainDexProguardConfig */);
+ ruleContext.assertNoErrors();
+
splitResourceApk = applicationManifest
.createSplitManifest(ruleContext, "android_resources", false)
.packWithResources(getDxArtifact(ruleContext, "android_resources.ap_"),
ruleContext,
resourceDeps,
- false,
+ false, /* createSource */
ProguardHelper.getProguardConfigArtifact(ruleContext, "incremental_split"),
null /* mainDexProguardConfig */);
ruleContext.assertNoErrors();
@@ -337,7 +384,6 @@
Artifact proguardMapping = ruleContext.getPrerequisiteArtifact(
"proguard_apply_mapping", Mode.TARGET);
-
return createAndroidBinary(
ruleContext,
filesBuilder,
@@ -351,6 +397,7 @@
applicationManifest,
resourceApk,
incrementalResourceApk,
+ instantRunResourceApk,
splitResourceApk,
/* shrinkResources */ true,
resourceClasses,
@@ -372,6 +419,7 @@
ApplicationManifest applicationManifest,
ResourceApk resourceApk,
ResourceApk incrementalResourceApk,
+ ResourceApk instantRunResourceApk,
ResourceApk splitResourceApk,
boolean shrinkResources,
JavaTargetAttributes resourceClasses,
@@ -481,7 +529,7 @@
.useParameterFile(ParameterFileType.UNQUOTED).build(ruleContext));
Artifact stubData = ruleContext.getImplicitOutputArtifact(
- AndroidRuleClasses.STUB_APPLICATION_DATA);
+ AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_DATA);
Artifact stubDex = getStubDex(ruleContext, javaSemantics, false);
ruleContext.assertNoErrors();
@@ -638,7 +686,7 @@
false, // text proto
androidCommon,
resourceClasses,
- resourceApk,
+ instantRunResourceApk,
nativeLibs,
debugKeystore);
@@ -650,7 +698,7 @@
true, // text proto
androidCommon,
resourceClasses,
- resourceApk,
+ instantRunResourceApk,
nativeLibs,
debugKeystore);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
index abd3135..a299489 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidRuleClasses.java
@@ -122,8 +122,10 @@
fromTemplates("%{name}_symbols/local-R.txt");
public static final ImplicitOutputsFunction ANDROID_PROCESSED_MANIFEST =
fromTemplates("%{name}_processed_manifest/AndroidManifest.xml");
- public static final SafeImplicitOutputsFunction STUB_APPLICATON_MANIFEST =
- fromTemplates("%{name}_files/stub/AndroidManifest.xml");
+ public static final SafeImplicitOutputsFunction MOBILE_INSTALL_STUB_APPLICATON_MANIFEST =
+ fromTemplates("%{name}_files/mobile_install/AndroidManifest.xml");
+ public static final SafeImplicitOutputsFunction INSTANT_RUN_STUB_APPLICATON_MANIFEST =
+ fromTemplates("%{name}_files/instant_run/AndroidManifest.xml");
public static final SafeImplicitOutputsFunction FULL_DEPLOY_MARKER =
fromTemplates("%{name}_files/full_deploy_marker");
public static final SafeImplicitOutputsFunction INCREMENTAL_DEPLOY_MARKER =
@@ -146,7 +148,7 @@
// This needs to be in its own directory because ApkBuilder only has a function (-rf) for source
// folders but not source files, and it's easiest to guarantee that nothing gets put beside this
// file in the ApkBuilder invocation in this manner
- public static final SafeImplicitOutputsFunction STUB_APPLICATION_DATA =
+ public static final SafeImplicitOutputsFunction MOBILE_INSTALL_STUB_APPLICATION_DATA =
fromTemplates("%{name}_files/stub_application_data/stub_application_data.txt");
public static final SafeImplicitOutputsFunction DEX_MANIFEST =
fromTemplates("%{name}_files/dexmanifest.txt");
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
index 4eace34..296f4af 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApplicationManifest.java
@@ -102,23 +102,24 @@
}
}
- public ApplicationManifest addStubApplication(RuleContext ruleContext)
+ public ApplicationManifest addMobileInstallStubApplication(RuleContext ruleContext)
throws InterruptedException {
- Artifact stubManifest =
- ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.STUB_APPLICATON_MANIFEST);
+ Artifact stubManifest = ruleContext.getImplicitOutputArtifact(
+ AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATON_MANIFEST);
SpawnAction.Builder builder = new SpawnAction.Builder()
.setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST))
- .setProgressMessage("Injecting stub application")
- .setMnemonic("InjectStubApplication")
+ .setProgressMessage("Injecting mobile install stub application")
+ .setMnemonic("InjectMobileInstallStubApplication")
+ .addArgument("--mode=mobile_install")
.addArgument("--input_manifest")
.addInputArgument(manifest)
.addArgument("--output_manifest")
.addOutputArgument(stubManifest)
.addArgument("--output_datafile")
- .addOutputArgument(
- ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.STUB_APPLICATION_DATA));
+ .addOutputArgument(ruleContext.getImplicitOutputArtifact(
+ AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_DATA));
String overridePackage = getOverridePackage(ruleContext);
if (overridePackage != null) {
@@ -131,6 +132,27 @@
return new ApplicationManifest(stubManifest);
}
+ public ApplicationManifest addInstantRunStubApplication(RuleContext ruleContext)
+ throws InterruptedException {
+
+ Artifact stubManifest = ruleContext.getImplicitOutputArtifact(
+ AndroidRuleClasses.INSTANT_RUN_STUB_APPLICATON_MANIFEST);
+
+ SpawnAction.Builder builder = new SpawnAction.Builder()
+ .setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST))
+ .setProgressMessage("Injecting instant run stub application")
+ .setMnemonic("InjectInstantRunStubApplication")
+ .addArgument("--mode=instant_run")
+ .addArgument("--input_manifest")
+ .addInputArgument(manifest)
+ .addArgument("--output_manifest")
+ .addOutputArgument(stubManifest);
+
+ ruleContext.registerAction(builder.build(ruleContext));
+
+ return new ApplicationManifest(stubManifest);
+ }
+
public static ApplicationManifest fromRule(RuleContext ruleContext) {
return new ApplicationManifest(ruleContext.getPrerequisiteArtifact("manifest", Mode.TARGET));
}
@@ -216,7 +238,8 @@
Mode.TARGET,
FileProvider.class)).build();
- return createApk(resourceApk,
+ return createApk(
+ resourceApk,
ruleContext,
false, /* isLibrary */
resourceDeps,
@@ -273,7 +296,8 @@
if (ruleContext.hasErrors()) {
return null;
}
- return createApk(resourceApk,
+ return createApk(
+ resourceApk,
ruleContext,
isLibrary,
resourceDeps,
diff --git a/tools/android/stubify_manifest.py b/tools/android/stubify_manifest.py
index 42fdd14..5bd79ad 100644
--- a/tools/android/stubify_manifest.py
+++ b/tools/android/stubify_manifest.py
@@ -29,6 +29,8 @@
from third_party.py import gflags
+gflags.DEFINE_string("mode", "mobile_install",
+ "mobile_install or instant_run mode")
gflags.DEFINE_string("input_manifest", None, "The input manifest")
gflags.DEFINE_string("output_manifest", None, "The output manifest")
gflags.DEFINE_string("output_datafile", None, "The output data file that will "
@@ -41,8 +43,10 @@
ANDROID = "http://schemas.android.com/apk/res/android"
READ_EXTERNAL_STORAGE = "android.permission.READ_EXTERNAL_STORAGE"
-STUB_APPLICATION = (
+MOBILE_INSTALL_STUB_APPLICATION = (
"com.google.devtools.build.android.incrementaldeployment.StubApplication")
+INSTANT_RUN_BOOTSTRAP_APPLICATION = (
+ "com.android.tools.fd.runtime.BootstrapApplication")
# This is global state, but apparently that's the best one can to with
# ElementTree :(
@@ -53,8 +57,8 @@
pass
-def Stubify(manifest_string):
- """Does the stubification on an XML string.
+def StubifyMobileInstall(manifest_string):
+ """Does the stubification on an XML string for mobile-install.
Args:
manifest_string: the input manifest as a string.
@@ -63,26 +67,12 @@
Raises:
Exception: if something goes wrong
"""
- manifest = ElementTree.fromstring(manifest_string)
- if manifest.tag != "manifest":
- raise BadManifestException("invalid input manifest")
+ manifest, application = _ParseManifest(manifest_string)
- app_list = manifest.findall("application")
- if len(app_list) == 1:
- # <application> element is present
- application = app_list[0]
- elif len(app_list) == 0: # pylint: disable=g-explicit-length-test
- # <application> element is not present
- application = ElementTree.Element("application")
- manifest.insert(0, application)
- else:
- raise BadManifestException("multiple <application> elements present")
+ old_application = application.get(
+ "{%s}name" % ANDROID, "android.app.Application")
- old_application = application.get("{%s}name" % ANDROID)
- if old_application is None:
- old_application = "android.app.Application"
-
- application.set("{%s}name" % ANDROID, STUB_APPLICATION)
+ application.set("{%s}name" % ANDROID, MOBILE_INSTALL_STUB_APPLICATION)
application.attrib.pop("{%s}hasCode" % ANDROID, None)
read_permission = manifest.findall(
'./uses-permission[@android:name="%s"]' % READ_EXTERNAL_STORAGE,
@@ -98,18 +88,72 @@
return (new_manifest, old_application, app_package)
+def StubifyInstantRun(manifest_string):
+ """Stubifies the manifest for Instant Run.
+
+ Args:
+ manifest_string: the input manifest as a string.
+ Returns:
+ The new manifest as a string.
+ Raises:
+ Exception: if somethign goes wrong
+ """
+ manifest, application = _ParseManifest(manifest_string)
+ old_application = application.get("{%s}name" % ANDROID)
+ if old_application:
+ application.set("name", old_application)
+ application.set("{%s}name" % ANDROID, INSTANT_RUN_BOOTSTRAP_APPLICATION)
+ return ElementTree.tostring(manifest)
+
+
+def _ParseManifest(manifest_string):
+ """Parses the given manifest xml.
+
+ Args:
+ manifest_string: the manifest as a string.
+ Returns:
+ a tuple of the manifest ElementTree and the application tag.
+ Raises:
+ BadManifestException: if the manifest is bad.
+ """
+ manifest = ElementTree.fromstring(manifest_string)
+ if manifest.tag != "manifest":
+ raise BadManifestException("invalid input manifest")
+
+ app_list = manifest.findall("application")
+ if len(app_list) == 1:
+ # <application> element is present
+ application = app_list[0]
+ elif len(app_list) == 0: # pylint: disable=g-explicit-length-test
+ # <application> element is not present
+ application = ElementTree.Element("application")
+ manifest.insert(0, application)
+ else:
+ raise BadManifestException("multiple <application> elements present")
+ return (manifest, application)
+
+
def main():
- with file(FLAGS.input_manifest) as input_manifest:
- new_manifest, old_application, app_package = Stubify(input_manifest.read())
+ if FLAGS.mode == "mobile_install":
+ with file(FLAGS.input_manifest) as input_manifest:
+ new_manifest, old_application, app_package = (
+ StubifyMobileInstall(input_manifest.read()))
- if FLAGS.override_package:
- app_package = FLAGS.override_package
+ if FLAGS.override_package:
+ app_package = FLAGS.override_package
- with file(FLAGS.output_manifest, "w") as output_xml:
- output_xml.write(new_manifest)
+ with file(FLAGS.output_manifest, "w") as output_xml:
+ output_xml.write(new_manifest)
- with file(FLAGS.output_datafile, "w") as output_file:
- output_file.write("\n".join([old_application, app_package]))
+ with file(FLAGS.output_datafile, "w") as output_file:
+ output_file.write("\n".join([old_application, app_package]))
+
+ elif FLAGS.mode == "instant_run":
+ with file(FLAGS.input_manifest) as input_manifest:
+ new_manifest = StubifyInstantRun(input_manifest.read())
+
+ with file(FLAGS.output_manifest, "w") as output_xml:
+ output_xml.write(new_manifest)
if __name__ == "__main__":
diff --git a/tools/android/stubify_manifest_test.py b/tools/android/stubify_manifest_test.py
index 796e199..ca23819 100644
--- a/tools/android/stubify_manifest_test.py
+++ b/tools/android/stubify_manifest_test.py
@@ -19,9 +19,11 @@
from tools.android.stubify_manifest import ANDROID
from tools.android.stubify_manifest import BadManifestException
+from tools.android.stubify_manifest import INSTANT_RUN_BOOTSTRAP_APPLICATION
+from tools.android.stubify_manifest import MOBILE_INSTALL_STUB_APPLICATION
from tools.android.stubify_manifest import READ_EXTERNAL_STORAGE
-from tools.android.stubify_manifest import STUB_APPLICATION
-from tools.android.stubify_manifest import Stubify
+from tools.android.stubify_manifest import StubifyInstantRun
+from tools.android.stubify_manifest import StubifyMobileInstall
MANIFEST_WITH_APPLICATION = """
@@ -73,7 +75,7 @@
"""
-class StubifyTest(unittest.TestCase):
+class StubifyMobileInstallTest(unittest.TestCase):
def GetApplication(self, manifest_string):
manifest = ElementTree.fromstring(manifest_string)
@@ -81,20 +83,23 @@
return application.get("{%s}name" % ANDROID)
def testReplacesOldApplication(self):
- new_manifest, old_application, app_pkg = Stubify(MANIFEST_WITH_APPLICATION)
+ new_manifest, old_application, app_pkg = StubifyMobileInstall(
+ MANIFEST_WITH_APPLICATION)
self.assertEqual("com.google.package", app_pkg)
self.assertEqual("old.application", old_application)
- self.assertEqual(STUB_APPLICATION, self.GetApplication(new_manifest))
+ self.assertEqual(
+ MOBILE_INSTALL_STUB_APPLICATION, self.GetApplication(new_manifest))
def testAddsNewAplication(self):
new_manifest, old_application, app_pkg = (
- Stubify(MANIFEST_WITHOUT_APPLICATION))
+ StubifyMobileInstall(MANIFEST_WITHOUT_APPLICATION))
self.assertEqual("com.google.package", app_pkg)
self.assertEqual("android.app.Application", old_application)
- self.assertEqual(STUB_APPLICATION, self.GetApplication(new_manifest))
+ self.assertEqual(
+ MOBILE_INSTALL_STUB_APPLICATION, self.GetApplication(new_manifest))
def testRemovesHasCode(self):
- new_manifest, _, _ = Stubify(MANIFEST_WITH_HASCODE)
+ new_manifest, _, _ = StubifyMobileInstall(MANIFEST_WITH_HASCODE)
application = ElementTree.fromstring(new_manifest).find("application")
self.assertFalse(("{%s}hasCode" % ANDROID) in application.attrib)
@@ -107,19 +112,40 @@
def testAddsPermission(self):
self.assertHasPermission(
- Stubify(MANIFEST_WITH_APPLICATION)[0], READ_EXTERNAL_STORAGE)
+ StubifyMobileInstall(
+ MANIFEST_WITH_APPLICATION)[0], READ_EXTERNAL_STORAGE)
def testDoesNotDuplicatePermission(self):
self.assertHasPermission(
- Stubify(MANIFEST_WITH_PERMISSION)[0], READ_EXTERNAL_STORAGE)
+ StubifyMobileInstall(
+ MANIFEST_WITH_PERMISSION)[0], READ_EXTERNAL_STORAGE)
def testBadManifest(self):
with self.assertRaises(BadManifestException):
- Stubify(BAD_MANIFEST)
+ StubifyMobileInstall(BAD_MANIFEST)
def testTooManyApplications(self):
with self.assertRaises(BadManifestException):
- Stubify(MULTIPLE_APPLICATIONS)
+ StubifyMobileInstall(MULTIPLE_APPLICATIONS)
+
+
+class StubifyInstantRunTest(unittest.TestCase):
+
+ def testReplacesOldApplication(self):
+ new_manifest = StubifyInstantRun(MANIFEST_WITH_APPLICATION)
+ manifest = ElementTree.fromstring(new_manifest)
+ application = manifest.find("application")
+ self.assertEqual(INSTANT_RUN_BOOTSTRAP_APPLICATION,
+ application.get("{%s}name" % ANDROID))
+ self.assertEqual("old.application", application.get("name"))
+
+ def testReplacesAndSavesOldApplication(self):
+ new_manifest = StubifyInstantRun(MANIFEST_WITHOUT_APPLICATION)
+ manifest = ElementTree.fromstring(new_manifest)
+ application = manifest.find("application")
+ self.assertEqual(INSTANT_RUN_BOOTSTRAP_APPLICATION,
+ application.get("{%s}name" % ANDROID))
+ self.assertEqual(None, application.get("name"))
if __name__ == "__main__":