fix(beam): write package modules to the package's own src/#4817
Merged
Conversation
A NuGet package source in a subdirectory was written to
`fable_modules/<pkg>/<subdir>/src/` instead of the package's own `src/`. No OTP
app covers that path — `{project_app_dirs, [".", "fable_modules/*"]}` globs one
level, and `<subdir>/` has no `.app.src` of its own — so rebar3 never compiled
those files and reported nothing. `dotnet fable` succeeded, `rebar3 compile`
succeeded, and the first symptom was `undef` at runtime on a module sitting
right there in the output directory.
Project sources were already flat; only package sources mirrored their
subdirectory. In one build of `Fable.Giraffe`'s Beam suite, 8 modules were
stranded — including all seven `Fable.Beam.Otp.*` ones.
Write package modules to `<pkg>/src/<module>.erl` regardless of source
subdirectory. The module name already encodes the path (`Sinks/Universal.fs` ->
`scriptorium_parchment_sinks_universal`), so an app is flat by construction and
there is no collision risk.
If the output path turns out to have no `fable_modules/<package>` segment to
anchor on, fail with a `FableError` rather than falling back to the old layout:
that fallback would silently reinstate the exact bug this fixes, and the only
symptom would again be `undef` at runtime.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
dbrattli
force-pushed
the
fix/beam-package-output-layout
branch
from
July 18, 2026 14:33
8b5b1a1 to
025234b
Compare
4 tasks
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.
Problem
A NuGet package source in a subdirectory was written to
fable_modules/<pkg>/<subdir>/src/instead of the package's ownsrc/. No OTP app covers that path —{project_app_dirs, [".", "fable_modules/*"]}globs one level, and<subdir>/has no.app.srcof its own — so rebar3 never compiled those files and reported nothing.dotnet fablesucceeded,rebar3 compilesucceeded, and the first symptom wasundefat runtime on a module sitting right there in the output directory.Project sources were already flat; only package sources mirrored their subdirectory. In one build of
Fable.Giraffe's Beam suite, 8 modules were stranded — including all sevenFable.Beam.Otp.*ones, so the bindings library ships a subdirectory of modules unreachable at runtime.Fix
Write package modules to
<pkg>/src/<module>.erlregardless of source subdirectory. The module name already encodes the path (Sinks/Universal.fs→scriptorium_parchment_sinks_universal), so an app is flat by construction and there is no collision risk.If the output path has no
fable_modules/<package>segment to anchor on, this now fails with aFableErrorinstead of falling back to the previous layout — that fallback would silently reinstate the exact bug being fixed, withundefat runtime as the only symptom.Two alternatives from the report were considered and dropped: populating
{modules, [...]}in the generated.app.srcvalidates nothing, since rebar3 fills the module list in the generated.appfrom the compiled beams and ignores.app.src; and a post-emit assertion would guard an invariant thatgetOutPath— the only writer — now establishes five lines away.Verification
In-tree BEAM suite: 2633 passed, 0 failed, plus both entry-point program tests.
Against
../Fable.Giraffe's Beam suite, with itsfind-based flatten workaround removed:*/src/*.erlunderfable_modules(was 8 stranded modules)fable_beam_otp_*plusscriptorium_parchment_sinks_universalcompile into their app'sebin, andcode:ensure_loaded/1resolves themtest/shared/RoutingTests.fs→src/shared_routing_tests.erlNo regression test in-tree: reproducing this needs a package with subdirectory sources in
fable_modules, which the Beam test projects do not have.Note
This PR previously also carried an unrelated async-stacktrace fix. That has been split out into its own PR so this one can land on its own evidence.
🤖 Generated with Claude Code