Features and Profiles

Notitie

Features and profiles are available starting from fpm v0.13.0.

Features and profiles provide fine-grained control over package configuration, enabling conditional compilation, compiler-specific settings, and platform-dependent behavior. Features are configurable collections of package properties that can be conditionally enabled based on compiler, operating system, or user selection. Profiles are named collections of features that can be activated together via the --profile CLI flag.

Quick start

A minimal example with debug and release profiles:

fpm.toml
name = "my-package"
version = "1.0.0"

[features]
# Debug feature with bounds checking
debug.flags = "-g"
debug.gfortran.flags = "-Wall -Wextra -fcheck=bounds"
debug.ifort.flags = "-warn all -check bounds"
debug.preprocess.cpp.macros = ["DEBUG"]

# Release feature with optimization
release.flags = "-O3"
release.gfortran.flags = "-funroll-loops"
release.ifort.flags = "-unroll"
release.preprocess.cpp.macros = ["NDEBUG"]

[profiles]
debug = ["debug"]
release = ["release"]

Build with a profile:

❯ fpm build --profile debug
❯ fpm build --profile release

CLI usage

Features and profiles are activated through command-line flags.

Using profiles

Activate a profile with the --profile flag:

❯ fpm build --profile release

The profile name can be a built-in profile (debug or release) or a custom profile defined in your manifest.

Using features

Activate individual features with the --features flag:

❯ fpm build --features mpi,openmp

Provide a comma-separated list of feature names (no spaces).

Mutual exclusivity

The --profile and --features flags cannot be used together:

# ERROR: Cannot use both flags
❯ fpm build --profile release --features mpi

# Use a profile that includes everything
❯ fpm build --profile parallel-mpi

# Or list all features explicitly
❯ fpm build --features release,mpi

Combining with other flags

Features and profiles work with all fpm commands:

❯ fpm build --compiler gfortran --profile release
❯ fpm test --profile ci --runner "mpiexec -np 4"

Defining features

Features are defined in the [features] section of the manifest using dot notation:

[features]
feature-name.property = "value"
feature-name.compiler.property = "value"
feature-name.os.property = "value"
feature-name.compiler.os.property = "value"

Where:

  • feature-name: The feature identifier (required)

  • compiler: Compiler name (optional) - see supported compilers

  • os: Operating system (optional) - see supported operating systems

  • property: The configuration property to set (required)

Notitie

Platform specifiers (compiler and OS) can be combined in any order: feature.compiler.os and feature.os.compiler are equivalent.

Configurable properties

Features can configure all package manifest properties.

Compiler flags

[features]
optimized.flags = "-O3 -funroll-loops"
optimized.c-flags = "-O3 -march=native"
optimized.cxx-flags = "-O3 -std=c++17"
optimized.link-time-flags = "-flto"

Available flag properties:

  • flags: Fortran compiler flags

  • c-flags: C compiler flags

  • cxx-flags: C++ compiler flags

  • link-time-flags: Linker flags

Build configuration

[features]
with-netcdf.build.link = ["netcdf", "netcdff"]
with-netcdf.build.external-modules = ["netcdf"]
testing.build.auto-tests = true

See the build configuration section in the manifest reference.

Fortran configuration

[features]
legacy.fortran.implicit-typing = true
fixed-form.fortran.source-form = "fixed"

See Fortran features in the manifest reference.

Configuratie voor bibliotheken

[features]
shared-lib.library.type = "shared"
custom.library.source-dir = "lib"
custom.library.include-dir = "inc"

See library configuration.

Dependencies

Features can add dependencies:

[features]
parallel.dependencies.openmp = "*"
parallel.dependencies.mpi = "*"

json-support.dependencies.json-fortran = { git = "https://github.com/jacobwilliams/json-fortran" }

testing.dev-dependencies.test-drive = { git = "https://github.com/fortran-lang/test-drive", tag = "v0.4.0" }

Belangrijk

Dependencies added by features are merged with the base package dependencies.

Executables, examples, and tests

[features]
demos.example = [
  { name = "demo1", source-dir = "examples", main = "demo1.f90" },
  { name = "demo2", source-dir = "examples", main = "demo2.f90" }
]

