Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions contrib/plugins/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Sample plugin that adds a new top level command: 'bob hello'
#
# Run e.g. 'bob hello root' to print the path of all matching packages.

import argparse

def doHello(packages, argv, bobRoot):
parser = argparse.ArgumentParser(prog="bob hello",
description="Print the path of all matching packages.")
parser.add_argument('package', nargs='?', default="",
help="Package to query (defaults to all root packages)")
args = parser.parse_args(argv)

for (stack, node) in packages.queryTreePath(args.package):
print("/".join(stack) if stack else "/")

manifest = {
'apiVersion' : "1.2.1.dev1",
'commands' : {
'hello' : {
'func' : doHello,
'help' : "Print all package paths",
},
},
}
65 changes: 65 additions & 0 deletions doc/manual/extending.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,18 @@ internal and might change without notice.
.. autoclass:: bob.input.Tool()
:members:

.. autoclass:: bob.pathspec.PackageSet()
:members: getRootPackage, queryPackagePath, getAliases

.. autoclass:: bob.errors.BobError
:members:

.. autoclass:: bob.errors.ParseError
:members:

.. autoclass:: bob.errors.BuildError
:members:

Hooks
-----

Expand Down Expand Up @@ -300,6 +312,59 @@ generator are the package objects returned by
}


.. _extending-commands:

Commands
--------

A plugin may register additional top level commands, e.g. to run ``bob
mycmd ...`` like any built-in command such as ``bob ls``. Bob takes care of
parsing the arguments that are common to (almost) all commands -- ``-D``,
``-c`` and the sandbox mode switches (``--sandbox``, ``--slim-sandbox``,
``--dev-sandbox``, ``--strict-sandbox``, ``--no-sandbox``) -- and of parsing
the recipes and generating the package graph. The plugin must not (and
cannot) parse recipes or generate packages itself.

A command function is called with 3 arguments:

* ``packages``: the :class:`bob.pathspec.PackageSet` holding the generated
package graph. Use e.g. :func:`bob.pathspec.PackageSet.queryPackagePath` or
:func:`bob.pathspec.PackageSet.getRootPackage` to inspect it.
* ``argv``: the list of arguments that were not consumed by Bob's standard
argument handling. The plugin is free to parse these with its own
``argparse.ArgumentParser`` (and should handle ``-h``/``--help`` itself, as
Bob does not intercept it).
* ``bobRoot``: the fully qualified path name to the Bob executable, as passed
to generators.

The function may return an integer that is used as the process exit code. Any
other return value (e.g. ``None``) is treated as success.

A simple command may look like this::

def doHello(packages, argv, bobRoot):
import argparse
parser = argparse.ArgumentParser(prog="bob hello")
parser.add_argument('package', nargs='?', default="")
args = parser.parse_args(argv)
for (stack, node) in packages.queryTreePath(args.package):
print("/".join(stack) if stack else "/")

manifest = {
'apiVersion' : "1.2.1.dev1",
'commands' : {
'hello' : {
'func' : doHello,
'help' : "Print all package paths",
},
},
}

Command names of Bob's built-in commands (e.g. ``build``, ``dev``, ``ls``,
...) are reserved and always take precedence over a plugin provided command
of the same name. It is not possible to re-define an already existing command
name defined by another plugin.

.. _extending-settings:

Plugin settings
Expand Down
18 changes: 2 additions & 16 deletions pym/bob/cmds/build/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from ...builder import LocalBuilder
from ...errors import ParseError
from ...input import RecipeSet
from ..helpers import processDefines
from ..helpers import processDefines, addStandardArgs
from string import Formatter
import argparse
import os
Expand Down Expand Up @@ -35,26 +35,12 @@ def doQueryPath(argv, bobRoot):
parser.add_argument('packages', metavar='PACKAGE', type=str, nargs='+',
help="(Sub-)package to query")
parser.add_argument('-f', help='Output format string', default='{name}\t{dist}', metavar='FORMAT')
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
parser.add_argument('-q', dest="quiet", action="store_true",
help="Be quiet in case of errors")
parser.add_argument('--fail', action="store_true",
help="Return a non-zero error code in case of errors")

group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")
addStandardArgs(parser)
parser.set_defaults(sandbox=None)

group = parser.add_mutually_exclusive_group()
Expand Down
18 changes: 2 additions & 16 deletions pym/bob/cmds/graph.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
from ..input import RecipeSet
from ..tty import colorize
from ..utils import runInEventLoop
from .helpers import processDefines
from .helpers import processDefines, addStandardArgs
import argparse
import asyncio
import json
Expand Down Expand Up @@ -507,21 +507,7 @@ def doGraph(argv, bobRoot):
parser = argparse.ArgumentParser(prog="bob graph", description='Generate dependency graph')
parser.add_argument('packages', metavar='PACKAGE', type=str, nargs='+',
help="Graph entry (sub-)package")
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")
addStandardArgs(parser)
parser.add_argument('--destination', metavar="DEST",
help="Destination of graph files.")
parser.add_argument('-e', '--exclude', default=[], action='append', dest="excludes",
Expand Down
31 changes: 29 additions & 2 deletions pym/bob/cmds/help.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# SPDX-License-Identifier: GPL-3.0-or-later

from ..errors import BobError
from ..input import RecipeSet
import argparse
import os.path
import subprocess
Expand All @@ -14,11 +15,37 @@ def doHelp(availableCommands, argv, bobRoot):
description="Display help information about command.")
# Help without a command parameter gets handled by the main argument parser
# in pym/bob/scripts.py.
parser.add_argument('command', help="Command to get help for")
parser.add_argument('command', nargs='?', help="Command to get help for")
parser.add_argument('-a', '--all', action='store_true',
help="print all available commands")

