Exposing new commands to flmake and expanding the FLASH workflow options is a simple
task which is explained in detail here. The flmake utility uses a dynamic registration
pattern to find commands. Three tasks must be completed add a command to flmake:
As a fictional example, suppose that FLASH requires delicious, slightly burnt bread.
A toast command seems imminent...
main function
Every flmake command must have a main function which can have any name, but is by
convention main(). This function must have a very specific signature:
-
flash.flmake.toast.main(opts, rc, msg)
Parameters : | opts : list
The command line arguments, as a list of strings, that come
after the flmake command. For flmake mv dir1 dir2 opts
would be ['dir1', 'dir2'].
rc : dict
The run control dictionary or an empty dict if the flashrc.py
does not exist.
msg : str or None
The message given by the user on the command line or None if
no message was supplied.
|
Therefore, a toast command’s main function - which accepts a toastiness level -
may be implemented in a toast.py module:
from flash.flmake import logger
def main(opts, rc, msg):
"""Makes delicious toast for you."""
toastiness = int(opts[0]) if 0 < len(opts) else 5
print "Engage the bread warming!"
if msg is None:
msg = "Toasting to level {}".format(toastiness)
logger.info(msg, "toast")
Moreover, the docstring of this function is used by flmake help as the
command’s summary description.
This function must then be registered with the main flamke command itself. This is
done by adding an entry to the commands dictionary in the flash.flmake.main
module. The keys in the dictionary are the names of the commands at the command line.
The values are 2-tuples of module relative import paths and the name of the main
function in this module:
commands = {
'setup': ('setup', 'main'),
'build': ('build', 'main'),
'run': ('run', 'main'),
'ls-runs': ('lsruns', 'main'),
# ...
'toast': ('toast', 'main'),
}
Enterprising developers may alter the commands dictionary from the flashrc.py
run control file as flmake itself is running!
help/usage
Similar to the commands main function, the flmake help command requires a usage
string which may be dynamically registered. The usage object may be a string
or a function which takes no arguments and returns a string. The latter function
pattern is useful if the usage string may change based on the state of the system.
Both static and dynamic examples are presented below:
import os
# static
USAGE = ("In flmake, command toasts you!\n\n"
"usage: flmake toast [<toastiness>]")
# dynamic
def usage():
slices = len(os.listdir('.'))
msg = "You can make {} pieces of toast!".format(slices)
This string or function object must then be added to the messages dictionary
in the flash.flmake.help_cmd module. The keys are the same as the keys in the
flmake main commands dictionary. The values are the usage objects themselves.
For example:
from . import build
from . import run
from . import restart
from . import clean
from . import metadata
from . import merge
# ...
from . import toast
messages = {
'build': build.usage,
'run': run.usage,
'restart': restart.usage,
'merge': merge.USAGE,
'clean': clean.USAGE,
'metadata': metadata.USAGE,
# ...
'toast': toast.USAGE,
}
bash completion
An optional, but useful, component of flmake is its support for bash completion.
This is the tool whereby pressing tab on the command line the potential valid
options are automatically filled in. Currently, whenever the Python API is
installed the flmake bash completion file is regenerated.
Therefore to add new command-specific options to flmake’s bash completion the
${FLASH_SRC_DIR}/tools/setup.py file must be edited. This module contains
a template that may be adjusted as needed.
Please refer to this tutorial for the new command’s
completion needs (part 1,
part 2).