extra-tests.test = [
  { name = "integration", source-dir = "tests/integration", main = "test_integration.f90" }
]

Preprocessor configuration

[features]
debug.preprocess.cpp.macros = ["DEBUG", "VERBOSE=1"]
cpp-preprocessing.preprocess.cpp.suffixes = ["F90", "f90"]

See preprocessor configuration.

Installation and metadata

[features]
install-lib.install.library = true
experimental.description = "Experimental features requiring latest compiler"

Platform-specific features

Features can be targeted to specific platforms using compiler and OS selectors.

Compiler-specific features

Target specific compilers by including the compiler name in the feature path:

[features]
optimized.flags = "-O3"
optimized.gfortran.flags = "-march=native -funroll-loops"
optimized.ifort.flags = "-xHost -unroll"
optimized.ifx.flags = "-xHost -unroll"
optimized.nvfortran.flags = "-fast"
optimized.nagfor.flags = "-O4"

Supported compiler names: gfortran, ifort, ifx, nvfortran, nagfor, flang, amdflang, lfortran, f95, ftn95, caf, pgfortran, xlf90, crayftn, lfc

Notitie

When a feature has both base properties and compiler-specific properties, compiler-specific settings extend (not replace) the base settings.

OS-specific features

Target specific operating systems:

[features]
platform-defs.linux.preprocess.cpp.macros = ["OS_LINUX"]
platform-defs.macos.preprocess.cpp.macros = ["OS_MACOS"]
platform-defs.windows.preprocess.cpp.macros = ["OS_WINDOWS"]

pic.linux.flags = "-fPIC"
pic.freebsd.flags = "-fPIC"

Supported OS names: linux, macos, windows, cygwin, solaris, freebsd, openbsd

Resolution order

When multiple feature variants match the current platform, fpm merges them in this order:

  1. Base feature properties (no platform specifiers)

  2. Compiler-specific properties

  3. OS-specific properties

  4. Combined compiler+OS properties

How properties are merged depends on their type:

  • Additive (concatenated): flags, c-flags, cxx-flags, link-time-flags — values from all matching variants are concatenated with spaces

  • Additive (appended): dependencies, dev-dependencies, executable, test, example, preprocess — arrays are appended together

  • Exclusive (first wins): build, install, fortran, library — can only be specified in one variant per feature; defining the same section in both a base variant and a platform-specific variant is an error

Voorbeeld:

[features]
# 1. Applied to all platforms (base)
debug.flags = "-g"

# 2. Added when using gfortran on any OS
debug.gfortran.flags = "-Wall -fcheck=bounds"

# 3. Added when building on Linux with any compiler
debug.linux.flags = "-fPIC"

# 4. Added when using gfortran on Linux
debug.gfortran.linux.flags = "-fsanitize=address"

When building with gfortran on Linux, the effective flags will be:

-g -Wall -fcheck=bounds -fPIC -fsanitize=address

Belangrijk

Exclusive properties like build, fortran, library, and install cannot be defined in more than one variant of the same feature. For example, setting both myfeature.library.type and myfeature.linux.library.type will produce an error.

Profiles

Profiles are named collections of features defined in the [profiles] section:

[profiles]
debug = ["debug"]
production = ["release", "optimized", "strip-symbols"]
parallel = ["release", "mpi", "openmp"]

Built-in and default profiles

fpm provides two built-in profiles: debug and release. If not explicitly defined in the manifest, these profiles use optimized default compiler flags (e.g. -O2 -g for debug, -O3 for release with gfortran).

When you define debug or release profiles in your manifest, your definitions fully replace the built-in defaults. This is backward-compatible: projects without a [profiles] section continue using the same flags as before.

[profiles]
# Override the built-in debug profile
debug = ["debug", "verbose", "strict-checks"]

# Override the built-in release profile
release = ["release", "link-time-optimization"]

The default profile

A special profile named default can be used to define baseline features that are automatically applied alongside debug and release:

[features]
baseline.preprocess.cpp.macros = ["BASELINE"]
debug.preprocess.cpp.macros = ["DEBUG"]
release.preprocess.cpp.macros = ["RELEASE"]

