dlang / dub

Package and build management system for D
MIT License
674 stars 227 forks source link

Dub build fails to build project on macOS with M1 chip #2954

Open Vladiwostok opened 3 months ago

Vladiwostok commented 3 months ago

System information

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?

  1. Get an M-chip macOS machine running (macOS 14.5) with the LDC compiler
  2. Clone the following repo: https://github.com/Dlang-UPB/D-scanner
  3. Run dub and watch the build fail:
    dub build
  4. 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
  5. 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
  6. 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.

Geod24 commented 3 months ago

@kinke : Any idea ?

kinke commented 3 months ago

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.

Vladiwostok commented 3 months ago

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.

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:

  1. 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)
  2. 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?

kinke commented 3 months ago

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.

kinke commented 3 months ago

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.

kinke commented 3 months ago

Looks as if https://github.com/dlang/dmd/pull/9275 actually broke working cross-compilation before, despite its name. ;)

Geod24 commented 3 months ago

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.

Vladiwostok commented 3 months ago

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.

kinke commented 3 months ago

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.

kinke commented 3 months ago

I've had a go at it: https://github.com/dlang/dmd/pull/16756