PAYATHON 2026

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。可以先检查这些项目:

  1. Xcode Command Line Tools 是否完整,当前选中的开发者目录是否正确。
  2. gfortran 是否使用了正确的 macOS SDK 路径。
  3. Homebrew GCC、Open MPI 和 Python 的架构与工具链是否兼容。
  4. 环境变量或构建脚本是否覆盖了系统的头文件、库及 SDK 搜索路径。
  5. Open MPI 是否包含 Fortran 支持,并且由当前的 gfortran 构建。

不要从其他系统下载 libm,也不要在 /usr/lib 中手工创建 libm.dylib 软链接。这样做可能暂时盖住配置问题,还可能引入架构或 ABI 不兼容。

这两行错误分别是什么意思

编译 Fortran 程序通常包括两个主要阶段:

  1. 编译:将 .f90 等源文件转换成目标文件。
  2. 链接:将目标文件、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,应以项目的构建说明为准。

建议的排查顺序

可以按以下顺序检查:

  1. 运行 xcrun --sdk macosx --show-sdk-path,确认 SDK 可用。
  2. 检查 SDK 中是否存在 usr/lib/libm.tbd。
  3. 用最小 Fortran 程序测试 gfortran -v。
  4. 如果测试失败,加入 -isysroot "$SDK_PATH" 后重试。
  5. 暂时清除可能污染搜索路径的环境变量。
  6. 检查 Python、GFortran、Homebrew 和 Open MPI 的架构及实际路径。
  7. 使用 mpifort 编译最小 MPI 程序。
  8. 取得 Python case 文件触发的完整编译命令,与最小测试中成功的参数对比。
  9. 确认安装残缺或工具链不兼容后,再重装 GCC、Open MPI 或 Command Line Tools。

如果最小 gfortran 测试能够通过,而原程序仍然失败,说明系统数学库和 GFortran 基本正常。问题通常出在项目的构建参数、MPI wrapper 或 Python 启动的子进程环境中。如果最小测试也失败,应先修复 Xcode SDK 与 GCC 的连接,暂时无须继续检查 case 文件。

备注:内容仅供参考。