使用包括 Lapack 的 CMake 将 Fortran 库从 Linux 交叉编译到 Windows

Cross-compile Fortran library from Linux to Windows using CMake including Lapack

我有一个 Fortran 库,我一直在 Linux 上开发它,我想与通常使用 Windows 的同事分享。我不希望他们帮助开发它,所以我想做的就是使用交叉编译器生成一个静态可执行文件,这样他们就可以 运行 它。

我可以使用 Linux (openSUSE) 上的交叉编译器工具链编译简单的 Hello world 程序,它在 Windows 上运行没有问题,但是当我尝试 link可执行文件到另一个库 (Lapack),Windows 抱怨它找不到 .dll 文件,它也是动态 linked 的。在我的问题中,我展示了如何使用

编译源代码

最小工作示例

我使用 openSUSE,它提供了一个交叉编译组件的存储库。首先,我安装了 mingw64-cross-toolchain 以及相关的 lapack 和 blas 开发文件:

mingw64-cross-gcc
mingw64-cross-g++
mingw64-cross-gfortran
mingw64-lapack-devel
mingw64-blas-devel

这还带来了一些其他必需的包

我的最小工作树如下所示:

├── linalg_mod.f90
└── main.f90

linalg_mod.f90 本质上是一些 lapack 例程的薄包装,而 main.f90 是我的主程序。它们本身并不是很有趣,main 包含一个简单的 3x3 矩阵,需要求解然后输出解。

> cat main.f90
program main
  use iso_fortran_env, only: wp=>real64
  use linalg, only: linsolve_quick
  implicit none

  integer :: ii
  real(wp) :: A(3,3), b(3), x(3)

  A = reshape([1, 2, -3, -2, 1, 2, 3, 1, -2], shape=([3,3]))
  b = [7, 4, -10]

  call linsolve_quick(3, A, 3, b, x)
  do ii = 1,3
    print'(a1,i1,a3,f8.5)', 'x', ii, ' = ', x(ii)
  enddo

end program main

linalg 模块有点大,所以我将其复制到 Github gist - 如果您有兴趣,可以阅读 here


在 Linux 上本地编译:

> gfortran -c linalg_mod.f90 main.f90
> gfortran linalg_mod.o main.o -o main -llapack -lblas
> ./main
  x1 =  2.00000000     
  x2 = -1.00000000     
  x3 =  1.00000000

据我所知,在 Linux 上为 Windows 交叉编译有点棘手,因为您需要将库捆绑为静态可执行文件。 Linux 我编译的本机代码是动态的,如下所示:

> ldd main
    linux-vdso.so.1 (0x00007fffb65be000)
    liblapack.so.3 => /usr/lib64/liblapack.so.3 (0x00007f9346bdc000)
    libblas.so.3 => /usr/lib64/libblas.so.3 (0x00007f9346982000)
    libgfortran.so.4 => /home/user/Software/gcc/install/lib64/gcc/x86_64-pc-linux-gnu/7.0.1/libgfortran.so.4 (0x00007f93465ad000)
    libm.so.6 => /lib64/libm.so.6 (0x00007f93462b0000)
    libgcc_s.so.1 => /lib64/libgcc_s.so.1 (0x00007f9346098000)
    libquadmath.so.0 => /home/user/Software/gcc/install/lib64/gcc/x86_64-pc-linux-gnu/7.0.1/libquadmath.so.0 (0x00007f9345e59000)
    libc.so.6 => /lib64/libc.so.6 (0x00007f9345ab6000)
    libgfortran.so.3 => /usr/lib64/libgfortran.so.3 (0x00007f934578c000)
    /lib64/ld-linux-x86-64.so.2 (0x0000558391f9e000)

添加 -static 属性创建静态可执行文件:

> gfortran -c mod.f90 linalg_mod.f90 main.f90
> gfortran linalg_mod.o mod.o main.o -static -o main -llapack -lblas 
> ldd main
    not a dynamic executable

尽管 main 不是动态可执行文件,但我仍然不完全相信它与 lapack 和 blas 动态库完全分离。有没有更好的检查方法?


从Linux交叉编译到Windows

我正在尝试使用这些相同的步骤将上述代码交叉编译为 Windows 可执行文件。在 linux 我正在做如下。 注意:lapack 和 blas 库可通过 mingw64 存储库作为静态库和动态库使用(具体来说,包 mingw64-liblapack3mingw64-lapack-devel):

/usr/x86_64-w64-mingw32/sys-root/mingw/bin/liblapack.dll
/usr/x86_64-w64-mingw32/sys-root/mingw/lib/liblapack.dll.a
/usr/x86_64-w64-mingw32/sys-root/mingw/bin/libblas.dll
/usr/x86_64-w64-mingw32/sys-root/mingw/lib/libblas.dll.a

我假设静态库是以 .dll.a 结尾的文件,而动态库是以 .dll 结尾的文件。与原生 Linux 上的编译步骤类似,以下是我尝试创建静态 main.exe 文件

的方式
> x86_64-w64-mingw32-gfortran -c mod.f90 linalg_mod.f90 main.f90
> x86_64-w64-mingw32-gfortran linalg_mod.o mod.o main.o -static -o main.exe -llapack -lblas 
> ldd main.exe
    not a dynamic executable

一切看起来都很好,除了当我将可执行文件移动到我的 Windows 机器时,我在尝试执行 Git bash 中的文件时遇到以下错误:

> ./main.exe
K:/temp-dump/main.exe: error while loading shared libraries: liblapack.dll: cannot open shared object file: No such file or directory

看它的动态库也有意思:

> ./ldd.exe ./main.exe
ntdll.dll => /c/windows/SYSTEM32/ntdll.dll (0x7ffe37c20000)
KERNEL32.DLL => /c/windows/system32/KERNEL32.DLL (0x7ffe35ab0000)
KERNELBASE.dll => /c/windows/system32/KERNELBASE.dll (0x7ffe34da0000)
msvcrt.dll => /c/windows/system32/msvcrt.dll (0x7ffe37430000)
USER32.dll => /c/windows/system32/USER32.dll (0x7ffe37760000)
GDI32.dll => /c/windows/system32/GDI32.dll (0x7ffe35920000)

出于某种原因,lapack 的静态 linking 在 Linux/Windows 期间未得到维护。有没有办法强制将交叉编译的可执行文件的静态 linking 从 Linux 转换为 Windows?

好吧,这花了很长时间,而且解决方案非常简单...

显然我需要做的就是将 .dll 文件与可执行文件放在同一目录中。我刚刚将以下 .dll 复制到我的 windows 机器上,现在它工作正常。这些是我需要复制的文件:

/path/to/windows/drive/
├── libblas.dll
├── libgcc_s_seh-1.dll
├── libgfortran-4.dll
├── liblapack.dll
├── libquadmath-0.dll
├── libwinpthread-1.dll
└── main.exe