/usr/local/include 无意中添加到我的编译命令导致构建失败

/usr/local/include unintendedly added to my compile commands results in build failing

背景

我正在一台运行 macOS 10.15 的机器上编译一个项目,该项目将旧版本的 libpng (1.6.17) 作为子模块提供。相应的代码可在 https://github.com/glennrp/libpng 获得。我还安装了 Homebrew 的 libpng 1.6.37。

直到不久前,我还能够使用 CMake 轻松编译 libpng 1.6.17。从最近开始(但我不知道确切日期),构建失败并出现如下错误:

FAILED: CMakeFiles/png16_static.dir/pngwutil.o
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc  -I/usr/local/include -I. -I../ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -MD -MT CMakeFiles/png16_static.dir/pngwutil.o -MF CMakeFiles/png16_static.dir/pngwutil.o.d -o CMakeFiles/png16_static.dir/pngwutil.o   -c ../pngwutil.c
../pngwutil.c:2413:20: error: use of undeclared identifier 'PNG_WEIGHT_SHIFT'
                   PNG_WEIGHT_SHIFT;
                   ^

我 运行 对我的项目副本进行了一些检查,该副本仍然正确编译,因为 CMake 本身不在其中 re-running。这两种情况之间的唯一区别是编译器调用中添加了 -I/usr/local/include 标志(我添加了一些标记以帮助查看区别):

我在正在运行的项目副本上 re-ran CMake,但我遇到了同样的错误,这让我遇到了 system-related 问题。然后我直接检查了 libpng 源并得到了同样的错误。

重现步骤

  1. 克隆 libpng 存储库并检查 v1.6.17
    git clone https://github.com/glennrp/libpng.git
    cd libpng
    git checkout v1.6.17
    
  2. 建立libpng
    cmake . -B build && cmake --build build
    

问题

什么将这个 -I/usr/local/include 标志添加到我的编译器调用中?

奖金问题(可能更有趣)

现在,它变得有趣了。如果您签出更新的 libpng(我尝试使用 1.6.21、1.6.25、1.6.28、1.6.33 和 1.6.37),问题就会消失,尽管标志仍然存在:

/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/cc  -I/usr/local/include -I. -I../ -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk -MD -MT CMakeFiles/png16_static.dir/pngrio.o -MF CMakeFiles/png16_static.dir/pngrio.o.d -o CMakeFiles/png16_static.dir/pngrio.o   -c ../pngrio.c

这意味着我可以使用这些版本之一更新我的子模块,我的问题就会消失。

但是,如果我没记错的话,-I 标志是从左到右解析的:因此我怀疑使用了我的 Homebrew headers 而不是源代码。如果我是对的,那么这并不能保证 libpng 的 Homebrew 升级不会再次破坏构建:它只是表明 libpng 的 API 自 v1.6.21 以来一直稳定并且我可以将 Homebrew headers 与我尝试编译的源一起使用。我是对的,还是我漏掉了什么?

  1. 问题回答:系统包含路径由compiler/preprocessor添加(this page将解释更多数据)
  2. 包含在CMake项目中的顺序可能会改变(不确定多少),CMake允许在列表中预先包含;尝试比较版本之间的 CMake 构建脚本。相信会有提到的变化。

是的,有时 cmake 会选择各种库并包含目录并自由混合和匹配。如果你需要保持多个版本,你应该使用 cmake 命令来传递正确的包。

我终于弄明白了。结果是 libpng 的 zlib 依赖项给我带来了麻烦(间接)。

发生了什么事

MacOS 附带 zlib,开发人员通常都知道它,因此觉得不需要安装第 3 方 zlib。然而,CMake 的 find_package 不知道这个偏好,并且会选择在 /usr/local 中找到的 zlib 实现,例如,如果它是由 Homewbrew 安装的。出于某种原因,第 3 方软件在我系统的这个位置安装了一个 zlib,而不是 Homebrew 包,这使得检测更加困难,它被 find_package.

检测到

对应的include目录为/usr/local/include。 libpng 的 CMake 代码是这样的,这个目录然后被添加到包含目录的列表中,这导致了问题中提到的 header 冲突。我通过 CMakeCache.txt(搜索 /usr/local/include)了解发生了什么,所以主要的教训是:在这种情况下不要忘记检查您的 CMake 缓存.

如何解决问题

懒惰的方式。删除不需要的 lib 文件。我 运行 brew doctor 并删除了不应该在此处的文件。但是,如果某些软件实际上需要 /usr/local 中的特定 zlib 版本,这可能会产生不良后果。

肮脏的 CMake 方式。修改 top-level CMake 代码以提示 find_package 它应该选择 zlib 的位置。使用 PATHS 参数 hard-code 提示或使用 ZLIB_ROOT 参数设置提示(您可能必须为此定义策略)。

我确信有更好的方法可以通过在 libpng 中 "doing CMake right" 和在系统路径中强制库搜索来处理这个问题,但我的 CMake 技能不足以说明应该做什么。不管怎样,关于这个问题有点off-topic。