eunit: de-dup deps by app_name so test target wins over prod variant#6
Merged
Conversation
When an app A test-depends on B and B depends on A, the eunit rule's dep list ended up containing both A's :test_erlang_app (ctx.attr.target) and A's :erlang_app (pulled in transitively via lib_info.deps). After e70ed5a these two write distinct .beam files at different execroot paths but erl_libs_contents keys destinations on app_name, producing two symlink actions for the same eunit_deps/A/ebin/A.beam output and an analysis-time conflicting actions error. Route the dep list through flat_deps with ctx.attr.target placed first so the first-wins de-dup by app_name keeps the test variant (a superset of the prod variant) in ERL_LIBS, matching the action-coalescing behaviour we had before e70ed5a.
wmnsk
approved these changes
Jun 25, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fix a Bazel analysis-time
conflicting actionserror ineunitwhen atarget's
test_depstransitively depend back on the target's own app.Background
Commit e70ed5a (#5, "extract_app: namespace .beam/.app outputs by target name")
intentionally writes
:erlang_appand:test_erlang_appoutputs to distinctpaths (
<target>/ebin/foo.beam) so both can live in the same package. TheErlangAppInfoprovider was deliberately left unchanged, on the assumptionthat downstream rules consume the provider and would be unaffected.
private/eunit.bzl::_implhowever builds its dependency list as:If
ctx.attr.targetis:test_erlang_appfor appA, and any entry inlib_info.deps(i.e. the resolveddeps + test_depsof:test_erlang_app)transitively pulls in
A's own:erlang_appvia atest_depscycle(
Atest-depends onB,Bdepends onA), the resulting list containstwo distinct
ErlangAppInfos sharingapp_name = "A".erl_libs_contentskeys its symlink destinations onapp_name:Before e70ed5a both source
.beamfiles lived at the same execroot path(
ebin/A.beam) and Bazel coalesced the two symlink actions into one.After the namespacing the source paths differ
(
erlang_app/ebin/A.beamvstest_erlang_app/ebin/A.beam) but thedestination is still
<name>_deps/A/ebin/A.beam, producing:Fix
De-duplicate the dependency list by
app_namewith the test target winning,by routing it through
flat_deps(first-wins semantics) withctx.attr.targetplaced first:This mirrors what the pre-e70ed5a action coalescing was effectively doing:
the test variant's beams (a superset of the prod variant's) end up in
ERL_LIBS, and the prod variant pulled in transitively is suppressed.private/ct.bzlis not affected because it never adds the test target toits dep list (test suites flow in via
compiled_suites, notdeps), sono analogous change is needed there.
Validation
cd test && bazelisk test //...-> 13/13 pass.cd examples/umbrella && bazelisk test //...-> 9/9 pass.with two affected apps (cyclic
test_deps):bazel build //app:eunitfails analysis with the conflictingactions error shown above (for two distinct apps).
eunittargets pass.