Skip to content

fobos — the FoBiS makefile

A fobos file is FoBiS.py's project configuration file. It replaces makefiles entirely, using a simple INI-like syntax.

Overview

When FoBiS.py runs, it automatically looks for a file named fobos in the current working directory. Options in the fobos file override the CLI defaults, and for cflags, lflags, and preproc they are combined with any additional values passed on the command line.

Design principle

Brevity is a design parameter. The fobos file uses the same option names as the CLI switches — there is nothing new to learn.

Basic structure

A minimal single-mode fobos file:

ini
[default]
compiler  = gnu
cflags    = -c -O2
src       = ./src/
build_dir = ./build/
obj_dir   = ./obj/
mod_dir   = ./mod/
target    = src/main.f90
output    = myapp

A multi-mode fobos file:

ini
[modes]
modes = debug release

[debug]
compiler  = gnu
cflags    = -c -O0 -g -Wall
build_dir = ./build/debug/

[release]
compiler  = gnu
cflags    = -c -O3
build_dir = ./build/release/

Loading a fobos file

ScenarioCommand
Default name and locationLoaded automatically
Custom name or pathfobis build -f /path/to/my_fobos
Select a specific modefobis build -mode release
List available modesfobis build -lmodes

Case sensitivity

By default fobos option names are case-sensitive. To make them case-insensitive:

bash
fobis build -fci

Or in the fobos file itself, this is implicit for the option names (values remain as written).

Available options

All CLI options accepted by build and clean are available in a fobos mode section with identical names (using the long form without the leading --). A representative set:

OptionDescription
compilerCompiler identifier (gnu, intel, …)
fcCompiler executable (for custom compiler)
cflagsCompilation flags
lflagsLinking flags
preprocPreprocessor macro flags
srcRoot source directory (space-separated list)
build_dirBuild output directory
obj_dirCompiled objects directory
mod_dirModule interface files directory
lib_dirExternal library search directories
includeInclude files search directories
targetBuild target file
outputOutput executable name
excludeFiles to exclude from the build
libsExternal libraries (full paths)
vlibsVolatile external libraries (full paths)
ext_libsExternal libraries (by name, in linker path)
ext_vlibsVolatile external libraries (by name)
dependonInterdependent external fobos files
mklibBuild a library: static or shared
arArchiver executable for static libraries (default: ar)
arflagsArchiver flags (default: -rcs)
ranlibRanlib executable; set to empty string to skip (default: ranlib)
mpiEnable MPI compiler variant
openmpEnable OpenMP
coarrayEnable coarrays
coverageEnable coverage instrumentation
jobsNumber of parallel compile jobs
colorsColoured terminal output
quietSuppress verbose output
logWrite build log file
cflags_heritageTrack flag changes and force rebuild on change
build_profileNamed compiler flag preset: debug, release, asan, coverage
cache_dirBuild cache directory (default: .fobis_cache)
no_cacheDisable build cache (true/false)
no_auto_discoverDisable convention-based source discovery (true/false)
pre_buildShell command to run before the build
post_buildShell command to run after a successful build
featuresSpace-separated feature names to activate for this mode (see Feature Flags)
varsetSpace-separated varset names to apply for this mode (see Varsets)
intrinsic_modulesSpace-separated module names to treat as intrinsic — silently skipped from the dependency graph (see below)

Special sections

Beyond the mode sections, a fobos file can contain several top-level sections with fixed names:

SectionPurposeDocumentation
[modes]Lists the available named mode sectionsMany-mode fobos
[project]Project metadata: name, version, authors, …Project metadata
[features]Named compile-time option sets, activated with --featuresFeature Flags
[feature:NAME]Per-feature metadata: flags, requires, conflictsFeature Flags
[feature-group:NAME]Mutually-exclusive feature group with optional defaultFeature Flags
[varsets]Varset metadata: default = NAMES (fobos-declared fallback)Varsets
[varset:NAME]Named bundle of $variable bindings, applied via --varsetVarsets
[include]Pull in sibling fobos files; ? prefix for optionalIncludes
[dependencies]GitHub-hosted build dependenciesFetch Dependencies
[test]Test runner defaults: suite, timeout, jobsTest Runner
[coverage]Coverage report settings: output dir, fail threshold, excludesCoverage
[externals]External library flags via pkg-config and MPI auto-detectionExternal Libraries
[pkgconfig]Generate a .pc file for your own projectExternal Libraries
[target.NAME]Named build target with per-target flag overridesbuild reference
[example.NAME]Named example target (same syntax as target.NAME)build reference
[rule-NAME]Custom shell-command rulesRules

[features] section

Defines named compile-time option sets that map to flags:

ini
[features]
default = release mpi             ; active when none are explicitly requested
release = -O3 -DNDEBUG
debug   = -g -O0 -fcheck=all
mpi     = -DUSE_MPI
hdf5    = -DUSE_HDF5 -I/opt/hdf5/include
omp_defs = -DUSE_OMP              ; define only — pair with --features openmp

# Composite features: prefix a name with @ to reference another feature.
# Activating `prod` pulls in `release`, `mpi`, and `hdf5`.
prod    = @release @mpi @hdf5
dev     = @debug @mpi

Flags are routed to cflags or lflags automatically by pattern. Composite expansion is recursive and cycle-safe.

Activation sources (merged in order, last wins on negation):

  1. [features] default = ... — project-wide baseline
  2. [mode-X] features = a b c — per-mode preset
  3. --features a,b on the CLI — user override; -name deactivates
