Skip to content

Commit d2d535f

Browse files
committed
Fix resource merge priority so consumer library overrides dependency resources
The Android resource busybox uses last-wins semantics when merging duplicate resource names. The current library's ResourcesNodeInfo was placed as the first direct item of the direct_resources_nodes preorder depset, causing it to be processed before its dependencies and lose to them on conflicts. Move the current library's node to be the last transitive element instead, so it is visited last in preorder iteration and its resources correctly override those of its dependencies — matching native Bazel and Gradle behavior. Add regression test test_consumer_resource_wins_over_dep to verify the dep node appears before the consumer node in direct_resources_nodes and that the consumer's value of string.my_string wins over the dependency's.
1 parent 8936d9e commit d2d535f

2 files changed

Lines changed: 72 additions & 15 deletions

File tree

rules/resources.bzl

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1698,23 +1698,28 @@ def _process_starlark(
16981698
package = java_package,
16991699
))
17001700
else:
1701-
# Depsets are ordered below to match the order in the legacy native rules.
1701+
# The busybox uses last-wins semantics for duplicate resource names, so the current
1702+
# library's node must appear last in preorder iteration to correctly override its
1703+
# dependencies' resources (matching native Bazel and Gradle behavior). Build a depset
1704+
# for the current node and append it after the deps' and exports' nodes.
1705+
current_resources_node = depset(
1706+
[ResourcesNodeInfo(
1707+
label = ctx.label,
1708+
assets = depset(assets),
1709+
assets_dir = assets_dir,
1710+
assets_symbols = parsed_assets,
1711+
compiled_assets = compiled_assets,
1712+
resource_apks = depset(resource_apks),
1713+
resource_files = depset(processed_resources),
1714+
compiled_resources = compiled_resources,
1715+
r_txt = out_aapt2_r_txt,
1716+
manifest = processed_manifest,
1717+
exports_manifest = exports_manifest,
1718+
)] if defines_resources else [],
1719+
)
17021720
resources_ctx[_PROVIDERS].append(StarlarkAndroidResourcesInfo(
17031721
direct_resources_nodes = depset(
1704-
[ResourcesNodeInfo(
1705-
label = ctx.label,
1706-
assets = depset(assets),
1707-
assets_dir = assets_dir,
1708-
assets_symbols = parsed_assets,
1709-
compiled_assets = compiled_assets,
1710-
resource_apks = depset(resource_apks),
1711-
resource_files = depset(processed_resources),
1712-
compiled_resources = compiled_resources,
1713-
r_txt = out_aapt2_r_txt,
1714-
manifest = processed_manifest,
1715-
exports_manifest = exports_manifest,
1716-
)] if defines_resources else [],
1717-
transitive = direct_resources_nodes + exports_direct_resources_nodes,
1722+
transitive = direct_resources_nodes + exports_direct_resources_nodes + [current_resources_node],
17181723
order = "preorder",
17191724
),
17201725
transitive_resources_nodes = depset(

test/rules/resources/BUILD

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,3 +1888,55 @@ package_resources_final_id_test(
18881888
final = True,
18891889
target_under_test = ":resource_package_with_res_and_multiple_dep",
18901890
)
1891+
1892+
# Regression test: consumer library's resources must take priority over a
1893+
# dependency's when both define the same resource name (busybox last-wins).
1894+
# The current library's ResourcesNodeInfo must appear LAST in preorder
1895+
# iteration of direct_resources_nodes so the busybox picks its value.
1896+
starlark_process(
1897+
name = "consumer_with_conflicting_dep_resources",
1898+
custom_package = "test.rules.resources",
1899+
manifest = "AndroidManifest.xml",
1900+
resource_files = glob(["res_b/**"]),
1901+
deps = [":another_resource_processing_lib"],
1902+
)
1903+
1904+
consumer_with_conflicting_dep_resources_node = ExpectedResourcesNodeInfo(
1905+
assets = [],
1906+
assets_dir = "",
1907+
assets_symbols =
1908+
"consumer_with_conflicting_dep_resources_symbols/assets.bin",
1909+
compiled_resources =
1910+
"consumer_with_conflicting_dep_resources_symbols/symbols.zip",
1911+
label = ":consumer_with_conflicting_dep_resources",
1912+
manifest =
1913+
"consumer_with_conflicting_dep_resources_processed_manifest/AndroidManifest.xml",
1914+
r_txt = "consumer_with_conflicting_dep_resources_symbols/R.aapt2.txt",
1915+
)
1916+
1917+
starlark_process_test(
1918+
name = "test_consumer_resource_wins_over_dep",
1919+
expected_r_class_fields = [
1920+
"string.my_string",
1921+
"string.my_other_string",
1922+
],
1923+
expected_starlark_android_resources_info = ExpectedStarlarkAndroidResourcesInfo(
1924+
# Dep node first, consumer node last — consumer's value wins with
1925+
# busybox last-wins semantics when both define string.my_string.
1926+
direct_resources_nodes = [
1927+
another_resource_processing_lib_resources_node,
1928+
consumer_with_conflicting_dep_resources_node,
1929+
],
1930+
transitive_assets = ["assets/some_asset.txt"],
1931+
transitive_assets_symbols = [
1932+
"consumer_with_conflicting_dep_resources_symbols/assets.bin",
1933+
"another_resource_processing_lib_symbols/assets.bin",
1934+
],
1935+
transitive_compiled_resources = [
1936+
"consumer_with_conflicting_dep_resources_symbols/symbols.zip",
1937+
"another_resource_processing_lib_symbols/symbols.zip",
1938+
],
1939+
transitive_resources_nodes = [],
1940+
),
1941+
target_under_test = ":consumer_with_conflicting_dep_resources",
1942+
)

0 commit comments

Comments
 (0)