diff options
author | 2015-04-28 18:14:25 +0000 | |
---|---|---|
committer | 2015-04-28 21:14:09 +0000 | |
commit | e6f78f0ef2d908a49ff33ce0d0d3c9b874d19dcc (patch) | |
tree | 3e1e7a9c5b1125dc6330943249ba1f15b956f10a | |
parent | 1f0f444d012f5326fffdce6c87df7a4c4ad540ed (diff) |
Very basic XcTest support in Bazel
--
MOS_MIGRATED_REVID=92267704
4 files changed, 281 insertions, 0 deletions
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 feb1402a63..75f1428145 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 @@ -76,6 +76,7 @@ import com.google.devtools.build.lib.rules.java.JavaOptions; import com.google.devtools.build.lib.rules.java.JavaToolchainRule; import com.google.devtools.build.lib.rules.java.Jvm; import com.google.devtools.build.lib.rules.java.JvmConfigurationLoader; +import com.google.devtools.build.lib.rules.objc.ExperimentalIosTestRule; import com.google.devtools.build.lib.rules.objc.IosApplicationRule; import com.google.devtools.build.lib.rules.objc.IosDeviceRule; import com.google.devtools.build.lib.rules.objc.IosExtensionBinaryRule; @@ -257,6 +258,7 @@ public class BazelRuleClassProvider { builder.addRuleDefinition(new JavaToolchainRule()); builder.addRuleDefinition(new BazelIosTestRule()); + builder.addRuleDefinition(new ExperimentalIosTestRule()); builder.addRuleDefinition(new IosDeviceRule()); builder.addRuleDefinition(new ObjcBinaryRule()); builder.addRuleDefinition(new ObjcBundleRule()); diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java new file mode 100644 index 0000000000..39f232bb2b --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTest.java @@ -0,0 +1,61 @@ +// Copyright 2014 Google Inc. 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.objc; + +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.ConfiguredTarget; +import com.google.devtools.build.lib.analysis.RuleConfiguredTargetBuilder; +import com.google.devtools.build.lib.analysis.RuleContext; +import com.google.devtools.build.lib.analysis.Runfiles; +import com.google.devtools.build.lib.analysis.RunfilesProvider; +import com.google.devtools.build.lib.analysis.RunfilesSupport; +import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; + +/** + * Implementation for {$code experimental_ios_test} rule in Bazel. + * + * <p>Note that this will be renamed to ${code ios_test}, and {@link + * com.google.devtools.build.lib.bazel.rules.objc.BazelIosTest} will be removed when it is slightly + * more feature complete. + */ +public final class ExperimentalIosTest extends IosTest { + @Override + public ConfiguredTarget create(RuleContext ruleContext, ObjcCommon common, + XcodeProvider xcodeProvider, NestedSet<Artifact> filesToBuild) throws InterruptedException { + + Runfiles.Builder runfilesBuilder = new Runfiles.Builder(); + NestedSetBuilder<Artifact> filesToBuildBuilder = NestedSetBuilder.<Artifact>stableOrder(); + filesToBuildBuilder.addTransitive(filesToBuild); + + TestSupport testSupport = new TestSupport(ruleContext) + .registerTestRunnerActionsForSimulator() + .addRunfiles(runfilesBuilder) + .addFilesToBuild(filesToBuildBuilder); + + Artifact executable = testSupport.generatedTestScript(); + + Runfiles runfiles = runfilesBuilder.build(); + RunfilesSupport runfilesSupport = + RunfilesSupport.withExecutable(ruleContext, runfiles, executable); + + return new RuleConfiguredTargetBuilder(ruleContext) + .setFilesToBuild(filesToBuildBuilder.build()) + .add(XcodeProvider.class, xcodeProvider) + .add(RunfilesProvider.class, RunfilesProvider.simple(runfiles)) + .setRunfilesSupport(runfilesSupport, executable) + .build(); + } +} diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java new file mode 100644 index 0000000000..f26b8468aa --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/ExperimentalIosTestRule.java @@ -0,0 +1,75 @@ +// Copyright 2014 Google Inc. 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.objc; + +import static com.google.devtools.build.lib.packages.Attribute.attr; +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.RuleDefinition; +import com.google.devtools.build.lib.analysis.RuleDefinitionEnvironment; +import com.google.devtools.build.lib.packages.ImplicitOutputsFunction; +import com.google.devtools.build.lib.packages.RuleClass; +import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType; + +/** + * Rule definition for {@code experimental_ios_test} rule in Bazel. + * + * <p>Note that this will be renamed to {@code ios_test}, and {@link + * com.google.devtools.build.lib.bazel.rules.objc.BazelIosTestRule} will be removed when it is + * slightly more feature complete. + */ +public final class ExperimentalIosTestRule implements RuleDefinition { + @Override + public RuleClass build(RuleClass.Builder builder, final RuleDefinitionEnvironment env) { + return builder + /*<!-- #BLAZE_RULE(experimental_ios_test).IMPLICIT_OUTPUTS --> + <ul> + <li><code><var>name</var>.ipa</code>: the test bundle as an + <code>.ipa</code> file + <li><code><var>name</var>.xcodeproj/project.pbxproj: An Xcode project file which can be + used to develop or build on a Mac.</li> + </ul> + <!-- #END_BLAZE_RULE.IMPLICIT_OUTPUTS -->*/ + .setImplicitOutputsFunction( + ImplicitOutputsFunction.fromFunctions(ReleaseBundlingSupport.IPA, XcodeSupport.PBXPROJ)) + .add(attr("$test_template", LABEL) + .value(env.getLabel("//tools/objc:ios_test.sh.bazel_template"))) + .build(); + } + + @Override + public Metadata getMetadata() { + return RuleDefinition.Metadata.builder() + .name("experimental_ios_test") + .type(RuleClassType.TEST) + .ancestors(BaseRuleClasses.BaseRule.class, BaseRuleClasses.TestBaseRule.class, + ObjcRuleClasses.IosTestBaseRule.class, ObjcRuleClasses.SimulatorRule.class) + .factoryClass(ExperimentalIosTest.class) + .build(); + } +} + +/*<!-- #BLAZE_RULE (NAME = experimental_ios_test, TYPE = TEST, FAMILY = Objective-C) --> + +${ATTRIBUTE_SIGNATURE} + +<p>This rule provides a way to build iOS unit tests written in KIF, GTM and XCTest test frameworks +on both iOS simulator and real devices. +</p> + +${ATTRIBUTE_DEFINITION} + +<!-- #END_BLAZE_RULE -->*/ diff --git a/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java b/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java new file mode 100644 index 0000000000..70c047f8b7 --- /dev/null +++ b/src/main/java/com/google/devtools/build/lib/rules/objc/TestSupport.java @@ -0,0 +1,143 @@ +// Copyright 2015 Google Inc. 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.objc; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.common.collect.Iterables; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.analysis.FileProvider; +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.actions.TemplateExpansionAction; +import com.google.devtools.build.lib.analysis.actions.TemplateExpansionAction.Substitution; +import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder; +import com.google.devtools.build.lib.util.FileType; + +import java.util.List; + +/** + * Support for running XcTests. + */ +class TestSupport { + private final RuleContext ruleContext; + + TestSupport(RuleContext ruleContext) { + this.ruleContext = ruleContext; + } + + /** + * Registers actions to create all files needed in order to actually run the test. + */ + TestSupport registerTestRunnerActionsForSimulator() { + registerTestScriptSubstitutionAction(); + return this; + } + + /** + * Returns the script which should be run in order to actually run the tests. + */ + Artifact generatedTestScript() { + return ObjcRuleClasses.artifactByAppendingToBaseName(ruleContext, "_test_script"); + } + + private void registerTestScriptSubstitutionAction() { + // testIpa is the app actually containing the XcTests + Artifact testIpa = testIpa(); + // xctestIpa is the app bundle being tested + Artifact xctestIpa = xctestIpa(); + + IosDeviceProvider targetDevice = targetDevice(); + + List<Substitution> substitutions = ImmutableList.of( + Substitution.of("%(test_app_ipa)s", testIpa.getRootRelativePathString()), + Substitution.of("%(test_app_name)s", baseNameWithoutIpa(testIpa)), + + Substitution.of("%(xctest_app_ipa)s", xctestIpa.getRootRelativePathString()), + Substitution.of("%(xctest_app_name)s", baseNameWithoutIpa(xctestIpa)), + + Substitution.of("%(iossim_path)s", iossim().getRootRelativePath().getPathString()), + Substitution.of("%(device_type)s", targetDevice.getType()), + Substitution.of("%(simulator_sdk)s", targetDevice.getIosVersion()) + ); + + Artifact template = ruleContext.getPrerequisiteArtifact("$test_template", Mode.TARGET); + + ruleContext.registerAction(new TemplateExpansionAction(ruleContext.getActionOwner(), + template, generatedTestScript(), substitutions, /*executable=*/true)); + } + + private IosDeviceProvider targetDevice() { + IosDeviceProvider targetDevice = + ruleContext.getPrerequisite("target_device", Mode.TARGET, IosDeviceProvider.class); + if (targetDevice == null) { + ObjcConfiguration objcConfiguration = ObjcRuleClasses.objcConfiguration(ruleContext); + targetDevice = new IosDeviceProvider.Builder() + // iPhone 6 should be the default, but 32-bit (i386) simulators don't support the + // iPhone 6. + .setType(objcConfiguration.getIosCpu().equals("x86_64") ? "iPhone 6" : "iPhone 5") + .setIosVersion(objcConfiguration.getIosSimulatorVersion()) + .setLocale("en") + .build(); + } + return targetDevice; + } + + private Artifact testIpa() { + return ruleContext.getImplicitOutputArtifact(ReleaseBundlingSupport.IPA); + } + + private Artifact xctestIpa() { + FileProvider fileProvider = + ruleContext.getPrerequisite("xctest_app", Mode.TARGET, FileProvider.class); + return Iterables.getOnlyElement( + Artifact.filterFiles(fileProvider.getFilesToBuild(), FileType.of(".ipa"))); + } + + private Artifact iossim() { + return ruleContext.getPrerequisiteArtifact("$iossim", Mode.HOST); + } + + /** + * Adds all files needed to run this test to the passed Runfiles builder. + */ + TestSupport addRunfiles(Runfiles.Builder runfilesBuilder) { + runfilesBuilder + .addArtifact(testIpa()) + .addArtifact(xctestIpa()) + .addArtifact(generatedTestScript()) + .addArtifact(iossim()); + return this; + } + + /** + * Adds files which must be built in order to run this test to builder. + */ + TestSupport addFilesToBuild(NestedSetBuilder<Artifact> builder) { + builder.add(testIpa()).add(xctestIpa()); + return this; + } + + /** + * Returns the base name of the artifact, with the .ipa stuffix stripped. + */ + private static String baseNameWithoutIpa(Artifact artifact) { + String baseName = artifact.getExecPath().getBaseName(); + Preconditions.checkState(baseName.endsWith(".ipa"), + "%s should end in .ipa but doesn't", baseName); + return baseName.substring(0, baseName.length() - 4); + } +} |