bash
fobis build                              # default features only
fobis build --features prod              # composite expansion
fobis build --features prod,-coverage    # drop coverage from the active set
fobis build --no-default-features        # ignore [features] default =

Well-known compiler capabilities (openmp/omp, mpi, coarray, coverage, profile) are implicit features — they work without a [features] section and resolve to the correct compiler-specific flag automatically.

[feature:NAME] and [feature-group:NAME] sections

For features that need constraints or grouping, declare them in dedicated sibling sections:

ini
[feature:hdf5]
requires = mpi                            ; auto-pulls mpi when hdf5 is active

[feature:static]
conflicts = shared                        ; hard error if both active

[feature-group:precision]
members = single double quad              ; at most one active at a time
default = double                          ; auto-activated when group is empty

requires cascades transitively (cycle-safe). conflicts and group violations abort the build with a verbose error tracing each side back to its originator. See Feature Flags for the full reference.

[varsets] and [varset:NAME] sections

Varsets are named bundles of $variable bindings selected at invocation time. They eliminate per-cluster / per-target mode duplication when the only difference between modes is a handful of paths or numeric parameters:

ini
[varsets]
default = local                              ; applied when --varset is omitted

[varset:local]
$HDF5_PREFIX = lib/hdf5/develop/nvf/26.1

[varset:leonardo]
$HDF5_PREFIX = /leonardo/prod/spack/.../hdf5-1.14.3
$ARCH        = cc80

[mode-prism]
template = template-nvf-oac
target   = adam_prism_fnl.F90
libs     = $HDF5_PREFIX/lib/libhdf5_fortran.a $HDF5_PREFIX/lib/libhdf5.a
lib_dir  = $HDF5_PREFIX/lib
include  = $HDF5_PREFIX/include
cflags   = -c -gpu=$ARCH
bash
fobis build --mode mode-prism                          # uses [varsets] default = local
fobis build --mode mode-prism --varset leonardo        # leonardo paths + cc80
fobis build --mode mode-prism --varset leonardo,cc89   # combined; last write wins

Variables defined inside [varset:NAME] sections do not leak into the implicit global pool — they apply only when the varset is selected. See Varsets for the full reference.

[include] section

Pulls in the contents of one or more sibling fobos files before any other processing. Useful for splitting a large fobos by concern, sharing configuration across sibling repos, or layering machine-local overrides on top of committed defaults:

ini
[include]
paths = templates.fobos
        rules.fobos
        ?fobos.local                ; '?' prefix → optional, silent if missing

Paths resolve relative to the including file's directory (after ${ENV} and ~ expansion). On conflict, the parent file wins; among siblings, later includes override earlier ones. Cycles abort with exit 1. By convention, included fragments use the .fobos extension (the resolver itself accepts any path). See Includes for the full reference.

[dependencies] section

Declares GitHub-hosted dependencies. Each entry maps a short name to a repository spec:

ini
[dependencies]
deps_dir = .fobis_deps          # optional: where to clone deps (same as --deps-dir)
penf     = https://github.com/szaghi/PENF :: tag=v1.5.0
stdlib   = https://github.com/fortran-lang/stdlib :: semver=>=0.5,<1.0 :: use=fobos :: mode=gnu

The use= field selects the integration mode:

  • sources (default) — dependency sources are compiled inline with your project
  • fobos — dependency is built as a separate library and linked

See Fetch Dependencies and Lock File & Semver for the full syntax and workflow.

[test] section

Sets defaults for fobis test:

ini
[test]
suite   = unit     ; only run tests tagged with this suite
timeout = 120      ; seconds per test
jobs    = 4        ; parallel compile jobs

[coverage] section

Sets defaults for fobis coverage:

ini
[coverage]
output_dir = coverage/
source_dir = src/
fail_under = 75
exclude    = test/*
            examples/*

[target.NAME] and [example.NAME] sections

Named targets allow per-target flag overrides without separate fobos modes:

ini
[target.solver]
target = src/solver.F90
output = solver
cflags = -c -O3 -DSOLVER

[example.demo]
target = examples/demo.F90
output = demo
bash
fobis build --target-filter solver
fobis build --examples

intrinsic_modules mode key

When the dependency scanner sees a use NAME line that doesn't resolve to any source file or compiled .mod, it warns "the file 'X' depends on 'NAME' that is unreachable". FoBiS already filters out the standard Fortran intrinsics (iso_fortran_env, iso_c_binding, ieee_*, openacc, omp_lib, mpi) and the modules supplied by the active compiler (cudafor etc. under nvfortran; ifport etc. under intel).

For modules that come from external libraries — hdf5, mpi_f08, vendor HPC libraries, or any project-specific helper that lives outside the source tree — declare them per-mode so the warning goes away:

ini
[release]
compiler          = nvfortran
cflags            = -cpp -c -O3
target            = src/main.F90
intrinsic_modules = hdf5 hdf5_hl mpi_f08

The names are case-insensitive (Fortran rules) and apply only to the mode they are declared in. They compose with the universal and compiler-specific sets — together they form the union the dependency scanner skips. See Compilers for the list of compiler-supplied modules and Architecture for the three-tier filter.

Comments

Lines beginning with # are ignored:

ini
[default]
# This is a comment
compiler = gnu  # inline comments are also supported
cflags   = -c -O2

Further reading