// Copyright 2018 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.base.Joiner; import com.google.common.collect.ImmutableMap; import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.analysis.actions.ActionConstructionContext; import com.google.devtools.build.lib.analysis.actions.CustomCommandLine; import com.google.devtools.build.lib.analysis.actions.FileWriteAction; import com.google.devtools.build.lib.analysis.actions.SpawnAction; import com.google.devtools.build.lib.analysis.configuredtargets.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.concurrent.ThreadSafety.Immutable; import com.google.devtools.build.lib.syntax.Type; import java.util.Map; import java.util.TreeMap; import javax.annotation.Nullable; /** An {@link AndroidManifest} stamped with the correct package. */ @Immutable public class StampedAndroidManifest extends AndroidManifest { StampedAndroidManifest(Artifact manifest, @Nullable String pkg, boolean exported) { super(manifest, pkg, exported); } @Override public StampedAndroidManifest stamp(AndroidDataContext dataContext) { // This manifest is already stamped return this; } /** * Gets the manifest artifact wrapped by this object. * *

The manifest is guaranteed to be stamped with the correct Android package. */ @Override public Artifact getManifest() { return super.getManifest(); } ProcessedAndroidManifest withProcessedManifest(Artifact processedManifest) { return new ProcessedAndroidManifest(processedManifest, getPackage(), isExported()); } /** Creates an empty manifest stamped with the default Java package for this target. */ public static StampedAndroidManifest createEmpty(RuleContext ruleContext, boolean exported) { return createEmpty(ruleContext, AndroidCommon.getJavaPackage(ruleContext), exported); } /** Creates an empty manifest stamped with a specified package. */ public static StampedAndroidManifest createEmpty( ActionConstructionContext context, String pkg, boolean exported) { Artifact generatedManifest = context.getUniqueDirectoryArtifact("_generated", "AndroidManifest.xml"); String contents = Joiner.on("\n") .join( "", "", " ", " ", ""); context.registerAction( FileWriteAction.create(context, generatedManifest, contents, /*makeExecutable=*/ false)); return new StampedAndroidManifest(generatedManifest, pkg, exported); } public StampedAndroidManifest addMobileInstallStubApplication(RuleContext ruleContext) throws InterruptedException { Artifact stubManifest = ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_MANIFEST); Artifact stubData = ruleContext.getImplicitOutputArtifact( AndroidRuleClasses.MOBILE_INSTALL_STUB_APPLICATION_DATA); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable(ruleContext.getExecutablePrerequisite("$stubify_manifest", Mode.HOST)) .setProgressMessage("Injecting mobile install stub application") .setMnemonic("InjectMobileInstallStubApplication") .addInput(getManifest()) .addOutput(stubManifest) .addOutput(stubData); CustomCommandLine.Builder commandLine = CustomCommandLine.builder() .add("--mode=mobile_install") .addExecPath("--input_manifest", getManifest()) .addExecPath("--output_manifest", stubManifest) .addExecPath("--output_datafile", stubData); String overridePackage = getManifestValues(ruleContext).get("applicationId"); if (overridePackage != null) { commandLine.add("--override_package", overridePackage); } builder.addCommandLine(commandLine.build()); ruleContext.registerAction(builder.build(ruleContext)); return new StampedAndroidManifest(stubManifest, getPackage(), isExported()); } public static Map getManifestValues(RuleContext context) { if (!context.attributes().isAttributeValueExplicitlySpecified("manifest_values")) { return ImmutableMap.of(); } Map manifestValues = new TreeMap<>(context.attributes().get("manifest_values", Type.STRING_DICT)); for (String variable : manifestValues.keySet()) { manifestValues.put( variable, context.getExpander().expand("manifest_values", manifestValues.get(variable))); } return ImmutableMap.copyOf(manifestValues); } public StampedAndroidManifest createSplitManifest( RuleContext ruleContext, String splitName, boolean hasCode) { // aapt insists that manifests be called AndroidManifest.xml, even though they have to be // explicitly designated as manifests on the command line Artifact splitManifest = AndroidBinary.getDxArtifact(ruleContext, "split_" + splitName + "/AndroidManifest.xml"); SpawnAction.Builder builder = new SpawnAction.Builder() .setExecutable( ruleContext.getExecutablePrerequisite("$build_split_manifest", Mode.HOST)) .setProgressMessage("Creating manifest for split %s", splitName) .setMnemonic("AndroidBuildSplitManifest") .addInput(getManifest()) .addOutput(splitManifest); CustomCommandLine.Builder commandLine = CustomCommandLine.builder() .addExecPath("--main_manifest", getManifest()) .addExecPath("--split_manifest", splitManifest) .add("--split", splitName); if (hasCode) { commandLine.add("--hascode"); } else { commandLine.add("--nohascode"); } String overridePackage = getManifestValues(ruleContext).get("applicationId"); if (overridePackage != null) { commandLine.add("--override_package", overridePackage); } builder.addCommandLine(commandLine.build()); ruleContext.registerAction(builder.build(ruleContext)); return new StampedAndroidManifest(splitManifest, getPackage(), isExported()); } public AndroidManifestInfo toProvider() { return AndroidManifestInfo.of(getManifest(), getPackage(), isExported()); } @Override public boolean equals(Object object) { return (object instanceof StampedAndroidManifest && super.equals(object)); } }