[profiles]
default = ["baseline"]
debug = ["debug"]
release = ["release"]

The default profile is applied when:

  • No explicit profile or features are specified (implicit debug build)

  • The debug or release profile is requested (default features are applied first, then the profile features)

The default profile is not applied when:

  • An explicit --features list is specified

  • A custom profile (anything other than debug or release) is requested

This makes the default profile useful for settings that should always be present in standard builds, such as baseline preprocessor macros or common dependencies.

Custom profiles

Create custom profiles for specific use cases:

[profiles]
testing = ["debug", "coverage", "test-fixtures"]
ci = ["release", "warnings-as-errors", "all-tests"]
dev-mpi = ["debug", "mpi", "verbose"]

Dependency features and profiles

You can control how dependencies are built by requesting specific features or a profile from them.

Dependency features

Activate individual features in a dependency using the features key:

[dependencies]
my-lib = { git = "https://github.com/user/my-lib", features = ["mpi"] }
another-lib = { path = "../another-lib", features = ["debug", "openmp"] }

Dependency profiles

Alternatively, activate a named profile defined in the dependency’s manifest using the profile key:

[dependencies]
my-lib = { git = "https://github.com/user/my-lib", profile = "parallel-mpi" }

Belangrijk

The features and profile keys are mutually exclusive. A dependency cannot have both.

Platform-specific dependency features

Combine dependency features with platform-specific feature definitions:

[features]
linux-build.linux.dependencies.my-lib = { path = "../my-lib", features = ["linux-optimized"] }

Examples

Basic debug and release features

fpm.toml
name = "my-package"
version = "1.0.0"

[features]
# Debug feature with bounds checking
debug.flags = "-g"
debug.gfortran.flags = "-Wall -Wextra -fcheck=bounds"
debug.ifort.flags = "-warn all -check bounds"
debug.preprocess.cpp.macros = ["DEBUG"]

# Release feature with optimization
release.flags = "-O3"
release.gfortran.flags = "-funroll-loops"
release.ifort.flags = "-unroll"
release.preprocess.cpp.macros = ["NDEBUG"]

[profiles]
debug = ["debug"]
release = ["release"]
❯ fpm build --profile debug
❯ fpm run --profile release

Parallel computing features

fpm.toml
name = "parallel-app"
version = "1.0.0"

[features]
# OpenMP support
openmp.dependencies.openmp = "*"
openmp.preprocess.cpp.macros = ["USE_OPENMP"]
openmp.flags = "-fopenmp"

# MPI support
mpi.dependencies.mpi = "*"
mpi.preprocess.cpp.macros = ["USE_MPI"]

# Release optimization
release.flags = "-O3"
release.gfortran.flags = "-funroll-loops"

[profiles]
parallel-openmp = ["release", "openmp"]
parallel-mpi = ["release", "mpi"]
parallel-hybrid = ["release", "mpi", "openmp"]
❯ fpm build --profile parallel-mpi
❯ fpm run --profile parallel-hybrid --runner "mpiexec -np 4"

Platform-dependent features

fpm.toml
name = "cross-platform"
version = "1.0.0"

[features]
# Shared library settings (OS-specific)
shared.library.type = "shared"
shared.linux.flags = "-fPIC"
shared.macos.flags = "-fPIC"
shared.windows.flags = "/LD"

# Platform-specific preprocessor defines
platform.linux.preprocess.cpp.macros = ["LINUX", "UNIX"]
platform.macos.preprocess.cpp.macros = ["MACOS", "UNIX"]
platform.windows.preprocess.cpp.macros = ["WINDOWS", "WIN32"]

# OS-specific dependencies
windows-support.windows.dependencies.win32api = { path = "external/win32" }

# Compiler-specific optimization
optimized.flags = "-O3"
optimized.gfortran.flags = "-march=native -funroll-loops"
optimized.ifort.flags = "-xHost -unroll"
optimized.nvfortran.flags = "-fast"

[profiles]
shared-library = ["shared", "platform"]
maximum-performance = ["optimized"]
❯ fpm build --profile shared-library
❯ fpm build --profile maximum-performance

For more information on related topics: