Skip to content

Commit cbdb2c7

Browse files
Googlercopybara-github
authored andcommitted
Add support for databinding in android_local_test.
Note that Android tests can successfully depend on libraries that have databinding enabled, but they seem to miss top-level generated files such as androidx.databinding.DataBinderMapperImpl which seems to only be generated in terminal/binary nodes in the build graph. This updates android_local_test to behave more like android_binary. Much of the code added to AndroidLocalTestBase is copied from AndroidCommon since it doesn't rely on AndroidCommon like AndroidLibrary, AndroidBinary, and AndroidTest do. Note that from what I can tell, this doesn't actually use the test-based parameters for the databinding tools and instead treats the build like android_binary. That seems to be consistent with android_test. Finally, this will introduce an incompatibility with kt_android_library: if the test itself wants to do any databinding generation, it won't work without a package change since the Kotlin code needs to be compiled in a separate library (which would need databinding enabled). The new test is mainly verifying that databinding properties are correctly being passed for android_local_test. The new databinding-specific test was verified as failing without the AndroidLocalTestBase changes. RELNOTES: Fix data-binding generation for android_local_test. PiperOrigin-RevId: 339386521
1 parent f4430e4 commit cbdb2c7

3 files changed

Lines changed: 176 additions & 14 deletions

File tree

src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBase.java

Lines changed: 47 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -90,16 +90,8 @@ public ConfiguredTarget create(RuleContext ruleContext)
9090
AndroidLocalTestConfiguration androidLocalTestConfiguration =
9191
ruleContext.getFragment(AndroidLocalTestConfiguration.class);
9292

93-
final JavaCommon javaCommon = new JavaCommon(ruleContext, javaSemantics);
94-
javaSemantics.checkRule(ruleContext, javaCommon);
95-
96-
// Use the regular Java javacopts. Enforcing android-compatible Java
97-
// (-source 7 -target 7 and no TWR) is unnecessary for robolectric tests
98-
// since they run on a JVM, not an android device.
99-
JavaTargetAttributes.Builder attributesBuilder = javaCommon.initCommon();
100-
101-
final AndroidDataContext dataContext = androidSemantics.makeContextForNative(ruleContext);
102-
final ResourceApk resourceApk =
93+
AndroidDataContext dataContext = androidSemantics.makeContextForNative(ruleContext);
94+
ResourceApk resourceApk =
10395
buildResourceApk(
10496
dataContext,
10597
androidSemantics,
@@ -114,6 +106,32 @@ public ConfiguredTarget create(RuleContext ruleContext)
114106
ruleContext.getExpander().withDataExecLocations().tokenized("nocompress_extensions"),
115107
ResourceFilterFactory.fromRuleContextAndAttrs(ruleContext));
116108

109+
JavaCommon javaCommon =
110+
AndroidCommon.createJavaCommonWithAndroidDataBinding(
111+
ruleContext, javaSemantics, resourceApk.asDataBindingContext(), /* isLibrary= */ false);
112+
javaSemantics.checkRule(ruleContext, javaCommon);
113+
114+
// Use the regular Java javacopts, plus any extra needed for databinding. Enforcing
115+
// android-compatible Java (-source 7 -target 7 and no TWR) is unnecessary for robolectric tests
116+
// since they run on a JVM, not an android device.
117+
JavaToolchainProvider javaToolchain = JavaToolchainProvider.from(ruleContext);
118+
ImmutableList.Builder<String> javacopts = ImmutableList.builder();
119+
javacopts.addAll(javaSemantics.getCompatibleJavacOptions(ruleContext, javaToolchain));
120+
resourceApk
121+
.asDataBindingContext()
122+
.supplyJavaCoptsUsing(ruleContext, /* isBinary= */ true, javacopts::addAll);
123+
JavaTargetAttributes.Builder attributesBuilder =
124+
javaCommon.initCommon(ImmutableList.of(), javacopts.build());
125+
126+
resourceApk
127+
.asDataBindingContext()
128+
.supplyAnnotationProcessor(
129+
ruleContext,
130+
(plugin, additionalOutputs) -> {
131+
attributesBuilder.addPlugin(plugin);
132+
attributesBuilder.addAdditionalOutputs(additionalOutputs);
133+
});
134+
117135
attributesBuilder.addRuntimeClassPathEntry(resourceApk.getResourceJavaClassJar());
118136

