PAYATHON 2026

在 macOS 安装 MAJIQ 时 Clang 不支持 -fopenmp

排障赵师傅

结论

报错中的 gcc 实际调用的是 Apple Clang,并非 GNU GCC。macOS 自带的 /usr/bin/gcc 是 Clang 的兼容入口。由于 Apple Clang 默认不支持 -fopenmp,MAJIQ 扩展会编译失败:

clang: error: unsupported option '-fopenmp'
error: command 'gcc' failed with exit status 1

可以选择以下任一方案:

  1. 安装 Homebrew 提供的 GNU GCC,并通过 CC 和 CXX 指定 Python 构建系统使用该编译器。这个方案通常更直接。
  2. 安装 Homebrew LLVM 和 libomp,再配置编译器及搜索路径。只执行 brew install llvm 或修改 PATH,未必能让 pip 使用正确的编译器。

Apple Clang、Homebrew LLVM 和 GNU GCC 使用的运行库不要混用。

为什么命令显示 gcc,错误却来自 Clang

先确认当前命令实际指向哪个程序:

which -a gcc g++ clang clang++
gcc --version
g++ --version
xcrun --find gcc

默认的 macOS 环境通常会输出:

Apple clang version ...

因此,日志中的命令即使显示为:

gcc ... -fopenmp

实际执行它的仍是 Apple Clang。命令名为 gcc,并不等于使用了 GNU GCC。

安装 Conda 的 gcc、llvm 或 Homebrew LLVM 后,如果没有设置 CC 和 CXX,setuptools、distutils 或 pip 仍可能调用系统编译器。

方案一:使用 Homebrew GNU GCC

安装 GCC:

brew install gcc

为避免覆盖系统命令,Homebrew 通常会给 GNU GCC 的命令名添加版本号,例如:

gcc-14
g++-14

版本号取决于当前安装的 Homebrew formula,应先查看本机的实际文件:

brew --prefix gcc
ls "$(brew --prefix gcc)/bin/gcc-"*
ls "$(brew --prefix gcc)/bin/g++-"*

如果检查后确认命令为 gcc-14 和 g++-14,请在准备安装 MAJIQ 的同一终端会话中设置:

export CC="$(brew --prefix gcc)/bin/gcc-14"
export CXX="$(brew --prefix gcc)/bin/g++-14"

"$CC" --version
"$CXX" --version

把示例中的 14 换成本机实际版本。输出中应明确包含 GNU GCC 信息,不能出现 Apple clang。

然后创建新的虚拟环境并重新安装:

python3 -m venv env
source env/bin/activate

python -m pip install --upgrade pip setuptools wheel
python -m pip install --upgrade cython numpy GitPython
python -m pip install --no-cache-dir \
  'git+https://bitbucket.org/biociphers/majiq_stable.git#egg=majiq'

使用 python -m pip 可以确认 pip 来自当前虚拟环境,避免误用其他 Python 环境中的 pip。

如果 MAJIQ 的构建脚本硬编码了绝对路径 /usr/bin/gcc,设置 CC 不会生效。此时需要检查源码中的 setup.py 或相关构建配置,让它读取 CC、CXX,或者将编译器路径改为实际的 Homebrew GCC 路径。不要替换或覆盖系统的 /usr/bin/gcc。

方案二:使用 Homebrew LLVM 和 libomp

继续使用 Clang 时,需要安装 Homebrew LLVM 和 OpenMP 运行库:

brew install llvm libomp

在当前终端中设置编译器和搜索路径:

export LLVM_PREFIX="$(brew --prefix llvm)"
export OMP_PREFIX="$(brew --prefix libomp)"

export PATH="$LLVM_PREFIX/bin:$PATH"
export CC="$LLVM_PREFIX/bin/clang"
export CXX="$LLVM_PREFIX/bin/clang++"

export CPPFLAGS="-I$OMP_PREFIX/include ${CPPFLAGS:-}"
export LDFLAGS="-L$OMP_PREFIX/lib -Wl,-rpath,$OMP_PREFIX/lib ${LDFLAGS:-}"

