brew 升级 cmake 失败:clang++ 找不到 iostream

摘要

  • 本机环境 15.8.1-x86_64。
  • Homebrew 7.0.8 不再为 Intel Mac 提供 cmake 的 bottle,brew upgrade cmake 会从源码执行 ./bootstrap。cmake 4.4.4 停在 Cannot find a C++ compiler that supports both C++11。
  • 失败点在 C++ 头文件的搜索路径,不在某一个依赖 cmake 的软件包。直接升级或安装 cmake,以及任何要在本机编译 cmake 的 formula,都会停在同一处。这次是升级 wget 时把它作为依赖编出来的。
  • /usr/bin/clang++ 是 Apple Clang 17.0.0,clang++ --version 正常,CC、CXX、CXXFLAGS 都没有设置。用一段 C++11 程序编译,报错是 fatal error: 'iostream' file not found。
  • iostream 在各个 SDK 里都有,例如 /Library/Developer/CommandLineTools/SDKs/MacOSX26.2.sdk/usr/include/c++/v1/iostream。编译器实际搜索的是 /Library/Developer/CommandLineTools/usr/include/c++/v1。这条路径按编译器自己的位置计算:InstalledDir 是 CommandLineTools/usr/bin,libc++ 头文件放在它的上一级 usr/include/c++/v1。这个目录还在,里面没有 iostream。目录一旦存在,驱动就只用它,不再把 SDK 里的那份加入搜索路径。
  • 把这个目录改名为 c++.backup 之后,同一段 C++11 程序可以编译并输出 123,再执行 brew upgrade cmake。
  • 日志里的 Intel x86_64、Tier 3 警告说明这次为什么要本地编译。Homebrew 在 Intel 上的支持变化见 MacOS软件包管理器--brew 文末「从 4.5 到 7.0」。

升级时的日志

  • 下面的 ./bootstrap 是 cmake 4.4.4 自己的配置步骤。当时它出现在 brew upgrade wget 安装依赖的过程中;单独执行 brew upgrade cmake 或 brew install cmake,日志停在同一段:

1
2
==> Upgrading wget dependency: cmake
==> ./bootstrap --prefix=/usr/local/Cellar/cmake/4.4.4 --no-system-libs --parallel=12
  • Bootstrap.cmk/cmake_bootstrap.log 里和编译器相关的几行:

1
2
3
4
5
6
7
CMake 4.4.4, Copyright 2000-2026 Kitware, Inc. and Contributors
C compiler on this system is: clang

Error when bootstrapping CMake:
Cannot find a C++ compiler that supports both C++11 and the specified C++ flags.
Please specify one using environment variable CXX.
The C++ flags are "".
  • CMake 这里只说明自己没能用当前环境编过一段 C++11。它不会写出缺的是哪一个头文件。后面要自己用 clang++ 编一段最小程序,才能看到 iostream。

本机环境

1
2
3
4
5
6
7
8
9
10
11
12
uname -m                         x86_64
macOS 15.8.1-x86_64
clang target x86_64-apple-darwin24.6.0
Homebrew 7.0.8
HOMEBREW_PREFIX /usr/local
clang / clang++ /usr/bin/clang、/usr/bin/clang++
Apple clang 17.0.0 (clang-1700.6.4.2)
InstalledDir /Library/Developer/CommandLineTools/usr/bin
xcode-select -p /Library/Developer/CommandLineTools
CLT 26.3.0.0.1.1771626560
Xcode 未安装
CPU(brew config) dodeca-core 64-bit kabylake
  • 六核 Core i7 开超线程之后,Homebrew 报成 12 核。xcrun --find clang++ 指向 /Library/Developer/CommandLineTools/usr/bin/clang++。

  • 当时 CC、CXX、CFLAGS、CXXFLAGS、CPPFLAGS、LDFLAGS、SDKROOT 都没有设置:

1
env | grep -E '^(CC|CXX|CFLAGS|CXXFLAGS|CPPFLAGS|LDFLAGS|SDKROOT)=' | sort

