Repository navigation
Build fails with Unbound module Stdlib when a project uses menhir with package management #15642
Copy link
Copy link
Labels
macosIssues that relate to Dune on macOSIssues that relate to Dune on macOSmenhirRelated to the internal menhir pluginRelated to the internal menhir pluginpackage management build failuresIssues arising from building a particular opam package under `dune pkg`Issues arising from building a particular opam package under `dune pkg`
Milestone
Description
Description
Since dune 3.24, dune pkg lock selects the relocatable compiler by default. In a project that uses package management and contains a (menhir ...) stanza, dune build then fails immediately with:
File "command line", line 1:
Error: Unbound module "Stdlib"
The failing step is menhir's type inference, which runs ocamlc -i over a generated mock module. With --display=short:
ocamlc parser__mock.mli.inferred (exit 2)
The failure appears with the default repository set and goes away if the relocatable repository is excluded from the solve, which puts the project back on ocaml-base-compiler. Nothing else about the project changes.
Reproduction
I created this minimal example:
$ cat dune
(library
(name repro))
(menhir
(modules parser))
$ cat dune-project
(lang dune 3.21)
(using menhir 2.0)
(package
(name repro)
(depends
(ocaml
(and
(>= 5.2.0)
(< 5.3.0)))
menhir)
(allow_empty))
$ cat dune-workspace
(lang dune 3.21)
(pkg enabled)
(context default)
$ cat parser.mly
%token <int> INT
%token EOF
%start <int> main
%%
main:
| i = INT EOF { i + 1 }
Then I ran:
$ dune pkg lock
Solution for dune.lock
Dependencies common to all supported platforms:
- compiler-cloning.enabled
- menhir.20260209
- menhirCST.20260209
- menhirGLR.20260209
- menhirLib.20260209
- menhirSdk.20260209
- ocaml.5.2.2
- ocaml-base-compiler.5.2.2
- ocaml-compiler.5.2.2
- relocatable-compiler.5.2.2.20251109.1
$ dune build
File "command line", line 1:
Error: Unbound module Stdlib
Then, I confirmed I can workaround the issue by updating dune-workspace to exclude the relocatable repository from lock_dir and rebuilding:
$ cat dune-workspace
(lang dune 3.21)
(pkg enabled)
(context default)
(lock_dir
(repositories
(:standard \ relocatable)))
$ dune pkg lock
Solution for dune.lock
Dependencies common to all supported platforms:
- menhir.20260209
- menhirCST.20260209
- menhirGLR.20260209
- menhirLib.20260209
- menhirSdk.20260209
- ocaml.5.2.1
- ocaml-base-compiler.5.2.1
- ocaml-config.3
$ dune build
$ echo $?
0
Specifications
$ dune --version
3.24.0
$ dune exec -- ocaml --version
The OCaml toplevel, version 5.2.1
System: macOS 26.5.2, arm64
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
macosIssues that relate to Dune on macOSIssues that relate to Dune on macOSmenhirRelated to the internal menhir pluginRelated to the internal menhir pluginpackage management build failuresIssues arising from building a particular opam package under `dune pkg`Issues arising from building a particular opam package under `dune pkg`
Activity
Alizter commentedon Aug 4, 2026
The difference between the relocatable and non-relocatable compiler cases comes down to whether or not the
ocamlcbinary appears in the workspace or not.When dune detects a compiler that is relocatable, it builds it as if it were any other opam package. When it detects a non-relocatable compiler, it builds it in a special external (and sometimes buggy) directory called the "toolchains cache".
When a rule is sandboxed, dependencies that are resolved inside the workspace are relocated to the sandbox. Typically this means that they will be symlinked inside the sandbox. When dependencies occur outside of the workspace, we don't do this kind of relocation and instead invoke them directly.
On Linux, when
ocamlcresolves itself using something likereadlink /proc/self/exe, it does so and observes that it is living inside of_build/_private/default/.pkg/ocaml-compiler-<hash>/bin, from which the relocatable machinary can hop around and find the stdlib. Notably, the fact thatocamloptitself was run from a symlink inside a sandbox didn't affect it.On macos, when
ocamlcresolves itself using_NSGetExecutablePath, it does so and observes that it is living at the location of the symlink. It therefore isn't able to hop-around like on linux since there is no sibling lib/ directory to look in.This is a classic case of under-specifying your dependencies. The invocation in the menhir rules that does
ocamlc -iis relying on the stdlib being present in its usual location relative to the compiler binary. This leads to the error we observe here.The fix is to stop invoking
ocamlc -iwithout the relevant stdlib include.Alizter commentedon Aug 4, 2026
Here is a reproduction case and fix: #15679
test: fix OPAMSWITCH in opam-var-os.t (#16238)
test: fix linking test failures on macosx (#16274)
fix: conditional on XCode 15 compat for c++ flags (#16275)
shonfeder commentedon Sep 11, 2026
I took the token on this issue from Ali a while back, but have been delayed in advancing my fix due to other responsibilities. I believe I've refined the root cause problem a bit, and have landed on a fix that also addresses the persistent MacOS failure visible on #15679 . It turns out two latent defects are surfacing here: (1) the missing dependency on the stdlib during interface generation and (2) miss-classification of the stdlib path in dune's internal path typing, which can prevent the added stdlib from being installed into the sandbox where the menhir rule is executed. I should have my fix ready for review tomorrow, which builds on and supersedes the draft PR.
test(pkg): cover a sandboxed compiler installed inside _build
Fix builds failing with `Unbound module Stdlib` when a project uses m…
shonfeder commentedon Sep 25, 2026
Thanks again for the report @agarwal , and sorry for the somewhat slow turnaround on a fix.
This is fixed in main now, with #16374 . It will be released in 3.25, but you can try or use it before then with
opam pin dune --dev-reponow or tomorrow via the nightly binary build (on MacOS or Linux) once the new daily build is deployed.