diff options
author | 2016-02-23 15:34:26 +0000 | |
---|---|---|
committer | 2016-02-23 22:17:11 +0000 | |
commit | 08bb682666990d562776d23f50cc09f851444217 (patch) | |
tree | 6151911eed7e7fda7947d74814ff38239f1df477 /src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingSupport.java | |
parent | a08a6e22d67d94e4d4d25fbf6129879d49e1646c (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.java | 267 |
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); |