Skip to content

Subcommands ​

FLAP builds git-style interfaces, where a program dispatches on a named command: fake_git commit -m "fix", fake_git tag -a v1. In FLAP's own terms a command is a group of command line arguments.

Concepts ​

  • The top level (the unnamed group) holds the arguments given before any command name.
  • Each command is a named group with its own arguments, its own help and its own builtins (--help, ...).
  • The arguments after a command name belong to that command, up to the next command name.
  • Commands are not nested: a command has no subcommands of its own. A command line can call several commands, one after the other (fake_git init commit -m first), each at most once; set_mutually_exclusive_groups forbids a pair.
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)
$ fake_git --help
usage: fake_git [--authors] [--help] [--markdown] [--version] {init,commit,tag} ...

A toy git-like program demonstrating FLAP

Optional switches:
  --authors, -a
      default value .false.
      Print the authors
  --help, -h
      Print this help message
  --markdown, -md
      Save this help message in a Markdown file
  --version, -v
      Print version

Commands:
  init
      Initialise versioning
  commit
      Commit changes to the current branch
  tag
      Tag the current commit

For more detailed commands help try:
  fake_git init -h,--help
  fake_git commit -h,--help
  fake_git tag -h,--help

Examples:
  fake_git --help
  fake_git init
  fake_git commit -m "fix bug #1"
  fake_git tag -a "v2.1.5"

Each command has its own help:

$ fake_git commit --help
usage: fake_git commit [--message value] [--help] [--markdown] [--version]

Commit changes to the current branch

Optional switches:
  --message value, -m value
      Commit message
  --help, -h
      Print this help message
  --markdown, -md
      Save this help message in a Markdown file
  --version, -v
      Print version

Adding a command — cli%add_group ​

fortran
call cli%add_group(group, description, help, exclude, examples, no_args_is_help, deprecated, aliases, error)
ArgumentTypePurpose
groupcharacter(*)The command name
descriptioncharacter(*), optionalShown in the top-level help and in the help of the command
helpcharacter(*), optionalText before the usage line of the command (default 'usage: ')
examplescharacter(*), dimension(:), optionalExamples shown in the help of the command
excludecharacter(*), optionalA command that cannot be called together with this one (as set_mutually_exclusive_groups)
no_args_is_helplogical, optionalprog <command> alone prints the help of the command (STATUS_NO_ARGS)
deprecatedcharacter(*), optionalThe command is deprecated: calling it prints a warning (see Advanced)
aliasescharacter(*), optionalOther names of the command, comma separated (see Aliases)
errorinteger, optionalError code (0 = success)

The arguments of a command are added with group=; add creates the command if it does not exist yet.

Which command was called — cli%run_command ​

cli%run_command('commit') is .true. when the command (or one of its aliases) is on the command line:

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 commit -m "fix bug"
Committing with message: "fix bug"
$ fake_git init commit -m first
Initialising versioning
Committing with message: "first"

Values that look like command names ​

A value is never mistaken for a command: in fake_git commit -m tag the message is tag and the tag command is not called, because -m takes exactly one value. This holds for every option with a fixed number of values (one value, or nargs='N'). A list with a variable number of values (nargs='+' or nargs='*') ends at the first command name: in prog --files a b init, the list is a b and init is called.

$ fake_git commit -m tag
Committing with message: "tag"

A command passed twice (directly, or through an alias) is ERROR_COMMAND_REPEATED (1013), reported before --help:

$ fake_git commit -m x commit
fake_git: error: the command "commit" is passed more than once!

Try 'fake_git --help' for help.
[exit status 1]

An unknown command gets the closest names as suggestions:

$ fake_git comit
fake_git: error: argument "comit" is unknown! Did you mean "commit"?

Try 'fake_git --help' for help.
[exit status 1]

Aliases ​

aliases gives a command other names: invoking an alias is invoking the command.

f90
call cli%add_group(group='compile', aliases='co, c', description='Compile the sources')
call cli%add_group(group='link', description='Link the objects')

Every query accepts an alias too: run_command('co') is run_command('compile'), get(group='co', ...) reads the compile options, and mutually exclusive commands can be declared through an alias. The help lists compile, co, c under Commands:, and the completion scripts offer the aliases. With init(case_insensitive=.true.) aliases match in any case, as command names do.

An alias equal to a command name or to another alias, repeated, blank, or equal to its own command, and a command name equal to an existing alias, are ERROR_GROUP_ALIAS (1008): printed, returned by error=, and kept on the command, so that parse fails too.

Reusable option sets — cli%copy_options ​

fortran
call cli%copy_options(to_group, from_group, switches, pref, error)

Options defined once (at the top level by default, or in from_group) are copied into a command:

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 = T
$ aliases compile --help
usage: aliases compile [--jobs value] [--verbose] [--help] [--markdown] [--version]

Compile the sources

Optional switches:
  --jobs value, -j value
      default value 1
      Parallel jobs
  --verbose
      default value .false.
      Verbose
  --help, -h
      Print this help message
  --markdown, -md
      Save this help message in a Markdown file
  --version, -v
      Print version
  • The definitions are copied by value, at the call: help, action, default, nargs, choices, envvar, hidden, negation, range, map, path checks, deprecation, ... Later changes to the source do not propagate.
  • Each copy has its own value: compile -j 4 sets the --jobs of compile only.
  • Only named options are copied; the builtins never (each command has its own). Naming a positional in switches is ERROR_COPY_POSITIONAL (1009), an unknown name ERROR_MISSING_CLA (nothing is copied then), an unknown group ERROR_MISSING_GROUP; a switch the target already defines is the group consistency error (100).
  • An explicit envvar is copied verbatim (every copy reads the same variable); a name generated by init(auto_envvar_prefix=) is generated again for the target (APP_COMPILE_JOBS).
  • A pairwise exclude= keeps working when both options are copied; with only one, the link is inert. Mutually exclusive sets (set_mutually_exclusive_switches) belong to a group and are not copied.

Mutually exclusive commands — cli%set_mutually_exclusive_groups ​

fortran
call cli%set_mutually_exclusive_groups(group1='commit', group2='init')

Both commands must be defined before the call. Calling both is ERROR_GROUP_M_EXCLUDE (101), reported by parse.