Fpm 0.12.0 released 🎉

Dear Fortran enthusiasts, we are excited to announce the release of fpm v0.12.0! :rocket:

This release brings a set of major new features focused on interoperability, modern tooling support, and modular build customization — alongside various fixes and improvements across platforms and compilers.

:wrench: Export compile_commands.json for IDE integration

A compile_commands.json file is now exported by default every time building with fpm, enabling editor features like:

  • clangd support
  • VS Code auto-completion and symbol navigation
  • Integration with CMake Tools, Language Servers, and more

This brings fpm in line with modern C/C++ workflows and improves IDE integration.

:package: New BLAS/LAPACK and NetCDF metapackages

Two new metapackages make it easier to link with high-performance scientific libraries:

  • BLAS/LAPACK: Automatically link to system-provided libraries or optimized builds, also when using stdlib.
  • NetCDF: Use pkg-config to locate NetCDF C and Fortran modules, with broad compiler support.

:puzzle_piece: Support for shared and static library targets

Library targets can now specify:

[library]
type = "static"  
type = "shared"
type = "monolithic" # default

With the shared and static options, one complete library per dependency package is built and installed. This allows for plugin-style builds, dynamic linking, further than just bundling all required objects into a monolithic archive.

:hammer_and_wrench: Additional improvements

  • Windows bootstrapping fixes and Flang OpenMP support
  • Optional user-defined compiler/linker flags for introspection
  • Support for custom --config files
  • Enhanced metapackage support for Intel MPI and SLURM srun
  • Ongoing internal refactoring for improved maintainability

:inbox_tray: Download

You can grab the latest version from our GitHub releases:
:link: GitHub Release Page

:waving_hand: New Contributor

Please welcome @krystophny, who contributed significant refactoring work and the new NetCDF and BLAS metapackages!

:speech_balloon: Feedback & Discussion

Thanks to everyone who contributed to this release! :raising_hands:

We eagerly await to hear your feedback, experiences, and questions on the new experimental features.

32 Likes

Thanks @FedericoPerini, @krystophny and all contributors. That’s a lot of exciting news in one update.

:puzzle_piece: Support for shared and static library targets

Yes! :rocket:

3 Likes

Thanks to @FedericoPerini, @krystophny and all contributors! I have already updated my fpm on one Ubuntu PC.

I have seen that fpm doc has been augmented, and I would like to work on updating the French translation. However, on https://hosted.weblate.org/projects/fortran-lang/fpm/ the French translation is considered to be 100% complete, which is not true. Can someone please synchronize fpm doc & Weblate?

2 Likes

This is incredibly amazing!!! I just upgraded fpm in my personal computer to test this:

This will enable easier setups for building Python extensions from Fortran and much more. :rocket:

I have to test this with a larger project at work :slight_smile:

There is just one question remaining, is there a way to avoid the labeled archive that changes with every build? I would prefer to have a fixed folder within the build directory to archive the libraries. For instance, with CMake I would typically have a Release, Debug, RelWithDebInfo folders under the build directory. I find this extremely convenient as I systematically know where to point to during development/debugging.

2 Likes

Nice! There is a small errata in the help text where a line goes over the eighty-column limit.

errata
diff --git a/src/fpm_command_line.f90 b/src/fpm_command_line.f90
index ffb5a5b61..d87741bf7 100644
--- a/src/fpm_command_line.f90
+++ b/src/fpm_command_line.f90
@@ -996,7 +996,8 @@ contains
     '          [--tests] [--no-prune] [--config-file PATH] [--dump [FILENAME]]       ', &
     '    new NAME [[--lib|--src] [--app] [--test] [--example]]|                      ', &
     '             [--full|--bare][--backfill]                                        ', &
-    '    update [NAME(s)] [--fetch-only] [--clean] [--config-file PATH] [--dump [FILENAME]]', &
+    '    update [NAME(s)] [--fetch-only] [--clean] [--config-file PATH]              ', &
+    '           [--dump [FILENAME]]                                                  ', &
     '    run [[--target] NAME(s)] [--profile PROF] [--flag FFLAGS] [--list] [--all]  ', &
     '        [--example] [--runner "CMD"] [--compiler COMPILER_NAME]                 ', &
     '        [--no-prune] [-- ARGS] [--config-file PATH]                             ', &

1 Like

The labeling allows for concurrent builds with different compiler options. Perhaps giving constant names to the default builds might partially solve the problem, so if you supplied no options the names would match the profile names (ie. “debug” and “release”) for those cases; but you can query the pathnames with --list or via the JSON or API. The names in the build directory are assumed to not be needed to be known by the user in the majority of cases, and can be extracted into known pathnames via the “install” subcommand or in some cases via the --runner option.

When do you need to point to the files directly? to invoke a debugger? In most cases
something like

fpm  test --runner gdb

might work for that case.

An example plugin that does that for simple cases with gdb and vim is GitHub - urbanjost/fpm-gdb: plugin for fpm(1) that runs gdb(1) in vim(1) on Linux.

