x86_64-macos incorrectly includes macOS libs/headers when cross compiling to Linux
Zig Version
0.10.0-dev.1107+7deadf430
Steps to Reproduce
src/main.zig:
const c = @cImport({
@cInclude("X11/Xlib.h");
});
pub fn main() void {
_ = c.XFree(null);
}
build.zig:
const std = @import("std");
pub fn build(b: *std.build.Builder) void {
const target = b.standardTargetOptions(.{});
const mode = b.standardReleaseOptions();
const exe = b.addExecutable("tmp", "src/main.zig");
exe.setTarget(target);
exe.setBuildMode(mode);
exe.install();
exe.linkLibC();
exe.linkSystemLibrary("X11");
}
brew install libx11
zig build -Dtarget=x86_64-linux-gnu --verbose
May only reproduce on x86 mac.
Expected Behavior
Zig should not attempt to use macOS headers/libraries when cross compiling to Linux.
Actual Behavior
Zig attempts to link a macOS library, and includes headers likely intended for macOS only, when building a binary for Linux:
/usr/local/bin/zig build-exe /Users/slimsag/Desktop/tmp/src/main.zig -lc -lX11 --cache-dir /Users/slimsag/Desktop/tmp/zig-cache --global-cache-dir /Users/slimsag/.cache/zig --name tmp -target x86_64-linux-gnu -mcpu x86_64 -I /usr/local/Cellar/libx11/1.7.3.1/include -I /usr/local/Cellar/libxcb/1.14_2/include -I /usr/local/Cellar/libxau/1.0.9/include -I /usr/local/Cellar/libxdmcp/1.1.3/include -I /usr/local/Cellar/xorgproto/2021.5/include -L /usr/local/Cellar/libx11/1.7.3.1/lib --enable-cache
ld.lld: error: undefined symbol: XFree
>>> referenced by main.zig:5
>>> /Users/slimsag/Desktop/tmp/zig-cache/o/01c3b52bbbabfbd0f8208918be0f4f24/tmp.o:(main.0)
>>> did you mean: _XFree
>>> defined in: /usr/local/Cellar/libx11/1.7.3.1/lib/libX11.a
error: LLDReportedFailure
tmp...The following command exited with error code 1:
/usr/local/bin/zig build-exe /Users/slimsag/Desktop/tmp/src/main.zig -lc -lX11 --cache-dir /Users/slimsag/Desktop/tmp/zig-cache --global-cache-dir /Users/slimsag/.cache/zig --name tmp -target x86_64-linux-gnu -mcpu x86_64 -I /usr/local/Cellar/libx11/1.7.3.1/include -I /usr/local/Cellar/libxcb/1.14_2/include -I /usr/local/Cellar/libxau/1.0.9/include -I /usr/local/Cellar/libxdmcp/1.1.3/include -I /usr/local/Cellar/xorgproto/2021.5/include -L /usr/local/Cellar/libx11/1.7.3.1/lib --enable-cache
error: the following build command failed with exit code 1:
/Users/slimsag/Desktop/tmp/zig-cache/o/2c392a7290db5827809b38e1a5c90300/build /usr/local/bin/zig /Users/slimsag/Desktop/tmp /Users/slimsag/Desktop/tmp/zig-cache /Users/slimsag/.cache/zig -Dtarget=x86_64-linux-gnu --verbose
I should also note, we have regressed here slightly in 6f42876e74831b7f9cecf5a863634df920c52b98 (although that change is not to blame for the issue here)
x86_64-macos -> x86_64-linux with Mach engine worked prior to that commit, but not after.
The reason is because despite this issue, Zig would previously find the libX11.so provided via a sysroot. However, with that commit and preference for static libs when available, the wrong-OS /usr/local/Cellar/libx11/1.7.3.1/lib/libX11.a is found first and so cross compilation now fails.
@slimsag I'm a little confused by the title of the issue: it looks to me like the issue is potentially linking in the wrong archive but not the header files? Now back to the linker finding the wrong archive: so you're saying that LLD is finding libX11.a which includes MachO objects, and fails instead of carrying on with the search? If that's the case, this sounds like LLD issue to me as otherwise, how can we tell the linker what to search for when utilising the system libs flags such as -L. -lX11 or something?
Pay close attention to the zig build-exe invocation:
/usr/local/bin/zig build-exe [...] -I /usr/local/Cellar/libx11/1.7.3.1/include -I /usr/local/Cellar/libxcb/1.14_2/include -I /usr/local/Cellar/libxau/1.0.9/include -I /usr/local/Cellar/libxdmcp/1.1.3/include -I /usr/local/Cellar/xorgproto/2021.5/include -L /usr/local/Cellar/libx11/1.7.3.1/lib [...]
I don't know for sure, but it seems to me that if you're targeting Linux from a macOS host that including headers -I and libraries -L from the brew cellar path would always be wrong, no? After all, they would be for the host OS/arch, not the target.
Agreed. Did you check why they end up in your search path when targeting linux then? I am asking since we don't add those automatically in zig, or shouldn't. The only automatic search paths should be those pointing at the SDK and only if not cross-compiling.
I checked on a recent Zig version, and following the reproduction steps in the issue description I can still reproduce this in a small example:
zig build -Dtarget=x86_64-linux-gnu --verbose /Users/slimsag/Desktop/zig/build/stage3/bin/zig build-exe /Users/slimsag/Desktop/zigissue/src/main.zig -lc -I/opt/homebrew/Cellar/libx11/1.8.1/include -I/opt/homebrew/Cellar/libxcb/1.15/include -I/opt/homebrew/Cellar/libxau/1.0.9/include -I/opt/homebrew/Cellar/libxdmcp/1.1.3/include -I/opt/homebrew/Cellar/xorgproto/2022.2/include -L/opt/homebrew/Cellar/libx11/1.8.1/lib -lX11 --cache-dir /Users/slimsag/Desktop/zigissue/zig-cache --global-cache-dir /Users/slimsag/.cache/zig --name tmp -target x86_64-linux-gnu -mcpu x86_64 --enable-cache
It seems notable to me that this is the zig build-exe invocation: zig build must have constructed this command invocation on it's own, so this must be in the realm of lib/std/build.zig somewhere I imagine.
If I get some extra time, I'll try to identify further. But seems clear this is a bug in zig build