aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java
diff options
context:
space:
mode:
authorGravatar Peter Schmitt <schmitt@google.com>2016-02-23 15:34:26 +0000
committerGravatar Damien Martin-Guillerez <dmarting@google.com>2016-02-23 22:17:11 +0000
commit08bb682666990d562776d23f50cc09f851444217 (patch)
tree6151911eed7e7fda7947d74814ff38239f1df477 /src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java
parenta08a6e22d67d94e4d4d25fbf6129879d49e1646c (diff)
Add user-defined IPA post-processors.
RELNOTES[NEW]: iOS ipa_post_processor attribute allows for user-defined IPA edits. -- MOS_MIGRATED_REVID=115338312
Diffstat (limited to 'src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java267
1 files changed, 158 insertions, 109 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java
index 1bfed353b3..9e15d8986d 100644
--- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java
+++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java
@@ -37,6 +37,7 @@ import com.google.devtools.build.lib.analysis.actions.BinaryFileWriteAction;
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.actions.SymlinkAction;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction;
import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution;
import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
@@ -83,11 +84,6 @@ public final class ReleaseBundlingSupport {
*/
public static final SafeImplicitOutputsFunction IPA = fromTemplates("%{name}.ipa");
- // This is not an actual implicit output. This function is used to compute the name of an
- // artifact.
- public static final SafeImplicitOutputsFunction IPA_UNSIGNED =
- fromTemplates("%{name}.ipa.unsigned");
-
@VisibleForTesting
static final String NO_ASSET_CATALOG_ERROR_FORMAT =
"a value was specified (%s), but this app does not have any asset catalogs";
@@ -232,6 +228,12 @@ public final class ReleaseBundlingSupport {
validateLaunchScreen();
+ AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class);
+ if (attributes.provisioningProfile() == null
+ && appleConfiguration.getBundlingPlatform() != Platform.IOS_SIMULATOR) {
+ ruleContext.attributeError("provisioning_profile", DEVICE_NO_PROVISIONING_PROFILE);
+ }
+
return this;
}
@@ -278,7 +280,7 @@ public final class ReleaseBundlingSupport {
* multi-architecture binary.
*
* @return this application support
- * @throws InterruptedException
+ * @throws InterruptedException
*/
ReleaseBundlingSupport registerActions() throws InterruptedException {
bundleSupport.registerActions(objcProvider);
@@ -287,18 +289,6 @@ public final class ReleaseBundlingSupport {
registerTransformAndCopyBreakpadFilesAction();
registerSwiftStdlibActionsIfNecessary();
- AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class);
- Artifact ipaOutput = ruleContext.getImplicitOutputArtifact(IPA);
-
- Artifact maybeSignedIpa;
- if (appleConfiguration.getBundlingPlatform() == Platform.IOS_SIMULATOR) {
- maybeSignedIpa = ipaOutput;
- } else if (attributes.provisioningProfile() == null) {
- throw new IllegalStateException(DEVICE_NO_PROVISIONING_PROFILE);
- } else {
- maybeSignedIpa = registerBundleSigningActions(ipaOutput);
- }
-
registerEmbedLabelPlistAction();
registerEnvironmentPlistAction();
registerAutomaticPlistAction();
@@ -307,10 +297,8 @@ public final class ReleaseBundlingSupport {
registerLaunchStoryboardPlistAction();
}
- BundleMergeControlBytes bundleMergeControlBytes = new BundleMergeControlBytes(
- bundling, maybeSignedIpa, appleConfiguration, bundleSupport.targetDeviceFamilies());
- registerBundleMergeActions(
- maybeSignedIpa, bundling.getBundleContentArtifacts(), bundleMergeControlBytes);
+ registerBundleMergeActions();
+ registerPostProcessAndSigningActions();
return this;
}
@@ -429,9 +417,93 @@ public final class ReleaseBundlingSupport {
return result;
}
- private Artifact registerBundleSigningActions(Artifact ipaOutput) throws InterruptedException {
- IntermediateArtifacts intermediateArtifacts =
- ObjcRuleClasses.intermediateArtifacts(ruleContext);
+ /**
+ * Registers all actions necessary to create a processed and signed IPA from the initial merged
+ * IPA.
+ *
+ * <p>Includes user-provided actions to process IPA contents (via {@code ipa_post_processor}),
+ * and signing actions if the IPA is being built for device architectures. If signing is necessary
+ * also includes entitlements generation and processing actions.
+ *
+ * <p>Note that multiple "actions" on the IPA contents may be run in a single blaze action to
+ * avoid excessive zipping/unzipping of IPA contents.
+ */
+ private void registerPostProcessAndSigningActions() throws InterruptedException {
+ Artifact processedIpa = ruleContext.getImplicitOutputArtifact(IPA);
+ Artifact unprocessedIpa = intermediateArtifacts.unprocessedIpa();
+
+ boolean processingNeeded = false;
+ NestedSetBuilder<Artifact> inputs =
+ NestedSetBuilder.<Artifact>stableOrder().add(unprocessedIpa);
+
+ String actionCommandLine =
+ "set -e && "
+ + "t=$(mktemp -d -t signing_intermediate) && "
+ + "trap \"rm -rf ${t}\" EXIT && "
+ // Get an absolute path since we need to cd into the temp directory for zip.
+ + "signed_ipa=${PWD}/"
+ + processedIpa.getShellEscapedExecPathString()
+ + " && "
+ + "/usr/bin/unzip -qq "
+ + unprocessedIpa.getShellEscapedExecPathString()
+ + " -d ${t} && ";
+
+ FilesToRunProvider processor = attributes.ipaPostProcessor();
+ if (processor != null) {
+ processingNeeded = true;
+ actionCommandLine += processor.getExecutable().getShellEscapedExecPathString() + " ${t} && ";
+ }
+
+ AppleConfiguration appleConfiguration = ruleContext.getFragment(AppleConfiguration.class);
+ if (appleConfiguration.getBundlingPlatform() == Platform.IOS_DEVICE) {
+ processingNeeded = true;
+ registerEntitlementsActions();
+ actionCommandLine += signingCommandLine();
+ inputs.add(attributes.provisioningProfile()).add(intermediateArtifacts.entitlements());
+ }
+
+ actionCommandLine += "cd ${t} && /usr/bin/zip -q -r \"${signed_ipa}\" .";
+
+ if (processingNeeded) {
+ SpawnAction.Builder processAction =
+ ObjcRuleClasses.spawnBashOnDarwinActionBuilder(actionCommandLine)
+ .setMnemonic("ObjcProcessIpa")
+ .setProgressMessage("Processing iOS IPA: " + ruleContext.getLabel())
+ .addTransitiveInputs(inputs.build())
+ .addOutput(processedIpa);
+
+ if (processor != null) {
+ processAction.addTool(processor);
+ }
+
+ ruleContext.registerAction(processAction.build(ruleContext));
+ } else {
+ ruleContext.registerAction(
+ new SymlinkAction(
+ ruleContext.getActionOwner(), unprocessedIpa, processedIpa, "Processing IPA"));
+ }
+ }
+
+ private String signingCommandLine() {
+ ImmutableList.Builder<String> dirsToSign = new ImmutableList.Builder<>();
+
+ // Explicitly sign Swift dylibs. Unfortunately --deep option on codesign doesn't do this
+ // automatically.
+ // The order here is important. The innermost code must singed first.
+ String bundleDir = ShellUtils.shellEscape(bundling.getBundleDir());
+ if (objcProvider.is(USES_SWIFT)) {
+ dirsToSign.add(bundleDir + "/Frameworks/*");
+ }
+ dirsToSign.add(bundleDir);
+
+ StringBuilder codesignCommandLineBuilder = new StringBuilder();
+ for (String dir : dirsToSign.build()) {
+ codesignCommandLineBuilder.append(codesignCommand("${t}/" + dir)).append(" && ");
+ }
+ return codesignCommandLineBuilder.toString();
+ }
+
+ private void registerEntitlementsActions() throws InterruptedException {
Artifact teamPrefixFile =
intermediateArtifacts.appendExtensionForEntitlementArtifact(".team_prefix_file");
registerExtractTeamPrefixAction(teamPrefixFile);
@@ -443,13 +515,7 @@ public final class ReleaseBundlingSupport {
".entitlements_with_variables");
registerExtractEntitlementsAction(entitlementsNeedingSubstitution);
}
- Artifact entitlements =
- intermediateArtifacts.appendExtensionForEntitlementArtifact(".entitlements");
- registerEntitlementsVariableSubstitutionAction(
- entitlementsNeedingSubstitution, entitlements, teamPrefixFile);
- Artifact ipaUnsigned = ruleContext.getImplicitOutputArtifact(IPA_UNSIGNED);
- registerSignBundleAction(entitlements, ipaOutput, ipaUnsigned);
- return ipaUnsigned;
+ registerEntitlementsVariableSubstitutionAction(entitlementsNeedingSubstitution, teamPrefixFile);
}
/**
@@ -687,69 +753,31 @@ public final class ReleaseBundlingSupport {
return buildSettings.build();
}
- private ReleaseBundlingSupport registerSignBundleAction(
- Artifact entitlements, Artifact ipaOutput, Artifact ipaUnsigned) {
- // TODO(bazel-team): Support variable substitution
-
- ImmutableList.Builder<String> dirsToSign = new ImmutableList.Builder<>();
-
- // Explicitly sign Swift dylibs. Unfortunately --deep option on codesign doesn't do this
- // automatically.
- // The order here is important. The innermost code must singed first.
- String bundleDir = ShellUtils.shellEscape(bundling.getBundleDir());
- if (objcProvider.is(USES_SWIFT)) {
- dirsToSign.add(bundleDir + "/Frameworks/*");
- }
- dirsToSign.add(bundleDir);
-
- StringBuilder codesignCommandLineBuilder = new StringBuilder();
- for (String dir : dirsToSign.build()) {
- codesignCommandLineBuilder
- .append(codesignCommand(entitlements, "${t}/" + dir))
- .append(" && ");
- }
-
- // TODO(bazel-team): Support nested code signing.
- String shellCommand = "set -e && "
- + "t=$(mktemp -d -t signing_intermediate) && "
- + "trap \"rm -rf ${t}\" EXIT && "
- // Get an absolute path since we need to cd into the temp directory for zip.
- + "signed_ipa=${PWD}/" + ipaOutput.getShellEscapedExecPathString() + " && "
- + "/usr/bin/unzip -qq " + ipaUnsigned.getShellEscapedExecPathString() + " -d ${t} && "
- + codesignCommandLineBuilder.toString()
- // Using zip since we need to preserve permissions
- + "cd ${t} && /usr/bin/zip -q -r \"${signed_ipa}\" .";
- ruleContext.registerAction(
- ObjcRuleClasses.spawnBashOnDarwinActionBuilder(shellCommand)
- .setMnemonic("IosSignBundle")
- .setProgressMessage("Signing iOS bundle: " + ruleContext.getLabel())
- .addInput(ipaUnsigned)
- .addInput(attributes.provisioningProfile())
- .addInput(entitlements)
- .addOutput(ipaOutput)
- .build(ruleContext));
-
- return this;
- }
-
- private void registerBundleMergeActions(Artifact ipaUnsigned,
- NestedSet<Artifact> bundleContentArtifacts, BundleMergeControlBytes controlBytes) {
+ private void registerBundleMergeActions() {
Artifact bundleMergeControlArtifact =
ObjcRuleClasses.artifactByAppendingToBaseName(ruleContext, ".ipa-control");
+ BundleMergeControlBytes controlBytes =
+ new BundleMergeControlBytes(
+ bundling,
+ intermediateArtifacts.unprocessedIpa(),
+ ruleContext.getFragment(AppleConfiguration.class),
+ bundleSupport.targetDeviceFamilies());
+
ruleContext.registerAction(
new BinaryFileWriteAction(
ruleContext.getActionOwner(), bundleMergeControlArtifact, controlBytes,
/*makeExecutable=*/false));
- ruleContext.registerAction(new SpawnAction.Builder()
- .setMnemonic("IosBundle")
- .setProgressMessage("Bundling iOS application: " + ruleContext.getLabel())
- .setExecutable(attributes.bundleMergeExecutable())
- .addInputArgument(bundleMergeControlArtifact)
- .addTransitiveInputs(bundleContentArtifacts)
- .addOutput(ipaUnsigned)
- .build(ruleContext));
+ ruleContext.registerAction(
+ new SpawnAction.Builder()
+ .setMnemonic("IosBundle")
+ .setProgressMessage("Bundling iOS application: " + ruleContext.getLabel())
+ .setExecutable(attributes.bundleMergeExecutable())
+ .addInputArgument(bundleMergeControlArtifact)
+ .addTransitiveInputs(bundling.getBundleContentArtifacts())
+ .addOutput(intermediateArtifacts.unprocessedIpa())
+ .build(ruleContext));
}
/**
@@ -841,28 +869,37 @@ public final class ReleaseBundlingSupport {
return this;
}
- private void registerEntitlementsVariableSubstitutionAction(Artifact in, Artifact out,
- Artifact prefix) {
+ private void registerEntitlementsVariableSubstitutionAction(
+ Artifact inputEntitlements, Artifact prefix) {
+ Artifact substitutedEntitlements = intermediateArtifacts.entitlements();
String escapedBundleId = ShellUtils.shellEscape(attributes.bundleId());
- String shellCommand = "set -e && "
- + "PREFIX=\"$(cat " + prefix.getShellEscapedExecPathString() + ")\" && "
- + "sed "
- // Replace .* from default entitlements file with bundle ID where suitable.
- + "-e \"s#${PREFIX}\\.\\*#${PREFIX}." + escapedBundleId + "#g\" "
-
- // Replace some variables that people put in their own entitlements files
- + "-e \"s#\\$(AppIdentifierPrefix)#${PREFIX}.#g\" "
- + "-e \"s#\\$(CFBundleIdentifier)#" + escapedBundleId + "#g\" "
-
- + in.getShellEscapedExecPathString() + " "
- + "> " + out.getShellEscapedExecPathString();
- ruleContext.registerAction(new SpawnAction.Builder()
- .setMnemonic("SubstituteIosEntitlements")
- .setShellCommand(shellCommand)
- .addInput(in)
- .addInput(prefix)
- .addOutput(out)
- .build(ruleContext));
+ String shellCommand =
+ "set -e && "
+ + "PREFIX=\"$(cat "
+ + prefix.getShellEscapedExecPathString()
+ + ")\" && "
+ + "sed "
+ // Replace .* from default entitlements file with bundle ID where suitable.
+ + "-e \"s#${PREFIX}\\.\\*#${PREFIX}."
+ + escapedBundleId
+ + "#g\" "
+
+ // Replace some variables that people put in their own entitlements files
+ + "-e \"s#\\$(AppIdentifierPrefix)#${PREFIX}.#g\" "
+ + "-e \"s#\\$(CFBundleIdentifier)#"
+ + escapedBundleId
+ + "#g\" "
+ + inputEntitlements.getShellEscapedExecPathString()
+ + " > "
+ + substitutedEntitlements.getShellEscapedExecPathString();
+ ruleContext.registerAction(
+ new SpawnAction.Builder()
+ .setMnemonic("SubstituteIosEntitlements")
+ .setShellCommand(shellCommand)
+ .addInput(inputEntitlements)
+ .addInput(prefix)
+ .addOutput(substitutedEntitlements)
+ .build(ruleContext));
}
/** Registers an action to copy Swift standard library dylibs into app bundle. */
@@ -892,8 +929,9 @@ public final class ReleaseBundlingSupport {
return "security cms -D -i " + ShellUtils.shellEscape(provisioningProfile.getExecPathString());
}
- private String codesignCommand(Artifact entitlements, String appDir) {
+ private String codesignCommand(String appDir) {
String signingCertName = ObjcRuleClasses.objcConfiguration(ruleContext).getSigningCertName();
+ Artifact entitlements = intermediateArtifacts.entitlements();
final String identity;
if (signingCertName != null) {
@@ -975,6 +1013,17 @@ public final class ReleaseBundlingSupport {
return ruleContext.getPrerequisiteArtifact(":default_provisioning_profile", Mode.TARGET);
}
+ /**
+ * Returns this target's user-specified {@code ipa_post_processor} or null if not present.
+ */
+ @Nullable
+ FilesToRunProvider ipaPostProcessor() {
+ if (!ruleContext.attributes().has("ipa_post_processor", BuildType.LABEL)) {
+ return null;
+ }
+ return ruleContext.getExecutablePrerequisite("ipa_post_processor", Mode.TARGET);
+ }
+
@Nullable
Artifact entitlements() {
return ruleContext.getPrerequisiteArtifact("entitlements", Mode.TARGET);