Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

homebrew-provided LLVM/Clang prevents zig from building C files #8860

Closed
Jarred-Sumner opened this issue May 22, 2021 · 30 comments · Fixed by Homebrew/homebrew-core#78336
Closed
Labels
bug Observed behavior contradicts documented or intended behavior downstream An issue with a third party project that uses Zig. os-macos
Milestone

Comments

@Jarred-Sumner
Copy link
Contributor

Jarred-Sumner commented May 22, 2021

Note from @andrewrk: A workaround for this is to use the tarballs from the download page.


zig version: 0.8.0-dev.2591+4b69bd61e
macOS version: 11.3.1

To repro, run this:

echo "#include <assert.h>" > file.c
zig cc -lc hello.c 

Response:

jarred@c-73-252-132-29 ~/Code [1]> zig cc -lc hello.c
hello.c:1:10: fatal error: cannot open file '/usr/local/Cellar/zig/HEAD-4b69bd6_1/lib/zig/include/assert.h': No such file or directory
#include <assert.h>
         ^

Passing the include path does not fix it:

zig cc -I/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include /Users/jarred/Code/esdev/src/deps/picohttpparser.c 
/Users/jarred/Code/esdev/src/deps/picohttpparser.c:27:10: fatal error: cannot open file '/usr/local/Cellar/zig/HEAD-4b69bd6_1/lib/zig/include/assert.h': No such file or directory
#include <assert.h>

This is consistent, whether it's zig translate-c, zig cc, @cInclude, or exe.addCSourceFile etc.

Initially, I thought it was something specific to my environment -- but after wiping my hard-drive & reinstalling macOS, it's hard to see how it could be.

Note that llvm 12 is installed and this occurs whether it's a copy of zig built from homebrew or manually running cmake

This seems to work on zig 0.7.1, so this seems to be something that broke recently:

jarred@c-73-252-132-29 ~/Code> zig cc -lc hello.c
Undefined symbol: C entry: _main
symbol(s) not found
error: FileNotFound

After adding a main function, zig 0.7.1 can build it, but zig on HEAD cannot

@Jarred-Sumner
Copy link
Contributor Author

Hm. After lots of retries, it seems to work now. But, I'm not 100% sure why.

I suspect the necessary step was adding this to my $HOME/.config/fish/config.fish prior to building zig from source:

set -ax LDFLAGS "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib"
set -ax CPPFLAGS "-I/usr/local/opt/llvm/include"
set -x SDKROOT (xcrun --sdk macosx --show-sdk-path)
set -x CC  /usr/local/opt/llvm/bin/clang
set -x CXX  /usr/local/opt/llvm/bin/clang++

There is a next step here, which is: change the wiki documentation to mention that these environment variables need to be added. /usr/local/opt/llvm/ can be replaced with $(brew --prefix llvm)

@jedisct1
Copy link
Contributor

Can you reproduce this with a precompiled snapshot downloaded from ziglang.org?

@kubkon
Copy link
Member

kubkon commented May 22, 2021

Hm. After lots of retries, it seems to work now. But, I'm not 100% sure why.

I suspect the necessary step was adding this to my $HOME/.config/fish/config.fish prior to building zig from source:

set -ax LDFLAGS "-L/usr/local/opt/llvm/lib -Wl,-rpath,/usr/local/opt/llvm/lib"
set -ax CPPFLAGS "-I/usr/local/opt/llvm/include"
set -x SDKROOT (xcrun --sdk macosx --show-sdk-path)
set -x CC  /usr/local/opt/llvm/bin/clang
set -x CXX  /usr/local/opt/llvm/bin/clang++

There is a next step here, which is: change the wiki documentation to mention that these environment variables need to be added. /usr/local/opt/llvm/ can be replaced with $(brew --prefix llvm)

None of this should be needed. Also, when reporting issues that are specific to the compiler (and leaving the linker out), it’s useful to force the C/C++ compiler to output a translation unit directly .o with -c flag. This way we’d have a better idea whether the error reported by 0.7.1 was actually coming from the linker and not the compiler.

Could you paste the source of your C program?

@Jarred-Sumner
Copy link
Contributor Author

Jarred-Sumner commented May 22, 2021

Can you reproduce this with a precompiled snapshot downloaded from ziglang.org?

It doesn't happen anymore, and I'm not sure why. I tried it on the snapshot builds and those work fine.

Could you paste the source of your C program?

The actual file I was trying to build is this: https://github.com/h2o/picohttpparser/blob/master/picohttpparser.c

But, the issue happened with a file as a simple as:

#include <sys/types.h>

int main(int argc, char *argv[]) {
}

In-case it's helpful, this was the output from zig cc -verbose t.c when it wasn't working:

clang version 12.0.0
Target: x86_64-unknown-macosx-gnu
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
 (in-process)
 "/Users/jarred/Build/zig/bin/zig" -cc1 -triple x86_64-unknown-macosx11.3.1-gnu -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj --mrelax-relocations -disable-free -disable-llvm-verifier -discard-value-names -main-file-name t.c -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fno-rounding-math -munwind-tables -target-sdk-version=11.3 -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -target-cpu penryn -tune-cpu generic -debugger-tuning=lldb -target-linker-version 609.8 -v -nostdsysteminc -nobuiltininc -resource-dir /Users/jarred/Build/zig/lib/clang/12.0.0 -dependency-file /Users/jarred/.cache/zig/tmp/be0a6a4223109b2e-t.o.d -MT /Users/jarred/.cache/zig/tmp/be0a6a4223109b2e-t.o -sys-header-deps -MV -isystem /Users/jarred/Build/zig/lib/zig/include -isystem /Users/jarred/Build/zig/lib/zig/libc/include/x86_64-macos-gnu -isystem /Users/jarred/Build/zig/lib/zig/libc/include/generic-darwin -isystem /Users/jarred/Build/zig/lib/zig/libc/include/x86_64-macos-any -isystem /Users/jarred/Build/zig/lib/zig/libc/include/any-macos-any -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -iwithsysroot /usr/include -iwithsysroot /usr/local/include -iframeworkwithsysroot /Library/Frameworks -iframeworkwithsysroot /System/Library/Frameworks -D _DEBUG -Og -fdebug-compilation-dir /Users/jarred/Code -ferror-limit 19 -fsanitize=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound -fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound -stack-protector 2 -stack-protector-buffer-size 4 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fmax-type-align=16 -fcolor-diagnostics -fno-spell-checking -target-cpu skylake -target-feature -16bit-mode -target-feature -32bit-mode -target-feature -3dnow -target-feature -3dnowa -target-feature +64bit -target-feature +adx -target-feature +aes -target-feature -amx-bf16 -target-feature -amx-int8 -target-feature -amx-tile -target-feature +avx -target-feature +avx2 -target-feature -avx512bf16 -target-feature -avx512bitalg -target-feature -avx512bw -target-feature -avx512cd -target-feature -avx512dq -target-feature -avx512er -target-feature -avx512f -target-feature -avx512ifma -target-feature -avx512pf -target-feature -avx512vbmi -target-feature -avx512vbmi2 -target-feature -avx512vl -target-feature -avx512vnni -target-feature -avx512vp2intersect -target-feature -avx512vpopcntdq -target-feature -avxvnni -target-feature +bmi -target-feature +bmi2 -target-feature -branchfusion -target-feature -cldemote -target-feature +clflushopt -target-feature -clwb -target-feature -clzero -target-feature +cmov -target-feature +cx16 -target-feature +cx8 -target-feature -enqcmd -target-feature +ermsb -target-feature +f16c -target-feature -false-deps-lzcnt-tzcnt -target-feature +false-deps-popcnt -target-feature -fast-11bytenop -target-feature +fast-15bytenop -target-feature -fast-7bytenop -target-feature -fast-bextr -target-feature +fast-gather -target-feature -fast-hops -target-feature -fast-lzcnt -target-feature +fast-scalar-fsqrt -target-feature -fast-scalar-shift-masks -target-feature +fast-shld-rotate -target-feature +fast-variable-shuffle -target-feature +fast-vector-fsqrt -target-feature -fast-vector-shift-masks -target-feature +fma -target-feature -fma4 -target-feature +fsgsbase -target-feature -fsrm -target-feature +fxsr -target-feature -gfni -target-feature -hreset -target-feature -idivl-to-divb -target-feature +idivq-to-divl -target-feature +invpcid -target-feature -kl -target-feature -lea-sp -target-feature -lea-uses-ag -target-feature -lvi-cfi -target-feature -lvi-load-hardening -target-feature -lwp -target-feature +lzcnt -target-feature +macrofusion -target-feature +mmx -target-feature +movbe -target-feature -movdir64b -target-feature -movdiri -target-feature -mwaitx -target-feature +nopl -target-feature -pad-short-functions -target-feature +pclmul -target-feature -pconfig -target-feature -pku -target-feature +popcnt -target-feature -prefer-128-bit -target-feature -prefer-256-bit -target-feature -prefer-mask-registers -target-feature -prefetchwt1 -target-feature +prfchw -target-feature -ptwrite -target-feature -rdpid -target-feature +rdrnd -target-feature +rdseed -target-feature -retpoline -target-feature -retpoline-external-thunk -target-feature -retpoline-indirect-branches -target-feature -retpoline-indirect-calls -target-feature -rtm -target-feature +sahf -target-feature -serialize -target-feature -seses -target-feature +sgx -target-feature -sha -target-feature -shstk -target-feature +slow-3ops-lea -target-feature -slow-incdec -target-feature -slow-lea -target-feature -slow-pmaddwd -target-feature -slow-pmulld -target-feature -slow-shld -target-feature -slow-two-mem-ops -target-feature -slow-unaligned-mem-16 -target-feature -slow-unaligned-mem-32 -target-feature -soft-float -target-feature +sse -target-feature +sse2 -target-feature +sse3 -target-feature +sse4.1 -target-feature +sse4.2 -target-feature -sse4a -target-feature -sse-unaligned-mem -target-feature +ssse3 -target-feature -tbm -target-feature -tsxldtrk -target-feature -uintr -target-feature -use-aa -target-feature -use-glm-div-sqrt-costs -target-feature -vaes -target-feature -vpclmulqdq -target-feature +vzeroupper -target-feature -waitpkg -target-feature -wbnoinvd -target-feature -widekl -target-feature +x87 -target-feature -xop -target-feature +xsave -target-feature +xsavec -target-feature +xsaveopt -target-feature +xsaves -o /Users/jarred/.cache/zig/tmp/be0a6a4223109b2e-t.o -x c t.c
