使用包括 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-liblapack3
和 mingw64-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
我有一个 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-liblapack3
和 mingw64-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