Skip to content

Conversation

perazz
Copy link
Member

@perazz perazz commented Sep 11, 2025

This PR implements comprehensive Features and Profiles support, enabling conditional compilation, platform-specific configurations, and dependency feature propagation.

Overview

This implementation adds two major capabilities to FPM:

  1. Features: Named sets of conditional build configurations that can be selectively enabled.
  2. Profiles: Predefined combinations of features for common build scenarios

Key Features

✅ Feature System

  • Complete control: Features are complete representations of an fpm manifest, allowing full customization
  • Platform/compiler targeting: Features can be scoped to specific platforms or compilers:
[features]
my_feat.flag = ["-O3"] # Always applied
my_feat.gfortran.flag = ["-123"] # gfortran only, all OSes
my_feat.windows.flag = ["-DWIN32"] # all compilers, Windows-only
my_feat.macos.ifx.flag = ["..."] #
my_feat.ifx.macos.flag = ["..."] # specific compiler/OS combinations
  • Conditional compilation: Enable/disable code paths with preprocessor macros.
  • Compiler flags: Add feature-specific compilation flags
  • Dependencies: Conditionally include dependencies based on active features
[features]
with_mpi.dependencies.mpi = ["*"]
with_mpi.preprocess.cpp.macros = ["HAVE_MPI"]
  • Dependency feature propagation: Pass features to dependencies
[dependencies]
# Request fortran-regex with optional `mpi` feature enabled
fortran-regex = { git="https://github.com/perazz/fortran-regex", tag="1.1.2", features = ["mpi"]}

✅ Profile System

  • Predefined configurations: Named combinations of features (e.g., development = ["debug","with_blas"])
  • Default profiles: Built-in debug and release profiles (backward compatible; can be overridden)
  • CLI integration: --profile <name> and --features <list> command line options
[features]
parallel_cpu.dependencies.mpi = "*"
parallel_cpu.dependencies.openmp = "*"
gpu_support.macos.flags = ["-framework OpenCL"]
gpu_support.unix.link = ["OpenCL"]
[profiles]
parallel = ["parallel_cpu", "gpu_support"]

✅ Advanced Capabilities

  • Nested targeting: feature.os or feature.compiler or feature.compiler.os or feature.os.compiler syntax for fine-grained control
  • Dependency features: Specify which features to enable in dependencies
  • Preprocessing integration: CPP macro definitions and conditional compilation
  • Validation: Comprehensive error checking for invalid feature/profile combinations

Usage Examples

Basic Feature Definition

[features]
debug.flags = "-g"
debug.preprocess.cpp.macros = "DEBUG"

release.flags = "-O3"
release.preprocess.cpp.macros = "RELEASE"

#  Platform/Compiler Specific Features

[features]
debug.gfortran.flags = "-Wall -fcheck=bounds"
debug.linux.preprocess.cpp.macros = "LINUX_BUILD"

#Dependency Features

[features]
mpi_support.dependencies.mpi_lib = { version = "*", features = ["parallel"] }

# Profiles

[profiles]
development = ["debug"]
production = ["release", "openmp"]

CLI Usage

Enable specific features

  fpm run --features debug,openmp

Use a profile

  fpm run --profile development

Build with dependency features

fpm build --features mpi_support

Backward Compatibility

  • ✅ Fully backward compatible: Existing fpm.toml files work unchanged
  • ✅ Default behavior preserved: No features => define default debug and release features and profiles
  • ✅ Optional sections: [features] and [profiles] sections are completely optional

Testing

  • Core implementation + comprehensive tests
  • Integration tests: Multi-platform CI testing via ci/test_features.sh
  • Unit tests: Feature parsing, validation, and merging
  • Example packages: Real-world usage demonstrations
  • CLI tests: Command-line interface validation

This implementation provides a solid foundation for conditional compilation and advanced build configurations in FPM while maintaining full backward compatibility with existing projects.

@davidpfister
Copy link
Contributor

@perazz, thanks for this PR. This should make the manifest cross-compiler and cross-plateform. Since I am working on both Linux and Windows, this feature has been desired for a long time 👏.
Just one idea that came to my mind (and I am not sure this should be the role of fpm): gfortran lacks the definitions of _WIN32 and _WIN64 while compilers like ifort/ifx implement these macros by default (I believe GCC too). Would it be interesting to have fpm implement those when missing?

@perazz
Copy link
Member Author

perazz commented Sep 24, 2025

I have thought about this although have not deployed that yet. Currently, a "collection" i.e. a series of platform/compiler versions, only applies to features but not to the main manifest. One way to accomplish that could be to enable compiler/OS variants also in the main fpm.toml. I believe that would be totally backward compatible:

[gfortran.windows]
preprocess.cpp.macros = ["_WIN32","_WIN64"]

@davidpfister
Copy link
Contributor

That would be really cool. I look forward to it.
That said, if you really want to make the manifest cross-platform, then the platform architecture should also be in the toml section. That would mean something like

[gfortran.windows.x64]
preprocess.cpp.macros = ["_WIN32","_WIN64"]
[gfortran.windows.x86]
preprocess.cpp.macros = ["_WIN32"]

because I do not want to define _WIN64 when I target a 32bits build.

@perazz
Copy link
Member Author

perazz commented Sep 27, 2025

Makes total sense and I think it deserves a separate PR as the current one is already huge enough.

@perazz perazz force-pushed the features_application branch from e994efc to db45d55 Compare September 27, 2025 20:56
@perazz perazz force-pushed the features_application branch from fd3525c to b587fdf Compare September 28, 2025 07:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants