Repository navigation
Open
Labels
bugObserved behavior contradicts documented or intended behaviorObserved behavior contradicts documented or intended behaviorcontributor friendlyThis issue is limited in scope and/or knowledge of Zig internals.This issue is limited in scope and/or knowledge of Zig internals.zig ccZig as a drop-in C compiler featureZig as a drop-in C compiler feature
Milestone
Description
Zig Version
0.15-dev
Steps to Reproduce and Observed Behavior
I figured out why it happens. zig cc is not behaving like gcc and clang do with -isystem and -I directories.
zig cc
[m@M-TH build]$ zig cc -E -H iconv.c -isystem /home/m/static-php-cli/buildroot/include | grep iconv
. /usr/lib/zig/libc/include/generic-glibc/iconv.h
.. /usr/lib/zig/libc/include/generic-glibc/features.h
... /usr/lib/zig/libc/include/generic-glibc/features-time64.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/timesize.h
..... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/lib/zig/libc/include/generic-glibc/stdc-predef.h
... /usr/lib/zig/libc/include/generic-glibc/sys/cdefs.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/long-double.h
... /usr/lib/zig/libc/include/x86_64-linux-gnu/gnu/stubs.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/gnu/stubs-64.h
...clang:
[m@M-TH build]$ clang -E -H iconv.c -isystem /home/m/static-php-cli/buildroot/include | grep iconv
. /home/m/static-php-cli/buildroot/include/iconv.h
.. /usr/bin/../lib/clang/19/include/stddef.h
... /usr/bin/../lib/clang/19/include/__stddef_header_macro.h
... /usr/bin/../lib/clang/19/include/__stddef_ptrdiff_t.h
... /usr/bin/../lib/clang/19/include/__stddef_size_t.h
... /usr/bin/../lib/clang/19/include/__stddef_wchar_t.h
... /usr/bin/../lib/clang/19/include/__stddef_null.h
... /usr/bin/../lib/clang/19/include/__stddef_max_align_t.h
... /usr/bin/../lib/clang/19/include/__stddef_offsetof.h
...gcc:
[m@M-TH build]$ gcc -E -H iconv.c -isystem /home/m/static-php-cli/buildroot/include | grep iconv
. /home/m/static-php-cli/buildroot/include/iconv.h
.. /usr/lib/gcc/x86_64-redhat-linux/14/include/stddef.h
.. /usr/include/errno.h
... /usr/include/features.h
.... /usr/include/features-time64.h
..... /usr/include/bits/wordsize.h
..... /usr/include/bits/timesize.h
...... /usr/include/bits/wordsize.h
.... /usr/include/sys/cdefs.h
..... /usr/include/bits/wordsize.h
..... /usr/include/bits/long-double.h
.... /usr/include/gnu/stubs.h
..... /usr/include/gnu/stubs-64.h
...But the real issue is this:
[m@M-TH build]$ zig cc -E -H iconv.c -isystem /home/m/static-php-cli/buildroot/include -I/home/m/static-php-cli/buildroot/include | grep iconv
. /usr/lib/zig/libc/include/generic-glibc/iconv.h
.. /usr/lib/zig/libc/include/generic-glibc/features.h
... /usr/lib/zig/libc/include/generic-glibc/features-time64.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/timesize.h
..... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/lib/zig/libc/include/generic-glibc/stdc-predef.h
# or
[m@M-TH build]$ zig cc -E -H iconv.c -I/home/m/static-php-cli/buildroot/include -isystem /home/m/static-php-cli/buildroot/include | grep iconv
. /usr/lib/zig/libc/include/generic-glibc/iconv.h
.. /usr/lib/zig/libc/include/generic-glibc/features.h
... /usr/lib/zig/libc/include/generic-glibc/features-time64.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
.... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/timesize.h
..... /usr/lib/zig/libc/include/x86_64-linux-gnu/bits/wordsize.h
... /usr/lib/zig/libc/include/generic-glibc/stdc-predef.hZig ignores the -I directory, if it is also specified as a -isystem dir.
zig cc without isystem:
[m@M-TH build]$ zig cc -E -H iconv.c -I/home/m/static-php-cli/buildroot/include | grep iconv
. /home/m/static-php-cli/buildroot/include/iconv.h
.. /usr/lib/zig/include/stddef.h
... /usr/lib/zig/include/__stddef_header_macro.h
... /usr/lib/zig/include/__stddef_ptrdiff_t.h
... /usr/lib/zig/include/__stddef_size_t.h
... /usr/lib/zig/include/__stddef_wchar_t.h
... /usr/lib/zig/include/__stddef_null.h
... /usr/lib/zig/include/__stddef_max_align_t.h
... /usr/lib/zig/include/__stddef_offsetof.h
.. /usr/lib/zig/libc/include/generic-glibc/errno.hSince the php build unconditionally adds all our directories as isystem directories to supress warnings during extension compilation, this cannot be worked around short of patching zig.
Metadata
Metadata
Assignees
Labels
bugObserved behavior contradicts documented or intended behaviorObserved behavior contradicts documented or intended behaviorcontributor friendlyThis issue is limited in scope and/or knowledge of Zig internals.This issue is limited in scope and/or knowledge of Zig internals.zig ccZig as a drop-in C compiler featureZig as a drop-in C compiler feature
Activity
henderkes commentedon Jun 23, 2025
FYI, deleting the generic-glibc/iconv.h file also works as a workaround, but obviously the root cause should be identified and fixed.
henderkes commentedon Jun 24, 2025
Conflicting command:
yellow are the -I directories added by the extension itself, grey are the -isystem directories added by the extension, the first green are necessary CFLAGS passed by php-src and the last green. Only the next paragraph are the CFLAGS and EXTRA_CFLAGS passed by us.
[-][zig cc] php-cli fails to build with zig cc, includes zig/libc/include/generic-glibc/iconv.h before -I/path/to/iconv.h[/-][+][zig cc] command line -isystem paths are incorrectly searched after zig's system paths, instead of before[/+]The-Briel-Deal commentedon Jul 1, 2025
Hey @henderkes!
I'm trying to reproduce this in a smaller environment outside of php currently, I am getting the behavior where if you specify an include path as
-isystemand-I, then it will make that include dir lower priority than a-Ipath that comes later. This behavior is odd but for some reason this behavior is consistent ongcc,clang, andzig cc.test.c
include1/foo.h
include2/foo.h
Usually I would expect the include path that comes first to be searched first. This behavior is as expected with clang here:
But as you showed above, if we add
-isystem include2then the orderedprecendence is completely ignored and include2 is treated like a
-isysteminclude.
Now if I do this with
zig cc, I get the exact same results.Without
-isystem:With
-isystem:Do you know if I'm doing something wrong here? I'm not able to get the behavior to differ from gcc or clang. I agree this behavior is unexpected, but it seems to be uniform with other compilers.
henderkes commentedon Jul 1, 2025
You're not doing anything wrong! The difference is that while gcc and clang process -isystem includes passed by the user before their own system paths, zig prefers to search its own system paths before.
Try to include "iconv.h" like I did in the first post - while gcc and clang will include your own file passed as -isystem, zig will instead include generic-glib/iconv.h.
The-Briel-Deal commentedon Jul 2, 2025
That makes sense, I'll try this again against
iconv.hin a bit! Thaticonv.hheader is part of the php interpreter's source code right?henderkes commentedon Jul 3, 2025
Iconv.h is part of libc and part of the installation of libiconv.
When you compile it yourself as a static library, you get extra functionality and definitions compared to the system library. Php's iconv extension requires a full libiconv installation and not just the system one.
But that's not really important to the issue here - just create a iconv.h file yourself and pass it as -isystem - zig will use the generic-glibc/iconv.h, while gcc and clang will use the one you passed in -isystem.
The-Briel-Deal commentedon Jul 3, 2025
Alright @henderkes, I think i've got this reproduced, let me know if this is the same issue. In this below example I've got the same directory structure as my above comment:
Ok, so if I export
C_INCLUDE_PATH=$PWD/include2/and use the flag-isystem include1then zig will override the path set via cli flag with theC_INCLUDE_PATHenv var. But with clang and gcc, the same setup prefers the path from the cli flag.So currently the zig include path search order is:
$C_INCLUDE_PATH-isystemBut the gcc and clang include path search order is:
-isystem$C_INCLUDE_PATHSo the desired behavior of zig cc is that the search order matches gcc and clang right?
The-Briel-Deal commentedon Jul 3, 2025
Out of curiousity I just took a look at at the ISO C99 spec to see if there is a defined way to order search paths and it says it is implementation defined for angle and quote includes in section
6.10.2 Source file inclusion:https://www.dii.uchile.cl/~daespino/files/Iso_C_1999_definition.pdf
So I'm assuming we want to just follow the behavior of clang and gcc as close as we can.
henderkes commentedon Jul 3, 2025
Correct, it should be adjusted to use -isystem before internal paths. I tried to do the change myself, but couldn't test because compiling zig failed on the step (3?) where zig compiles zig.
The-Briel-Deal commentedon Jul 3, 2025
Oh! I had a similar issue, try downloading the latest nightly release and just do a
zig buildwith that. I had issues with the cmake based compilation too.Follow https://github.com/ziglang/zig/wiki/Building-Zig-From-Source#option-b-use-a-pre-built-zig-binary where you use zig bootstrap to get the right llvm version too.
10 remaining items
fix(cc): move user provided `zig cc` args to front of command passed β¦
fix(cc): move user provided `zig cc` args to front of command passed β¦
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `zig cc` args to front of command
fix(cc): move user provided `-isystem` paths to front of command
fix(cc): move user provided `-isystem` paths to front of command
fix(cc): move user provided `-isystem` paths to front of command
fix(cc): move user provided `-isystem` paths to front of command