clang -cc1 version 12.0.0 based upon LLVM 12.0.0 default target x86_64-apple-darwin20.4.0
ignoring nonexistent directory "/Users/jarred/Build/zig/lib/zig/libc/include/generic-darwin"
ignoring nonexistent directory "/Users/jarred/Build/zig/lib/zig/libc/include/x86_64-macos-any"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /Users/jarred/Build/zig/lib/zig/include
 /Users/jarred/Build/zig/lib/zig/libc/include/x86_64-macos-gnu
 /Users/jarred/Build/zig/lib/zig/libc/include/any-macos-any
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks (framework directory)
End of search list.
t.c:1:10: fatal error: cannot open file '/Users/jarred/Build/zig/lib/zig/include/sys/types.h': No such file or directory
#include <sys/types.h>

clang and gcc built the same file fine, it was only zig cc that failed to build.

@kubkon
Copy link
Member

kubkon commented May 22, 2021

Huh, fascinating! You’ve got the same problem as @jedisct1 it seems. Note that in the last error message, your include path has two zig path components, which is wrong. The headers live in lib/libc/include rather than lib/zig/libc/include. I think there’s something wrong with the path substitution somewhere in Zig. @jedisct1 did you make any progress in debugging this by any chance? Also, any ideas @andrewrk? The problem for me in debugging this is that I can’t repro that exact problem on any of my machines...

@kubkon kubkon added os-macos zig cc Zig as a drop-in C compiler feature labels May 22, 2021
@kubkon kubkon added this to the 0.8.0 milestone May 22, 2021
@kubkon
Copy link
Member

kubkon commented May 22, 2021

Lemme try and come up with a patch, but I’d be indebted if you could try it out before we commit anything in into master. Would that be ok with you @Jarred-Sumner?

@Jarred-Sumner
Copy link
Contributor Author

Lemme try and come up with a patch, but I’d be indebted if you could try it out before we commit anything in into master. Would that be ok with you @Jarred-Sumner?

Happy to try it, but I'm not sure if it will be useful unless we can reproduce the issue happening again for the same reason. I'm rebuilding another copy of zig in a different folder without the environment variables I mentioned earlier and seeing if that will put it into the state where it doesn't work

@Jarred-Sumner
Copy link
Contributor Author

Yep, that reproduces the issue.

bad build:

zig.bad/build on  master via △ v3.20.2 via ↯ v0.8.0-dev.2591+4b69bd61e 
❯ ./bin/zig cc ~/Code/t.c 
/Users/jarred/Code/t.c:1:10: fatal error: cannot open file '/Users/jarred/Build/zig.bad/build/lib/zig/include/sys/types.h': No such file or directory
#include <sys/types.h>
         ^
1 error generated.

good build:

❯ zig cc ~/Code/t.c 

bad build verbose:

