[fpm] can fpm be used to build a dynamic link library?

Dear @mskocic, I think you’re right: -fPIC is part of the “default compiler flags” for all targets, but they are only applied to Fortran sources. Would you mind to open an issue/PR on the fpm repository about this?

Regarding items #2,#3, does your test case actually call any procedures from said libraries? While fpm does link dependencies when creating shared libraries (via get_package_libraries_link()), there’s no mechanism to ensure those dependencies are encoded in the shared library’s NEEDED section. The linker will only include symbols actually referenced, not transitive dependencies.

On Linux, we’d need something like -Wl,--no-as-needed or explicitly marking each dependency library.

No problem I’ll open an issue regarding the fPIC flag for C sources :grinning_face:

Actually it does not call directly a procedure from stdlib. It calls a procedure from libmytest.so which in turn uses the string_type from stdlib.

I’ll try to call directly a procedure from stdlib.

Thank you guys!

If ldd lists those libraries as not found, then follow @zedthree 's advice of passing -rpath to the linker.

Otherwise, you’re probably set —i.e., either the libraries are not needed or they were linked statically (you can use nm to look for symbols).

ldd does not list those libraries but when I looked at the verbose output of fpm, I saw it did the linking.
When I used nm, the function from the shared library was listed in the table of symbols.

I’ll make another test and I’ll post the outputs.

If the linking was done using -lstdlib -lmytest the linker might have picked the staic ones (if they’re available in one of the -L paths).

Also, in the verbose build, look for -shared or -Wl,-shared.

1 Like

I found what was the issue.
I forgot that the dependency on stdlib is no more part of the package, as each dependency is compiled separately, and therefore must be treated as an “external” dependency. The link option needs to be declared in the [build] section.

fpm.toml

name="mytest"
...

Package:

app
     main.f90
src
    core.f90

  1. Default settings in fpm.toml
name="mytest"
...
[library]
type="monolithic"

[dependencies]
stdlib="*"
...

Everything compiles fine as usual.


  1. Settings for shared library in fpm.toml
name="mytest"
...
[library]
type="shared"

[dependencies]
stdlib="*"
...
  • libstdlib.so is compiled fine
  • libmytest.so is compiled but NOT linked to libstdlib.so:
+ gfortran -shared build/gfortran_2B4944C3A077DCE8/mytest/src_core.f90.o -Lbuild/gfortran_358323723C01102E   -o build/gfortran_358323723C01102E/libmytest.so
[ 50%]                   libmytest.so  done.
  • the compilation of the executable also fails. It is only linked to libmytest.so as expected but the latter is not linked to libstdlib:
+ gfortran   -cpp -Wall -Wextra -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -fPIC -fimplicit-none -Werror=implicit-interface  build/gfortran_2B4944C3A077DCE8/mytest/app_main.f90.o -Lbuild/gfortran_358323723C01102E -lmytest  -o build/gfortran_F92076EC7505EC5C/app/mytest
/usr/bin/ld: build/gfortran_358323723C01102E/libmytest.so: undefined reference to __stdlib_string_type_MOD_move_string_char
/usr/bin/ld: build/gfortran_358323723C01102E/libmytest.so: undefined reference to __stdlib_string_type_MOD_new_string
collect2: error: ld returned 1 exit status
[100%]                         mytest  done

Checking the output of ldd confirms that libmytest.so is not linked to libstdlib.so.

ldd $(find ./build/gfortran* -type f -name libmytest.so)
        linux-vdso.so.1 (0x00007f8627dd9000)
        libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007f8627a00000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f8627910000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f862771a000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f8627d5e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f8627ddb000)

Now, if I add stdlib as an external dependency:

name="mytest"
...
[build]
...
link="stdlib"

[library]
type="shared"

[dependencies]
stdlib="*"
...

Everything compiles fine.

 + gfortran -shared build/gfortran_2B4944C3A077DCE8/mytest/src_core.f90.o -Lbuild/gfortran_358323723C01102E   -lstdlib -o build/gfortran_358323723C01102E/libmytest.so
[ 50%]                   libmytest.so  done.
[ 50%]                       main.f90
...
 + gfortran   -cpp -Wall -Wextra -fPIC -fmax-errors=1 -g -fcheck=bounds -fcheck=array-temps -fbacktrace -fcoarray=single -fPIC -fimplicit-none -Werror=implicit-interface  build/gfortran_2B4944C3A077DCE8/mytest/app_main.f90.o -Lbuild/gfortran_358323723C01102E -lmytest  -lstdlib -o build/gfortran_F92076EC7505EC5C/app/mytest
[100%]                         mytest  done.

ldd outputs

ldd $(find ./build/gfortran* -type f -name libmytest.so)
        linux-vdso.so.1 (0x00007fae365ad000)
      ->libstdlib.so => /home/mskocic/.local/lib/libstdlib.so (0x00007fae34e00000)
        libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007fae34a00000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007fae3646f000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007fae3480a000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007fae36442000)
        libmvec.so.1 => /lib/x86_64-linux-gnu/libmvec.so.1 (0x00007fae34d07000)
        /lib64/ld-linux-x86-64.so.2 (0x00007fae365af000)
ldd $(find ./build/gfortran* -type f -name mytest)
        linux-vdso.so.1 (0x00007f43eaed9000)
      ->libmytest.so => not found
        libgfortran.so.5 => /lib/x86_64-linux-gnu/libgfortran.so.5 (0x00007f43eaa00000)
        libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f43ead9b000)
        libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f43ea80a000)
        libgcc_s.so.1 => /lib/x86_64-linux-gnu/libgcc_s.so.1 (0x00007f43ead6e000)
        /lib64/ld-linux-x86-64.so.2 (0x00007f43eaedb000)

@FedericoPerini Do you if this is the expected behavior when compiling shared libraries with fpm? Do we need to declared dependencies in the build section even if they are already declared in the dependencies section of the fpm.toml file?

@mskocic package dependencies are tracked from actual usage - if you could post (here or on GH) a minimal reproducer of your fpm package, I will check that

Here we go:

1 Like

What I understood from testing fpm for shared libraries is following:

  • the executable is always linked with the library of the package
  • however the library of the package is NOT linked to each dependency that is defined in the fpm .toml

I haven’t looked at the source code, but I think the solution is to link the package library by default to all its dependencies.