diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java index cb058fa9bcffdf..86b29eb5777a23 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/AarImport.java @@ -32,6 +32,7 @@ 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.android.databinding.DataBinding; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingV2Provider; import com.google.devtools.build.lib.rules.java.ImportDepsCheckActionBuilder; import com.google.devtools.build.lib.rules.java.JavaCommon; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; @@ -46,6 +47,7 @@ import com.google.devtools.build.lib.rules.java.JavaSourceJarsProvider; import com.google.devtools.build.lib.rules.java.JavaStarlarkApiProvider; import com.google.devtools.build.lib.rules.java.JavaToolchainProvider; +import com.google.devtools.build.lib.skylarkbuildapi.android.DataBindingV2ProviderApi; import com.google.devtools.build.lib.vfs.PathFragment; import javax.annotation.Nullable; @@ -92,8 +94,12 @@ public ConfiguredTarget create(RuleContext ruleContext) SpecialArtifact resources = createAarTreeArtifact(ruleContext, "resources"); SpecialArtifact assets = createAarTreeArtifact(ruleContext, "assets"); + SpecialArtifact databindingBrFiles = createAarTreeArtifact(ruleContext, "data-binding-br"); + SpecialArtifact databindingSetterStoreFiles = + createAarTreeArtifact(ruleContext, "data-binding-setter_store"); ruleContext.registerAction( - createAarResourcesExtractorActions(ruleContext, aar, resources, assets)); + createAarResourcesExtractorActions( + ruleContext, aar, resources, assets, databindingBrFiles, databindingSetterStoreFiles)); AndroidDataContext dataContext = androidSemantics.makeContextForNative(ruleContext); StampedAndroidManifest manifest = AndroidManifest.forAarImport(androidManifestArtifact); @@ -226,6 +232,9 @@ public ConfiguredTarget create(RuleContext ruleContext) common.addTransitiveInfoProviders( ruleBuilder, javaInfoBuilder, filesToBuild, /*classJar=*/ null); + DataBindingV2Provider dataBindingV2Provider = + createDatabindingProvider(ruleContext, databindingBrFiles, databindingSetterStoreFiles); + resourceApk.addToConfiguredTargetBuilder( ruleBuilder, ruleContext.getLabel(), @@ -237,6 +246,7 @@ public ConfiguredTarget create(RuleContext ruleContext) .addSkylarkTransitiveInfo( JavaStarlarkApiProvider.NAME, JavaStarlarkApiProvider.fromRuleContext()) .addProvider(RunfilesProvider.class, RunfilesProvider.EMPTY) + .addNativeDeclaredProvider(dataBindingV2Provider) .addNativeDeclaredProvider( new AndroidNativeLibsInfo( AndroidCommon.collectTransitiveNativeLibs(ruleContext).add(nativeLibs).build())) @@ -307,7 +317,13 @@ private static Action[] createSingleFileExtractorActions( } private static Action[] createAarResourcesExtractorActions( - RuleContext ruleContext, Artifact aar, Artifact resourcesDir, Artifact assetsDir) { + RuleContext ruleContext, + Artifact aar, + Artifact resourcesDir, + Artifact assetsDir, + Artifact databindingBrFiles, + Artifact databindingSetterStoreFiles) { + return new SpawnAction.Builder() .useDefaultShellEnvironment() .setExecutable( @@ -317,11 +333,15 @@ private static Action[] createAarResourcesExtractorActions( .addInput(aar) .addOutput(resourcesDir) .addOutput(assetsDir) + .addOutput(databindingBrFiles) + .addOutput(databindingSetterStoreFiles) .addCommandLine( CustomCommandLine.builder() .addExecPath("--input_aar", aar) .addExecPath("--output_res_dir", resourcesDir) .addExecPath("--output_assets_dir", assetsDir) + .addExecPath("--output_databinding_br_dir", databindingBrFiles) + .addExecPath("--output_databinding_setter_store_dir", databindingSetterStoreFiles) .build()) .build(ruleContext); } @@ -389,6 +409,36 @@ private static Action[] createAarNativeLibsFilterActions( return actionBuilder.build(ruleContext); } + private static DataBindingV2Provider createDatabindingProvider( + RuleContext ruleContext, + SpecialArtifact databindingBrFiles, + SpecialArtifact databindingSetterStoreFiles) { + + Iterable> databindingProvidersFromDeps = + ruleContext.getPrerequisites("deps", TransitionMode.TARGET, DataBindingV2Provider.PROVIDER); + + Iterable> databindingProvidersFromExports = + ruleContext.getPrerequisites( + "exports", TransitionMode.TARGET, DataBindingV2Provider.PROVIDER); + + DataBindingV2Provider dataBindingV2Provider = + DataBindingV2Provider.createProvider( + databindingSetterStoreFiles, + /* classInfoFile= */ null, + databindingBrFiles, + ruleContext.getRule().getLabel().toString(), + // TODO: The aar's Java package isn't available during analysis (it's in the manifest + // inside the aar, or can maybe be inferred elsewhere). This is mostly used for + // constructing a nice error message if multiple android_library rules try to generate + // databinding conflicting classes into the same Java package, so it's not as important + // for aars. + /* javaPackage= */ null, + databindingProvidersFromDeps, + databindingProvidersFromExports); + + return dataBindingV2Provider; + } + private static Artifact createAarArtifact(RuleContext ruleContext, String name) { return ruleContext.getUniqueDirectoryArtifact( "_aar", name, ruleContext.getBinOrGenfilesDirectory()); diff --git a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java index c78a4e06c8289a..7515580fa6add3 100644 --- a/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java +++ b/src/main/java/com/google/devtools/build/lib/rules/android/databinding/DataBindingV2Context.java @@ -150,6 +150,7 @@ public ImmutableList processDeps(RuleContext ruleContext, boolean isBi for (Entry> entry : javaPackagesToLabel.asMap().entrySet()) { if (entry.getValue().size() > 1) { + String javaPackage = entry.getKey().isEmpty() ? "" : entry.getKey(); ruleContext.attributeError( "deps", String.format( @@ -159,7 +160,7 @@ public ImmutableList processDeps(RuleContext ruleContext, boolean isBi + "android_library targets are:\n" + " Java package %s:\n" + " %s", - entry.getKey(), Joiner.on("\n ").join(entry.getValue()))); + javaPackage, Joiner.on("\n ").join(entry.getValue()))); } } } diff --git a/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java b/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java index eafeb06a206f9b..8b7ed2ed932816 100644 --- a/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java +++ b/src/test/java/com/google/devtools/build/lib/rules/android/AarImportTest.java @@ -14,6 +14,7 @@ package com.google.devtools.build.lib.rules.android; import static com.google.common.truth.Truth.assertThat; +import static java.util.stream.Collectors.toList; import com.google.common.base.Predicates; import com.google.common.collect.ImmutableMap; @@ -30,6 +31,7 @@ import com.google.devtools.build.lib.analysis.util.BuildViewTestCase; import com.google.devtools.build.lib.cmdline.Label; import com.google.devtools.build.lib.collect.nestedset.NestedSet; +import com.google.devtools.build.lib.rules.android.databinding.DataBindingV2Provider; import com.google.devtools.build.lib.rules.java.JavaCompilationArgsProvider; import com.google.devtools.build.lib.rules.java.JavaCompilationInfoProvider; import com.google.devtools.build.lib.rules.java.JavaConfiguration.ImportDepsCheckingLevel; @@ -189,6 +191,29 @@ public void testResourcesProvided() throws Exception { assertThat(assetsTreeArtifact.getExecPathString()).endsWith("_aar/unzipped/assets/foo"); } + @Test + public void testDatabindingInfoProvided() throws Exception { + ConfiguredTarget aarImportTarget = getConfiguredTarget("//a:last"); + + DataBindingV2Provider provider = aarImportTarget.get(DataBindingV2Provider.PROVIDER); + + Artifact setterStore = Iterables.getOnlyElement(provider.getSetterStores()); + assertThat(setterStore.isTreeArtifact()).isTrue(); + assertThat(setterStore.getExecPathString()) + .endsWith("_aar/unzipped/data-binding-setter_store/last"); + + assertThat( + provider.getTransitiveBRFiles().toList().stream() + .map(Artifact::getRootRelativePathString) + .collect(toList())) + .containsExactly( + "a/_aar/unzipped/data-binding-br/baz", + "a/_aar/unzipped/data-binding-br/foo", + "a/_aar/unzipped/data-binding-br/bar", + "a/_aar/unzipped/data-binding-br/intermediate", + "a/_aar/unzipped/data-binding-br/last"); + } + @Test public void testSourceJarsProvided() throws Exception { ConfiguredTarget aarImportTarget = getConfiguredTarget("//a:foo"); @@ -249,6 +274,12 @@ public void testResourcesExtractor() throws Exception { .get(0); Artifact assetsTreeArtifact = assets.getAssets().get(0); + DataBindingV2Provider dataBindingV2Provider = + getConfiguredTarget("//a:foo").get(DataBindingV2Provider.PROVIDER); + Artifact databindingBrTreeArtifact = + dataBindingV2Provider.getTransitiveBRFiles().toList().get(0); + Artifact databindingSetterStoreTreeArtifact = dataBindingV2Provider.getSetterStores().get(0); + assertThat(getGeneratingSpawnAction(resourceTreeArtifact).getArguments()) .containsExactly( aarResourcesExtractor.getExecPathString(), @@ -257,7 +288,11 @@ public void testResourcesExtractor() throws Exception { "--output_res_dir", resourceTreeArtifact.getExecPathString(), "--output_assets_dir", - assetsTreeArtifact.getExecPathString()); + assetsTreeArtifact.getExecPathString(), + "--output_databinding_br_dir", + databindingBrTreeArtifact.getExecPathString(), + "--output_databinding_setter_store_dir", + databindingSetterStoreTreeArtifact.getExecPathString()); } @Test diff --git a/tools/android/aar_resources_extractor.py b/tools/android/aar_resources_extractor.py index 29d2b4b047d269..49ce7f1b19e58c 100644 --- a/tools/android/aar_resources_extractor.py +++ b/tools/android/aar_resources_extractor.py @@ -45,6 +45,10 @@ flags.DEFINE_string("output_res_dir", None, "Output resources directory") flags.mark_flag_as_required("output_res_dir") flags.DEFINE_string("output_assets_dir", None, "Output assets directory") +flags.DEFINE_string("output_databinding_br_dir", None, + "Output directory for databinding br files") +flags.DEFINE_string("output_databinding_setter_store_dir", None, + "Output directory for databinding setter_store.json files") def ExtractResources(aar, output_res_dir): @@ -79,6 +83,14 @@ def ExtractAssets(aar, output_assets_dir): WriteFileWithJunctions(empty_asset_filename, b"") +def ExtractDatabinding(aar, file_suffix, output_databinding_dir): + """Extracts databinding metadata files from an `aar`.""" + output_databinding_dir_abs = os.path.abspath(output_databinding_dir) + for name in aar.namelist(): + if name.startswith("data-binding/") and name.endswith(file_suffix): + ExtractOneFile(aar, name, output_databinding_dir_abs) + + def WriteFileWithJunctions(filename, content): """Writes file including creating any junctions or directories necessary.""" def _WriteFile(filename): @@ -132,6 +144,12 @@ def main(unused_argv): ExtractResources(aar, FLAGS.output_res_dir) if FLAGS.output_assets_dir is not None: ExtractAssets(aar, FLAGS.output_assets_dir) + if FLAGS.output_databinding_br_dir is not None: + ExtractDatabinding(aar, "br.bin", FLAGS.output_databinding_br_dir) + if FLAGS.output_databinding_setter_store_dir is not None: + ExtractDatabinding(aar, "setter_store.json", + FLAGS.output_databinding_setter_store_dir) + if __name__ == "__main__": FLAGS(sys.argv) diff --git a/tools/android/aar_resources_extractor_test.py b/tools/android/aar_resources_extractor_test.py index 9ec3a99390fb81..4bb196ab71add7 100644 --- a/tools/android/aar_resources_extractor_test.py +++ b/tools/android/aar_resources_extractor_test.py @@ -113,6 +113,31 @@ def testContainsAssets(self): with open("out_dir/assets/b", "r") as layout_xml: self.assertEqual("some other asset", layout_xml.read()) + def testDatabinding(self): + aar = zipfile.ZipFile(io.BytesIO(), "w") + + br_filepath = ( + "data-binding/com.android.databinding.library.baseAdapters--br.bin") + setter_store_filepath = ( + "data-binding/" + + "com.android.databinding.library.baseAdapters--setter_store.json") + + aar.writestr(br_filepath, "br data") + aar.writestr(setter_store_filepath, "setter store data") + + os.makedirs("out_dir/br") + os.makedirs("out_dir/setter_store") + + aar_resources_extractor.ExtractDatabinding(aar, "br.bin", "out_dir/br") + aar_resources_extractor.ExtractDatabinding(aar, "setter_store.json", + "out_dir/setter_store") + + with open("out_dir/br/" + br_filepath, "r") as f: + self.assertEqual("br data", f.read()) + + with open("out_dir/setter_store/" + setter_store_filepath, "r") as f: + self.assertEqual("setter store data", f.read()) + if __name__ == "__main__": unittest.main()