Mac 使用 GFortran 报错“ld: library not found for -lm”
结论
ld: library not found for -lm 是链接阶段的错误。-lm 表示链接数学库 libm,ld 是负责查找并组合这些库的链接器。这个错误说明编译已经进入链接阶段,但链接器没有在有效的 macOS SDK 或库搜索路径中找到 libm。
在 macOS 上,数学库属于系统运行库。只要正确安装并选中了 Xcode Command Line Tools,编译器通常会通过 macOS SDK 找到它,无须单独安装 libm。可以先检查这些项目:
- Xcode Command Line Tools 是否完整,当前选中的开发者目录是否正确。
gfortran是否使用了正确的 macOS SDK 路径。- Homebrew GCC、Open MPI 和 Python 的架构与工具链是否兼容。
- 环境变量或构建脚本是否覆盖了系统的头文件、库及 SDK 搜索路径。
- Open MPI 是否包含 Fortran 支持,并且由当前的
gfortran构建。
不要从其他系统下载 libm,也不要在 /usr/lib 中手工创建 libm.dylib 软链接。这样做可能暂时盖住配置问题,还可能引入架构或 ABI 不兼容。
这两行错误分别是什么意思
编译 Fortran 程序通常包括两个主要阶段:
- 编译:将
.f90等源文件转换成目标文件。 - 链接:将目标文件、Fortran 运行库、数学库和 MPI 等依赖组合成可执行文件。
第一行错误是:
ld: library not found for -lm
其中各部分的含义如下:
ld是链接器。-lxxx表示链接名为libxxx的库。-lm表示链接数学库libm。library not found表示链接器搜索了已知目录,但没有找到可用的库文件或 SDK stub。
第二行是:
collect2: error: ld returned 1 exit status
GCC 的辅助程序 collect2 调用了链接器,链接器随后以错误状态退出。这一行是前一行错误引发的结果,需要处理的是链接器找不到 -lm。
numpy、scipy 或 Python 脚本本身通常无法解决这个错误,因为问题出在本地编译器与系统开发环境之间。
第一步:确认 Xcode Command Line Tools
先运行:
xcode-select -p
xcrun --find clang
xcrun --find ld
xcrun --sdk macosx --show-sdk-path
正常情况下:
xcode-select -p会输出一个确实存在的开发者目录。xcrun --find ld能找到 Apple 的链接器。- 最后一条命令会输出一个以
.sdk结尾的 macOS SDK 路径。
如果出现 “unable to get active developer directory” 或 “SDK cannot be located” 等错误,请安装 Command Line Tools:
xcode-select --install
安装完成后重新打开终端,再执行前面的检查命令。
如果完整 Xcode 和独立的 Command Line Tools 都已安装,需要明确选择其中一套。选择独立工具:
sudo xcode-select --switch /Library/Developer/CommandLineTools
选择完整 Xcode:
sudo xcode-select --switch /Applications/Xcode.app/Contents/Developer
如果选择完整 Xcode 后提示尚未接受许可证,或首次初始化没有完成,可以执行:
sudo xcodebuild -license accept
sudo xcodebuild -runFirstLaunch
命令中的路径要与本机的实际安装位置一致。如果 Xcode 不在 /Applications/Xcode.app,第二条 --switch 命令中的路径也要相应修改。
第二步:检查 SDK 中是否存在数学库入口
取得 SDK 路径并检查 libm:
SDK_PATH="$(xcrun --sdk macosx --show-sdk-path)"
printf '%s\n' "$SDK_PATH"
ls -l "$SDK_PATH/usr/lib/libm.tbd"
现代 macOS SDK 经常使用 .tbd 文本 stub,不会把完整的系统 .dylib 放进 SDK。只要 libm.tbd 存在,链接器就应该能通过正确的 SDK 路径解析 -lm。
如果 xcrun 返回了 SDK 路径,但该路径不存在或 SDK 内容残缺,请修复或重装与当前 macOS 版本兼容的 Xcode Command Line Tools。不要单独从网上复制 libm.tbd。
第三步:单独测试 GFortran
先确认终端实际调用了哪个编译器:
type -a gfortran
command -v gfortran
gfortran --version
gfortran -print-prog-name=ld
gfortran -print-search-dirs
系统中可能同时装有多个版本的 GFortran,因此需要检查 type -a gfortran 的输出。程序实际调用的编译器,可能与手动检查版本时调用的不是同一个。
创建一个最小测试文件:
program test_math
implicit none
print *, sqrt(2.0)
end program test_math
将它保存为 test_math.f90,然后执行:
gfortran -v test_math.f90 -o test_math
./test_math
-v 会显示编译器实际调用的程序和链接参数。如果这个最小程序也出现 library not found for -lm,就可以排除 Python case 文件,将检查范围缩小到 GCC、SDK 和环境变量。
接着可以显式指定 SDK:
SDK_PATH="$(xcrun --sdk macosx --show-sdk-path)"
gfortran -isysroot "$SDK_PATH" -v test_math.f90 -o test_math
如果普通命令失败,而加入 -isysroot 后成功,说明构建过程没有把有效的 macOS SDK 传给 GFortran。此时需要把同样的选项加入项目的编译配置,例如:
-isysroot "$(xcrun --sdk macosx --show-sdk-path)"
具体配置位置取决于项目使用的是 Make、CMake、Python 构建脚本还是其他构建系统。只在某个终端会话中设置该选项,并不能保证所有构建工具都会读取它。
如果构建系统单独向 Apple 链接器传递参数,可能还需要:
-Wl,-syslibroot,"$(xcrun --sdk macosx --show-sdk-path)"
一般先尝试 -isysroot。确认构建系统单独配置链接参数后,再考虑 -Wl,-syslibroot,...。
第四步:排除错误的环境变量
一些旧安装教程会要求全局设置 LDFLAGS、CPPFLAGS、LIBRARY_PATH 或 SDKROOT。如果这些变量指向已经删除的 Xcode、旧版 SDK、Intel Homebrew 目录或其他机器上的路径,就会干扰 GCC。
检查当前环境:
env | grep -E '^(SDKROOT|CPATH|C_INCLUDE_PATH|CPLUS_INCLUDE_PATH|LIBRARY_PATH|LD_LIBRARY_PATH|DYLD_LIBRARY_PATH|LDFLAGS|CPPFLAGS)='
可以在当前终端中暂时清除这些变量,然后重新测试:
unset SDKROOT
unset CPATH
unset C_INCLUDE_PATH
unset CPLUS_INCLUDE_PATH
unset LIBRARY_PATH
unset LD_LIBRARY_PATH
unset DYLD_LIBRARY_PATH
unset LDFLAGS
unset CPPFLAGS
gfortran -v test_math.f90 -o test_math
unset 只影响当前终端,不会删除任何软件。如果清除变量后能够成功编译,请检查 ~/.zshrc、~/.zprofile、~/.bash_profile 或项目启动脚本,找出写入错误路径的配置。
不建议用下面的选项解决问题:
-L/usr/lib
现代 macOS 主要通过 SDK 管理系统库,/usr/lib 不一定包含开发阶段所需的普通库文件。强行添加这个目录通常没有效果。
第五步:检查是否混用了不同架构
Apple Silicon Mac 可以原生运行 arm64 程序,也能通过 Rosetta 运行 x86_64 程序。Intel Mac 则应使用 x86_64 工具。两套不同架构的 Homebrew、Python、GFortran 和 Open MPI 混在一起,很容易引发链接问题。
检查基本架构:
uname -m
arch
file "$(command -v gfortran)"
python3 -c 'import platform, sys; print(platform.machine()); print(sys.executable)'
再检查 Homebrew:
brew --prefix
brew config
常见的 Homebrew 前缀为:
- Apple Silicon 原生安装:
/opt/homebrew - Intel 安装:
/usr/local
前缀只能作为参考,不能据此判断所有安装的架构。仍需结合 file、arch 和实际执行路径确认。
如果 Python 是 arm64,而 GFortran 或 Open MPI 是 x86_64,建议将它们统一重装为同一架构。架构不匹配时更常见的错误是 wrong architecture,但排查工具链时仍应确认这一项。
“Development headers must be present” 是什么意思
Development headers 是编译程序时使用的头文件及配套开发文件。运行已经编译好的软件可能只需要动态库,从源代码编译软件还会用到以下内容:
- C/C++ 头文件,例如
stdio.h、Python.h和mpi.h - Fortran 模块文件,例如
mpi.mod、mpi_f08.mod - 库的链接信息,例如
.tbd、.dylib或.a - macOS SDK
- 编译器、链接器及相关构建工具
在 macOS 上,系统头文件通常位于 Xcode 或 Command Line Tools 提供的 SDK 中,不要求全部出现在 /usr/include。
可以用以下命令检查系统 SDK:
SDK_PATH="$(xcrun --sdk macosx --show-sdk-path)"
test -d "$SDK_PATH/usr/include" && echo "SDK headers found"
test -e "$SDK_PATH/usr/lib/libm.tbd" && echo "libm stub found"
通过下面的命令可以找到 Python 开发头文件:
python3 -c 'import sysconfig; print(sysconfig.get_paths()["include"])'
MPI 的 C 头文件和 Fortran 模块位置应由 MPI wrapper 报告,不要手工复制到系统目录。
“OpenMPI support requires Fortran-compatible libraries be built” 是什么意思
这句话表示 Open MPI 必须启用 Fortran bindings,并使用与项目兼容的 Fortran 编译器构建。系统中有 mpicc,不代表 Fortran 支持一定可用。
先检查 wrapper:
command -v mpicc
command -v mpifort
mpifort --version
mpifort --showme:command
mpifort --showme:compile
mpifort --showme:link
mpifort 是 Open MPI 推荐的 Fortran 编译入口,它会自动加入 MPI 所需的模块目录和链接库。不要自行拼接一长串 -I、-L 和 -lmpi... 参数。
创建 MPI 测试文件 mpi_test.f90:
program mpi_test
use mpi
implicit none
integer :: ierr
integer :: rank
integer :: size
call MPI_Init(ierr)
call MPI_Comm_rank(MPI_COMM_WORLD, rank, ierr)
call MPI_Comm_size(MPI_COMM_WORLD, size, ierr)
print *, "rank", rank, "of", size
call MPI_Finalize(ierr)
end program mpi_test
编译并运行:
mpifort -v mpi_test.f90 -o mpi_test
mpirun -np 2 ./mpi_test
如果找不到 mpifort、Fortran bindings 不可用,或它显示的底层编译器不是预期的 gfortran,就需要重新安装或重新构建 Open MPI。
使用 Homebrew 时,可以先查看当前安装信息:
brew info gcc
brew info open-mpi
brew linkage --test open-mpi
重新安装前,要确认当前 PATH 优先指向准备使用的 GFortran。是否需要从源码构建,取决于 Homebrew 当前公式、操作系统版本和可用的 bottle,不能只根据版本号决定。必要时可以尝试:
brew reinstall gcc
brew reinstall open-mpi
如果必须从源码构建 Open MPI,需要在配置阶段明确指定编译器并启用 Fortran 支持,例如:
./configure \
--prefix=/your/openmpi/prefix \
CC=/path/to/gcc \
CXX=/path/to/g++ \
FC=/path/to/gfortran \
--enable-mpi-fortran
请将这些路径替换为 command -v gcc、command -v g++ 和 command -v gfortran 查到的真实路径。源码安装还涉及 PATH 和动态库路径的管理。对初学者来说,先修复 Homebrew 工具链通常更省事。
第六步:查看程序实际执行的链接命令
Python case 文件可能在后台调用 Make、CMake、F2PY、MPI wrapper 或项目自带的构建脚本。只看屏幕上的最后两行错误,无法判断错误参数来自哪里。
重新运行程序并保存完整输出:
python3 your_case.py 2>&1 | tee build.log
然后在日志中查找:
grep -nE 'gfortran|mpifort|collect2|ld | -lm|isysroot|syslibroot' build.log
查看报错前的完整命令,逐项确认:
- 调用的是
gfortran还是mpifort - 编译器路径是否符合预期
- 命令中是否包含
-isysroot或-syslibroot - SDK 路径是否仍然存在
- 是否使用了
-nostdlib、-nodefaultlibs等特殊选项 -L是否指向另一套 Homebrew、旧 Xcode 或错误架构的目录
如果软件支持传入 FC、CC 和 CXX,可以在构建前明确指定:
export FC="$(command -v gfortran)"
export CC="$(command -v gcc)"
export CXX="$(command -v g++)"
使用 MPI 时,项目通常应通过 wrapper 编译:
export FC="$(command -v mpifort)"
export CC="$(command -v mpicc)"
export CXX="$(command -v mpicxx)"
具体该使用普通编译器还是 MPI wrapper,应以项目的构建说明为准。
建议的排查顺序
可以按以下顺序检查:
- 运行
xcrun --sdk macosx --show-sdk-path,确认 SDK 可用。 - 检查 SDK 中是否存在
usr/lib/libm.tbd。 - 用最小 Fortran 程序测试
gfortran -v。 - 如果测试失败,加入
-isysroot "$SDK_PATH"后重试。 - 暂时清除可能污染搜索路径的环境变量。
- 检查 Python、GFortran、Homebrew 和 Open MPI 的架构及实际路径。
- 使用
mpifort编译最小 MPI 程序。 - 取得 Python case 文件触发的完整编译命令,与最小测试中成功的参数对比。
- 确认安装残缺或工具链不兼容后,再重装 GCC、Open MPI 或 Command Line Tools。
如果最小 gfortran 测试能够通过,而原程序仍然失败,说明系统数学库和 GFortran 基本正常。问题通常出在项目的构建参数、MPI wrapper 或 Python 启动的子进程环境中。如果最小测试也失败,应先修复 Xcode SDK 与 GCC 的连接,暂时无须继续检查 case 文件。
备注:内容仅供参考。