But I am not quite sure if I understand your use-case.

My use case is:

  • I do not use fpm for myself, and it is not (so far) my main build system. (I’m preparing things to have it ready next to CMake)
  • I do not build libraries for my own use (only) but to be given. The libraries are part of a bigger solution for which another tool (Artifactory from Jenkins) is used to put everything together (executables, static libraries, shared libraries, documentation, resources, etc etc)

So, I would like to have a predefined librarian to know from where to extract the binaries. If I could set a local folder that would be already enough.

I have never found any utility in the labeling for concurrent builds honestly, but I won’t argue about that, I’m most likely not seeing something. The CMake convention

Is more than enough and I haven’t had the need for more than that. Typically, if I change some flags it is because I’m testing options to decide which will be the optimal release.

1 Like

Thanks a lot @urbanjost - errata: CLI help line too long by perazz · Pull Request #1144 · fortran-lang/fpm · GitHub

exactly, this was defined at Enable multiple build output directories by awvwgk · Pull Request #575 · fortran-lang/fpm · GitHub.
What’s hashed is the set of build flags so essentially different folders characterize different languages (Fortran/C/C++) AND profiles including sets of CLI flags.

Of course one can always then install everything in a user-defined folder, I doubt that would be time consuming (that’s what I do when developing fpm btw).

