Dub build fails to build project on macOS with M1 chip
System information
- dub version: 1.38.0
- OS Platform and distribution: macOS 14.5 (M1 chip)
- compiler version LDC 1.39.0
Bug Description
Repo: https://github.com/Dlang-UPB/D-scanner
When trying to build the project's DMD dependency with dub build, the build fails with 'exit code 1' when running the following pregenerate command: https://github.com/dlang/dmd/blob/master/dub.sdl#L53-L61
When running the command manually, the linker reports that the 'phobos2-ldc' lib is not found. When specifying the library path via 'LIBRARY_PATH' or 'DFLAGS' env vars, the manual command works, but dub build still won't build the project.
How to reproduce?
- Get an M-chip macOS machine running (macOS 14.5) with the LDC compiler
- Clone the following repo: https://github.com/Dlang-UPB/D-scanner
- Run dub and watch the build fail:
dub build
- Run the pregen command manually (used dir paths specific to my macOS machine) and observe the linker error:
dub --arch=aarch64 --compiler=ldc2 --single /Users/runner/.dub/packages/dmd/~master/dmd/config.d -- /Users/runner/.dub/packages/dmd/~master/dmd/generated/dub /Users/runner/.dub/packages/dmd/~master/dmd/VERSION /etc
- Set either one env var:
export LIBRARY_PATH=/Users/runner/hostedtoolcache/dc/ldc2-1.39.0/arm64/ldc2-1.39.0-osx-universal/lib-arm64
export DFLAGS=-L-L/Users/runner/hostedtoolcache/dc/ldc2-1.39.0/arm64/ldc2-1.39.0-osx-universal/lib-arm64
- Run the pregen command manually (step 5) and watch the command execute successfully, run dub build (step 3) and watch the build fail.
Expected Behavior
The program should build successfully when using dub, and without having to set any additional library paths.
Logs
$ dub build
Error Expected one or zero arguments.
Run "dub run -h" for more information about the "run" command.
Error Command failed with exit code 1:
"${DUB_EXE}" \
--arch=${DUB_ARCH} \
--compiler=${DC} \
--single "${DUB_PACKAGE_DIR}config.d" \
-- "${DUB_PACKAGE_DIR}generated/dub" \
"${DUB_PACKAGE_DIR}VERSION" \
/etc
$ dub build -vverbose
...
Running
"${DUB_EXE}" \
--arch=${DUB_ARCH} \
--compiler=${DC} \
--single "${DUB_PACKAGE_DIR}config.d" \
-- "${DUB_PACKAGE_DIR}generated/dub" \
"${DUB_PACKAGE_DIR}VERSION" \
/etc
Error Expected one or zero arguments.
Run "dub run -h" for more information about the "run" command.
Error Command failed with exit code 1:
"${DUB_EXE}" \
--arch=${DUB_ARCH} \
--compiler=${DC} \
--single "${DUB_PACKAGE_DIR}config.d" \
-- "${DUB_PACKAGE_DIR}generated/dub" \
"${DUB_PACKAGE_DIR}VERSION" \
/etc
Full exception: object.Exception@source/dub/internal/utils.d(189): Command failed with exit code 1:
"${DUB_EXE}" \
--arch=${DUB_ARCH} \
--compiler=${DC} \
--single "${DUB_PACKAGE_DIR}config.d" \
-- "${DUB_PACKAGE_DIR}generated/dub" \
"${DUB_PACKAGE_DIR}VERSION" \
/etc
----------------
??:? object.Throwable.TraceInfo core.runtime.defaultTraceHandler(void*) [0x10233c2f3]
??:? _d_run_main [0x102344cf7]
??:? start [0x181c7e0df]
??:? 0x0 [0xe8177fffffffffff]
$ dub --arch=aarch64 --compiler=ldc2 --single /Users/runner/.dub/packages/dmd/~master/dmd/config.d -- /Users/runner/.dub/packages/dmd/~master/dmd/generated/dub /Users/runner/.dub/packages/dmd/~master/dmd/VERSION /etc
Starting Performing "debug" build using ldc2 for aarch64, arm_hardfloat.
Building config ~master: building configuration [application]
Linking config
ld: library 'phobos2-ldc' not found
clang: error: linker command failed with exit code 1 (use -v to see invocation)
Error: /usr/bin/cc failed with status: 1
Error ldc2 failed with exit code 1.
Additional information
The same project built using makefile on the same OS and with the same compiler builds successfully, without having to set any additional library paths.
If needed, we can provide access to the target macOS machine via our Github actions flow.
@kinke : Any idea ?
So the immediate problem is this?
dub --arch=aarch64 --compiler=ldc2 --single /Users/runner/.dub/packages/dmd/~master/dmd/config.d -- /Users/runner/.dub/packages/dmd/~master/dmd/generated/dub /Users/runner/.dub/packages/dmd/~master/dmd/VERSION /etc
The most suspicious thing IMO is the --arch=aarch64, so I'd try removing it, and adding -v for more dub output. And probably having the compiler emit the invoked clang cmdline via DFLAGS=-v or so.
So the immediate problem is this?
dub --arch=aarch64 --compiler=ldc2 --single /Users/runner/.dub/packages/dmd/~master/dmd/config.d -- /Users/runner/.dub/packages/dmd/~master/dmd/generated/dub /Users/runner/.dub/packages/dmd/~master/dmd/VERSION /etc
The most suspicious thing IMO is the
--arch=aarch64, so I'd try removing it, and adding-vfor more dub output. And probably having the compiler emit the invoked clang cmdline viaDFLAGS=-vor so.
I've run the command without the arch specification and the build executed successfully.
Running the command with verbose compiler logging, I noticed the following diff:
- When running with
--arch=aarch64
config /Users/runner/hostedtoolcache/dc/ldc2-1.39.0/arm64/ldc2-1.39.0-osx-universal/etc/ldc2.conf (aarch64-apple-darwin23.5.0)
- When running without
--arch=aarch64
config /Users/runner/hostedtoolcache/dc/ldc2-1.39.0/arm64/ldc2-1.39.0-osx-universal/etc/ldc2.conf (arm64-apple-darwin23.5.0)
Looking back in DMD's dub file, I can see the arch being propagated using dub's arch env var (which I presume to be aarch64:
--arch=$${DUB_ARCH}
Does this still look like a dub issue?
Okay thx, now I think I know what happens - the ldc2.conf file expects arm64-apple-… triples, not aarch64-…. Apple went with this scheme for clang, so we/LDC adopted it.
Now I'm definitely no fan of recursive dub calls in pregenerate/build commands, as used by the DMD dub recipe here. I've seen weird issues due to the extra environment variables.
Not sure what the best way forward is - either killing the --arch specification in DMD's dub.sdl, or creating an ugly special case for dub's DUB_ARCH (arm64 for Apple AArch64 targets), or even supporting aarch64-apple-… triples in LDC's ldc2.conf.
Okay, after looking at DMD's dub.sdl, I think that should be fixed - it builds & runs a little config.d tool, so it must be runnable on the compiling machine. One might be cross-compiling, so DUB_ARCH does NOT guarantee the config.d executable can be run.
Looks as if https://github.com/dlang/dmd/pull/9275 actually broke working cross-compilation before, despite its name. ;)
Now I'm definitely no fan of recursive dub calls in pregenerate/build commands, as used by the DMD dub recipe here. I've seen weird issues due to the extra environment variables.
I've had issues as well. Recursive dub calls are quite popular from my experience, so we probably need to harden the testsuite around it, because they are quite convenient.
I'm thinking about drafting a PR in dmd repo and removing the --arch=$${DUB_ARCH} argument from the dub file. Not sure what the implication of this would be for other platforms.
I think using a macOS-arm64 triple would be better for the pregen command, but I think that would mean the pregen command for platform="posix" should be changed in individual commands for all POSIX platforms, so that it won't get applied to macOS arm. Adding so many pregen commands would pollute the dub file.
The thing is: that config.d tool doesn't need to be built for the same build target as the main dub build. And it cannot, just imagine some guy cross-compiling some dub project depending on the dmd package on a macOS arm64 box to Windows x64 - have fun running that config.exe on macOS arm64!
So the only robust way of making sure you build a config executable that can be run on the compiling host is to NOT specify any target, letting the compiler pick its default target (the host's native target).
Using .d files for autogenerating little VERSION files derived from git describe --tags etc. is IMO bad practice - when cross-compiling, you require a D compiler that can a) cross-compile, and b) build successfully for the native platform too. Not sure this approach will e.g. ever work with GDC, where you have different toolchains for each host->target combination.
And that platform="posix" is another dub weakness - you can only differentiate between target platforms, not host platforms. So if config.d only supports Posix, we'd ideally run it on every Posix host, regardless of the target platform. But after a quick look at config.d, it doesn't seem as if it wouldn't work on non-Posix - there's just some SYSCONFDIR.imp thingy that is Posix-only, and versioned out appropriately already. Edit: Well, 'appropriately' - that extra thing is done on Posix hosts only, the target is irrelevant. Edit2: In case I'm being unclear - I suggest simply removing the platform="posix", and maybe look into why that SYSCONFDIR.imp thingy is restricted to Posix hosts/targets.
I've had a go at it: https://github.com/dlang/dmd/pull/16756