clang version 12.0.0
Target: x86_64-unknown-macosx-gnu
Thread model: posix
InstalledDir: /usr/local/opt/llvm/bin
 (in-process)
 "/Users/jarred/Build/zig.bad/build/bin/zig" -cc1 -triple x86_64-unknown-macosx11.3.1-gnu -Wundef-prefix=TARGET_OS_ -Werror=undef-prefix -Wdeprecated-objc-isa-usage -Werror=deprecated-objc-isa-usage -emit-obj --mrelax-relocations -disable-free -disable-llvm-verifier -discard-value-names -main-file-name t.c -mrelocation-model pic -pic-level 2 -mframe-pointer=none -fno-rounding-math -munwind-tables -target-sdk-version=11.3 -fcompatibility-qualified-id-block-type-checking -fvisibility-inlines-hidden-static-local-var -target-cpu penryn -tune-cpu generic -debugger-tuning=lldb -target-linker-version 609.8 -v -nostdsysteminc -nobuiltininc -resource-dir /Users/jarred/Build/zig.bad/build/lib/clang/12.0.0 -dependency-file /Users/jarred/.cache/zig/tmp/2fd7754650b4e350-t.o.d -MT /Users/jarred/.cache/zig/tmp/2fd7754650b4e350-t.o -sys-header-deps -MV -isystem /Users/jarred/Build/zig.bad/build/lib/zig/include -isystem /Users/jarred/Build/zig.bad/build/lib/zig/libc/include/x86_64-macos-gnu -isystem /Users/jarred/Build/zig.bad/build/lib/zig/libc/include/generic-darwin -isystem /Users/jarred/Build/zig.bad/build/lib/zig/libc/include/x86_64-macos-any -isystem /Users/jarred/Build/zig.bad/build/lib/zig/libc/include/any-macos-any -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk -iwithsysroot /usr/include -iwithsysroot /usr/local/include -iframeworkwithsysroot /Library/Frameworks -iframeworkwithsysroot /System/Library/Frameworks -D _DEBUG -Og -fdebug-compilation-dir /Users/jarred/Build/zig.bad/build -ferror-limit 19 -fsanitize=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound -fsanitize-trap=alignment,array-bounds,bool,builtin,enum,float-cast-overflow,function,integer-divide-by-zero,nonnull-attribute,null,object-size,pointer-overflow,return,returns-nonnull-attribute,shift-base,shift-exponent,signed-integer-overflow,unreachable,vla-bound -stack-protector 2 -stack-protector-buffer-size 4 -fblocks -fencode-extended-block-signature -fregister-global-dtors-with-atexit -fgnuc-version=4.2.1 -fmax-type-align=16 -fcolor-diagnostics -fno-spell-checking -target-cpu skylake -target-feature -16bit-mode -target-feature -32bit-mode -target-feature -3dnow -target-feature -3dnowa -target-feature +64bit -target-feature +adx -target-feature +aes -target-feature -amx-bf16 -target-feature -amx-int8 -target-feature -amx-tile -target-feature +avx -target-feature +avx2 -target-feature -avx512bf16 -target-feature -avx512bitalg -target-feature -avx512bw -target-feature -avx512cd -target-feature -avx512dq -target-feature -avx512er -target-feature -avx512f -target-feature -avx512ifma -target-feature -avx512pf -target-feature -avx512vbmi -target-feature -avx512vbmi2 -target-feature -avx512vl -target-feature -avx512vnni -target-feature -avx512vp2intersect -target-feature -avx512vpopcntdq -target-feature -avxvnni -target-feature +bmi -target-feature +bmi2 -target-feature -branchfusion -target-feature -cldemote -target-feature +clflushopt -target-feature -clwb -target-feature -clzero -target-feature +cmov -target-feature +cx16 -target-feature +cx8 -target-feature -enqcmd -target-feature +ermsb -target-feature +f16c -target-feature -false-deps-lzcnt-tzcnt -target-feature +false-deps-popcnt -target-feature -fast-11bytenop -target-feature +fast-15bytenop -target-feature -fast-7bytenop -target-feature -fast-bextr -target-feature +fast-gather -target-feature -fast-hops -target-feature -fast-lzcnt -target-feature +fast-scalar-fsqrt -target-feature -fast-scalar-shift-masks -target-feature +fast-shld-rotate -target-feature +fast-variable-shuffle -target-feature +fast-vector-fsqrt -target-feature -fast-vector-shift-masks -target-feature +fma -target-feature -fma4 -target-feature +fsgsbase -target-feature -fsrm -target-feature +fxsr -target-feature -gfni -target-feature -hreset -target-feature -idivl-to-divb -target-feature +idivq-to-divl -target-feature +invpcid -target-feature -kl -target-feature -lea-sp -target-feature -lea-uses-ag -target-feature -lvi-cfi -target-feature -lvi-load-hardening -target-feature -lwp -target-feature +lzcnt -target-feature +macrofusion -target-feature +mmx -target-feature +movbe -target-feature -movdir64b -target-feature -movdiri -target-feature -mwaitx -target-feature +nopl -target-feature -pad-short-functions -target-feature +pclmul -target-feature -pconfig -target-feature -pku -target-feature +popcnt -target-feature -prefer-128-bit -target-feature -prefer-256-bit -target-feature -prefer-mask-registers -target-feature -prefetchwt1 -target-feature +prfchw -target-feature -ptwrite -target-feature -rdpid -target-feature +rdrnd -target-feature +rdseed -target-feature -retpoline -target-feature -retpoline-external-thunk -target-feature -retpoline-indirect-branches -target-feature -retpoline-indirect-calls -target-feature -rtm -target-feature +sahf -target-feature -serialize -target-feature -seses -target-feature +sgx -target-feature -sha -target-feature -shstk -target-feature +slow-3ops-lea -target-feature -slow-incdec -target-feature -slow-lea -target-feature -slow-pmaddwd -target-feature -slow-pmulld -target-feature -slow-shld -target-feature -slow-two-mem-ops -target-feature -slow-unaligned-mem-16 -target-feature -slow-unaligned-mem-32 -target-feature -soft-float -target-feature +sse -target-feature +sse2 -target-feature +sse3 -target-feature +sse4.1 -target-feature +sse4.2 -target-feature -sse4a -target-feature -sse-unaligned-mem -target-feature +ssse3 -target-feature -tbm -target-feature -tsxldtrk -target-feature -uintr -target-feature -use-aa -target-feature -use-glm-div-sqrt-costs -target-feature -vaes -target-feature -vpclmulqdq -target-feature +vzeroupper -target-feature -waitpkg -target-feature -wbnoinvd -target-feature -widekl -target-feature +x87 -target-feature -xop -target-feature +xsave -target-feature +xsavec -target-feature +xsaveopt -target-feature +xsaves -o /Users/jarred/.cache/zig/tmp/2fd7754650b4e350-t.o -x c /Users/jarred/Code/t.c
clang -cc1 version 12.0.0 based upon LLVM 12.0.0 default target x86_64-apple-darwin20.4.0
ignoring nonexistent directory "/Users/jarred/Build/zig.bad/build/lib/zig/libc/include/generic-darwin"
ignoring nonexistent directory "/Users/jarred/Build/zig.bad/build/lib/zig/libc/include/x86_64-macos-any"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/usr/local/include"
ignoring nonexistent directory "/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/Library/Frameworks"
#include "..." search starts here:
#include <...> search starts here:
 /Users/jarred/Build/zig.bad/build/lib/zig/include
 /Users/jarred/Build/zig.bad/build/lib/zig/libc/include/x86_64-macos-gnu
 /Users/jarred/Build/zig.bad/build/lib/zig/libc/include/any-macos-any
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/usr/include
 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.3.sdk/System/Library/Frameworks (framework directory)
End of search list.
/Users/jarred/Code/t.c:1:10: fatal error: cannot open file '/Users/jarred/Build/zig.bad/build/lib/zig/include/sys/types.h': No such file or directory
#include <sys/types.h>
         ^
1 error generated.

--verbose isn't printing anything with the working zig cc version

@jedisct1
Copy link
Contributor

Still trying to figure out what's going on as well.

This doesn't seem to be a recent regression in Zig. I went back as far as right after the LLVM 12 merge, and the behavior is the same.

Setting up environment variables as described by OP doesn't make a difference either.

However, builds from CI have the correct paths.

@Jarred-Sumner
Copy link
Contributor Author

Note that in the last error message, your include path has two zig path components, which is wrong. The headers live in lib/libc/include rather than lib/zig/libc/include. I think there’s something wrong with the path substitution somewhere in Zig.

I actually had some issues with std.fs.path.join last week and ended up partially porting the version from Node, though my use-case needs to handle weirder user input than normal probably:

const tester = @import("../test/tester.zig");

const std = @import("std");

threadlocal var parser_join_input_buffer: [1024]u8 = undefined;
threadlocal var parser_buffer: [1024]u8 = undefined;

// This function is based on Node.js' path.normalize function.
// https://github.com/nodejs/node/blob/36bb31be5f0b85a0f6cbcb36b64feb3a12c60984/lib/path.js#L66
pub fn normalizeStringGeneric(str: []const u8, buf: []u8, comptime allow_above_root: bool, comptime separator: u8, comptime isPathSeparator: anytype, lastIndexOfSeparator: anytype) []u8 {
    var i: usize = 0;
    var last_segment_length: i32 = 0;
    var last_slash: i32 = -1;
    var dots: i32 = 0;
    var code: u8 = 0;

    var written_len: usize = 0;
    const stop_len = str.len;

    while (i <= stop_len) : (i += 1) {
        if (i < stop_len) {
            code = str[i];
        } else if (@call(std.builtin.CallOptions{ .modifier = .always_inline }, isPathSeparator, .{code})) {
            break;
        } else {
            code = separator;
        }

        if (@call(std.builtin.CallOptions{ .modifier = .always_inline }, isPathSeparator, .{code})) {
            if (last_slash == @intCast(i32, i) - 1 or dots == 1) {
                // NOOP
            } else if (dots == 2) {
                if (written_len < 2 or last_segment_length != 2 or buf[written_len - 1] != '.' or buf[written_len - 2] != '.') {
                    if (written_len > 2) {
                        if (lastIndexOfSeparator(buf[0..written_len])) |last_slash_index| {
                            written_len = last_slash_index;
                            last_segment_length = @intCast(i32, written_len - 1 - (lastIndexOfSeparator(buf[0..written_len]) orelse 0));
                        } else {
                            written_len = 0;
                        }
                        last_slash = @intCast(i32, i);
                        dots = 0;
                        continue;
                    } else if (written_len != 0) {
                        written_len = 0;
                        last_segment_length = 0;
                        last_slash = @intCast(i32, i);
                        dots = 0;
                        continue;
                    }

                    if (allow_above_root) {
                        if (written_len > 0) {
                            buf[written_len] = separator;
                            written_len += 1;
                        }

                        buf[written_len] = '.';
                        written_len += 1;
                        buf[written_len] = '.';
                        written_len += 1;

                        last_segment_length = 2;
                    }
                }
            } else {
                if (written_len > 0) {
                    buf[written_len] = separator;
                    written_len += 1;
                }

                const slice = str[@intCast(usize, @intCast(usize, last_slash + 1))..i];
                std.mem.copy(u8, buf[written_len .. written_len + slice.len], slice);
                written_len += slice.len;
                last_segment_length = @intCast(i32, i) - last_slash - 1;
            }

            last_slash = @intCast(i32, i);
            dots = 0;
        } else if (code == '.' and dots != -1) {
            dots += 1;
        } else {
            dots = -1;
        }
    }

    return buf[0..written_len];
}