I’m interested to discuss opinions and ideas as I’m planning to start looking at optional dependencies and different profiles, they would certainly affect this (for example: if a user-defined profile (that defines a set of build flags has a build-dir or a name, then its build folder could be taken from that

1 Like

This would be great! A build-dir like option could solve it!

I understood that when installing, it copies libraries to a predefined folder in the system. What I’m looking for is more of a local archiving (within the project’s build directory)

fpm install --prefix=build/Release

should do exactly what you’re asking (there will be build/Release/lib build/Release/include and build/release/bin)

2 Likes
fpm install --no-rebuild --prefix build/debug

might be what you are looking for, but I would in general not recommend placing files in build/, as it is often something you want to clean up (ie. remove) and it would remove the directory.

fpm install --no-build --prefix=releases/v1.0.0  

or something like that (which would put everything in the directory releases/v1.0.0/ in the project area) might be better, and let you also delete and rebuild the “releases” as well.

So an inadvertent “clean” might remove the files. Also, the build/ directory is excluded from inclusion into git by default, whereas a new directory just for releases can be recorded by git.

The feature to note is you can specify with --prefix where the install is directed.

4 Likes

I was a bit too slow and see this approach was already suggested, but the key is you can install wherever you like with --prefix.
From you description you also probably want to change the default install options in your fpm.toml file so “library=true”. Per the manual

**strong text**[install] # Options for the "install" subcommand

  # When you run the "install" subcommand only executables are installed by
  # default on the local system. Library projects that will be used outside of
  # "fpm" can set the "library" boolean to also allow installing the module
  # files and library archive. Without this being set to "true" an "install"
  # subcommand ignores parameters that specify library installation.

  # If your project sets `[library] type = "shared"`, enabling this option
  # will install the compiled `.so`, `.dylib`, or `.dll` files into the
  # appropriate `lib/` folder. This applies equally to static archives.
  #
  # For shared libraries, installing is typically required for runtime usage.

library = false
2 Likes

Thank you both @FedericoPerini @urbanjost, I’ll try to use your recommendations next week to polish my current use of fpm!

An early branch of fpm(1) allowed you to give a name to your options and to supply compiler options only on the build command. You then used that name on the other commands. The consensus at the time was that the options should become user-defined options in the fpm-toml so that it would become part of the package and be able to be used when the package was a dependency, so that option was removed and --profile was added. But since then many compiler and load options have been made available via switches and environment variables and response files. There was a project and pull request to implement the original vision that I thought was practically finished (a GSOC project (?)) that seems to never have made it into production that dealt with applying switches to specific compilers, conditionally selecting options based on the OS, applying options only to particular files, and so on. But experience has shown there are options for development (debugging, but also for profiling, coverage tools, and other metrics) that might not be needed as part of the distribution; that conditional selection by name might suffice but that something like if-else-endif selection that is aware of the compiler and OS might be nice, and that being able to put the options in a file that can be shared between projects would be nice for the developer but might make it harder to ensure the options are included in the package distribution.

Not sure what the flaw was in the previous mods to create user-defined profiles but even if fpm has diverged too much to salvage any of that directly there are at least some ideas and lessons to be taken from that project I would guess.

So

  1. What users are not using fpm because of a lack of customizable package profiles?
  2. How is this feature implemented in other packages?

I think a separate toml file called profile.toml that is able to be rewritten by utilities that contains any profiles needed by the package when used as a dependency would be nice. You could have others searched for or in a config file but entries can be updated by adding a flag like --new-profile NAME to a build subcommand. That would take the environment variables and switches used on the build command and build a profile and put it into the profile.toml file. A new fpm-profile plug-in would let you list, remove, and add to the file. At a minimum you can specify a default for a compiler and OS combination so someone using the package as a dependency gets the right switches used.

One difficulty is deciding if dependencies that call dependencies get options passed on and is so how? So if on my build command I add a compiler flag does it get passed on to the build like a current dependency so I can optimize, profile, or debug all the code my application is using? Or can I specify that a dependency (and perhaps its dependencies) must use a certain set of options when used as a dependency?

1 Like

First of all: shared library support is amazing! Thanks!!!

However, I think there is a little wrinkle that would need to be ironed out.

This example is for a Mac. Say I’m using type = "shared" for a program that has various dependencies:

./fpm install --profile release --prefix ./install

So far so good, it looks like it built all the .dylib’s and put them in ./install/lib. However, if we use otool like so on the main program:

> otool -L ./install/bin/coefficients               
./install/bin/coefficients:
        build/gfortran_9CDE8BC29ABA481B/libroots-fortran.dylib (compatibility version 0.0.0, current version 0.0.0)
        build/gfortran_9CDE8BC29ABA481B/libpyplot-fortran.dylib (compatibility version 0.0.0, current version 0.0.0)
        build/gfortran_9CDE8BC29ABA481B/libFACE.dylib (compatibility version 0.0.0, current version 0.0.0)
        build/gfortran_9CDE8BC29ABA481B/libmpfun2020-var1.dylib (compatibility version 0.0.0, current version 0.0.0)
        build/gfortran_9CDE8BC29ABA481B/librklib.dylib (compatibility version 0.0.0, current version 0.0.0)
        @rpath/libgfortran.5.dylib (compatibility version 6.0.0, current version 6.0.0)
        @rpath/libquadmath.0.dylib (compatibility version 1.0.0, current version 1.0.0)
        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1351.0.0)

we notice that the paths to the original locations where they were built are embedded in the application. So, when the build folder is deleted, the program will no longer work.

So, I think we have to do something here… I admit I’ve never fully understood the rpath thing, maybe someone here knows the solution for that? I think you have to patch the binary to change the path using something like patchelf, which is, I believe, what conda does when you have shared libs in conda packages.

EDIT: I guess, on the Mac you would use install_name_tool to do something like this:

install_name_tool -change build/gfortran_9CDE8BC29ABA481B/libpyplot-fortran.dylib @rpath/libpyplot-fortran.dylib ./coefficients
1 Like

Thanks! I’m really glad you’re trying out the new shared support.

You are totally right: the .dylib paths are hardcoded to the build directory because we haven’t yet implemented proper @rpath rewriting in the installation step. For this initial version, my main goal was to ensure that:

  • all dependencies are linked correctly in topological order,
  • the build succeeds and produces valid .dylibs,
  • and that fpm run and fpm test work by temporarily adjusting LD_LIBRARY_PATH (or PATH on Windows).

I intentionally left out install_name_tool logic for this release, hoping to discuss with the community on what the best default behavior should be, since it can vary between packaging use cases (e.g., relocatable installations vs. system-wide installs vs. development workflows).

At work, we explicitly set the install name of each .dylib to use @rpath:

-Wl,-install_name,@rpath/$(TARGET_OUTPUT_FILENAME)

Then, for each executable, we ensure it can locate those by adding an rpath at runtime:

install_name_tool -add_rpath @executable_path ./my_program

This tells the dynamic loader to look for libraries in the same directory as the binary itself. It’s a portable pattern that works well for self-contained app bundles or local installs.

Going forward, I’d like to explore having fpm:

  1. assume all shared libraries are in a common location (e.g., prefix/lib)
  2. rewrite the install names using @rpath
  3. inject @executable_path/../lib as a default rpath in installed binaries

But I’m also open to more flexible options, any thoughts or suggestions on the best default behavior (or optional behaviors) are very welcome!

3 Likes

Thanks - I’m aware of that prototype, and I think it would be a good place to build upon.

I see a profile as analogous to what Cargo offers: a named configuration that can control compiler flags, feature toggles, and even optional dependencies - basically a whole fpm.toml all encoded as a sub-tree of the manifest. This makes the configuration reproducible, distributable, and dependency-aware.

Supporting user-defined profiles at this level would let us separate development options (like coverage, profiling, or debug builds) from production ones, while still enabling local overrides or tools to generate new profiles (--new-profile, as you suggested).

So yeah it’s definitely worth revisiting and iterating upon in light of the more complex use cases and the maturity of fpm’s infrastructure.

1 Like

I’m considering to help with the development of fpm for quite some time, but I lost track of all the new features. What’s the status of the documentation? If it’s up-to-date, I would first read through it. If not, I would start improving the documentation. Or is there anything else where I could start?

5 Likes

fpm 0.12 failed with gfortran 15.1.1 with a segment fault error, at mean time fpm 0.11 works with gfortran 15.1.1

Likely due to this gcc15 bug:

Note gcc-15 is not among the fpm-supported compilers (at least yet)