Appearance
Cookbook
Short answers to "how do I ...?". Each recipe shows the code and its real output; the reference has the details.
A required option
f90
program minimal
!< A minimal FLAP program: one required option.
use flap
implicit none
type(command_line_interface) :: cli
character(99) :: string
integer :: error
call cli%init(description='minimal FLAP example')
call cli%add(switch='--string', switch_ab='-s', help='a string', required=.true., act='store', error=error)
if (error /= 0) stop 1, quiet=.true.
call cli%parse(error=error)
if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='-s', val=string, error=error)
if (error /= 0) stop 1, quiet=.true.
print '(A)', 'String = '//trim(string)
endprogram minimal$ minimal
minimal: error: named option "--string" is required!
usage: minimal --string value [--help] [--markdown] [--version]
minimal FLAP example
Required switches:
--string value, -s value
a string
Optional switches:
--help, -h
Print this help message
--markdown, -md
Save this help message in a Markdown file
--version, -v
Print version
Try 'minimal --help' for help.
[exit status 1]An option with a default, read into a number
f90
call cli%init(progname = 'myapp', &
version = 'v1.0', &
description = 'Demonstration program', &
examples = ['myapp -i a.dat -o b.dat ', &
'myapp -i a.dat -n 100 --tol 1'])
call cli%add(switch='--input', switch_ab='-i', help='Input file', required=.true., act='store', error=error)
call cli%add(switch='--output', switch_ab='-o', help='Output file', required=.false., act='store', def='out.dat', &
error=error)
call cli%add(switch='--niter', switch_ab='-n', help='Iterations', required=.false., act='store', def='100', &
error=error)
call cli%add(switch='--tol', switch_ab='-t', help='Tolerance', required=.false., act='store', def='1.0e-6', &
error=error)
call cli%add(switch='--verbose', help='Verbose output', required=.false., act='store_true', &
def='.false.', error=error)f90
call cli%parse(error=error)
if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='-i', val=input, error=error) ; if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='-o', val=output, error=error) ; if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='-n', val=n, error=error) ; if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='-t', val=tol, error=error) ; if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='--verbose', val=verbose, error=error) ; if (error /= 0) stop 1, quiet=.true.$ myapp -i a.dat -n 50 --verbose
input = a.dat
output = out.dat
niter = 50
tol = 1.0E-06
verbose = Tget converts to the type and kind of the variable; a value that is not a number is ERROR_CASTING_NUMBER.
A flag and its negation
f90
! a flag with its negation: the last one passed wins
call cli%add(switch='--restart', switch_neg='--no-restart', help='Restart from the checkpoint', required=.false., &
act='store_true', def='.true.')$ actions --no-restart
verbosity = 0
include = .
format = text
restart = FA flag that is true unless passed
f90
call cli%add(switch='--no-color', help='Plain output', required=.false., act='store_false', def='.true.')$ store_false --no-color
color = FA verbosity counter
f90
! count: -v -v, -vv or --verbose --verbose give 2
call cli%add(switch='--verbose', switch_ab='-v', help='Verbosity (repeatable)', required=.false., act='count')$ actions -vvv
verbosity = 3
include = .
format = text
restart = TA repeatable option
f90
! append: one value per occurrence
call cli%add(switch='--include', switch_ab='-I', help='Include directory (repeatable)', required=.false., &
act='append', def='.')$ actions -I src --include=lib
verbosity = 0
include = src, lib
format = text
restart = TRead it with get_varying, as a list.
An option whose value is optional
f90
! store*: the value is optional, the switch alone gives the default
call cli%add(switch='--format', help='Output format', required=.false., act='store*', def='text')$ actions --format=json -v --verbose
verbosity = 2
include = .
format = json
restart = TA value can always be given inline, --format=json; for an optional value it is the only unambiguous way.
An option hidden from the help
f90
! a hidden flag: parsed, but not shown in the help
call cli%add(switch='--debug-internal', help='Internal debug flag', required=.false., act='store_true', def='.false.', &
hidden=.true.)A value from a fixed list
f90
call cli%add(switch='--level', switch_ab='-l', help='Verbosity level', required=.false., act='store', def='1', &
choices='1,3,5')
! case_sensitive=.false.: WENO5 matches weno5, and get returns the declared spelling
call cli%add(switch='--scheme', help='Space scheme', required=.false., act='store', def='muscl', &
choices='weno5,muscl', case_sensitive=.false.)$ choices --level 3 --scheme WENO5
level = 3, scheme = weno5$ choices --level 2
choices: error: value of named option "--level" must be chosen in: (1,3,5) but "2" has been passed!
[exit status 1]A number within bounds
f90
call cli%add(switch='--cfl', help='CFL number', required=.false., act='store', def='0.8', &
min='0', min_open=.true., max='1') ! (0, 1]
call cli%add(switch='--threads', help='OpenMP threads', required=.false., act='store', def='1', &
min='1', max='256', clamp=.true.) ! [1, 256], out-of-range values clamped$ ranges --cfl 0.5 --threads 999
cfl = 0.50, threads = 256$ ranges --cfl 1.5
ranges: error: value "1.5" of "--cfl" is out of range (0, 1]!
[exit status 1]A list of files
f90
call cli%add(switch='--mesh', help='Mesh file', required=.true., act='store', metavar='FILE')
! exactly 3 values
call cli%add(switch='--coords', help='X Y Z of the probe', required=.false., act='store', nargs='3', def='0 0 0')
! zero or more values: the switch alone is an empty list
call cli%add(switch='--fields', help='Fields to save', required=.false., act='store', nargs='*', def='u', &
metavar='NAME')
! one or more values
call cli%add(switch='--weights', help='Weights', required=.false., act='store', nargs='+', def='1.0')f90
call cli%get(switch='--mesh', val=mesh, error=error)
call cli%get(switch='--coords', val=coords, error=error) ! a fixed-size array of the right size
call cli%get_varying(switch='--fields', val=fields, error=error) ! allocatable arrays, any size
call cli%get_varying(switch='--weights', val=weights, error=error)$ lists --mesh wing.grd --coords 1 2 3 --fields u v p --weights 0.5 0.25
mesh = wing.grd
coords = 1 2 3
fields = (3) u v p
weights = 0.50 0.25An input file given without a switch
f90
call cli%add(positional=.true., position=1, help='Input file', required=.true., act='store', metavar='INPUT')
call cli%add(positional=.true., position=2, help='Scale factor', required=.false., act='store', def='1.0', &
metavar='SCALE')
call cli%add(switch='--verbose', help='Verbose', required=.false., act='store_true', def='.false.')$ positional in.dat 2.5
input = in.dat, scale = 2.50, verbose = FA file that must exist
f90
call cli%add(switch='--mesh', help='Mesh file', required=.true., act='store', readable=.true.)
call cli%add(switch='--log', help='Log file', required=.false., act='store', def='-', writable=.true., allow_dash=.true.)$ paths --mesh wnig.grd
paths: error: option "--mesh": path "wnig.grd" does not exist!
Try 'paths --help' for help.
[exit status 1]KEY=VALUE overrides of an input deck
f90
call cli%add(switch='--set', switch_ab='-s', help='Override input-deck parameters', required=.false., act='store', &
nargs='+', map=.true., map_keys='cfl,nx,ny,t_end', def='cfl=0.8')f90
cfl = 0.9_8 ; nx = 128 ! the values of the input deck
call cli%get_map_value(switch='--set', key='cfl', val=cfl, found=found) ! overridden only if given
call cli%get_map_value(switch='--set', key='nx', val=nx, found=found)$ map --set cfl=0.5 nx=256
cfl = 0.50, nx = 256Two options that cannot go together
f90
call cli%add(switch='--json', help='JSON output', required=.false., act='store_true', def='.false.', exclude='--csv')
call cli%add(switch='--csv', help='CSV output', required=.false., act='store_true', def='.false.', exclude='--json')$ exclusive --mesh m.grd --json --csv
exclusive: error: the options "--json" and "--csv" are mutually exclusive, but both have been passed!
Try 'exclusive --help' for help.
[exit status 1]Exactly one of two options
f90
call cli%add(switch='--mesh', switch_ab='-m', help='Mesh file', required=.false., act='store', def='')
call cli%add(switch='--restart', switch_ab='-r', help='Restart file', required=.false., act='store', def='')
call cli%add(switch='--left', help='Go left', required=.false., act='store_true', def='.false.')
call cli%add(switch='--right', help='Go right', required=.false., act='store_true', def='.false.')
call cli%set_mutually_exclusive_switches(switches='--mesh,--restart', required=.true.) ! exactly one
call cli%set_mutually_exclusive_switches(switches='--left,--right') ! at most one$ exclusive
exclusive: error: one of "--mesh", "--restart" is required!
usage: exclusive [--json] [--csv] (--mesh value | --restart value) [--left | --right] [--help] [--markdown] [--version]
Try 'exclusive --help' for help.
[exit status 1]Values from environment variables
f90
call cli%init(progname='environment', auto_envvar_prefix='SOLVER')
call cli%add(switch='--mesh-file', help='Mesh', required=.true., act='store') ! SOLVER_MESH_FILE
call cli%add(switch='--workers', help='Workers', required=.false., act='store', nargs='+', def='1') ! SOLVER_WORKERS
call cli%add(switch='--fast', help='Fast mode', required=.false., act='store_true', def='.false.') ! SOLVER_FAST
call cli%add(switch='--token', help='API token', required=.false., act='store', def='none', envvar='MY_TOKEN')$ SOLVER_MESH_FILE=wing.grd MY_TOKEN=abc environment
mesh = wing.grd, token = abc, workers = 1
fast = F
--mesh-file = wing.grd [environment: SOLVER_MESH_FILE]
--workers = 1 [default]
--fast = .false. [default]
--token = abc [environment: MY_TOKEN]Ignoring the environment, for reproducible runs
f90
call cli%init(progname='ignore_env', ignore_env=.true.)
call cli%add(switch='--threads', help='Threads', required=.false., act='store', def='1', envvar='OMP_NUM_THREADS')$ OMP_NUM_THREADS=64 ignore_env
threads = 1Values from a configuration file
f90
call cli%init(progname='config', auto_envvar_prefix='SOLVER')
call cli%add(switch='--mesh-file', help='Mesh', required=.true., act='store')
call cli%add(switch='--cfl', help='CFL', required=.false., act='store', def='0.5')
call cli%add(switch='--config', help='Configuration file', required=.false., act='config', def='solver.ini')
call cli%add_group(group='post', description='Post processing')
call cli%add(group='post', switch='--format', help='Format', required=.false., act='store', def='vtk')ini
# solver.ini
mesh-file = wing.grd ; keys are the long switches without the dashes
cfl = 0.8
[post] # a section is a command
format = vtu$ config
--mesh-file = wing.grd [config: solver.ini]
--cfl = 0.8 [config: solver.ini]
--config = solver.ini [default]A configuration file chosen by the program
f90
call cli%add(switch='--cfl', help='CFL', required=.false., act='store', def='0.5')
call cli%add(switch='--steps', help='Time steps', required=.false., act='store', def='100')
call cli%set_config(file='defaults.ini') ! skipped when missing; required=.true. makes that an errorini
# defaults.ini: the defaults of the program
cfl = 0.8
steps = 500$ set_config --cfl 0.3
--cfl = 0.3 [command line]
--steps = 500 [config: defaults.ini]Logging where every value comes from
f90
call cli%parse(error=error)
if (error /= 0) stop 1, quiet=.true.
print '(A)', cli%provenance()$ SOLVER_CFL=0.6 config --mesh-file body.grd post
--mesh-file = body.grd [command line]
--cfl = 0.6 [environment: SOLVER_CFL]
--config = solver.ini [default]
post --format = vtu [config: solver.ini]Was an option passed, and where does its value come from?
f90
print '(A,I0,A,L1)', 'threads = ', threads, ', passed on the command line: ', cli%is_passed(switch='--threads')
select case(cli%get_source(switch='--threads'))
case(SOURCE_COMMANDLINE) ; print '(A)', 'from the command line'
case(SOURCE_ENVIRONMENT) ; print '(A)', 'from the environment'
case(SOURCE_CONFIG) ; print '(A)', 'from the configuration file'
case(SOURCE_DEFAULT) ; print '(A)', 'the default'
endselect$ OMP_NUM_THREADS=8 passed
threads = 8, passed on the command line: F
from the environment$ OMP_NUM_THREADS=8 passed --threads 2
threads = 2, passed on the command line: T
from the command lineSubcommands
f90
call cli%init(progname = 'fake_git', &
version = 'v2.1.5', &
authors = 'Stefano Zaghi', &
license = 'MIT', &
description = 'A toy git-like program demonstrating FLAP', &
examples = ['fake_git --help ', &
'fake_git init ', &
'fake_git commit -m "fix bug #1"', &
'fake_git tag -a "v2.1.5" '])
! a top-level option
call cli%add(switch='--authors', switch_ab='-a', help='Print the authors', required=.false., act='store_true', &
def='.false.', error=error)
! the commands
call cli%add_group(group='init', description='Initialise versioning')
call cli%add_group(group='commit', description='Commit changes to the current branch')
call cli%add_group(group='tag', description='Tag the current commit')
! the options of the commands
call cli%add(group='commit', switch='--message', switch_ab='-m', help='Commit message', required=.false., act='store', &
def='', error=error)
call cli%add(group='tag', switch='--annotate', switch_ab='-a', help='Tag annotation', required=.false., act='store', &
def='', error=error)f90
call cli%parse(error=error)
if (error /= 0) stop 1, quiet=.true.
call cli%get(switch='-a', val=authors, error=error)
if (authors) print '(A)', 'Authors: '//cli%authors
if (cli%run_command('init')) print '(A)', 'Initialising versioning'
if (cli%run_command('commit')) then
call cli%get(group='commit', switch='-m', val=message, error=error)
print '(A)', 'Committing with message: "'//trim(message)//'"'
endif
if (cli%run_command('tag')) then
call cli%get(group='tag', switch='-a', val=annotation, error=error)
print '(A)', 'Tagging with annotation: "'//trim(annotation)//'"'
endif$ fake_git init commit -m first
Initialising versioning
Committing with message: "first"Short names for commands, options shared by commands
f90
call cli%add_group(group='compile', aliases='co, c', description='Compile the sources')
call cli%add_group(group='link', description='Link the objects')f90
! defined once at the top level, then copied into the commands
call cli%add(switch='--jobs', switch_ab='-j', help='Parallel jobs', required=.false., act='store', def='1')
call cli%add(switch='--verbose', help='Verbose', required=.false., act='store_true', def='.false.')
call cli%copy_options(to_group='compile') ! --jobs and --verbose
call cli%copy_options(to_group='link', switches='--verbose') ! --verbose only$ aliases co -j 4 link --verbose
compile, jobs = 4
link, verbose = TTwo commands that cannot go together
f90
call cli%add_group(group='run', description='Run a simulation')
call cli%add_group(group='clean', description='Remove the results')
call cli%set_mutually_exclusive_groups(group1='run', group2='clean')$ exclusive_groups run clean
exclusive_groups: error: the group "run" and "clean" are mutually exclusive, but both have been called!
Try 'exclusive_groups run --help' for help.
[exit status 1]An option that lists something and exits
f90
call cli%add(switch='--mesh', help='Mesh file', required=.true., act='store')
call cli%add(switch='--list-models', help='List the turbulence models and exit', act='alternate')f90
call cli%parse(error=error)
if (error == STATUS_ALTERNATE) then
if (cli%is_passed(switch='--list-models')) print '(A)', 'spalart-allmaras, k-omega-sst'
stop
elseif (error /= 0) then
stop 1, quiet=.true.
endif$ alternate --list-models
spalart-allmaras, k-omega-sstRetiring an option
f90
call cli%add(switch='--grid', help='Grid file', required=.false., act='store', def='g.grd', &
deprecated='use --mesh instead')
call cli%add(switch='--mesh', help='Mesh file', required=.false., act='store', def='m.grd')
call cli%add_group(group='legacy', description='The old solver', deprecated='use run')
call cli%add_group(group='run', description='The solver')$ deprecated --grid w.grd legacy
deprecated: warning: option "--grid" is deprecated: use --mesh instead
deprecated: warning: command "legacy" is deprecated: use run
okReporting a check of the program in FLAP's style
f90
call cli%get(switch='--nx', val=nx, error=error)
if (mod(nx, 2) /= 0) error = cli%raise_error('must be even', switch='--nx')
if (error /= 0) stop 1, quiet=.true.$ raise_error --nx 33
raise_error: error: switch "--nx": must be even
usage: raise_error [--nx value] [--help] [--markdown] [--version]
[exit status 1]A shorter output after an error
f90
call cli%init(progname='usage_on_error', usage_on_error='usage')$ usage_on_error
usage_on_error: error: named option "--mesh" is required!
usage: usage_on_error --mesh value [--cfl value] [--help] [--markdown] [--version]
Try 'usage_on_error --help' for help.
[exit status 1]The help and the errors in a file
f90
open(newunit=log, file='flap.log', action='write')
call cli%init(progname='units', usage_lun=log, error_lun=log) ! the help and the errors go to the log$ units --nxx 4 || cat flap.log
units: error: switch "--nxx" is unknown! Did you mean "--nx"?
Try 'units --help' for help.The error message and the usage as text
f90
open(newunit=quiet, status='scratch')
call cli%init(progname='messages', description='Messages as text', &
error_lun=quiet, usage_lun=quiet) ! FLAP prints nowhere: the program reports
call cli%add(switch='--nx', help='Cells', required=.false., act='store', def='64')
call cli%parse(args='--nxx 4', error=error)
if (error /= 0) print '(A,I0,A)', 'code ', error, ': '//cli%error_message
print '(A)', 'signature:'//cli%signature() ! the arguments, as in the usage line
print '(A)', cli%usage(g=0) ! the help of the top level (g: the index of a command)$ messages
code 15: messages: error: switch "--nxx" is unknown! Did you mean "--nx"?
signature: [--nx value] [--help] [--markdown] [--version]
usage: messages [--nx value] [--help] [--markdown] [--version]
Messages as text
Optional switches:
--nx value
default value 64
Cells
--help, -h
Print this help message
--markdown, -md
Save this help message in a Markdown file
--version, -v
Print versionColours
f90
call cli%init(progname='colors', description='Coloured output', error_color='red', error_style='bold_on')
call cli%add(switch='--mesh', switch_ab='-m', help='Mesh file', required=.false., act='store', def='m.grd', &
help_color='cyan', help_style='bold_on')
call cli%add(switch='--cfl', help='CFL number', required=.false., act='store', def='0.5', help_color='yellow')$ colors --mehs x
colors: error: switch "--mehs" is unknown! Did you mean "--mesh"?
Try 'colors --help' for help.
[exit status 1]Switches in any case
f90
call cli%init(progname='case_insensitive', case_insensitive=.true.) ! before any add$ case_insensitive --MESH wing.grd RUN
mesh = wing.grd, run = TA man page, Markdown, shell completion
f90
call cli%init(progname='completion', version='v1.0', description='A solver', &
completion_options=.true., man_option=.true.)$ completion --install-completion bash
completion script installed in "/home/user/.completion-completion.bash", loaded by "/home/user/.bashrc"f90
call cli%save_man_page(man_file='save_outputs.1', error=error)
call cli%save_usage_to_markdown(markdown_file='save_outputs.md', error=error)
call cli%save_bash_completion(bash_file='save_outputs.bash', error=error)
call cli%save_zsh_completion(zsh_file='save_outputs.zsh', error=error)
call cli%save_fish_completion(fish_file='save_outputs.fish', error=error)
call cli%save_powershell_completion(powershell_file='save_outputs.ps1', error=error)Cleaning up before exiting on --help
f90
call cli%init(progname='statuses', version='v2.4.0', standalone=.false., no_args_is_help=.true.)f90
call cli%parse(error=error)
select case(error)
case(0)
print '(A)', 'running'
case(STATUS_PRINT_H, STATUS_PRINT_V, STATUS_PRINT_M)
print '(A)', 'help, version or Markdown printed: cleaning up, then exit status 0'
case(STATUS_NO_ARGS)
stop 2, quiet=.true.
case default
stop 1, quiet=.true.
endselect$ statuses --help
usage: statuses [--mesh value] [--help] [--markdown] [--version]
Optional switches:
--mesh value
default value m.grd
Mesh file
--help, -h
Print this help message
--markdown, -md
Save this help message in a Markdown file
--version, -v
Print version
help, version or Markdown printed: cleaning up, then exit status 0Testing the command line
f90
! a command line given as a string
call cli%parse(args='--nx 128', error=error)
call check(error == 0 .and. cli%get_source(switch='--nx') == SOURCE_COMMANDLINE, 'parse --nx 128')
call cli%get(switch='--nx', val=nx, error=error)
call check(nx == 128, 'get --nx')
! parse another command line with the same definitions
call cli%reset_parse
call cli%parse(args='', error=error)
call check(cli%get_source(switch='--nx') == SOURCE_DEFAULT, 'the default')
! errors and statuses are returned, never stop the program (standalone=.false.)
call cli%reset_parse
call cli%parse(args='--nx 1 --nx 2', error=error)
call check(error == ERROR_DUPLICATED_CLAS, 'a repeated switch')
call cli%reset_parse
call cli%parse(args='--help', error=error)
call check(error == STATUS_PRINT_H, '--help returns a status')
call check(caught('Optional switches:'), 'the help is written on the unit')See chapter 8 of the tutorial.
Arguments that are not yours
With ignore_unknown_clas, parse reads every known value, prints nothing about the unknown arguments and returns ERROR_UNKNOWN_CLAS_IGNORED when there are some (cli%error_message then describes one of them):
f90
call cli%init(progname='ignore_unknown', ignore_unknown_clas=.true.) ! unknown arguments are not an error
call cli%add(switch='--nx', help='Cells', required=.false., act='store', def='64')
call cli%parse(error=error)
if (error == ERROR_UNKNOWN_CLAS_IGNORED) then
print '(A)', 'some arguments are left to the solver library' ! FLAP prints nothing: the program may say something
elseif (error /= 0) then
stop 1, quiet=.true.
endif$ ignore_unknown --nx 8 --solver-option fast
some arguments are left to the solver library
nx = 8Passing arguments through to another program
Everything after -- is collected, unparsed, in the hidden list of --:
fortran
character(256), allocatable :: rest(:)
call cli%get_varying(switch='--', val=rest, error=error) ! prog --nx 4 -- mpirun -np 8 -> rest = [mpirun, -np, 8]Asking the user
f90
call m%init(question='What is your favorite food?')
call m%add_option(text='Pizza')
call m%add_option(text='Ice Cream')
call m%add_option(text='Tacos')
call m%run(choice, merror)$ printf '2\n' | menus single
1) Pizza
2) Ice Cream
3) Tacos
What is your favorite food?
choice = 2Asking for several choices
f90
call m%init(question='Which toppings?', multiple=.true.)
call m%add_option(text='Cheese', is_default=.true.)
call m%add_option(text='Mushrooms')
call m%add_option(text='Olives', is_default=.true.)
call m%run(choices, merror) ! "3 1" gives [3, 1]; an empty answer the defaults, [1, 3]$ printf '3 1\n' | menus multiple
1) *Cheese
2) Mushrooms
3) *Olives
Which toppings?
choices = 3,1A yes/no question
f90
call m%init(question='Overwrite the restart file?')
call m%yes_no(answer, default='n', error=merror)$ printf 'yes\n' | menus yes_no
Overwrite the restart file? (y/N)
answer = TAsking again after a wrong answer
f90
call m%init(question='What is your favorite food?', loop_on_invalid=.true., tries=3)$ printf '7\n2\n' | menus retry
1) Pizza
2) Ice Cream
3) Tacos
What is your favorite food? error: invalid response: 7 (2 tries left)
1) Pizza
2) Ice Cream
3) Tacos
What is your favorite food?
choice = 2