pub const Platform = enum {
    auto,
    loose,
    windows,
    posix,

    pub fn isSeparator(comptime _platform: Platform, char: u8) bool {
        const platform = _platform.resolve();
        switch (platform) {
            .auto => unreachable,
            .loose => {
                return isSepAny(char);
            },
            .windows => {
                return isSepWin32(char);
            },
            .posix => {
                return isSepPosix(char);
            },
        }
    }

    pub fn leadingSeparatorIndex(comptime _platform: Platform, path: anytype) ?usize {
        switch (_platform.resolve()) {
            .windows => {
                if (path.len < 1)
                    return null;

                if (path[0] == '/')
                    return 0;

                if (path[0] == '\\')
                    return 0;

                if (path.len < 3)
                    return null;

                // C:\
                // C:/
                if (path[0] >= 'A' and path[0] <= 'Z' and path[1] == ':') {
                    if (path[2] == '/')
                        return 2;
                    if (path[2] == '\\')
                        return 2;
                }

                return null;
            },
            .posix => {
                if (path.len > 0 and path[0] == '/') {
                    return 0;
                } else {
                    return null;
                }
            },
            else => {
                return leadingSeparatorIndex(.windows, path) orelse leadingSeparatorIndex(.posix, path);
            },
        }
    }

    pub fn resolve(comptime _platform: Platform) Platform {
        if (_platform == .auto) {
            switch (std.Target.current.os.tag) {
                .windows => {
                    return .windows;
                },

                .freestanding, .emscripten, .other => {
                    return .loose;
                },

                else => {
                    return .posix;
                },
            }
        }

        return _platform;
    }
};

pub fn normalizeString(str: []const u8, comptime allow_above_root: bool, comptime _platform: Platform) []u8 {
    return normalizeStringBuf(str, &parser_buffer, allow_above_root, _platform);
}

pub fn normalizeStringBuf(str: []const u8, buf: []u8, comptime allow_above_root: bool, comptime _platform: Platform) []u8 {
    comptime const platform = _platform.resolve();

    switch (platform) {
        .auto => unreachable,

        .windows => {
            return normalizeStringWindowsBuf(str, buf, allow_above_root);
        },
        .posix => {
            return normalizeStringPosixBuf(str, buf, allow_above_root);
        },

        .loose => {
            return normalizeStringLooseBuf(str, buf, allow_above_root);
        },
    }
}

pub fn normalizeStringAlloc(allocator: *std.mem.Allocator, str: []const u8, comptime allow_above_root: bool, comptime _platform: Platform) ![]const u8 {
    return try allocator.dupe(u8, normalizeString(str, allow_above_root, _platform));
}

pub fn normalizeAndJoin2(_cwd: []const u8, comptime _platform: Platform, part: anytype, part2: anytype) []const u8 {
    const parts = [_][]const u8{ part, part2 };
    const slice = normalizeAndJoinString(_cwd, &parts, _platform);
    return slice;
}

pub fn normalizeAndJoin(_cwd: []const u8, comptime _platform: Platform, part: anytype) []const u8 {
    const parts = [_][]const u8{
        part,
    };
    const slice = normalizeAndJoinString(_cwd, &parts, _platform);
    return slice;
}

// Convert parts of potentially invalid file paths into a single valid filpeath
// without querying the filesystem
// This is the equivalent of
pub fn normalizeAndJoinString(_cwd: []const u8, parts: anytype, comptime _platform: Platform) []const u8 {
    return normalizeAndJoinStringBuf(_cwd, &parser_join_input_buffer, parts, _platform);
}

pub fn normalizeAndJoinStringBuf(_cwd: []const u8, buf: []u8, parts: anytype, comptime _platform: Platform) []const u8 {
    if (parts.len == 0) {
        return _cwd;
    }

    if ((_platform == .loose or _platform == .posix) and parts.len == 1 and parts[0].len == 1 and parts[0] == std.fs.path.sep_posix) {
        return "/";
    }

    var cwd = _cwd;
    var out: usize = 0;
    // When parts[0] is absolute, we treat that as, effectively, the cwd
    var ignore_cwd = cwd.len == 0;

    // Windows leading separators can be a lot of things...
    // So we need to do this instead of just checking the first char.
    var leading_separator: []const u8 = "";
    if (_platform.leadingSeparatorIndex(parts[0])) |leading_separator_i| {
        leading_separator = parts[0][0 .. leading_separator_i + 1];
        ignore_cwd = true;
    }

    if (!ignore_cwd) {
        leading_separator = cwd[0 .. 1 + (_platform.leadingSeparatorIndex(_cwd) orelse unreachable)]; // cwd must be absolute
        cwd = _cwd[leading_separator.len..cwd.len];
        out = cwd.len;
        std.debug.assert(out < buf.len);
        std.mem.copy(u8, buf[0..out], cwd);
    }

    for (parts) |part, i| {
        // This never returns leading separators.
        var normalized_part = normalizeString(part, true, _platform);
        if (normalized_part.len == 0) {
            continue;
        }
        switch (_platform.resolve()) {
            .windows => {
                buf[out] = std.fs.path.sep_windows;
            },
            else => {
                buf[out] = std.fs.path.sep_posix;
            },
        }

        out += 1;

        const start = out;
        out += normalized_part.len;
        std.debug.assert(out < buf.len);
        std.mem.copy(u8, buf[start..out], normalized_part);
    }

    // One last normalization, to remove any ../ added
    const result = normalizeStringBuf(buf[0..out], parser_buffer[leading_separator.len..parser_buffer.len], false, _platform);
    std.mem.copy(u8, buf[0..leading_separator.len], leading_separator);
    std.mem.copy(u8, buf[leading_separator.len .. result.len + leading_separator.len], result);

    return buf[0 .. result.len + leading_separator.len];
}

pub fn isSepPosix(char: u8) bool {
    return char == std.fs.path.sep_posix;
}

pub fn isSepWin32(char: u8) bool {
    return char == std.fs.path.sep_windows;
}

pub fn isSepAny(char: u8) bool {
    return @call(.{ .modifier = .always_inline }, isSepPosix, .{char}) or @call(.{ .modifier = .always_inline }, isSepWin32, .{char});
}

pub fn lastIndexOfSeparatorWindows(slice: []const u8) ?usize {
    return std.mem.lastIndexOfScalar(u8, slice, std.fs.path.sep_windows);
}

pub fn lastIndexOfSeparatorPosix(slice: []const u8) ?usize {
    return std.mem.lastIndexOfScalar(u8, slice, std.fs.path.sep_posix);
}

pub fn lastIndexOfSeparatorLoose(slice: []const u8) ?usize {
    return std.mem.lastIndexOfAny(u8, slice, "/\\");
}

pub fn normalizeStringPosix(str: []const u8, comptime allow_above_root: bool) []u8 {
    return normalizeStringGenericBuf(str, &parser_buffer, allow_above_root, std.fs.path.sep_posix, isSepPosix, lastIndexOfSeparatorPosix);
}

pub fn normalizeStringPosixBuf(str: []const u8, buf: []u8, comptime allow_above_root: bool) []u8 {
    return normalizeStringGeneric(str, buf, allow_above_root, std.fs.path.sep_posix, isSepPosix, lastIndexOfSeparatorPosix);
}

