PAYATHON 2026

MSYS2 构建 Windows→Linux GCC 时为何出现 sysrootC:/ 头文件路径?

数据小许

结论

日志显示 GCC 已经使用了 sysroot,但参与拼接的系统头文件目录成了带盘符的 Windows 路径。异常路径是:

C:/msys64/mingw64/home/prajval/cross/sysrootC:/msys64/usr/include

正常情况下,该目录应为:

C:/msys64/mingw64/home/prajval/cross/sysroot/usr/include

构建 GCC 时,MSYS2 可能把应保留为 /usr/include 的目标系统路径转成了 C:/msys64/usr/include,值得优先检查。这与 MSYS2 的自动路径转换机制吻合,但现有日志还无法确定转换发生在哪一步,也不足以认定这是 GCC 13.3.0 的已确认缺陷。

为什么会出现 C:/msys64/usr/include?

这里涉及两种含义不同的路径:

  • sysroot 的位置属于 Windows 文件系统,可以写成 C:/msys64/.../sysroot。
  • /usr/include 属于目标 Linux 文件系统,GCC 应当在 sysroot 内解释它。

--with-native-system-header-dir=/usr/include 与 --with-sysroot 配合使用时,指定的是 sysroot 内的系统头文件目录,不能将前者改成 Windows 上某个头文件目录的绝对路径。GCC 配置文档

MSYS2 启动原生 Windows 程序时,会自动转换看起来像 Unix 路径的参数,构建命令中的路径字符串也可能因此受影响。使用 mingw64.exe 不会关闭这一机制。MSYS2 路径转换文档

GCC 13.3.0 的构建规则会通过 -DNATIVE_SYSTEM_HEADER_DIR=... 和 -DLOCAL_INCLUDE_DIR=... 将默认头文件目录编译进程序。因此,即使 configure 的参数记录仍显示 /usr/include,也值得检查构建时传给原生编译器的宏值。GCC 13.3.0 构建规则

先确定路径在哪一步发生变化

在原构建目录中检查生成的配置:

rg -n 'NATIVE_SYSTEM_HEADER_DIR|LOCAL_INCLUDE_DIR|TARGET_SYSTEM_ROOT|C:/msys64/usr/include' \
  gcc/Makefile gcc/config.log

重点查看 NATIVE_SYSTEM_HEADER_DIR 是否仍为 /usr/include。

如果 Makefile 中已经出现 C:/msys64/usr/include,应向前检查 configure 参数、递归配置和环境变量。如果 Makefile 中仍是 /usr/include,则继续检查 cppdefault.o 的实际编译过程。

可以复制构建日志中编译 cppdefault.o 的命令并加入 -###,查看宿主 GCC 转发的参数,确认最终的宏值是否仍为:

-DNATIVE_SYSTEM_HEADER_DIR="/usr/include"

只看 make 打印的原始命令不一定足够,因为自动转换可能发生在随后启动原生编译器时。

另外,提供的 configure 命令包含 --with-build-sysroot,但 gcc -v 的 Configured with: 记录中没有该选项。应确认检查的可执行文件确实来自这次构建,避免混用旧安装结果。

如何重新构建并保留目标路径?

如果确认问题来自参数转换,可以只对需要保留 Linux 路径含义的参数设置排除规则,让安装目录、源码目录等宿主路径继续正常转换。MSYS2 支持使用分号分隔的参数前缀设置 MSYS2_ARG_CONV_EXCL。MSYS2 参数转换排除规则

下面是重建示例。请在与原构建目录同级的新目录中执行,并确认源码相对路径有效。示例为 sysroot 和安装位置显式生成 Windows 混合格式路径,同时保留目标系统内部的 /usr/include 和 /usr/local:

export MSYS2_ARG_CONV_EXCL='--with-native-system-header-dir=;--with-local-prefix=;-DNATIVE_SYSTEM_HEADER_DIR=;-DLOCAL_INCLUDE_DIR='

sysroot_win="$(cygpath -m /mingw64/home/prajval/cross/sysroot)"
install_win="$(cygpath -m /mingw64/home/prajval/cross/install)"

../../src/gcc-13.3.0/configure \
  --build=x86_64-w64-mingw32 \
  --host=x86_64-w64-mingw32 \
  --target=x86_64-linux-gnu \
  --prefix="$install_win" \
  --with-sysroot="$sysroot_win" \
  --with-build-sysroot="$sysroot_win" \
  --with-native-system-header-dir=/usr/include \
  --with-local-prefix=/usr/local \
  --enable-languages=c,c++ \
  --disable-multilib \
  --disable-nls \
  --disable-bootstrap \
  --enable-shared \
  --enable-threads=posix

