From e345ba54feda0c630679d18e3d039fd1ee8843bd Mon Sep 17 00:00:00 2001 From: Googler Date: Thu, 21 Apr 2016 22:52:35 +0000 Subject: Add android deploy info. Each android binary build operation will output a deploy info proto providing information about how to deploy and launch the APK. The information will vary between build mode (normal, mobile-install, split-apk) and is configuration-dependent. NO_SQ: Presubmit broken -- MOS_MIGRATED_REVID=120494036 --- src/main/java/com/google/devtools/build/lib/BUILD | 1 + .../build/lib/rules/android/AndroidBinary.java | 48 +++++++- .../lib/rules/android/AndroidDeployInfoAction.java | 132 +++++++++++++++++++++ .../lib/rules/android/AndroidRuleClasses.java | 6 + .../build/lib/rules/android/ApkProvider.java | 13 +- 5 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 src/main/java/com/google/devtools/build/lib/rules/android/AndroidDeployInfoAction.java (limited to 'src/main/java/com/google/devtools/build/lib') diff --git a/src/main/java/com/google/devtools/build/lib/BUILD b/src/main/java/com/google/devtools/build/lib/BUILD index ecf9acf8df..ffb3ff33ef 100644 --- a/src/main/java/com/google/devtools/build/lib/BUILD +++ b/src/main/java/com/google/devtools/build/lib/BUILD @@ -757,6 +757,7 @@ java_library( "//src/main/java/com/google/devtools/build/lib/actions", "//src/main/java/com/google/devtools/build/lib/rules/cpp", "//src/main/java/com/google/devtools/common/options", + "//src/main/protobuf:android_deploy_info_java_proto", "//src/main/protobuf:apk_manifest_java_proto", "//third_party:jsr305", "//third_party/protobuf", 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 e534f372a8..9e8117ad2a 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 @@ -365,6 +365,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { splitResourceApk, resourceClasses, ImmutableList.of(), + ImmutableList.of(), proguardMapping); } @@ -385,6 +386,7 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { ResourceApk splitResourceApk, JavaTargetAttributes resourceClasses, ImmutableList apksUnderTest, + ImmutableList additionalMergedManifests, Artifact proguardMapping) throws InterruptedException { ImmutableList proguardSpecs = ProguardHelper.collectTransitiveProguardSpecs( @@ -449,6 +451,14 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { filesBuilder.add(unsignedApk); filesBuilder.add(zipAlignedApk); + Artifact deployInfo = ruleContext.getImplicitOutputArtifact(AndroidRuleClasses.DEPLOY_INFO); + AndroidDeployInfoAction.createDeployInfoAction(ruleContext, + deployInfo, + applicationManifest.getManifest(), + additionalMergedManifests, + Iterables.concat(ImmutableList.of(zipAlignedApk), apksUnderTest)); + filesBuilder.add(deployInfo); + NestedSet filesToBuild = filesBuilder.build(); NestedSet coverageMetadata = (androidCommon.getInstrumentedJar() != null) @@ -518,6 +528,25 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { nativeLibs, stubData); + Artifact incrementalDeployInfo = ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.DEPLOY_INFO_INCREMENTAL); + + AndroidDeployInfoAction.createDeployInfoAction(ruleContext, + incrementalDeployInfo, + applicationManifest.getManifest(), + additionalMergedManifests, + ImmutableList.of()); + + NestedSet fullOutputGroup = NestedSetBuilder.stableOrder() + .add(fullDeployMarker) + .add(incrementalDeployInfo) + .build(); + + NestedSet incrementalOutputGroup = NestedSetBuilder.stableOrder() + .add(incrementalDeployMarker) + .add(incrementalDeployInfo) + .build(); + NestedSetBuilder splitApkSetBuilder = NestedSetBuilder.compileOrder(); // Put the Android resource APK first so that this split gets installed first. @@ -599,9 +628,19 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { createSplitInstallAction(ruleContext, splitDeployMarker, argsArtifact, splitMainApk, splitApks, stubData); + Artifact splitDeployInfo = ruleContext.getImplicitOutputArtifact( + AndroidRuleClasses.DEPLOY_INFO_SPLIT); + AndroidDeployInfoAction.createDeployInfoAction( + ruleContext, + splitDeployInfo, + applicationManifest.getManifest(), + additionalMergedManifests, + ImmutableList.of()); + NestedSet splitOutputGroup = NestedSetBuilder.stableOrder() .addTransitive(allSplitApks) .add(splitDeployMarker) + .add(splitDeployInfo) .build(); Artifact apkManifest = @@ -653,10 +692,13 @@ public abstract class AndroidBinary implements RuleConfiguredTargetFactory { .add( ApkProvider.class, new ApkProvider( - NestedSetBuilder.create(Order.STABLE_ORDER, zipAlignedApk), coverageMetadata)) + NestedSetBuilder.create(Order.STABLE_ORDER, zipAlignedApk), + coverageMetadata, + NestedSetBuilder.create(Order.STABLE_ORDER, applicationManifest.getManifest()) + )) .add(AndroidPreDexJarProvider.class, new AndroidPreDexJarProvider(jarToDex)) - .addOutputGroup("mobile_install_full", fullDeployMarker) - .addOutputGroup("mobile_install_incremental", incrementalDeployMarker) + .addOutputGroup("mobile_install_full", fullOutputGroup) + .addOutputGroup("mobile_install_incremental", incrementalOutputGroup) .addOutputGroup("mobile_install_split", splitOutputGroup) .addOutputGroup("apk_manifest", apkManifest) .addOutputGroup("apk_manifest_text", apkManifestText); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDeployInfoAction.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDeployInfoAction.java new file mode 100644 index 0000000000..cae2cece29 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidDeployInfoAction.java @@ -0,0 +1,132 @@ +// Copyright 2015 The Bazel Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.devtools.build.lib.rules.android; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.collect.ImmutableList; +import com.google.common.io.ByteStreams; +import com.google.devtools.build.lib.actions.Action; +import com.google.devtools.build.lib.actions.ActionExecutionContext; +import com.google.devtools.build.lib.actions.ActionOwner; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.actions.AbstractFileWriteAction; +import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; +import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass; +import com.google.devtools.build.lib.rules.android.deployinfo.AndroidDeployInfoOuterClass.AndroidDeployInfo; +import com.google.devtools.build.lib.util.Fingerprint; +import com.google.protobuf.ByteString; +import com.google.protobuf.InvalidProtocolBufferException; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; + +/** + * Writes AndroidDeployInfo proto message. This proto describes how + * to deploy and launch an android_binary/android_test. + */ +@Immutable +public final class AndroidDeployInfoAction extends AbstractFileWriteAction { + + private static Iterable makeInputs( + Artifact mergedManifest, + Iterable additionalMergedManifests, + Iterable apksToDeploy) { + + return ImmutableList.builder() + .add(mergedManifest) + .addAll(additionalMergedManifests) + .addAll(apksToDeploy) + .build(); + } + + private static final String GUID = "eda283ba-9000-4b80-8dc4-7939101c44ba"; + private final ByteString byteString; + + AndroidDeployInfoAction( + ActionOwner owner, + Artifact outputFile, + Artifact mergedManifest, + Iterable additionalMergedManifests, + Iterable apksToDeploy) { + super(owner, makeInputs(mergedManifest, additionalMergedManifests, apksToDeploy), + outputFile, false); + AndroidDeployInfoOuterClass.AndroidDeployInfo.Builder builder = + AndroidDeployInfoOuterClass.AndroidDeployInfo.newBuilder(); + builder.setMergedManifest(makeArtifactProto(mergedManifest)); + for (Artifact additionMergedManifest : additionalMergedManifests) { + builder.addAdditionalMergedManifests(makeArtifactProto(additionMergedManifest)); + } + for (Artifact apk : apksToDeploy) { + builder.addApksToDeploy(makeArtifactProto(apk)); + } + this.byteString = builder.build().toByteString(); + } + + static void createDeployInfoAction( + RuleContext ruleContext, + Artifact deployInfo, + Artifact mergedManifest, + Iterable additionalMergedManifests, + Iterable apksToDeploy) { + Action action = new AndroidDeployInfoAction(ruleContext.getActionOwner(), + deployInfo, mergedManifest, additionalMergedManifests, apksToDeploy); + ruleContext.registerAction(action); + } + + @Override + public DeterministicWriter newDeterministicWriter(ActionExecutionContext ctx) throws IOException { + + return new DeterministicWriter() { + @Override + public void writeOutputFile(OutputStream out) throws IOException { + try (InputStream in = byteString.newInput()) { + ByteStreams.copy(in, out); + } + out.flush(); + } + }; + } + + @VisibleForTesting + public AndroidDeployInfo getDeployInfo() throws InvalidProtocolBufferException { + return AndroidDeployInfo.parseFrom(byteString); + } + + @Override + protected String computeKey() { + Fingerprint f = new Fingerprint() + .addString(GUID); + + try (InputStream in = byteString.newInput()) { + byte[] buffer = new byte[512]; + int amountRead; + while ((amountRead = in.read(buffer)) != -1) { + f.addBytes(buffer, 0, amountRead); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + + return f.hexDigestAndReset(); + } + + private static AndroidDeployInfoOuterClass.Artifact makeArtifactProto(Artifact artifact) { + return AndroidDeployInfoOuterClass.Artifact.newBuilder() + .setExecRootPath(artifact.getExecPathString()) + .build(); + } +} 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 56db345650..7cd976d654 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 @@ -137,6 +137,12 @@ public final class AndroidRuleClasses { fromTemplates("%{name}_files/apk_manifest"); public static final SafeImplicitOutputsFunction APK_MANIFEST_TEXT = fromTemplates("%{name}_files/apk_manifest_text"); + public static final SafeImplicitOutputsFunction DEPLOY_INFO = + fromTemplates("%{name}_files/deploy_info.deployinfo.pb"); + public static final SafeImplicitOutputsFunction DEPLOY_INFO_INCREMENTAL = + fromTemplates("%{name}_files/deploy_info_incremental.deployinfo.pb"); + public static final SafeImplicitOutputsFunction DEPLOY_INFO_SPLIT = + fromTemplates("%{name}_files/deploy_info_split.deployinfo.pb"); // 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 diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java b/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java index 0f943dcfa2..2ac6c0d69b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/ApkProvider.java @@ -28,9 +28,13 @@ public final class ApkProvider implements TransitiveInfoProvider { private final NestedSet coverageMetadata; - public ApkProvider(NestedSet transitiveApks, NestedSet coverageMetdata) { + private final NestedSet mergedManifests; + + public ApkProvider(NestedSet transitiveApks, NestedSet coverageMetdata, + NestedSet mergedManifests) { this.transitiveApks = transitiveApks; this.coverageMetadata = coverageMetdata; + this.mergedManifests = mergedManifests; } /** @@ -46,4 +50,11 @@ public final class ApkProvider implements TransitiveInfoProvider { public NestedSet getCoverageMetadata() { return coverageMetadata; } + + /** + * Returns the merged manifests + */ + public NestedSet getMergedManifests() { + return mergedManifests; + } } -- cgit v1.2.3