pub fn normalizeStringWindows(str: []const u8, comptime allow_above_root: bool) []u8 {
    return normalizeStringGenericBuf(str, &parser_buffer, allow_above_root, std.fs.path.sep_windows, isSepWin32, lastIndexOfSeparatorWindows);
}

pub fn normalizeStringWindowsBuf(str: []const u8, buf: []u8, comptime allow_above_root: bool) []u8 {
    return normalizeStringGeneric(str, buf, allow_above_root, std.fs.path.sep_windows, isSepWin32, lastIndexOfSeparatorWindows);
}

pub fn normalizeStringLoose(str: []const u8, comptime allow_above_root: bool) []u8 {
    return normalizeStringGenericBuf(str, &parser_buffer, allow_above_root, std.fs.path.sep_posix, isSepAny, lastIndexOfSeparatorLoose);
}

pub fn normalizeStringLooseBuf(str: []const u8, buf: []u8, comptime allow_above_root: bool) []u8 {
    return normalizeStringGeneric(str, buf, allow_above_root, std.fs.path.sep_posix, isSepAny, lastIndexOfSeparatorLoose);
}

test "normalizeAndJoinStringPosix" {
    var t = tester.Tester.t(std.heap.c_allocator);
    defer t.report(@src());
    const string = []const u8;
    const cwd = "/Users/jarredsumner/Code/app";

    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/bar/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "bar", "file.js" }, .posix),
        @src(),
    );
    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "bar", "../file.js" }, .posix),
        @src(),
    );
    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "./bar", "../file.js" }, .posix),
        @src(),
    );

    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "././././foo", "././././bar././././", "../file.js" }, .posix),
        @src(),
    );
    _ = t.expect(
        "/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", "././././bar././././", "../file.js" }, .posix),
        @src(),
    );

    _ = t.expect(
        "/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", ".", "././././bar././././", ".", "../file.js" }, .posix),
        @src(),
    );

    _ = t.expect(
        "/Code/app/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", "..", "././././bar././././", ".", "../file.js" }, .posix),
        @src(),
    );
}

test "normalizeAndJoinStringLoose" {
    var t = tester.Tester.t(std.heap.c_allocator);
    defer t.report(@src());
    const string = []const u8;
    const cwd = "/Users/jarredsumner/Code/app";

    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/bar/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "bar", "file.js" }, .loose),
        @src(),
    );
    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "bar", "../file.js" }, .loose),
        @src(),
    );
    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "./bar", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "././././foo", "././././bar././././", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", "././././bar././././", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", ".", "././././bar././././", ".", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Code/app/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", "..", "././././bar././././", ".", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/bar/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "bar", "file.js" }, .loose),
        @src(),
    );
    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "bar", "../file.js" }, .loose),
        @src(),
    );
    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "foo", "./bar", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Users/jarredsumner/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ ".\\.\\.\\.\\foo", "././././bar././././", "..\\file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", "././././bar././././", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Code/app/foo/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", ".", "././././bar././././", ".", "../file.js" }, .loose),
        @src(),
    );

    _ = t.expect(
        "/Code/app/file.js",
        normalizeAndJoinString(cwd, [_]string{ "/Code/app", "././././foo", "..", "././././bar././././", ".", "../file.js" }, .loose),
        @src(),
    );
}

test "normalizeStringPosix" {
    var t = tester.Tester.t(std.heap.c_allocator);
    defer t.report(@src());

    // Don't mess up strings that
    _ = t.expect("foo/bar.txt", try normalizeStringAlloc(std.heap.c_allocator, "/foo/bar.txt", true, .posix), @src());
    _ = t.expect("foo/bar.txt", try normalizeStringAlloc(std.heap.c_allocator, "/foo/bar.txt", false, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/foo/bar", true, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/foo/bar", false, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/././foo/././././././bar/../bar/../bar", true, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/foo/bar", false, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/foo/bar//////", false, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/////foo/bar//////", false, .posix), @src());
    _ = t.expect("foo/bar", try normalizeStringAlloc(std.heap.c_allocator, "/////foo/bar", false, .posix), @src());
    _ = t.expect("", try normalizeStringAlloc(std.heap.c_allocator, "/////", false, .posix), @src());
    _ = t.expect("..", try normalizeStringAlloc(std.heap.c_allocator, "../boom/../", true, .posix), @src());
    _ = t.expect("", try normalizeStringAlloc(std.heap.c_allocator, "./", true, .posix), @src());
}

test "normalizeStringWindows" {
    var t = tester.Tester.t(std.heap.c_allocator);
    defer t.report(@src());

    // Don't mess up strings that
    _ = t.expect("foo\\bar.txt", try normalizeStringAlloc(std.heap.c_allocator, "\\foo\\bar.txt", true, .windows), @src());
    _ = t.expect("foo\\bar.txt", try normalizeStringAlloc(std.heap.c_allocator, "\\foo\\bar.txt", false, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\foo\\bar", true, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\foo\\bar", false, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\.\\.\\foo\\.\\.\\.\\.\\.\\.\\bar\\..\\bar\\..\\bar", true, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\foo\\bar", false, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\foo\\bar\\\\\\\\\\\\", false, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\\\\\\\\\foo\\bar\\\\\\\\\\\\", false, .windows), @src());
    _ = t.expect("foo\\bar", try normalizeStringAlloc(std.heap.c_allocator, "\\\\\\\\\\foo\\bar", false, .windows), @src());
    _ = t.expect("", try normalizeStringAlloc(std.heap.c_allocator, "\\\\\\\\\\", false, .windows), @src());
    _ = t.expect("..", try normalizeStringAlloc(std.heap.c_allocator, "..\\boom\\..\\", true, .windows), @src());
    _ = t.expect("", try normalizeStringAlloc(std.heap.c_allocator, ".\\", true, .windows), @src());
}

@kubkon
Copy link
Member

kubkon commented May 22, 2021

OK, so @jedisct1 and I confirmed it’s a deficiency in zig ld. If you add -DZIG_STATIC_LLVM=on to CMake when building stage1, this will fix your issues. The reason we need this flag is that llvm-config is a special cookie and passes some LLVM static archives using the shared lib syntax, e.g., -lsome which the linker should understand as either libsome.dylib or libsome.a. Currently, zig ld expects static libs part of the input files in the linker line hence as a result we don’t link everything that’s required and this seems to clobber some values in build when building stage1. I’ll provide a fix ASAP and report back. In the meantime, just add -DZIG_STATIC_LLVM=on to your CMake line.

@kubkon kubkon added linking bug Observed behavior contradicts documented or intended behavior labels May 22, 2021
@kubkon
Copy link
Member

kubkon commented May 22, 2021

@jedisct1 still reports some problems with zig cc -target wasm32-wasi which manifests itself in wrong include paths. I'll add a zld patch so that -DZIG_STATIC_LLVM=on is no longer necessary anyhow, but if you still encounter problems, lemme know!

@LemonBoy
Copy link
Contributor

I think the extra /lib/ component is introduced by testZigInstallPrefix, the check is probably correct only for std.zig and not for the various header files.

@floooh
Copy link
Contributor

floooh commented May 22, 2021

Hi, just want to mention that I'm seeing a similar problem with the latest zig 0.8.0 dev versions on macOS when compiling C and C++ code (0.7.1 works fine), the header search path problem doesn't seem to only affect standard library headers, but also "regular" project headers. For instance I'm getting errors like this:

error(compilation): clang failed with stderr: /Users/floh/projects/sokol-tools/ext/fmt/src/posix.cc:13:10: fatal error: cannot open file '/Users/floh/projects/sokol-tools/ext/fmt/src/fmt/posix.h': No such file or directory

That problematic header path /Users/floh/projects/sokol-tools/ext/fmt/src/fmt/posix.h is the directory of the source file (/Users/floh/projects/sokol-tools/ext/fmt/src/) with the include path (fmt/posix.h) added.

Somehow the compiler doesn't seem to be aware of header search paths.

The actual location of the include file is /Users/floh/projects/sokol-tools/ext/fmt/include/fmt/posix.h.

The location ext/fmt/include has been added as header search path to the build target.

If you want to play around with it, the project is here:

https://github.com/floooh/sokol-tools

Clone with submodules, and then run zig build:

> git clone --recursive https://github.com/floooh/sokol-tools
> cd sokol-tools
> zig build

This is a pure C++ command line tool, I'm currently experimenting with using zig as C++ build chain for cross-compiling :)

@andrewrk
Copy link
Member

There is a next step here, which is: change the wiki documentation to mention that these environment variables need to be added. /usr/local/opt/llvm/ can be replaced with $(brew --prefix llvm)

Please never do anything like this. We need to fix the issue, not put bandages on it.

@andrewrk
Copy link
Member

Anyone in this thread who is experiencing the problem: do you experience the problem with a tarball from ziglang.org/download/ ?

@jedisct1
Copy link
Contributor

Anyone in this thread who is experiencing the problem: do you experience the problem with a tarball from ziglang.org/download/ ?

Nope, this doesn't happen with these tarballs. The culprit seems to be LLVM, as installed by Homebrew.

@andrewrk andrewrk added the downstream An issue with a third party project that uses Zig. label May 22, 2021
@andrewrk andrewrk modified the milestones: 0.8.0, 0.9.0 May 22, 2021
@andrewrk andrewrk changed the title Zig is unable to build C files that use libc with fresh copy of macOS homebrew-provided LLVM/Clang prevents zig from building C files May 22, 2021
@Jarred-Sumner
Copy link
Contributor Author

Jarred-Sumner commented May 22, 2021 via email

@kubkon kubkon removed the linking label May 23, 2021
@floooh
Copy link
Contributor

floooh commented May 23, 2021

Anyone in this thread who is experiencing the problem: do you experience the problem with a tarball from ziglang.org/download/ ?

My build is still running, but it appears that the official tarball doesn't have this problem, and it's instead a problem in the brew-installed version. That's good to know! I guess a lot of people on macOS will install via brew, maybe the brew-formula should be changed to install the tarballs instead?

Tarball version is: 0.8.0-dev.2631+d321a4b76

(the brew version also has a small annoyance that zig version only prints "zig 0.8.0" instead of showing the git commit hash when building the bleeding edge version).

PS: build has finished without problems

@kubkon
Copy link
Member

kubkon commented May 23, 2021

Y'all, after some more debugging, this issue is with Homebrew and how LLVM is packaged rather than Zig. It turns out actually that we already dealt with it in the past back when we used LLVM-10: link.

The meat of the issue is that currently, Homebrew distributed LLVM, configures libLLVM for dynamic linking, while libClang for static linking, causing some bizarre init order fiasco/interdependence which results in the errors seen in this issue as well as other as reported by you. There are two workarounds to this currently:

  1. cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) -DZIG_PREFER_CLANG_CPP_DYLIB=on which will force both libLLVM and libClang to be dynamically linked, or
  2. cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) -DZIG_STATIC_LLVM=on which will force both libLLVM and libClang to be statically linked

