in src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java [85:409]
public ConfiguredTarget create(RuleContext ruleContext)
throws InterruptedException, RuleErrorException, ActionConflictException {
androidSemantics.checkForMigrationTag(ruleContext);
ruleContext.checkSrcsSamePackage(true);
JavaSemantics javaSemantics = createJavaSemantics();
AndroidSemantics androidSemantics = createAndroidSemantics();
AndroidLocalTestConfiguration androidLocalTestConfiguration =
ruleContext.getFragment(AndroidLocalTestConfiguration.class);
AndroidDataContext dataContext = androidSemantics.makeContextForNative(ruleContext);
ResourceApk resourceApk =
buildResourceApk(
dataContext,
androidSemantics,
ruleContext,
DataBinding.contextFrom(ruleContext, dataContext.getAndroidConfig()),
AndroidManifest.fromAttributes(ruleContext, dataContext),
AndroidResources.from(ruleContext, "resource_files"),
AndroidAssets.from(ruleContext),
ResourceDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false),
AssetDependencies.fromRuleDeps(ruleContext, /* neverlink = */ false),
StampedAndroidManifest.getManifestValues(ruleContext),
ruleContext.getExpander().withDataExecLocations().tokenized("nocompress_extensions"),
ResourceFilterFactory.fromRuleContextAndAttrs(ruleContext));
JavaCommon javaCommon =
AndroidCommon.createJavaCommonWithAndroidDataBinding(
ruleContext,
javaSemantics,
resourceApk.asDataBindingContext(),
/* isLibrary */ false,
/* shouldCompileJavaSrcs */ true);
javaSemantics.checkRule(ruleContext, javaCommon);
// Use the regular Java javacopts, plus any extra needed for databinding. 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.
JavaToolchainProvider javaToolchain = JavaToolchainProvider.from(ruleContext);
ImmutableList.Builder<String> javacopts = ImmutableList.builder();
javacopts.addAll(javaSemantics.getCompatibleJavacOptions(ruleContext, javaToolchain));
resourceApk
.asDataBindingContext()
.supplyJavaCoptsUsing(ruleContext, /* isBinary= */ true, javacopts::addAll);
JavaTargetAttributes.Builder attributesBuilder =
javaCommon.initCommon(ImmutableList.of(), javacopts.build());
resourceApk
.asDataBindingContext()
.supplyAnnotationProcessor(
ruleContext,
(plugin, additionalOutputs) -> {
attributesBuilder.addPlugin(plugin);
attributesBuilder.addAdditionalOutputs(additionalOutputs);
});
attributesBuilder.addRuntimeClassPathEntry(resourceApk.getResourceJavaClassJar());
// Exclude the Rs from the library from the runtime classpath.
NestedSet<Artifact> excludedRuntimeArtifacts = getLibraryResourceJars(ruleContext);
attributesBuilder.addExcludedArtifacts(excludedRuntimeArtifacts);
// Create robolectric test_config.properties file
String name = "_robolectric/" + ruleContext.getRule().getName() + "_test_config.properties";
Artifact propertiesFile = ruleContext.getGenfilesArtifact(name);
String resourcesLocation =
resourceApk.getValidatedResources().getMergedResources().getRunfilesPathString();
Template template =
Template.forResource(AndroidLocalTestBase.class, "robolectric_properties_template.txt");
List<Substitution> substitutions = new ArrayList<>();
substitutions.add(
Substitution.of(
"%android_merged_manifest%", resourceApk.getManifest().getRunfilesPathString()));
substitutions.add(
Substitution.of("%android_merged_resources%", "jar:file:" + resourcesLocation + "!/res"));
substitutions.add(
Substitution.of("%android_merged_assets%", "jar:file:" + resourcesLocation + "!/assets"));
substitutions.add(
Substitution.of(
"%android_custom_package%", resourceApk.getValidatedResources().getJavaPackage()));
boolean generateBinaryResources =
androidLocalTestConfiguration.useAndroidLocalTestBinaryResources();
if (generateBinaryResources) {
substitutions.add(
Substitution.of(
"%android_resource_apk%", resourceApk.getArtifact().getRunfilesPathString()));
}
ruleContext.registerAction(
new TemplateExpansionAction(
ruleContext.getActionOwner(),
propertiesFile,
template,
substitutions,
/* makeExecutable= */ false));
// Add the properties file to the test jar as a java resource
attributesBuilder.addResource(
PathFragment.create("com/android/tools/test_config.properties"), propertiesFile);
String testClass = getAndCheckTestClass(ruleContext, javaCommon.getSrcsArtifacts());
getAndCheckTestSupport(ruleContext);
if (Allowlist.hasAllowlist(ruleContext, "multiple_proto_rule_types_in_deps_allowlist")
&& !Allowlist.isAvailable(ruleContext, "multiple_proto_rule_types_in_deps_allowlist")) {
javaSemantics.checkForProtoLibraryAndJavaProtoLibraryOnSameProto(ruleContext, javaCommon);
}
if (ruleContext.hasErrors()) {
return null;
}
// Databinding metadata that the databinding annotation processor reads.
ImmutableList<Artifact> additionalJavaInputsFromDatabinding =
resourceApk.asDataBindingContext().processDeps(ruleContext, /* isBinary= */ true);
JavaCompilationHelper helper =
getJavaCompilationHelperWithDependencies(
ruleContext,
javaSemantics,
javaCommon,
attributesBuilder,
additionalJavaInputsFromDatabinding);
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);
JavaCompilationArtifacts.Builder javaArtifactsBuilder = new JavaCompilationArtifacts.Builder();
Artifact executable; // the artifact for the rule itself
if (OS.getCurrent() == OS.WINDOWS) {
executable =
ruleContext.getImplicitOutputArtifact(ruleContext.getTarget().getName() + ".exe");
} else {
executable = ruleContext.createOutputArtifact();
}
String mainClass = javaSemantics.getTestRunnerMainClass();
String originalMainClass = mainClass;
if (ruleContext.getConfiguration().isCodeCoverageEnabled()) {
mainClass =
addCoverageSupport(
ruleContext,
javaSemantics,
helper,
executable,
/* instrumentationMetadata= */ null,
javaArtifactsBuilder,
attributesBuilder,
mainClass);
}
JavaCompileOutputs<Artifact> outputs = helper.createOutputs(classJar);
JavaRuleOutputJarsProvider.Builder javaRuleOutputJarsProviderBuilder =
JavaRuleOutputJarsProvider.builder()
.addJavaOutput(
JavaOutput.builder()
.fromJavaCompileOutputs(outputs)
.setCompileJar(classJar)
.setCompileJdeps(
javaCommon.getJavaCompilationArtifacts().getCompileTimeDependencyArtifact())
.addSourceJar(srcJar)
.build());
javaArtifactsBuilder.setCompileTimeDependencies(outputs.depsProto());
NestedSetBuilder<Artifact> filesToBuildBuilder =
NestedSetBuilder.<Artifact>stableOrder().add(classJar).add(executable);
GeneratedExtensionRegistryProvider generatedExtensionRegistryProvider =
javaSemantics.createGeneratedExtensionRegistry(
ruleContext,
javaCommon,
filesToBuildBuilder,
javaArtifactsBuilder,
javaRuleOutputJarsProviderBuilder,
javaSourceJarsProviderBuilder);
JavaTargetAttributes attributes = attributesBuilder.build();
addJavaClassJarToArtifactsBuilder(javaArtifactsBuilder, attributes, classJar);
helper.createCompileAction(outputs);
helper.createSourceJarAction(srcJar, outputs.genSource());
setUpJavaCommon(javaCommon, helper, javaArtifactsBuilder.build(), attributes);
Artifact launcher = JavaHelper.launcherArtifactForTarget(javaSemantics, ruleContext);
String javaExecutable;
if (javaSemantics.isJavaExecutableSubstitution()) {
javaExecutable = JavaCommon.getJavaBinSubstitution(ruleContext, launcher);
} else {
javaExecutable = JavaCommon.getJavaExecutableForStub(ruleContext, launcher);
}
javaSemantics.createStubAction(
ruleContext,
javaCommon,
getJvmFlags(ruleContext, testClass),
executable,
mainClass,
originalMainClass,
filesToBuildBuilder,
javaExecutable,
/* createCoverageMetadataJar= */ true);
Artifact oneVersionOutputArtifact = null;
JavaConfiguration javaConfig = ruleContext.getFragment(JavaConfiguration.class);
OneVersionEnforcementLevel oneVersionEnforcementLevel = javaConfig.oneVersionEnforcementLevel();
boolean doOneVersionEnforcement =
oneVersionEnforcementLevel != OneVersionEnforcementLevel.OFF
&& javaConfig.enforceOneVersionOnJavaTests();
if (doOneVersionEnforcement) {
oneVersionOutputArtifact =
OneVersionCheckActionBuilder.newBuilder()
.withEnforcementLevel(oneVersionEnforcementLevel)
.outputArtifact(
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_ONE_VERSION_ARTIFACT))
.useToolchain(javaToolchain)
.checkJars(
NestedSetBuilder.fromNestedSet(attributes.getRuntimeClassPath())
.add(classJar)
.build())
.build(ruleContext);
}
NestedSet<Artifact> filesToBuild = filesToBuildBuilder.build();
Runfiles defaultRunfiles =
collectDefaultRunfiles(
ruleContext,
javaCommon,
filesToBuild,
resourceApk.getManifest(),
resourceApk.getResourceJavaClassJar(),
resourceApk.getValidatedResources().getMergedResources(),
generateBinaryResources ? resourceApk : null);
RunfilesSupport runfilesSupport =
RunfilesSupport.withExecutable(ruleContext, defaultRunfiles, executable);
Artifact deployJar =
ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_DEPLOY_JAR);
// 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.of())
.setAttributes(attributes)
.addRuntimeJars(javaCommon.getJavaCompilationArtifacts().getRuntimeJars())
.setIncludeBuildData(true)
.setRunfilesMiddleman(runfilesSupport.getRunfilesMiddleman())
.setCompression(COMPRESSED)
.setLauncher(launcher)
.setOneVersionEnforcementLevel(
doOneVersionEnforcement ? oneVersionEnforcementLevel : OneVersionEnforcementLevel.OFF,
javaToolchain.getOneVersionAllowlist())
.build();
JavaSourceJarsProvider sourceJarsProvider = javaSourceJarsProviderBuilder.build();
NestedSet<Artifact> transitiveSourceJars = sourceJarsProvider.getTransitiveSourceJars();
RuleConfiguredTargetBuilder builder = new RuleConfiguredTargetBuilder(ruleContext);
if (generatedExtensionRegistryProvider != null) {
builder.addNativeDeclaredProvider(generatedExtensionRegistryProvider);
}
resourceApk.asDataBindingContext().addProvider(builder, ruleContext);
JavaRuleOutputJarsProvider ruleOutputJarsProvider = javaRuleOutputJarsProviderBuilder.build();
JavaInfo.Builder javaInfoBuilder = JavaInfo.Builder.create();
javaCommon.addTransitiveInfoProviders(builder, javaInfoBuilder, filesToBuild, classJar);
javaCommon.addGenJarsProvider(
builder, javaInfoBuilder, outputs.genClass(), outputs.genSource());
// Just confirming that there are no aliases being used here.
AndroidFeatureFlagSetProvider.getAndValidateFlagMapFromRuleContext(ruleContext);
if (oneVersionOutputArtifact != null) {
builder.addOutputGroup(OutputGroupInfo.HIDDEN_TOP_LEVEL, oneVersionOutputArtifact);
}
NestedSet<Artifact> extraFilesToRun =
NestedSetBuilder.create(Order.STABLE_ORDER, runfilesSupport.getRunfilesMiddleman());
JavaInfo javaInfo =
javaInfoBuilder
.addProvider(JavaSourceJarsProvider.class, sourceJarsProvider)
.addProvider(JavaRuleOutputJarsProvider.class, ruleOutputJarsProvider)
.build();
return builder
.setFilesToBuild(filesToBuild)
.addNativeDeclaredProvider(javaInfo)
.addProvider(
RunfilesProvider.class,
RunfilesProvider.withData(
defaultRunfiles,
new Runfiles.Builder(ruleContext.getWorkspaceName())
.merge(runfilesSupport)
.build()))
.addFilesToRun(extraFilesToRun)
.setRunfilesSupport(runfilesSupport, executable)
.addProvider(
JavaRuntimeClasspathProvider.class,
new JavaRuntimeClasspathProvider(javaCommon.getRuntimeClasspath()))
.addProvider(JavaPrimaryClassProvider.class, new JavaPrimaryClassProvider(testClass))
.addOutputGroup(JavaSemantics.SOURCE_JARS_OUTPUT_GROUP, transitiveSourceJars)
.addOutputGroup(
JavaSemantics.DIRECT_SOURCE_JARS_OUTPUT_GROUP,
NestedSetBuilder.wrap(Order.STABLE_ORDER, sourceJarsProvider.getSourceJars()))
.build();
}