Cross-compiler Fortran bibliotek fra Linux til Windows ved hjælp af CMake herunder Lapack

Indlæg af Hanne Mølgaard Plasc

Problem



Jeg har et Fortran bibliotek, jeg har udviklet på Linux, som jeg vil dele med kolleger, der normalt bruger Windows. Jeg forventer ikke, at de hjælper med at udvikle det, så alt jeg vil gøre er at producere en statisk eksekverbar ved hjælp af en kryds-compiler, så de kan køre den.


Jeg kan kompilere simple Hello World-programmer ved hjælp af kryds-compiler toolchain på Linux (openSUSE), som arbejder på Windows uden et problem, men når jeg forsøger at linke den eksekverbare op til et andet bibliotek (Lapack), klager Windows, at den ikke kan finde den .dll fil, at den også er dynamisk forbundet. I mit spørgsmål viste jeg, hvordan jeg kompilerer kilden ved hjælp af





Mindste arbejdseksempel



Jeg bruger openSUSE, som giver et lager af krydskompilerende komponenter. For at komme i gang installerede jeg mingw64-cross-toolchain samt de tilknyttede lapack- og blas-udviklingsfiler:


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


Det medførte også en række andre nødvendige pakker


Mit minimale arbejdstræ ser sådan ud:


├── linalg\_mod.f90
└── main.f90


linalg\_mod.f90 er i det væsentlige en tynd wrapper omkring nogle lapack-rutiner, og main.f90 er mit hovedprogram. I sig selv er de ikke meget interessante, hoved indeholder en simpel 3x3 matrix, der skal løses og derefter udløser løsningen.


> 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-modulet er lidt stort, så jeg kopierede det til et Github-værk - du kan læse det her, hvis du er interesseret [20]





Kompilering indbygget på 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


Så vidt jeg forstår, er krydsopsamling på Linux til Windows lidt sværere, fordi du skal bundle biblioteket som en statisk eksekverbar. Linux-oprindelige kode, jeg kompilerede, var dynamisk, som det kan ses her:


> 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)


Tilføj attributten -static oprettet en statisk eksekverbar:


> 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


Selv om main ikke er en dynamisk eksekverbar, er jeg stadig ikke helt overbevist om, at den er fuldstændig afskåret fra lapack og blas dynamic libs. Er der en bedre måde at kontrollere?





Cross-compiling fra Linux til Windows



Jeg forsøger at bruge disse samme trin til at krydskompilere ovenstående kode til en Windows-eksekverbar. På Linux gør jeg som følger. BEMÆRK : Lapack- og blas-bibliotekerne er tilgængelige som statiske og dynamiske biblioteker gennem mingw64-arkivet (specifikt pakker mingw64-liblapack3 og 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


Jeg antager, at de statiske biblioteker er filerne, der slutter i .dll.a, og de dynamiske biblioteker er dem, der slutter på bare .dll. I lighed med kompileringstrinnet på indfødt Linux, her er jeg forsøgt at oprette en statisk main.exe-fil


> 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


Alt ser godt ud, medmindre jeg flytter den eksekverbare til min Windows-maskine, får jeg følgende fejl ved at prøve at udføre filen i 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


Kig på det er dynamiske biblioteker også interessant:


> ./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)


Af en eller anden grund er den statiske sammenkobling af lapack ikke vedligeholdt mellem Linux/Windows. Er der en måde at tvinge en statisk sammenkobling af en krydskompileret eksekverbar fra Linux til Windows?

Bedste reference


Nå det tog for evigt, og løsningen var pinligt let ...


Tilsyneladende var alt, hvad jeg behøvede at gøre, lagt til .dll filer sammen med den eksekverbare i samme mappe. Jeg har lige kopieret over følgende .dll s til min Windows-maskine, og nu fungerer det fint. Dette er de filer, jeg havde brug for at kopiere over:


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