Both workarounds should get rid of the problem if you're building zig from source using system tools and Homebrew shipped LLVM. The end solution however will be to take it up with Homebrew devs themselves to work out a suitable patch. The issue is so much more annoying since this means that Zig distributed via Homebrew (built from HEAD) will inevitably be broken.

@kubkon kubkon removed the zig cc Zig as a drop-in C compiler feature label May 23, 2021
@Jarred-Sumner
Copy link
Contributor Author

Jarred-Sumner commented May 23, 2021 via email

@andrewrk andrewrk pinned this issue May 23, 2021
@kubkon
Copy link
Member

kubkon commented May 24, 2021

Cant you pass custom CMake flags to the zig Homebrew script so at least brew install zig —head works? Cant cmake detect its on macOS, then detect if “brew —prefix llvm” returns a zero status code, and then relaunch itself with the right config options?

But this wouldn't be fixing the underlying issue, would it? It's always better to fix the issue at its root rather than put a bandaid on top of it. I've now made sure that when we release 0.8.0, homebrew's CI will reject any zig formula bumps until this issue is resolved, and in the meantime, I'll try resolving it myself and submitting a patch to homebrew.

@kubkon
Copy link
Member

kubkon commented May 24, 2021

Some more update on this issue. Today, I went as far as installing homebrew on a linux laptop, and replaying all the steps just as we'd do them on macOS. To my surprise, everything works just fine and I should note that libLLVM is dynamically linked while libClang statically, but there are no problems with zig cc whatsoever, which makes me more convinced that either homebrew added some additional flag to CMake when configuring LLVM (likely), or the dynamic loader on macOS dyld has a bug (unlikely).

I'm investigating the former as we speak.

@kubkon
Copy link
Member

kubkon commented May 25, 2021

Just an FYI that I submitted a bug report downstream Homebrew/homebrew-core#78025

@kubkon
Copy link
Member

kubkon commented May 27, 2021

Y'all, after some more debugging, this issue is with Homebrew and how LLVM is packaged rather than Zig. It turns out actually that we already dealt with it in the past back when we used LLVM-10: link.

The meat of the issue is that currently, Homebrew distributed LLVM, configures libLLVM for dynamic linking, while libClang for static linking, causing some bizarre init order fiasco/interdependence which results in the errors seen in this issue as well as other as reported by you. There are two workarounds to this currently:

  1. cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) -DZIG_PREFER_CLANG_CPP_DYLIB=on which will force both libLLVM and libClang to be dynamically linked, or
  2. cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) -DZIG_STATIC_LLVM=on which will force both libLLVM and libClang to be statically linked

Both workarounds should get rid of the problem if you're building zig from source using system tools and Homebrew shipped LLVM. The end solution however will be to take it up with Homebrew devs themselves to work out a suitable patch. The issue is so much more annoying since this means that Zig distributed via Homebrew (built from HEAD) will inevitably be broken.

Alas, I've got some bad news. This workaround doesn't seem to fix everything. Regardless if you add either flag to CMake, the resultant build of zig while passes the augmented Homebrew tests, it fails at stage2 tests which involve pulling in LLVM:

$ brew test zig
==> Testing zig
==> /usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig build-exe hello.zig
==> ./hello
==> /usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig cc hello.c -o hello
==> ./hello

$ zig build test-stage2 -Denable-llvm
error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang.cpp:22:10: fatal error: 'clang/Frontend/ASTUnit.h' file not found

error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang_cc1as_main.cpp:14:10: fatal error: 'clang/Basic/Diagnostic.h' file not found

error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang_cc1_main.cpp:15:10: fatal error: 'clang/Basic/Stack.h' file not found

error(compilation): clang failed with stderr: In file included from /Users/jakubkonka/dev/zig/src/zig_llvm.cpp:16:
/Users/jakubkonka/dev/zig/src/zig_llvm.h:13:10: fatal error: 'llvm-c/Core.h' file not found

error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang_driver.cpp:14:10: fatal error: 'clang/Driver/Driver.h' file not found

