aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorGravatar Googler <noreply@google.com>2017-04-28 20:31:08 +0200
committerGravatar Vladimir Moskva <vladmos@google.com>2017-04-30 23:11:49 +0200
commit3eeb660ef4a7d175669995ba8139218b052b3b51 (patch)
tree881d6de4fd31a69f35e561073ce60da8a953de03 /src
parent8488c7f7545a054ef78daa9ca664b6580adda729 (diff)
Introduce a base class for AndroidLocalTest, AndroidLocalTestBase. This is a first step for future Bazel android_local_test rule support.
RELNOTES: None PiperOrigin-RevId: 154562617
Diffstat (limited to 'src')
-rw-r--r--src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java447
1 files changed, 447 insertions, 0 deletions
diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
new file mode 100644
index 0000000000..1ef476f87f
--- /dev/null
+++ b/src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java
@@ -0,0 +1,447 @@
+// Copyright 2017 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;
+package com.google.devtools.build.lib.rules.android;
+
+import static com.google.devtools.build.lib.packages.BuildType.LABEL_LIST;
+import static com.google.devtools.build.lib.rules.java.DeployArchiveBuilder.Compression.COMPRESSED;
+
+import com.google.common.base.Joiner;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Sets;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.analysis.ConfiguredTarget;
+import com.google.devtools.build.lib.analysis.FileProvider;
+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.Runfiles;
+import com.google.devtools.build.lib.analysis.RunfilesProvider;
+import com.google.devtools.build.lib.analysis.RunfilesSupport;
+import com.google.devtools.build.lib.analysis.TransitiveInfoCollection;
+import com.google.devtools.build.lib.collect.nestedset.NestedSet;
+import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
+import com.google.devtools.build.lib.rules.RuleConfiguredTargetFactory;
+import com.google.devtools.build.lib.rules.android.AndroidLibraryAarProvider.Aar;
+import com.google.devtools.build.lib.rules.java.ClasspathConfiguredFragment;
+import com.google.devtools.build.lib.rules.java.DeployArchiveBuilder;
+import com.google.devtools.build.lib.rules.java.JavaCommon;
+import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider;
+import com.google.devtools.build.lib.rules.java.JavaCompilationArtifacts;
+import com.google.devtools.build.lib.rules.java.JavaCompilationHelper;
+import com.google.devtools.build.lib.rules.java.JavaHelper;
+import com.google.devtools.build.lib.rules.java.JavaPrimaryClassProvider;
+import com.google.devtools.build.lib.rules.java.JavaRuleOutputJarsProvider;
+import com.google.devtools.build.lib.rules.java.JavaRunfilesProvider;
+import com.google.devtools.build.lib.rules.java.JavaRuntimeClasspathProvider;
+import com.google.devtools.build.lib.rules.java.JavaSemantics;
+import com.google.devtools.build.lib.rules.java.JavaSkylarkApiProvider;
+import com.google.devtools.build.lib.rules.java.JavaSourceInfoProvider;
+import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider;
+import com.google.devtools.build.lib.rules.java.JavaTargetAttributes;
+import com.google.devtools.build.lib.rules.java.SingleJarActionBuilder;
+import com.google.devtools.build.lib.rules.java.proto.GeneratedExtensionRegistryProvider;
+import com.google.devtools.build.lib.syntax.Type;
+import com.google.devtools.build.lib.util.Preconditions;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * An base implementation for the "android_local_test" rule.
+ */
+public abstract class AndroidLocalTestBase implements RuleConfiguredTargetFactory {
+
+ @Override
+ public ConfiguredTarget create(RuleContext ruleContext)
+ throws InterruptedException, RuleErrorException {
+
+ ruleContext.checkSrcsSamePackage(true);
+
+ JavaSemantics javaSemantics = createJavaSemantics();
+
+ final JavaCommon javaCommon = new JavaCommon(ruleContext, javaSemantics);
+ // Use the regular Java javacopts. Enforcing android-compatible Java
+ // (-source 7 -target 7 and no TWR) is unnecessary for robolectric tests
+ // since they run on a JVM, not an android device.
+ JavaTargetAttributes.Builder attributesBuilder = javaCommon.initCommon();
+
+ String testClass =
+ getAndCheckTestClass(ruleContext, ImmutableList.copyOf(attributesBuilder.getSourceFiles()));
+ getAndCheckTestSupport(ruleContext);
+ javaSemantics.checkForProtoLibraryAndJavaProtoLibraryOnSameProto(ruleContext, javaCommon);
+ if (ruleContext.hasErrors()) {
+ return null;
+ }
+
+ Artifact srcJar = ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_SOURCE_JAR);
+ JavaSourceJarsProvider.Builder javaSourceJarsProviderBuilder =
+ JavaSourceJarsProvider.builder()
+ .addSourceJar(srcJar)
+ .addAllTransitiveSourceJars(javaCommon.collectTransitiveSourceJars(srcJar));
+
+ Artifact classJar = ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_CLASS_JAR);
+ JavaRuleOutputJarsProvider.Builder javaRuleOutputJarsProviderBuilder =
+ JavaRuleOutputJarsProvider.builder()
+ .addOutputJar(
+ classJar,
+ classJar,
+ srcJar == null ? ImmutableList.<Artifact>of() : ImmutableList.of(srcJar));
+
+ JavaCompilationArtifacts.Builder javaArtifactsBuilder = new JavaCompilationArtifacts.Builder();
+ JavaCompilationHelper helper =
+ getJavaCompilationHelperWithDependencies(ruleContext, javaSemantics, javaCommon,
+ attributesBuilder);
+ Artifact instrumentationMetadata =
+ helper.createInstrumentationMetadata(classJar, javaArtifactsBuilder);
+ Artifact executable = ruleContext.createOutputArtifact(); // the artifact for the rule itself
+ NestedSetBuilder<Artifact> filesToBuildBuilder =
+ NestedSetBuilder.<Artifact>stableOrder().add(classJar).add(executable);
+
+ GeneratedExtensionRegistryProvider generatedExtensionRegistryProvider =
+ javaSemantics.createGeneratedExtensionRegistry(
+ ruleContext,
+ javaCommon,
+ filesToBuildBuilder,
+ javaArtifactsBuilder,
+ javaRuleOutputJarsProviderBuilder,
+ javaSourceJarsProviderBuilder);
+
+ String mainClass =
+ getMainClass(
+ ruleContext,
+ javaSemantics,
+ helper,
+ executable,
+ instrumentationMetadata,
+ javaArtifactsBuilder,
+ attributesBuilder);
+
+ // JavaCompilationHelper.getAttributes() builds the JavaTargetAttributes, after which the
+ // JavaTargetAttributes becomes immutable. This is an extra safety check to avoid inconsistent
+ // states (i.e. building the JavaTargetAttributes then modifying it again).
+ addRuntimeJarsToArtifactsBuilder(javaArtifactsBuilder, helper.getAttributes(), classJar);
+
+ // The gensrc jar is created only if the target uses annotation processing. Otherwise,
+ // it is null, and the source jar action will not depend on the compile action.
+ Artifact manifestProtoOutput = helper.createManifestProtoOutput(classJar);
+
+ Artifact genClassJar = null;
+ Artifact genSourceJar = null;
+ if (helper.usesAnnotationProcessing()) {
+ genClassJar = helper.createGenJar(classJar);
+ genSourceJar = helper.createGensrcJar(classJar);
+ helper.createGenJarAction(classJar, manifestProtoOutput, genClassJar);
+ }
+ Artifact outputDepsProtoArtifact =
+ helper.createOutputDepsProtoArtifact(classJar, javaArtifactsBuilder);
+ javaRuleOutputJarsProviderBuilder.setJdeps(outputDepsProtoArtifact);
+ helper.createCompileAction(
+ classJar,
+ manifestProtoOutput,
+ genSourceJar,
+ outputDepsProtoArtifact,
+ instrumentationMetadata);
+ helper.createSourceJarAction(srcJar, genSourceJar);
+
+ setUpJavaCommon(javaCommon, helper, javaArtifactsBuilder.build());
+
+ Artifact launcher = JavaHelper.launcherArtifactForTarget(javaSemantics, ruleContext);
+
+ javaSemantics.createStubAction(
+ ruleContext,
+ javaCommon,
+ getJvmFlags(ruleContext, testClass),
+ executable,
+ mainClass,
+ JavaCommon.getJavaBinSubstitution(ruleContext, launcher));
+
+ Artifact deployJar =
+ ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_JAR);
+
+ NestedSet<Artifact> filesToBuild = filesToBuildBuilder.build();
+
+ Iterable<AndroidLibraryAarProvider> androidAarProviders =
+ Sets.newLinkedHashSet(
+ Iterables.concat(
+ ruleContext.getPrerequisites(
+ "runtime_deps", Mode.TARGET, AndroidLibraryAarProvider.class),
+ ruleContext.getPrerequisites(
+ "deps", Mode.TARGET, AndroidLibraryAarProvider.class)));
+
+ Runfiles defaultRunfiles =
+ collectDefaultRunfiles(ruleContext, javaCommon, filesToBuild, androidAarProviders);
+
+ ImmutableList<String> cmdLineArgs =
+ ImmutableList.of(
+ "--android_libraries=" + getTransitiveLibrariesArg(androidAarProviders),
+ "--strict_libraries=" + getStrictLibrariesArg(androidAarProviders));
+
+ RunfilesSupport runfilesSupport =
+ RunfilesSupport.withExecutable(ruleContext, defaultRunfiles, executable, cmdLineArgs);
+
+ // Create the deploy jar and make it dependent on the runfiles middleman if an executable is
+ // created. Do not add the deploy jar to files to build, so we will only build it when it gets
+ // requested.
+ new DeployArchiveBuilder(javaSemantics, ruleContext)
+ .setOutputJar(deployJar)
+ .setJavaStartClass(mainClass)
+ .setDeployManifestLines(ImmutableList.<String>of())
+ .setAttributes(helper.getAttributes())
+ .addRuntimeJars(javaCommon.getJavaCompilationArtifacts().getRuntimeJars())
+ .setIncludeBuildData(true)
+ .setRunfilesMiddleman(runfilesSupport.getRunfilesMiddleman())
+ .setCompression(COMPRESSED)
+ .setLauncher(launcher)
+ .build();
+
+ JavaSourceJarsProvider sourceJarsProvider = javaSourceJarsProviderBuilder.build();
+ NestedSet<Artifact> transitiveSourceJars = sourceJarsProvider.getTransitiveSourceJars();
+
+ // TODO(bazel-team): if (getOptions().sourceJars) then make this a dummy prerequisite for the
+ // DeployArchiveAction ? Needs a few changes there as we can't pass inputs
+ SingleJarActionBuilder.createSourceJarAction(
+ ruleContext,
+ ImmutableMap.<PathFragment, Artifact>of(),
+ transitiveSourceJars.toCollection(),
+ ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_SOURCE_JAR));
+
+ RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);
+
+ if (generatedExtensionRegistryProvider != null) {
+ builder.addProvider(
+ GeneratedExtensionRegistryProvider.class,
+ generatedExtensionRegistryProvider);
+ }
+
+ addExtraProviders(builder, javaCommon, classJar, srcJar, genClassJar, genSourceJar);
+
+ JavaRuleOutputJarsProvider ruleOutputJarsProvider = javaRuleOutputJarsProviderBuilder.build();
+ JavaSkylarkApiProvider.Builder skylarkApiProvider =
+ JavaSkylarkApiProvider.builder()
+ .setRuleOutputJarsProvider(ruleOutputJarsProvider)
+ .setSourceJarsProvider(sourceJarsProvider);
+
+ javaCommon.addTransitiveInfoProviders(builder, skylarkApiProvider, filesToBuild, classJar);
+ javaCommon.addGenJarsProvider(builder, skylarkApiProvider, genClassJar, genSourceJar);
+
+ // No need to use the flag map here - just confirming that dynamic configurations are in use.
+ // TODO(mstaib): remove when static configurations are removed.
+ AndroidFeatureFlagSetProvider.getAndValidateFlagMapFromRuleContext(ruleContext);
+
+ return builder
+ .setFilesToBuild(filesToBuild)
+ .addSkylarkTransitiveInfo(JavaSkylarkApiProvider.NAME, skylarkApiProvider.build())
+ .addProvider(ruleOutputJarsProvider)
+ .addProvider(
+ RunfilesProvider.class,
+ RunfilesProvider.withData(
+ defaultRunfiles,
+ new Runfiles.Builder(ruleContext.getWorkspaceName())
+ .merge(runfilesSupport)
+ .build()))
+ .setRunfilesSupport(runfilesSupport, executable)
+ .addProvider(
+ JavaRuntimeClasspathProvider.class,
+ new JavaRuntimeClasspathProvider(javaCommon.getRuntimeClasspath()))
+ .addProvider(JavaSourceJarsProvider.class, sourceJarsProvider)
+ .addProvider(JavaPrimaryClassProvider.class, new JavaPrimaryClassProvider(testClass))
+ .addProvider(
+ JavaSourceInfoProvider.class,
+ JavaSourceInfoProvider.fromJavaTargetAttributes(helper.getAttributes(), javaSemantics))
+ .addOutputGroup(JavaSemantics.SOURCE_JARS_OUTPUT_GROUP, transitiveSourceJars)
+ .build();
+ }
+
+ protected abstract JavaSemantics createJavaSemantics();
+
+ protected abstract void addExtraProviders(
+ RuleConfiguredTargetBuilder builder,
+ JavaCommon javaCommon,
+ Artifact classJar,
+ Artifact srcJar,
+ Artifact genClassJar,
+ Artifact genSourceJar);
+
+ protected abstract ImmutableList<String> getJvmFlags(RuleContext ruleContext, String testClass);
+
+ protected abstract String getMainClass(
+ RuleContext ruleContext,
+ JavaSemantics javaSemantics,
+ JavaCompilationHelper helper,
+ Artifact executable,
+ Artifact instrumentationMetadata,
+ JavaCompilationArtifacts.Builder javaArtifactsBuilder,
+ JavaTargetAttributes.Builder attributesBuilder)
+ throws InterruptedException;
+
+ protected abstract JavaCompilationHelper getJavaCompilationHelperWithDependencies(
+ RuleContext ruleContext, JavaSemantics javaSemantics, JavaCommon javaCommon,
+ JavaTargetAttributes.Builder javaTargetAttributesBuilder);
+
+ protected abstract void getJavaContracts(
+ RuleContext ruleContext, List<TransitiveInfoCollection> depsForRunfiles);
+
+ protected static TransitiveInfoCollection getAndCheckTestSupport(RuleContext ruleContext) {
+ // Add the unit test support to the list of dependencies.
+ TransitiveInfoCollection testSupport = null;
+ TransitiveInfoCollection t =
+ Iterables.getOnlyElement(ruleContext.getPrerequisites("$testsupport", Mode.TARGET));
+ if (t.getProvider(JavaCompilationArgsProvider.class) != null) {
+ testSupport = t;
+ } else {
+ ruleContext.attributeError(
+ "$testsupport", "this prerequisite is not a java_library rule, or contains errors");
+ }
+ return testSupport;
+ }
+
+ private static void setUpJavaCommon(
+ JavaCommon common,
+ JavaCompilationHelper helper,
+ JavaCompilationArtifacts javaCompilationArtifacts) {
+ common.setJavaCompilationArtifacts(javaCompilationArtifacts);
+ common.setClassPathFragment(
+ new ClasspathConfiguredFragment(
+ common.getJavaCompilationArtifacts(),
+ helper.getAttributes(),
+ false,
+ helper.getBootclasspathOrDefault()));
+ }
+
+ private static void addRuntimeJarsToArtifactsBuilder(
+ JavaCompilationArtifacts.Builder javaArtifactsBuilder,
+ JavaTargetAttributes attributes,
+ Artifact classJar) {
+ if (attributes.hasSources() || attributes.hasResources()) {
+ // We only want to add a jar to the classpath of a dependent rule if it has content.
+ javaArtifactsBuilder.addRuntimeJar(classJar);
+ }
+ }
+
+ private Runfiles collectDefaultRunfiles(
+ RuleContext ruleContext,
+ JavaCommon javaCommon,
+ NestedSet<Artifact> filesToBuild,
+ Iterable<AndroidLibraryAarProvider> androidLibraryAarProviders) {
+ Runfiles.Builder builder = new Runfiles.Builder(ruleContext.getWorkspaceName());
+ builder.addTransitiveArtifacts(filesToBuild);
+ builder.addArtifacts(javaCommon.getJavaCompilationArtifacts().getRuntimeJars());
+
+ builder.addRunfiles(ruleContext, RunfilesProvider.DEFAULT_RUNFILES);
+ builder.add(ruleContext, JavaRunfilesProvider.TO_RUNFILES);
+
+ List<TransitiveInfoCollection> depsForRunfiles = new ArrayList<>();
+
+ if (ruleContext.isAttrDefined("$robolectric", LABEL_LIST)) {
+ depsForRunfiles.addAll(ruleContext.getPrerequisites("$robolectric", Mode.TARGET));
+ }
+ depsForRunfiles.addAll(ruleContext.getPrerequisites("runtime_deps", Mode.TARGET));
+
+ getJavaContracts(ruleContext, depsForRunfiles);
+
+ depsForRunfiles.add(getAndCheckTestSupport(ruleContext));
+
+ builder.addTargets(depsForRunfiles, JavaRunfilesProvider.TO_RUNFILES);
+ builder.addTargets(depsForRunfiles, RunfilesProvider.DEFAULT_RUNFILES);
+
+ for (AndroidLibraryAarProvider aarProvider : androidLibraryAarProviders) {
+ if (aarProvider.getAar() != null) {
+ builder.addArtifact(aarProvider.getAar().getAar());
+ builder.addArtifact(aarProvider.getAar().getManifest());
+ }
+ for (Aar aar : aarProvider.getTransitiveAars()) {
+ builder.addArtifact(aar.getAar());
+ builder.addArtifact(aar.getManifest());
+ }
+ }
+
+ if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
+ Artifact instrumentedJar = javaCommon.getJavaCompilationArtifacts().getInstrumentedJar();
+ if (instrumentedJar != null) {
+ builder.addArtifact(instrumentedJar);
+ }
+ }
+
+ builder.addArtifacts(javaCommon.getRuntimeClasspath());
+
+ // Add the JDK files if it comes from P4 (see java_stub_template.txt).
+ TransitiveInfoCollection javabaseTarget = ruleContext.getPrerequisite(":jvm", Mode.TARGET);
+
+ if (javabaseTarget != null) {
+ builder.addTransitiveArtifacts(
+ javabaseTarget.getProvider(FileProvider.class).getFilesToBuild());
+ }
+ return builder.build();
+ }
+
+ private static String getAndCheckTestClass(
+ RuleContext ruleContext, ImmutableList<Artifact> sourceFiles) {
+ String testClass =
+ ruleContext.getRule().isAttrDefined("test_class", Type.STRING)
+ ? ruleContext.attributes().get("test_class", Type.STRING)
+ : "";
+
+ if (testClass.isEmpty()) {
+ testClass = JavaCommon.determinePrimaryClass(ruleContext, sourceFiles);
+ if (testClass == null) {
+ ruleContext.ruleError(
+ "cannot determine junit.framework.Test class "
+ + "(Found no source file '"
+ + ruleContext.getTarget().getName()
+ + ".java' and package name doesn't include 'java' or 'javatests'. "
+ + "You might want to rename the rule or add a 'test_class' "
+ + "attribute.)");
+ }
+ }
+ return testClass;
+ }
+
+ protected static String getTransitiveLibrariesArg(
+ Iterable<AndroidLibraryAarProvider> aarProviders) {
+ Set<String> args = new LinkedHashSet<>();
+
+ for (AndroidLibraryAarProvider aarProvider : aarProviders) {
+ for (Aar aar : aarProvider.getTransitiveAars()) {
+ Preconditions.checkNotNull(aar.getAar());
+ Preconditions.checkNotNull(aar.getManifest());
+ args.add(
+ aar.getManifest().getRootRelativePathString()
+ + ":"
+ + aar.getAar().getRootRelativePathString());
+ }
+ }
+ return Joiner.on(",").join(args);
+ }
+
+ protected static String getStrictLibrariesArg(Iterable<AndroidLibraryAarProvider> aarProviders) {
+ Set<String> args = new LinkedHashSet<>();
+
+ for (AndroidLibraryAarProvider aarProvider : aarProviders) {
+ Aar aar = aarProvider.getAar();
+ if (aar != null) {
+ Preconditions.checkNotNull(aar.getAar());
+ Preconditions.checkNotNull(aar.getManifest());
+ args.add(
+ aar.getManifest().getRootRelativePathString()
+ + ":"
+ + aar.getAar().getRootRelativePathString());
+ }
+ }
+ return Joiner.on(",").join(args);
+ }
+}