119137
// Exclude the Rs from the library from the runtime classpath.
@@ -169,9 +187,17 @@ public ConfiguredTarget create(RuleContext ruleContext)
169187
return null;
170188
}
171189

190+
// Databinding metadata that the databinding annotation processor reads.
191+
ImmutableList<Artifact> additionalJavaInputsFromDatabinding =
192+
resourceApk.asDataBindingContext().processDeps(ruleContext, /* isBinary= */ true);
193+
172194
JavaCompilationHelper helper =
173195
getJavaCompilationHelperWithDependencies(
174-
ruleContext, javaSemantics, javaCommon, attributesBuilder);
196+
ruleContext,
197+
javaSemantics,
198+
javaCommon,
199+
attributesBuilder,
200+
additionalJavaInputsFromDatabinding);
175201

176202
Artifact srcJar = ruleContext.getImplicitOutputArtifact(JavaSemantics.JAVA_BINARY_SOURCE_JAR);
177203
JavaSourceJarsProvider.Builder javaSourceJarsProviderBuilder =
@@ -266,7 +292,6 @@ public ConfiguredTarget create(RuleContext ruleContext)
266292
boolean doOneVersionEnforcement =
267293
oneVersionEnforcementLevel != OneVersionEnforcementLevel.OFF
268294
&& javaConfig.enforceOneVersionOnJavaTests();
269-
JavaToolchainProvider javaToolchain = JavaToolchainProvider.from(ruleContext);
270295
if (doOneVersionEnforcement) {
271296
oneVersionOutputArtifact =
272297
OneVersionCheckActionBuilder.newBuilder()
@@ -326,6 +351,8 @@ public ConfiguredTarget create(RuleContext ruleContext)
326351
builder.addNativeDeclaredProvider(generatedExtensionRegistryProvider);
327352
}
328353

354+
resourceApk.asDataBindingContext().addProvider(builder, ruleContext);
355+
329356
JavaRuleOutputJarsProvider ruleOutputJarsProvider = javaRuleOutputJarsProviderBuilder.build();
330357

331358
JavaInfo.Builder javaInfoBuilder = JavaInfo.Builder.create();
@@ -566,11 +593,17 @@ private JavaCompilationHelper getJavaCompilationHelperWithDependencies(
566593
RuleContext ruleContext,
567594
JavaSemantics javaSemantics,
568595
JavaCommon javaCommon,
569-
JavaTargetAttributes.Builder javaTargetAttributesBuilder)
596+
JavaTargetAttributes.Builder javaTargetAttributesBuilder,
597+
ImmutableList<Artifact> additionalArtifacts)
570598
throws RuleErrorException {
571599
JavaCompilationHelper javaCompilationHelper =
572600
new JavaCompilationHelper(
573-
ruleContext, javaSemantics, javaCommon.getJavacOpts(), javaTargetAttributesBuilder);
601+
ruleContext,
602+
javaSemantics,
603+
javaCommon.getJavacOpts(),
604+
javaTargetAttributesBuilder,
605+
additionalArtifacts,
606+
/* disableStrictDeps= */ false);
574607