/Users/jakubkonka/dev/zig/src/zig_clang.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_clang_cc1as_main.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_llvm.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_clang_driver.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_clang_cc1_main.cpp:1:1: error: unable to build C object: clang exited with code 1
The following command exited with error code 1:
/usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig test /Users/jakubkonka/dev/zig/src/test.zig -cflags -std=c++14 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment -DNDEBUG=1 -- /Users/jakubkonka/dev/zig/src/zig_llvm.cpp /Users/jakubkonka/dev/zig/src/zig_clang.cpp /Users/jakubkonka/dev/zig/src/zig_clang_driver.cpp /Users/jakubkonka/dev/zig/src/zig_clang_cc1_main.cpp /Users/jakubkonka/dev/zig/src/zig_clang_cc1as_main.cpp /Users/jakubkonka/dev/zig/src/windows_sdk.cpp -lclangFrontendTool -lclangCodeGen -lclangFrontend -lclangDriver -lclangSerialization -lclangSema -lclangStaticAnalyzerFrontend -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangAnalysis -lclangASTMatchers -lclangAST -lclangParse -lclangSema -lclangBasic -lclangEdit -lclangLex -lclangARCMigrate -lclangRewriteFrontend -lclangRewrite -lclangCrossTU -lclangIndex -lclangToolingCore -llldDriver -llldMinGW -llldELF -llldCOFF -llldMachO -llldWasm -llldReaderWriter -llldCore -llldYAML -llldCommon -lLLVMWindowsManifest -lLLVMXRay -lLLVMLibDriver -lLLVMDlltoolDriver -lLLVMCoverage -lLLVMLineEditor -lLLVMXCoreDisassembler -lLLVMXCoreCodeGen -lLLVMXCoreDesc -lLLVMXCoreInfo -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMWebAssemblyDisassembler -lLLVMWebAssemblyAsmParser -lLLVMWebAssemblyCodeGen -lLLVMWebAssemblyDesc -lLLVMWebAssemblyInfo -lLLVMSystemZDisassembler -lLLVMSystemZAsmParser -lLLVMSystemZCodeGen -lLLVMSystemZDesc -lLLVMSystemZInfo -lLLVMSparcDisassembler -lLLVMSparcAsmParser -lLLVMSparcCodeGen -lLLVMSparcDesc -lLLVMSparcInfo -lLLVMRISCVDisassembler -lLLVMRISCVAsmParser -lLLVMRISCVCodeGen -lLLVMRISCVDesc -lLLVMRISCVInfo -lLLVMPowerPCDisassembler -lLLVMPowerPCAsmParser -lLLVMPowerPCCodeGen -lLLVMPowerPCDesc -lLLVMPowerPCInfo -lLLVMNVPTXCodeGen -lLLVMNVPTXDesc -lLLVMNVPTXInfo -lLLVMMSP430Disassembler -lLLVMMSP430AsmParser -lLLVMMSP430CodeGen -lLLVMMSP430Desc -lLLVMMSP430Info -lLLVMMipsDisassembler -lLLVMMipsAsmParser -lLLVMMipsCodeGen -lLLVMMipsDesc -lLLVMMipsInfo -lLLVMLanaiDisassembler -lLLVMLanaiCodeGen -lLLVMLanaiAsmParser -lLLVMLanaiDesc -lLLVMLanaiInfo -lLLVMHexagonDisassembler -lLLVMHexagonCodeGen -lLLVMHexagonAsmParser -lLLVMHexagonDesc -lLLVMHexagonInfo -lLLVMBPFDisassembler -lLLVMBPFAsmParser -lLLVMBPFCodeGen -lLLVMBPFDesc -lLLVMBPFInfo -lLLVMAVRDisassembler -lLLVMAVRAsmParser -lLLVMAVRCodeGen -lLLVMAVRDesc -lLLVMAVRInfo -lLLVMARMDisassembler -lLLVMARMAsmParser -lLLVMARMCodeGen -lLLVMARMDesc -lLLVMARMUtils -lLLVMARMInfo -lLLVMAMDGPUDisassembler -lLLVMAMDGPUAsmParser -lLLVMAMDGPUCodeGen -lLLVMAMDGPUDesc -lLLVMAMDGPUUtils -lLLVMAMDGPUInfo -lLLVMAArch64Disassembler -lLLVMAArch64AsmParser -lLLVMAArch64CodeGen -lLLVMAArch64Desc -lLLVMAArch64Utils -lLLVMAArch64Info -lLLVMOrcJIT -lLLVMMCJIT -lLLVMJITLink -lLLVMOrcTargetProcess -lLLVMOrcShared -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMDebugInfoGSYM -lLLVMOption -lLLVMObjectYAML -lLLVMMCA -lLLVMMCDisassembler -lLLVMLTO -lLLVMPasses -lLLVMCFGuard -lLLVMCoroutines -lLLVMObjCARCOpts -lLLVMHelloNew -lLLVMipo -lLLVMVectorize -lLLVMLinker -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMFrontendOpenACC -lLLVMExtensions -lLLVMDWARFLinker -lLLVMGlobalISel -lLLVMMIRParser -lLLVMAsmPrinter -lLLVMDebugInfoDWARF -lLLVMSelectionDAG -lLLVMCodeGen -lLLVMIRReader -lLLVMAsmParser -lLLVMInterfaceStub -lLLVMFileCheck -lLLVMFuzzMutate -lLLVMTarget -lLLVMScalarOpts -lLLVMInstCombine -lLLVMAggressiveInstCombine -lLLVMTransformUtils -lLLVMBitWriter -lLLVMAnalysis -lLLVMProfileData -lLLVMObject -lLLVMTextAPI -lLLVMMCParser -lLLVMMC -lLLVMDebugInfoCodeView -lLLVMDebugInfoMSF -lLLVMBitReader -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMSupport -lLLVMDemangle -lc++ -lc --pkg-begin build_options /Users/jakubkonka/dev/zig/zig-cache/test_build_options.zig --pkg-end --cache-dir /Users/jakubkonka/dev/zig/zig-cache --global-cache-dir /Users/jakubkonka/.cache/zig --name test --pkg-begin stage2_tests /Users/jakubkonka/dev/zig/test/stage2/test.zig --pkg-end
error: the following build command failed with exit code 1:
/Users/jakubkonka/dev/zig/zig-cache/o/638aabbbae2995a29c3549f94d117c93/build /usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig /Users/jakubkonka/dev/zig /Users/jakubkonka/dev/zig/zig-cache /Users/jakubkonka/.cache/zig test-stage2 -Denable-llvm

Why that is, I have no idea, and I'll need to investigate further. I will also report this result downstream.

@kubkon
Copy link
Member

kubkon commented May 27, 2021

Y'all, after some more debugging, this issue is with Homebrew and how LLVM is packaged rather than Zig. It turns out actually that we already dealt with it in the past back when we used LLVM-10: link.
The meat of the issue is that currently, Homebrew distributed LLVM, configures libLLVM for dynamic linking, while libClang for static linking, causing some bizarre init order fiasco/interdependence which results in the errors seen in this issue as well as other as reported by you. There are two workarounds to this currently:

  1. cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) -DZIG_PREFER_CLANG_CPP_DYLIB=on which will force both libLLVM and libClang to be dynamically linked, or
  2. cmake .. -DCMAKE_PREFIX_PATH=$(brew --prefix llvm) -DZIG_STATIC_LLVM=on which will force both libLLVM and libClang to be statically linked

Both workarounds should get rid of the problem if you're building zig from source using system tools and Homebrew shipped LLVM. The end solution however will be to take it up with Homebrew devs themselves to work out a suitable patch. The issue is so much more annoying since this means that Zig distributed via Homebrew (built from HEAD) will inevitably be broken.

Alas, I've got some bad news. This workaround doesn't seem to fix everything. Regardless if you add either flag to CMake, the resultant build of zig while passes the augmented Homebrew tests, it fails at stage2 tests which involve pulling in LLVM:

$ brew test zig
==> Testing zig
==> /usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig build-exe hello.zig
==> ./hello
==> /usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig cc hello.c -o hello
==> ./hello

$ zig build test-stage2 -Denable-llvm
error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang.cpp:22:10: fatal error: 'clang/Frontend/ASTUnit.h' file not found

error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang_cc1as_main.cpp:14:10: fatal error: 'clang/Basic/Diagnostic.h' file not found

error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang_cc1_main.cpp:15:10: fatal error: 'clang/Basic/Stack.h' file not found

error(compilation): clang failed with stderr: In file included from /Users/jakubkonka/dev/zig/src/zig_llvm.cpp:16:
/Users/jakubkonka/dev/zig/src/zig_llvm.h:13:10: fatal error: 'llvm-c/Core.h' file not found

error(compilation): clang failed with stderr: /Users/jakubkonka/dev/zig/src/zig_clang_driver.cpp:14:10: fatal error: 'clang/Driver/Driver.h' file not found

