Skip to content

Commit d4abac2

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. Fix the ordering at the consumption point in busybox.bzl: reverse the direct_resources_nodes list when building --directData (and --directAssets) arguments. This ensures the current library's node (first in preorder) ends up last in the busybox flag, so 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 that consumer resources take priority over dependency resources when both define the same resource name.
1 parent 8936d9e commit d4abac2

3 files changed

Lines changed: 66 additions & 5 deletions

File tree

rules/busybox.bzl

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -311,15 +311,17 @@ def _package(
311311
map_each = _make_package_resources_flags,
312312
join_with = ",",
313313
)
314+
# Reverse so the current library's node (first in preorder) ends up last,
315+
# matching busybox last-wins semantics: a library's resources override its deps.
314316
args.add_joined(
315317
"--directData",
316-
direct_resources_nodes,
318+
reversed(direct_resources_nodes.to_list()),
317319
map_each = _make_package_resources_flags,
318320
join_with = ",",
319321
)
320322
args.add_joined(
321323
"--directAssets",
322-
direct_resources_nodes,
324+
reversed(direct_resources_nodes.to_list()),
323325
map_each = _make_package_assets_flags,
324326
join_with = "&",
325327
omit_if_empty = True,
@@ -532,9 +534,11 @@ def _merge_assets(
532534
symbols = symbols,
533535
),
534536
)
537+
# Reverse so the current library's node (first in preorder) ends up last,
538+
# matching busybox last-wins semantics.
535539
args.add_joined(
536540
"--directData",
537-
direct_resources_nodes,
541+
reversed(direct_resources_nodes.to_list()),
538542
map_each = _make_merge_assets_flags,
539543
join_with = "&",
540544
)
@@ -782,9 +786,11 @@ def _merge_compiled(
782786
),
783787
)
784788
input_files.append(compiled_resources)
789+
# Reverse so the current library's node (first in preorder) ends up last,
790+
# matching busybox last-wins semantics.
785791
args.add_joined(
786792
"--directData",
787-
direct_resources_nodes,
793+
reversed(direct_resources_nodes.to_list()),
788794
map_each = _make_merge_compiled_flags,
789795
join_with = "&",
790796
)

rules/resources.bzl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1698,7 +1698,6 @@ def _process_starlark(
16981698
package = java_package,
16991699
))
17001700
else:
1701-
# Depsets are ordered below to match the order in the legacy native rules.
17021701
resources_ctx[_PROVIDERS].append(StarlarkAndroidResourcesInfo(
17031702
direct_resources_nodes = depset(
17041703
[ResourcesNodeInfo(

test/rules/resources/BUILD

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1888,3 +1888,59 @@ 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 busybox --directData list is reversed at consumption so the current
1895+
# library's node (first in preorder) ends up last, overriding its deps.
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+
# In legacy manifest_merge_order (used in CI), the dep's node is moved
1925+
# to transitive_resources_nodes by the fix_resource_transitivity swap,
1926+
# leaving only the consumer's own node in direct_resources_nodes.
1927+
# The busybox receives dep via --data and consumer via --directData
1928+
# (reversed), so consumer's resource value wins.
1929+
direct_resources_nodes = [
1930+
consumer_with_conflicting_dep_resources_node,
1931+
],
1932+
transitive_assets = ["assets/some_asset.txt"],
1933+
transitive_assets_symbols = [
1934+
"consumer_with_conflicting_dep_resources_symbols/assets.bin",
1935+
"another_resource_processing_lib_symbols/assets.bin",
1936+
],
1937+
transitive_compiled_resources = [
1938+
"consumer_with_conflicting_dep_resources_symbols/symbols.zip",
1939+
"another_resource_processing_lib_symbols/symbols.zip",
1940+
],
1941+
transitive_resources_nodes = [
1942+
another_resource_processing_lib_resources_node,
1943+
],
1944+
),
1945+
target_under_test = ":consumer_with_conflicting_dep_resources",
1946+
)

0 commit comments

Comments
 (0)