575608
if (ruleContext.isAttrDefined("$junit", BuildType.LABEL)) {
576609
// JUnit jar must be ahead of android runtime jars since these contain stubbed definitions

src/main/java/com/google/devtools/build/lib/rules/android/AndroidLocalTestBaseRule.java

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import static com.google.devtools.build.lib.packages.Attribute.attr;
1717
import static com.google.devtools.build.lib.packages.BuildType.LABEL;
1818
import static com.google.devtools.build.lib.packages.BuildType.LABEL_KEYED_STRING_DICT;
19+
import static com.google.devtools.build.lib.packages.Type.BOOLEAN;
1920
import static com.google.devtools.build.lib.packages.Type.STRING;
2021
import static com.google.devtools.build.lib.packages.Type.STRING_DICT;
2122
import static com.google.devtools.build.lib.packages.Type.STRING_LIST;
@@ -27,6 +28,7 @@
2728
import com.google.devtools.build.lib.analysis.config.ExecutionTransitionFactory;
2829
import com.google.devtools.build.lib.packages.RuleClass;
2930
import com.google.devtools.build.lib.packages.RuleClass.Builder.RuleClassType;
31+
import com.google.devtools.build.lib.rules.android.databinding.DataBinding;
3032
import com.google.devtools.build.lib.rules.config.ConfigFeatureFlagProvider;
3133
import com.google.devtools.build.lib.rules.java.JavaConfiguration;
3234
import com.google.devtools.build.lib.util.FileTypeSet;
@@ -137,6 +139,26 @@ public RuleClass build(RuleClass.Builder builder, RuleDefinitionEnvironment envi
137139
you will likely need to use <code>test_class</code> as well.
138140
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
139141
.add(attr("custom_package", STRING))
142+
/* <!-- #BLAZE_RULE($android_resource_support).ATTRIBUTE(enable_data_binding) -->
143+
If true, this rule processes
144+
<a href="https://developer.android.com/topic/libraries/data-binding/index.html">data
145+
binding</a> references used in data-binding enabled dependencies used by this test. Without
146+
this setting, data-binding dependencies won't have necessary binary-level code generation,
147+
and may produce build failures.
148+
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */
149+
.add(attr("enable_data_binding", BOOLEAN))
150+
// The javac annotation processor from Android's data binding library that turns
151+
// processed XML expressions into Java code.
152+
.add(
153+
attr(DataBinding.DATABINDING_ANNOTATION_PROCESSOR_ATTR, LABEL)
154+
.cfg(ExecutionTransitionFactory.create())
155+
.value(
156+
environment.getToolsLabel("//tools/android:databinding_annotation_processor")))
157+
.add(
158+
attr(DataBinding.DATABINDING_EXEC_PROCESSOR_ATTR, LABEL)
159+
.cfg(ExecutionTransitionFactory.create())
160+
.exec()
161+
.value(environment.getToolsLabel("//tools/android:databinding_exec")))
140162
/* <!-- #BLAZE_RULE($android_local_test_base).ATTRIBUTE(nocompress_extensions) -->
141163
A list of file extensions to leave uncompressed in the resource apk.
142164
<!-- #END_BLAZE_RULE.ATTRIBUTE --> */

src/test/java/com/google/devtools/build/lib/rules/android/AndroidDataBindingV2Test.java

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,37 @@ private void writeDataBindingLibrariesFiles() throws Exception {
7676
"java/android/library/MyLib.java", "package android.library; public class MyLib {};");
7777
}
7878

79+
private void writeNonDataBindingLocalTestFiles() throws Exception {
80+
81+
scratch.file(
82+
"javatests/android/test/BUILD",
83+
"android_local_test(",
84+
" name = 'databinding_enabled_test',",
85+
" deps = ['//java/android/library:lib_with_databinding'],",
86+
" manifest = 'AndroidManifest.xml',",
87+
" srcs = ['MyTest.java'],",
88+
")");
89+
90+
scratch.file(
91+
"javatests/android/test/MyTest.java", "package android.test; public class MyTest {};");
92+
}
93+
94+
private void writeDataBindingLocalTestFiles() throws Exception {
95+
96+
scratch.file(
97+
"javatests/android/test/BUILD",
98+
"android_local_test(",
99+
" name = 'databinding_enabled_test',",
100+
" deps = ['//java/android/library:lib_with_databinding'],",
101+
" enable_data_binding = 1,",
102+
" manifest = 'AndroidManifest.xml',",
103+
" srcs = ['MyTest.java'],",
104+
")");
105+
106+
scratch.file(
107+
"javatests/android/test/MyTest.java", "package android.test; public class MyTest {};");
108+
}
109+
79110
private void writeDataBindingFiles() throws Exception {
80111

81112
writeDataBindingLibrariesFiles();
@@ -1197,6 +1228,82 @@ public void dataBinding_shrinkAapt2Action_withAndroidX_passesAndroidXFlag() thro
11971228
.contains("--useDataBindingAndroidX");
11981229
}
11991230

1231+
@Test
1232+
public void dataBinding_androidLocalTest_dataBindingDisabled_doesNotUseDataBindingFlags()
1233+
throws Exception {
1234+
useConfiguration(
1235+
"--experimental_android_databinding_v2", "--android_databinding_use_v3_4_args");
1236+
writeDataBindingFiles();
1237+
writeNonDataBindingLocalTestFiles();
1238+
1239+
ConfiguredTarget testTarget =
1240+
getConfiguredTarget("//javatests/android/test:databinding_enabled_test");
1241+
Set<Artifact> allArtifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(testTarget));
1242+
JavaCompileAction testCompileAction =
1243+
(JavaCompileAction)
1244+
getGeneratingAction(
1245+
getFirstArtifactEndingWith(allArtifacts, "databinding_enabled_test-class.jar"));
1246+
ImmutableList<String> expectedMissingJavacopts =
1247+
ImmutableList.of(
1248+
"-Aandroid.databinding.sdkDir=/not/used",
1249+
"-Aandroid.databinding.artifactType=APPLICATION",
1250+
"-Aandroid.databinding.exportClassListOutFile=/tmp/exported_classes",
1251+
"-Aandroid.databinding.modulePackage=android.test",
1252+
"-Aandroid.databinding.minApi=14",
1253+
"-Aandroid.databinding.enableV2=1",
1254+
"-Aandroid.databinding.directDependencyPkgs=[android.library]");
1255+
assertThat(getJavacArguments(testCompileAction)).containsNoneIn(expectedMissingJavacopts);
1256+
1257+
JavaCompileInfo javaCompileInfo =
1258+
testCompileAction
1259+
.getExtraActionInfo(actionKeyContext)
1260+
.getExtension(JavaCompileInfo.javaCompileInfo);
1261+
assertThat(javaCompileInfo.getJavacOptList()).containsNoneIn(expectedMissingJavacopts);
1262+
}
1263+
1264+
@Test
1265+
public void dataBinding_androidLocalTest_dataBindingEnabled_usesDataBindingFlags()
1266+
throws Exception {
1267+
useConfiguration(
1268+
"--experimental_android_databinding_v2", "--android_databinding_use_v3_4_args");
1269+
writeDataBindingFiles();
1270+
writeDataBindingLocalTestFiles();
1271+
1272+
ConfiguredTarget testTarget =
1273+
getConfiguredTarget("//javatests/android/test:databinding_enabled_test");
1274+
Set<Artifact> allArtifacts = actionsTestUtil().artifactClosureOf(getFilesToBuild(testTarget));
1275+
JavaCompileAction testCompileAction =
1276+
(JavaCompileAction)
1277+
getGeneratingAction(
1278+
getFirstArtifactEndingWith(allArtifacts, "databinding_enabled_test-class.jar"));
1279+
String dataBindingFilesDir =
1280+
targetConfig
1281+
.getBinDirectory(RepositoryName.MAIN)
1282+
.getExecPath()
1283+
.getRelative("javatests/android/test/databinding/databinding_enabled_test")
1284+
.getPathString();
1285+
String inputDir = dataBindingFilesDir + "/" + DataBinding.DEP_METADATA_INPUT_DIR;
1286+
String outputDir = dataBindingFilesDir + "/" + DataBinding.METADATA_OUTPUT_DIR;
1287+
ImmutableList<String> expectedJavacopts =
1288+
ImmutableList.of(
1289+
"-Aandroid.databinding.dependencyArtifactsDir=" + inputDir,
1290+
"-Aandroid.databinding.aarOutDir=" + outputDir,
1291+
"-Aandroid.databinding.sdkDir=/not/used",
1292+
"-Aandroid.databinding.artifactType=APPLICATION",
1293+
"-Aandroid.databinding.exportClassListOutFile=/tmp/exported_classes",
1294+
"-Aandroid.databinding.modulePackage=android.test",
1295+
"-Aandroid.databinding.minApi=14",
1296+
"-Aandroid.databinding.enableV2=1",
1297+
"-Aandroid.databinding.directDependencyPkgs=[android.library]");
1298+
assertThat(getJavacArguments(testCompileAction)).containsAtLeastElementsIn(expectedJavacopts);
1299+
1300+
JavaCompileInfo javaCompileInfo =
1301+
testCompileAction
1302+
.getExtraActionInfo(actionKeyContext)
1303+
.getExtension(JavaCompileInfo.javaCompileInfo);
1304+
assertThat(javaCompileInfo.getJavacOptList()).containsAtLeastElementsIn(expectedJavacopts);
1305+
}
1306+
12001307
private Artifact getAapt2PackgeActionArtifact(Set<Artifact> allArtifacts) {
12011308
return getArtifactForTool(allArtifacts, /* toolName= */ "AAPT2_PACKAGE");
12021309
}

0 commit comments

Comments
 (0)