RELNOTES: Invoking blaze run on an objc_binary target launches iossim with the resulting app. This adds very basic run support for objc_binary targets. It simply launches them in iossim with the default device and SDK. -- MOS_MIGRATED_REVID=87286604
diff --git a/src/main/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionAction.java b/src/main/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionAction.java index b2c83fb..0a80682 100644 --- a/src/main/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionAction.java +++ b/src/main/java/com/google/devtools/build/lib/analysis/actions/TemplateExpansionAction.java
@@ -18,6 +18,7 @@ import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Joiner; +import com.google.common.base.Objects; import com.google.common.collect.ImmutableList; import com.google.common.collect.Iterables; import com.google.devtools.build.lib.actions.ActionOwner; @@ -92,6 +93,29 @@ } }; } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + if (object instanceof Substitution) { + Substitution substitution = (Substitution) object; + return substitution.getKey().equals(this.getKey()) + && substitution.getValue().equals(this.getValue()); + } + return false; + } + + @Override + public int hashCode() { + return Objects.hashCode(getKey(), getValue()); + } + + @Override + public String toString() { + return "Substitution(" + getKey() + " -> " + getValue() + ")"; + } } /**
diff --git a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java index 8797a2e..b679327 100644 --- a/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java +++ b/src/main/java/com/google/devtools/build/lib/bazel/rules/BazelRuleClassProvider.java
@@ -252,6 +252,7 @@ builder.addRuleDefinition(ObjcRuleClasses.IosTestBaseRule.class); builder.addRuleDefinition(ObjcRuleClasses.BundlingRule.class); builder.addRuleDefinition(ObjcRuleClasses.ReleaseBundlingRule.class); + builder.addRuleDefinition(ObjcRuleClasses.SimulatorRule.class); builder.addRuleDefinition(ObjcRuleClasses.CompilingRule.class); builder.addRuleDefinition(ObjcRuleClasses.LinkingRule.class); builder.addRuleDefinition(ObjcRuleClasses.ResourcesRule.class);
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java b/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java index 5542a11..4e166ea 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/BinaryLinkingTargetFactory.java
@@ -23,7 +23,9 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.RunfilesSupport; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; import com.google.devtools.build.lib.rules.objc.ObjcActionsBuilder.ExtraLinkArgs; @@ -31,6 +33,7 @@ import com.google.devtools.build.lib.rules.objc.ObjcCommon.CompilationAttributes; import com.google.devtools.build.lib.rules.objc.ObjcCommon.ResourceAttributes; import com.google.devtools.build.lib.rules.objc.ReleaseBundlingSupport.LinkedBinary; +import com.google.devtools.build.xcode.common.Platform; /** * Implementation for rules that link binaries. @@ -70,9 +73,12 @@ return null; } + IntermediateArtifacts intermediateArtifacts = + ObjcRuleClasses.intermediateArtifacts(ruleContext); + XcodeProvider.Builder xcodeProviderBuilder = new XcodeProvider.Builder(); NestedSetBuilder<Artifact> filesToBuild = NestedSetBuilder.<Artifact>stableOrder() - .add(ObjcRuleClasses.intermediateArtifacts(ruleContext).singleArchitectureBinary()); + .add(intermediateArtifacts.singleArchitectureBinary()); new CompilationSupport(ruleContext) .registerJ2ObjcCompileAndArchiveActions(optionsProvider, objcProvider) @@ -82,6 +88,7 @@ .validateAttributes(); Optional<XcTestAppProvider> xcTestAppProvider; + Optional<RunfilesSupport> maybeRunfilesSupport = Optional.<RunfilesSupport>absent(); switch (hasReleaseBundlingSupport) { case YES: // TODO(bazel-team): Remove once all bundle users are migrated to ios_application. @@ -94,6 +101,12 @@ .addFilesToBuild(filesToBuild) .validateAttributes(); xcTestAppProvider = Optional.of(releaseBundlingSupport.xcTestAppProvider()); + if (ObjcRuleClasses.objcConfiguration(ruleContext).getPlatform() == Platform.SIMULATOR) { + Artifact runnerScript = intermediateArtifacts.runnerScript(); + Artifact ipaFile = ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA); + releaseBundlingSupport.registerGenerateRunnerScriptAction(runnerScript, ipaFile); + maybeRunfilesSupport = Optional.of(releaseBundlingSupport.runfilesSupport(runnerScript)); + } break; case NO: xcTestAppProvider = Optional.absent(); @@ -120,12 +133,16 @@ // TODO(bazel-team): Stop exporting an XcTestAppProvider once objc_binary no longer creates an // application bundle. - return common.configuredTarget( + RuleConfiguredTargetBuilder target = common.configuredTargetBuilder( filesToBuild.build(), Optional.of(xcodeProvider), Optional.of(objcProvider), xcTestAppProvider, Optional.<J2ObjcSrcsProvider>absent()); + for (RunfilesSupport runfilesSupport : maybeRunfilesSupport.asSet()) { + target.setRunfilesSupport(runfilesSupport, runfilesSupport.getExecutable()); + } + return target.build(); } private OptionsProvider optionsProvider(RuleContext ruleContext) {
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java index 2961d2a..f2e7bdc 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IntermediateArtifacts.java
@@ -223,4 +223,11 @@ public Artifact breakpadSym() { return appendExtension(".breakpad"); } + + /** + * Shell script that launches the binary. + */ + public Artifact runnerScript() { + return appendExtension("_runner.sh"); + } }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java index d931dae..d35bfa0 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplication.java
@@ -15,8 +15,11 @@ package com.google.devtools.build.lib.rules.objc; import com.google.common.base.Optional; +import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.xcode.common.Platform; /** * Implementation for {@code ios_application}. @@ -37,4 +40,16 @@ ruleContext.getPrerequisite("options", Mode.TARGET, OptionsProvider.class))) .build(); } + + @Override + protected void configureTarget(RuleConfiguredTargetBuilder target, RuleContext ruleContext, + ReleaseBundlingSupport releaseBundlingSupport) { + // If this is an application built for the simulator, make it runnable. + if (ObjcRuleClasses.objcConfiguration(ruleContext).getPlatform() == Platform.SIMULATOR) { + Artifact runnerScript = ObjcRuleClasses.intermediateArtifacts(ruleContext).runnerScript(); + Artifact ipaFile = ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA); + releaseBundlingSupport.registerGenerateRunnerScriptAction(runnerScript, ipaFile); + target.setRunfilesSupport(releaseBundlingSupport.runfilesSupport(runnerScript), runnerScript); + } + } }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java index dd54b68..b4c8605 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/IosApplicationRule.java
@@ -14,7 +14,9 @@ package com.google.devtools.build.lib.rules.objc; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.Type.BOOLEAN; import static com.google.devtools.build.lib.packages.Type.LABEL; import com.google.devtools.build.lib.analysis.BaseRuleClasses; @@ -33,7 +35,8 @@ ancestors = { BaseRuleClasses.BaseRule.class, ObjcRuleClasses.ReleaseBundlingRule.class, - ObjcRuleClasses.XcodegenRule.class, }) + ObjcRuleClasses.XcodegenRule.class, + ObjcRuleClasses.SimulatorRule.class }) public class IosApplicationRule implements RuleDefinition { @Override @@ -58,6 +61,10 @@ .allowedFileTypes() .mandatory() .direct_compile_time_input()) + .add(attr("$runner_script_template", LABEL).cfg(HOST) + .value(env.getLabel("//tools/objc:ios_runner.sh.mac_template"))) + .add(attr("$is_executable", BOOLEAN).value(true) + .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")) .build(); } } @@ -72,4 +79,4 @@ ${ATTRIBUTE_DEFINITION} -<!-- #END_BLAZE_RULE -->*/ \ No newline at end of file +<!-- #END_BLAZE_RULE -->*/
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java index 41ac421..fb887c5 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcBinaryRule.java
@@ -14,6 +14,11 @@ package com.google.devtools.build.lib.rules.objc; +import static com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition.HOST; +import static com.google.devtools.build.lib.packages.Attribute.attr; +import static com.google.devtools.build.lib.packages.Type.BOOLEAN; +import static com.google.devtools.build.lib.packages.Type.LABEL; + import com.google.devtools.build.lib.analysis.BaseRuleClasses; import com.google.devtools.build.lib.analysis.BlazeRule; import com.google.devtools.build.lib.analysis.RuleDefinition; @@ -32,7 +37,8 @@ BaseRuleClasses.BaseRule.class, ObjcRuleClasses.LinkingRule.class, ObjcRuleClasses.XcodegenRule.class, - ObjcRuleClasses.ReleaseBundlingRule.class }) + ObjcRuleClasses.ReleaseBundlingRule.class, + ObjcRuleClasses.SimulatorRule.class }) public class ObjcBinaryRule implements RuleDefinition { @Override @@ -48,6 +54,11 @@ <!-- #END_BLAZE_RULE.IMPLICIT_OUTPUTS -->*/ .setImplicitOutputsFunction( ImplicitOutputsFunction.fromFunctions(ReleaseBundlingSupport.IPA, XcodeSupport.PBXPROJ)) + // TODO(bazel-team): Remove these when this rule no longer produces a bundle. + .add(attr("$runner_script_template", LABEL).cfg(HOST) + .value(env.getLabel("//tools/objc:ios_runner.sh.mac_template"))) + .add(attr("$is_executable", BOOLEAN).value(true) + .nonconfigurable("Called from RunCommand.isExecutable, which takes a Target")) .build(); } }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java index 95d546a..89d8618 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcCommon.java
@@ -584,6 +584,8 @@ } /** + * Returns a {@link RuleConfiguredTargetBuilder}. + * * @param filesToBuild files to build for this target. These also become the data runfiles. Note * that this method may add more files to create the complete list of files to build for this * target. @@ -592,7 +594,7 @@ * present whenever {@code objc_} rules may depend on this target. * @param maybeJ2ObjcSrcsProvider the {@link J2ObjcSrcsProvider} for this target. */ - public ConfiguredTarget configuredTarget(NestedSet<Artifact> filesToBuild, + public RuleConfiguredTargetBuilder configuredTargetBuilder(NestedSet<Artifact> filesToBuild, Optional<XcodeProvider> maybeTargetProvider, Optional<ObjcProvider> maybeExportedProvider, Optional<XcTestAppProvider> maybeXcTestAppProvider, Optional<J2ObjcSrcsProvider> maybeJ2ObjcSrcsProvider) { @@ -623,6 +625,25 @@ for (J2ObjcSrcsProvider j2ObjcSrcsProvider : maybeJ2ObjcSrcsProvider.asSet()) { target.addProvider(J2ObjcSrcsProvider.class, j2ObjcSrcsProvider); } - return target.build(); + return target; + } + + /** + * Creates a {@link ConfiguredTarget}. + * + * @param filesToBuild files to build for this target. These also become the data runfiles. Note + * that this method may add more files to create the complete list of files to build for this + * target. + * @param maybeTargetProvider the provider for this target. + * @param maybeExportedProvider the {@link ObjcProvider} for this target. This should generally be + * present whenever {@code objc_} rules may depend on this target. + * @param maybeJ2ObjcSrcsProvider the {@link J2ObjcSrcsProvider} for this target. + */ + public ConfiguredTarget configuredTarget(NestedSet<Artifact> filesToBuild, + Optional<XcodeProvider> maybeTargetProvider, Optional<ObjcProvider> maybeExportedProvider, + Optional<XcTestAppProvider> maybeXcTestAppProvider, + Optional<J2ObjcSrcsProvider> maybeJ2ObjcSrcsProvider) { + return configuredTargetBuilder(filesToBuild, maybeTargetProvider, maybeExportedProvider, + maybeXcTestAppProvider, maybeJ2ObjcSrcsProvider).build(); } }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java index d8c9f75..cce0b8b 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ObjcRuleClasses.java
@@ -650,7 +650,8 @@ ancestors = { ReleaseBundlingRule.class, LinkingRule.class, - XcodegenRule.class, }) + XcodegenRule.class, + SimulatorRule.class }) public static class IosTestBaseRule implements RuleDefinition { @Override public RuleClass build(Builder builder, final RuleDefinitionEnvironment env) { @@ -823,6 +824,22 @@ } /** + * Common attributes for {@code objc_*} rules that use the iOS simulator. + */ + @BlazeRule(name = "$objc_simulator_rule", + type = RuleClassType.ABSTRACT) + public static class SimulatorRule implements RuleDefinition { + @Override + public RuleClass build(Builder builder, RuleDefinitionEnvironment env) { + return builder + // Needed to run the binary in the simulator. + .add(attr("$iossim", LABEL).cfg(HOST).exec() + .value(env.getLabel("//third_party/iossim:iossim"))) + .build(); + } + } + + /** * Object that supplies tools used by all rules which have the helper tools common to most rule * implementations. */
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 aa39dc4..e00a98d 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
@@ -26,9 +26,13 @@ import com.google.devtools.build.lib.analysis.FilesToRunProvider; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.Runfiles; +import com.google.devtools.build.lib.analysis.RunfilesSupport; 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.SpawnAction; +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.collect.nestedset.NestedSet; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.collect.nestedset.Order; @@ -287,6 +291,35 @@ ruleContext.getImplicitOutputArtifact(IPA), partialObjcProvider); } + /** + * Registers an action to generate a runner script based on a template. + */ + ReleaseBundlingSupport registerGenerateRunnerScriptAction(Artifact runnerScript, + Artifact ipaInput) { + ImmutableList<Substitution> substitutions = ImmutableList.of( + Substitution.of("%app_name%", ruleContext.getLabel().getName()), + Substitution.of("%ipa_file%", ipaInput.getRootRelativePath().getPathString()), + Substitution.of("%iossim%", attributes.iossim().getRootRelativePath().getPathString())); + + ruleContext.registerAction( + new TemplateExpansionAction(ruleContext.getActionOwner(), attributes.runnerScriptTemplate(), + runnerScript, substitutions, true)); + return this; + } + + /** + * Returns a {@link RunfilesSupport} that uses the provided runner script as the executable. + */ + RunfilesSupport runfilesSupport(Artifact runnerScript) { + Artifact ipaFile = ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA); + Runfiles runfiles = new Runfiles.Builder() + .addArtifact(ipaFile) + .addArtifact(runnerScript) + .addArtifact(attributes.iossim()) + .build(); + return RunfilesSupport.withExecutable(ruleContext, runfiles, runnerScript); + } + private ExtraActoolArgs extraActoolArgs() { ImmutableList.Builder<String> extraArgs = ImmutableList.builder(); if (attributes.appIcon() != null) { @@ -590,6 +623,15 @@ return checkNotNull(ruleContext.getExecutablePrerequisite("$bundlemerge", Mode.HOST)); } + Artifact iossim() { + return checkNotNull(ruleContext.getPrerequisiteArtifact("$iossim", Mode.HOST)); + } + + Artifact runnerScriptTemplate() { + return checkNotNull( + ruleContext.getPrerequisiteArtifact("$runner_script_template", Mode.HOST)); + } + String bundleId() { return checkNotNull(stringAttribute("bundle_id")); }
diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java index d02093e..4bae44a 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ReleaseBundlingTargetFactory.java
@@ -20,6 +20,7 @@ import com.google.devtools.build.lib.actions.Artifact; import com.google.devtools.build.lib.analysis.ConfiguredTarget; import com.google.devtools.build.lib.analysis.RuleConfiguredTarget.Mode; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; import com.google.devtools.build.lib.analysis.RuleContext; import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory; @@ -88,12 +89,14 @@ exposedObjcProvider = Optional.absent(); } - return common.configuredTarget( + RuleConfiguredTargetBuilder target = common.configuredTargetBuilder( filesToBuild.build(), Optional.of(xcodeProviderBuilder.build()), exposedObjcProvider, Optional.<XcTestAppProvider>absent(), Optional.<J2ObjcSrcsProvider>absent()); + configureTarget(target, ruleContext, releaseBundlingSupport); + return target.build(); } /** @@ -101,6 +104,13 @@ */ protected abstract OptionsProvider optionsProvider(RuleContext ruleContext); + /** + * Performs additional configuration of the target. The default implementation does nothing, but + * subclasses may override it to add logic. + */ + protected void configureTarget(RuleConfiguredTargetBuilder target, RuleContext ruleContext, + ReleaseBundlingSupport releaseBundlingSupport) {} + private ObjcCommon common(RuleContext ruleContext) { return new ObjcCommon.Builder(ruleContext) .setIntermediateArtifacts(ObjcRuleClasses.intermediateArtifacts(ruleContext))