make all-gcc
make install-gcc

这里设置 --with-local-prefix,是为了让额外的本地系统头文件目录也采用目标 Linux 路径含义;它仍可能引入 sysroot 内的 /usr/local/include。如果需要严格限定搜索目录,应另行使用显式搜索选项。

排除规则在 configure 和 make 阶段都需要持续生效。仅在运行已构建好的交叉编译器时设置该变量,无法修复已经编译进去的错误默认路径。

也不要把全局 MSYS2_ARG_CONV_EXCL='*' 直接当作完整构建方案:关闭所有转换后,还需自行处理传给原生工具的其他 /mingw64/... 路径。

验证系统头文件是否恢复

使用明确的编译器路径进行检查,避免 PATH 指向旧版本:

cross_gcc=/mingw64/home/prajval/cross/install/bin/x86_64-linux-gnu-gcc.exe

"$cross_gcc" -print-sysroot
"$cross_gcc" -E -v -x c - < /dev/null

检查有效搜索列表是否包含 sysroot 下的 usr/include,以及是否不再出现 sysrootC:/... 这样的拼接结果。GCC 自带的 include、include-fixed 仍可能保留,这是正常的。

再验证实际头文件:

printf '#include <stdc-predef.h>\n' |
  "$cross_gcc" -E -v -x c - > /dev/null

printf '#include <stdio.h>\n' |
  "$cross_gcc" -E -v -x c - > /dev/null

这些是验证命令。现有材料没有提供修复后的执行结果,还不能据此宣称整个工具链已经构建成功。

能否临时显式指定 sysroot 头文件?

可以用绝对路径加入系统头文件目录,判断问题是否集中在默认搜索路径:

sysroot_win="$(cygpath -m /mingw64/home/prajval/cross/sysroot)"
cross_gcc=/mingw64/home/prajval/cross/install/bin/x86_64-linux-gnu-gcc.exe

printf '#include <stdc-predef.h>\n' |
  "$cross_gcc" \
    --sysroot="$sysroot_win" \
    -isystem "$sysroot_win/usr/include" \
    -E -v -x c - > /dev/null

如果该命令成功,只能说明预处理器可以使用显式指定的目录,不能证明默认路径配置、链接路径或目标运行库都已正确。

如果要求排除所有默认系统目录,可以使用 -nostdinc 后逐项添加目录,但这也会移除 GCC 自带头文件的搜索路径,需要一并补齐。对于完整的 C/C++ 工具链,通常应修复默认配置。

Ubuntu sysroot 和后续构建还需要检查什么?

Ubuntu 的头文件可能同时分布在以下目录:

<sysroot>/usr/include
<sysroot>/usr/include/x86_64-linux-gnu

如果 stdc-predef.h 恢复后,又出现 bits/... 等头文件找不到的错误,应检查实际 multiarch 布局及编译器的搜索列表。GCC 文档说明,显式设置 --with-native-system-header-dir 会影响默认的 multiarch 自动检测条件;对于采用上述布局的 sysroot,可以在重新配置时显式加入 --enable-multiarch。GCC multiarch 配置说明

复制 sysroot 时还应保留目录结构,并检查符号链接是否仍能在复制后的树中解析。某个 stdc-predef.h 文件存在,只能证明该文件在磁盘上,不能证明整套 glibc 开发文件、启动文件和库已经齐全。

--with-build-sysroot 用于构建目标库时选择 sysroot,不会修正错误的头文件目录字符串。当前还设置了 --disable-bootstrap,所以后续 make all 的失败应根据实际失败目标判断,不能仅凭“stage2”称呼认定是 bootstrap 第二阶段。GCC sysroot 配置说明

日志中的 C:\Program Files\LLVM\include 也需要单独追踪。它表明还有额外的搜索路径来源,但现有材料不足以确定来自哪个变量或配置。可以先在同一终端检查:

env | rg -i 'CPATH|C_INCLUDE_PATH|CPLUS_INCLUDE_PATH|OBJC_INCLUDE_PATH|GCC_EXEC_PREFIX|COMPILER_PATH|LLVM'

这种故障表现与 MSYS2 的路径转换机制一致,但还需要更多证据才能判断是否对应某个特定 GCC 缺陷。建议先定位路径转换发生的环节,保留目标路径含义,在新构建目录重建,再分别验证预处理和目标库构建。

备注:内容仅供参考。