args = parser.parse_args(argv)

if args.command in availableCommands:
recipes = RecipeSet()
recipes.parseConfigs()

if args.command is None:
lines = ["The following high level commands are available:", ""]
lines.extend(sorted([ " {:16s}{}".format(k, v[2])
for (k,v) in availableCommands.items() if v[0] == 'hl' ]))
lines.extend(["", "The following scripting commands are available:", ""])
lines.extend(sorted([ " {:16s}{}".format(k, v[2])
for (k,v) in availableCommands.items() if v[0] == 'll' ]))
if args.all:
lines.extend(["", "The following plugin defined commands are available:", ""])

for name, spec in sorted(recipes.getCommands().items()):
lines.append(" {:16s}{}".format(name, spec.get("help", "")))

print("\n".join(lines))
return 0

if args.command in recipes.getCommands().keys():
h = recipes.getCommands()[args.command].get("help", "")
print(f"'{args.command}' is provided by a plugin: {h}")
return 0

if args.command in availableCommands.keys():
manPage = "bob-" + args.command
manSection = "1"
elif args.command == "bob":
Expand Down
18 changes: 18 additions & 0 deletions pym/bob/cmds/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,24 @@ def processDefines(defs):
defines[key] = value
return defines

def addStandardArgs(parser):
"""Add the -D/-c/sandbox arguments common to most Bob sub-commands"""
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")


def dumpYaml(doc, indent):
if indent is None:
Expand Down
83 changes: 6 additions & 77 deletions pym/bob/cmds/misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

from ..input import RecipeSet
from ..errors import ParseError, BuildError
from .helpers import processDefines
from .helpers import processDefines, addStandardArgs
import argparse
import codecs
import sys
Expand Down Expand Up @@ -83,21 +83,7 @@ def doLS(argv, bobRoot):
help="Prints the full path prefix for each package")
group.add_argument('-d', '--direct', default=False, action='store_true',
help="List packages themselves, not their contents")
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")
addStandardArgs(parser)
args = parser.parse_args(argv)

defines = processDefines(args.defines)
Expand Down Expand Up @@ -135,23 +121,9 @@ def doQueryMeta(argv, bobRoot):
formatter_class=argparse.RawDescriptionHelpFormatter,
description="""Query meta information of packages.""")
parser.add_argument('packages', nargs='+', help="(Sub-)packages to query")
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
parser.add_argument('-r', '--recursive', default=False, action='store_true',
help="Recursively display dependencies")
group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")
addStandardArgs(parser)
args = parser.parse_args(argv)

defines = processDefines(args.defines)
Expand Down Expand Up @@ -196,26 +168,12 @@ def doQuerySCM(argv, bobRoot):
""")
parser.add_argument('packages', nargs='+', help="(Sub-)packages to query")

parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
parser.add_argument('-f', default=[], action='append', dest="formats",
help="Output format for scm (syntax: scm=format). Can be specified multiple times.")
parser.add_argument('--default', default="", help='Default for missing attributes (default: "")')
parser.add_argument('-r', '--recursive', default=False, action='store_true',
help="Recursively display dependencies")
group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")
addStandardArgs(parser)

formats = {
'git' : "git {package} {dir} {url} {branch}",
Expand Down Expand Up @@ -269,21 +227,7 @@ def doQueryRecipe(argv, bobRoot):
parser = argparse.ArgumentParser(prog="bob query-recipe",
description="Query recipe and class files of package.")
parser.add_argument('packages', nargs='+', help="(Sub-)packages to query")
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")
group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing")
addStandardArgs(parser)

args = parser.parse_args(argv)

Expand Down Expand Up @@ -343,22 +287,7 @@ def doInit(argv, bobRoot):

def doLsRecipes(argv, bobRoot):
parser = argparse.ArgumentParser(prog="bob ls-recipes", description="List all known recipes.")
parser.add_argument('-D', default=[], action='append', dest="defines",
help="Override default environment variable")
parser.add_argument('-c', dest="configFile", default=[], action='append',
help="Use config File")

group = parser.add_mutually_exclusive_group()
group.add_argument('--sandbox', action='store_true', default=False,
help="Enable sandboxing")
group.add_argument('--slim-sandbox', action='store_false', dest='sandbox',
help="Enable slim sandboxing")
group.add_argument('--dev-sandbox', action='store_true', dest='sandbox',
help="Enable development sandboxing")
group.add_argument('--strict-sandbox', action='store_true', dest='sandbox',
help="Enable strict sandboxing")
group.add_argument('--no-sandbox', action='store_false', dest='sandbox',
help="Disable sandboxing (default)")
addStandardArgs(parser)

group = parser.add_mutually_exclusive_group()
group.add_argument('--all', action='store_const', dest='mode', const='all', default='all',
Expand Down
Loading
Loading