确认当前选中的编译器不是 /usr/bin/clang:

"$CC" --version
"$CXX" --version
command -v clang

再运行一个最小的 OpenMP 测试:

cat > /tmp/openmp-test.c <<'EOF'
#include <omp.h>
#include <stdio.h>

int main(void) {
    printf("%d\n", omp_get_max_threads());
    return 0;
}
EOF

"$CC" -fopenmp /tmp/openmp-test.c -o /tmp/openmp-test
/tmp/openmp-test

测试成功后,再安装 MAJIQ:

python3 -m venv env
source env/bin/activate

python -m pip install --upgrade pip setuptools wheel
python -m pip install --upgrade cython numpy GitPython
python -m pip install --no-cache-dir \
  'git+https://bitbucket.org/biociphers/majiq_stable.git#egg=majiq'

旧资料中经常使用下面的参数绕过 Apple Clang 对 OpenMP 的限制:

-Xpreprocessor -fopenmp

这种写法适用于部分可以自行控制编译参数的项目。MAJIQ 的构建脚本已经直接添加了 -fopenmp。如果脚本无法分别设置编译参数和链接参数,这种处理方式并不稳妥,建议使用 Homebrew LLVM 或 GNU GCC。

正确配置 HTSlib

HTSlib 和 OpenMP 是两个独立的依赖。解决编译器问题后,如果 HTSlib 不在构建系统的默认搜索路径中,还需要设置:

brew install htslib

export HTSLIB_LIBRARY_DIR="$(brew --prefix htslib)/lib"
export HTSLIB_INCLUDE_DIR="$(brew --prefix htslib)/include"

可以用以下命令检查目录:

test -d "$HTSLIB_LIBRARY_DIR" && echo "HTSlib library directory found"
test -d "$HTSLIB_INCLUDE_DIR" && echo "HTSlib include directory found"

Apple Silicon 上的 Homebrew 默认前缀通常是 /opt/homebrew,Intel Mac 常见的是 /usr/local。使用 brew --prefix 获取路径比直接写死前缀更可靠。

原有配置为什么没有生效

下面的配置只添加了库文件和头文件的搜索路径:

export LDFLAGS="-L/usr/local/opt/llvm/lib"
export CPPFLAGS="-I/usr/local/opt/llvm/include"

它没有指定 Python 构建系统应该调用哪个编译器。如果 CC 仍指向系统 gcc,实际执行的依然是 Apple Clang。

原命令还先为 LDFLAGS 设置了 -Wl,-rpath,随后又执行:

export LDFLAGS="-L/usr/local/opt/llvm/lib"

后一次赋值会覆盖前一次赋值,导致 rpath 参数丢失。

当前 macOS 的默认 shell 通常是 zsh。写入 ~/.bash_profile 的配置不会被 zsh 会话自动读取。临时配置可以直接在当前终端执行。需要长期保存时,可根据实际使用的 shell 写入 ~/.zshrc,然后重新打开终端,或运行:

source ~/.zshrc
hash -r

安装前的最终检查

使用 GNU GCC 时,检查以下内容:

echo "$CC"
echo "$CXX"
"$CC" --version
"$CXX" --version
python -m pip --version

使用 Homebrew LLVM 时,还要再次测试 OpenMP:

"$CC" -fopenmp /tmp/openmp-test.c -o /tmp/openmp-test
/tmp/openmp-test

如果安装日志中仍然出现:

Apple clang version ...

或者编译命令仍由 /usr/bin/gcc 执行,说明环境变量没有传递到运行 pip 的终端,或构建脚本覆盖了编译器设置。

日志中的 NumPy deprecated API 信息只是警告,不会直接导致本次安装失败。退出状态为 1 是因为 Apple Clang 拒绝了 -fopenmp。

还要避免使用 sudo pip,也不要以 root 身份创建虚拟环境。示例日志中的路径位于 /private/var/root/env,说明当时很可能是在 root 环境中安装。使用普通用户的虚拟环境,更容易让 Homebrew、Python、编译器和依赖库保持一致的架构与路径。

备注:内容仅供参考。