用一段 C++11 程序看头文件

  • clang++ --version 只证明编译器可执行文件在。C++ 标准库要靠一次真正的编译。std::unique_ptr 在 C++11 里就有,和 CMake bootstrap 检查的语言标准一致:

1
2
3
4
5
6
7
8
9
10
11
12
13
cat >/tmp/test-cxx11.cpp <<'EOF'
#include <iostream>
#include <memory>

int main() {
std::unique_ptr<int> p(new int(123));
std::cout << *p << '\n';
return 0;
}
EOF

clang++ -std=c++11 /tmp/test-cxx11.cpp -o /tmp/test-cxx11
echo "compile exit code: $?"
  • 本机结果:

1
2
3
4
5
/tmp/test-cxx11.cpp:1:10: fatal error: 'iostream' file not found
1 | #include <iostream>
| ^~~~~~~~~~
1 error generated.
compile exit code: 1
  • SDK 本身是存在的:

1
2
xcrun --sdk macosx --show-sdk-path
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk

头文件在 SDK 里

  • 在 Command Line Tools 下面按文件名找,iostream 出现在多个历史 SDK 中,当前默认 SDK 里也有:

1
2
3
4
5
6
/Library/Developer/CommandLineTools/SDKs/MacOSX13.1.sdk/usr/include/c++/v1/iostream
/Library/Developer/CommandLineTools/SDKs/MacOSX14.5.sdk/usr/include/c++/v1/iostream
/Library/Developer/CommandLineTools/SDKs/MacOSX15.2.sdk/usr/include/c++/v1/iostream
/Library/Developer/CommandLineTools/SDKs/MacOSX15.4.sdk/usr/include/c++/v1/iostream
/Library/Developer/CommandLineTools/SDKs/MacOSX15.5.sdk/usr/include/c++/v1/iostream
/Library/Developer/CommandLineTools/SDKs/MacOSX26.2.sdk/usr/include/c++/v1/iostream
  • 默认 SDK 是一个指向 MacOSX26.2.sdk 的符号链接。机器上还留着 13.1、14.5、15.2、15.4、15.5,都是历次系统升级剩下的 SDK。

1
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -> MacOSX26.2.sdk
  • 工具链自己的 C++ 头文件目录也还在,ls -ld 显示日期是 2025-11-19。这个目录里没有 iostream:

1
2
3
4
5
/Library/Developer/CommandLineTools/usr/include/c++/v1          目录存在
/Library/Developer/CommandLineTools/usr/include/c++/v1/iostream No such file or directory

/Library/Developer/CommandLineTools/SDKs/MacOSX26.2.sdk/usr/include/c++/v1/iostream
-rw-r--r-- 1 root wheel 1707 Nov 9 2025

编译器搜到了残缺目录

  • echo | clang++ -v -E -x c++ - 把实际搜索顺序列出来。本机 C++ 头文件的那一项是工具链目录,系统头文件才走当前 SDK:

1
2
3
4
5
6
7
8
9
-isysroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk
-internal-isystem /Library/Developer/CommandLineTools/usr/bin/../include/c++/v1

#include <...> search starts here:
/usr/local/include
/Library/Developer/CommandLineTools/usr/bin/../include/c++/v1
/Library/Developer/CommandLineTools/usr/lib/clang/17/include
/Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include
/Library/Developer/CommandLineTools/usr/include
  • usr/bin/../include/c++/v1 就是 /Library/Developer/CommandLineTools/usr/include/c++/v1。它出现在搜索列表里,是驱动自己加的 -internal-isystem,不是 CPATH、CPLUS_INCLUDE_PATH 或 CMake 传进来的。

  • /usr/bin/clang++ 会转到 xcode-select -p 选中的编译器。当时选中的是 /Library/Developer/CommandLineTools,所以 InstalledDir 是 /Library/Developer/CommandLineTools/usr/bin。Apple Clang 把 libc++ 头文件放在编译器旁边,路径按 InstalledDir/../include/c++/v1 计算:从 usr/bin 退回 usr,再进 include/c++/v1。v1 是 libc++ 头文件的目录名。

  • C 头文件和 C++ 头文件分开找。平台头文件和 C 库走 -isysroot 下的 MacOSX.sdk/usr/include。iostream 属于 libc++,走紧挨着编译器的那条路径。SDK 里的 usr/include/c++/v1/iostream 因此不会自动补进搜索列表。驱动只看工具链这个 c++/v1 目录在不在,不看里面有没有 iostream。目录还在,就固定用它。

  • 把工具链的 c++ 目录改名之后,这个位置不存在了,驱动才改用当前 SDK 里的 usr/include/c++/v1。

  • 因此把 SDKROOT 指到已经含有 iostream 的 MacOSX26.2.sdk,结果不变,仍然是 iostream file not found:

1
2
SDKROOT=/Library/Developer/CommandLineTools/SDKs/MacOSX26.2.sdk \
clang++ -std=c++11 /tmp/test-cxx11.cpp -o /tmp/test-cxx11
  • 重装 Command Line Tools、从 SDK 里单独拷贝 iostream,或把 CXX 设成 /usr/bin/clang++,都不会改掉上面这条搜索路径。要挪开的是编译器第一个会去看的那份残缺目录。

修复

  • 先确认没有别的编译任务在跑。把残缺目录改名,先留着,不删除:

1
2
3
sudo mv \
/Library/Developer/CommandLineTools/usr/include/c++ \
/Library/Developer/CommandLineTools/usr/include/c++.backup
  • 目录让开之后,Apple Clang 会改用 SDK 里的 libc++ 头文件。用上面同一段程序再编译一次:

1
clang++ -std=c++11 /tmp/test-cxx11.cpp -o /tmp/test-cxx11 && /tmp/test-cxx11
  • 正常输出:

1
123
  • 可以再看一眼搜索路径,确认 include/c++/v1 不再指向刚才改名的那份残缺目录:

1
echo | clang++ -v -E -x c++ - 2>&1

再升级 cmake

  • C++11 测试输出 123 之后:

1
brew upgrade cmake
  • 头文件恢复后,其他要从源码编译、并且依赖 cmake 的包也可以继续装。c++.backup 先留着,cmake 升级成功再决定要不要删掉。需要退回时把它改回原名:

1
2
3
sudo mv \
/Library/Developer/CommandLineTools/usr/include/c++.backup \
/Library/Developer/CommandLineTools/usr/include/c++

改名之后仍然找不到 iostream

  • 如果 SDK 里的 iostream 也不存在,改名解决不了。那时再重装 Command Line Tools。本机不需要这一步,因为 MacOSX26.2.sdk 里的头文件是完整的。

1
2
sudo rm -rf /Library/Developer/CommandLineTools
xcode-select --install
  • 装完后确认:

1
2
3
xcode-select -p
ls -l /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk/usr/include/c++/v1/iostream
clang++ -std=c++11 /tmp/test-cxx11.cpp -o /tmp/test-cxx11 && /tmp/test-cxx11

和 Intel Tier 3 的关系

  • 同一次升级末尾还有一段:

1
2
3
4
5
Warning: You are using macOS on Intel x86_64.
We do not provide support for this platform (as-of September 2026, announced August 2025).
Homebrew no longer builds bottles for this configuration.
This is a Tier 3 configuration:
https://docs.brew.sh/Support-Tiers#tier-3
  • 2026 年 9 月起,Homebrew 不再为 Intel Mac 构建新的 bottle。cmake 4.4.4 因此走 ./bootstrap 本地编译,机器上这份残缺的 C++ 头文件目录才被碰到。以前有 bottle 时,Homebrew 直接倒预编译包,不会调用 clang++。

  • 警告里提到的 MacPorts 是 Intel 上继续使用预编译包的一条路,安装和迁移见 MacOS软件包管理器--MacPorts。继续用 Homebrew 从源码装 cmake 时,先把上面的头文件搜索恢复。