/Users/jakubkonka/dev/zig/src/zig_clang.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_clang_cc1as_main.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_llvm.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_clang_driver.cpp:1:1: error: unable to build C object: clang exited with code 1
/Users/jakubkonka/dev/zig/src/zig_clang_cc1_main.cpp:1:1: error: unable to build C object: clang exited with code 1
The following command exited with error code 1:
/usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig test /Users/jakubkonka/dev/zig/src/test.zig -cflags -std=c++14 -D__STDC_CONSTANT_MACROS -D__STDC_FORMAT_MACROS -D__STDC_LIMIT_MACROS -D_GNU_SOURCE -fvisibility-inlines-hidden -fno-exceptions -fno-rtti -Werror=type-limits -Wno-missing-braces -Wno-comment -DNDEBUG=1 -- /Users/jakubkonka/dev/zig/src/zig_llvm.cpp /Users/jakubkonka/dev/zig/src/zig_clang.cpp /Users/jakubkonka/dev/zig/src/zig_clang_driver.cpp /Users/jakubkonka/dev/zig/src/zig_clang_cc1_main.cpp /Users/jakubkonka/dev/zig/src/zig_clang_cc1as_main.cpp /Users/jakubkonka/dev/zig/src/windows_sdk.cpp -lclangFrontendTool -lclangCodeGen -lclangFrontend -lclangDriver -lclangSerialization -lclangSema -lclangStaticAnalyzerFrontend -lclangStaticAnalyzerCheckers -lclangStaticAnalyzerCore -lclangAnalysis -lclangASTMatchers -lclangAST -lclangParse -lclangSema -lclangBasic -lclangEdit -lclangLex -lclangARCMigrate -lclangRewriteFrontend -lclangRewrite -lclangCrossTU -lclangIndex -lclangToolingCore -llldDriver -llldMinGW -llldELF -llldCOFF -llldMachO -llldWasm -llldReaderWriter -llldCore -llldYAML -llldCommon -lLLVMWindowsManifest -lLLVMXRay -lLLVMLibDriver -lLLVMDlltoolDriver -lLLVMCoverage -lLLVMLineEditor -lLLVMXCoreDisassembler -lLLVMXCoreCodeGen -lLLVMXCoreDesc -lLLVMXCoreInfo -lLLVMX86Disassembler -lLLVMX86AsmParser -lLLVMX86CodeGen -lLLVMX86Desc -lLLVMX86Info -lLLVMWebAssemblyDisassembler -lLLVMWebAssemblyAsmParser -lLLVMWebAssemblyCodeGen -lLLVMWebAssemblyDesc -lLLVMWebAssemblyInfo -lLLVMSystemZDisassembler -lLLVMSystemZAsmParser -lLLVMSystemZCodeGen -lLLVMSystemZDesc -lLLVMSystemZInfo -lLLVMSparcDisassembler -lLLVMSparcAsmParser -lLLVMSparcCodeGen -lLLVMSparcDesc -lLLVMSparcInfo -lLLVMRISCVDisassembler -lLLVMRISCVAsmParser -lLLVMRISCVCodeGen -lLLVMRISCVDesc -lLLVMRISCVInfo -lLLVMPowerPCDisassembler -lLLVMPowerPCAsmParser -lLLVMPowerPCCodeGen -lLLVMPowerPCDesc -lLLVMPowerPCInfo -lLLVMNVPTXCodeGen -lLLVMNVPTXDesc -lLLVMNVPTXInfo -lLLVMMSP430Disassembler -lLLVMMSP430AsmParser -lLLVMMSP430CodeGen -lLLVMMSP430Desc -lLLVMMSP430Info -lLLVMMipsDisassembler -lLLVMMipsAsmParser -lLLVMMipsCodeGen -lLLVMMipsDesc -lLLVMMipsInfo -lLLVMLanaiDisassembler -lLLVMLanaiCodeGen -lLLVMLanaiAsmParser -lLLVMLanaiDesc -lLLVMLanaiInfo -lLLVMHexagonDisassembler -lLLVMHexagonCodeGen -lLLVMHexagonAsmParser -lLLVMHexagonDesc -lLLVMHexagonInfo -lLLVMBPFDisassembler -lLLVMBPFAsmParser -lLLVMBPFCodeGen -lLLVMBPFDesc -lLLVMBPFInfo -lLLVMAVRDisassembler -lLLVMAVRAsmParser -lLLVMAVRCodeGen -lLLVMAVRDesc -lLLVMAVRInfo -lLLVMARMDisassembler -lLLVMARMAsmParser -lLLVMARMCodeGen -lLLVMARMDesc -lLLVMARMUtils -lLLVMARMInfo -lLLVMAMDGPUDisassembler -lLLVMAMDGPUAsmParser -lLLVMAMDGPUCodeGen -lLLVMAMDGPUDesc -lLLVMAMDGPUUtils -lLLVMAMDGPUInfo -lLLVMAArch64Disassembler -lLLVMAArch64AsmParser -lLLVMAArch64CodeGen -lLLVMAArch64Desc -lLLVMAArch64Utils -lLLVMAArch64Info -lLLVMOrcJIT -lLLVMMCJIT -lLLVMJITLink -lLLVMOrcTargetProcess -lLLVMOrcShared -lLLVMInterpreter -lLLVMExecutionEngine -lLLVMRuntimeDyld -lLLVMSymbolize -lLLVMDebugInfoPDB -lLLVMDebugInfoGSYM -lLLVMOption -lLLVMObjectYAML -lLLVMMCA -lLLVMMCDisassembler -lLLVMLTO -lLLVMPasses -lLLVMCFGuard -lLLVMCoroutines -lLLVMObjCARCOpts -lLLVMHelloNew -lLLVMipo -lLLVMVectorize -lLLVMLinker -lLLVMInstrumentation -lLLVMFrontendOpenMP -lLLVMFrontendOpenACC -lLLVMExtensions -lLLVMDWARFLinker -lLLVMGlobalISel -lLLVMMIRParser -lLLVMAsmPrinter -lLLVMDebugInfoDWARF -lLLVMSelectionDAG -lLLVMCodeGen -lLLVMIRReader -lLLVMAsmParser -lLLVMInterfaceStub -lLLVMFileCheck -lLLVMFuzzMutate -lLLVMTarget -lLLVMScalarOpts -lLLVMInstCombine -lLLVMAggressiveInstCombine -lLLVMTransformUtils -lLLVMBitWriter -lLLVMAnalysis -lLLVMProfileData -lLLVMObject -lLLVMTextAPI -lLLVMMCParser -lLLVMMC -lLLVMDebugInfoCodeView -lLLVMDebugInfoMSF -lLLVMBitReader -lLLVMCore -lLLVMRemarks -lLLVMBitstreamReader -lLLVMBinaryFormat -lLLVMSupport -lLLVMDemangle -lc++ -lc --pkg-begin build_options /Users/jakubkonka/dev/zig/zig-cache/test_build_options.zig --pkg-end --cache-dir /Users/jakubkonka/dev/zig/zig-cache --global-cache-dir /Users/jakubkonka/.cache/zig --name test --pkg-begin stage2_tests /Users/jakubkonka/dev/zig/test/stage2/test.zig --pkg-end
error: the following build command failed with exit code 1:
/Users/jakubkonka/dev/zig/zig-cache/o/638aabbbae2995a29c3549f94d117c93/build /usr/local/Cellar/zig/HEAD-d8d92da_1/bin/zig /Users/jakubkonka/dev/zig /Users/jakubkonka/dev/zig/zig-cache /Users/jakubkonka/.cache/zig test-stage2 -Denable-llvm

Why that is, I have no idea, and I'll need to investigate further. I will also report this result downstream.

OK, better news, this only happens with zig master built using homebrew brew install zig --head.

@kubkon
Copy link
Member

kubkon commented May 27, 2021

Nvm, I think this might be a regression in zig itself as I can repro this regardless of whether brew's LLVM was used or not.

@carlocab
Copy link

Homebrew/homebrew-core#78336 which fixes this in Homebrew has been merged.

@jedisct1
Copy link
Contributor

Great news! 🎉

@kubkon
Copy link
Member

kubkon commented May 29, 2021

Closing as it's been fixed in Homebrew. However, I also note that there is one final bit of functionality missing in zig ld before we get a fully functional zig binary via Homebrew as documented here #8935.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Observed behavior contradicts documented or intended behavior downstream An issue with a third party project that uses Zig. os-macos
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants