"""\ nbblib.commands - cmdline command infrastructure and implementation Copyright (C) 2008 Hans Ulrich Niedermann """ import os import sys import logging import nbblib.package as package import nbblib.plugins as plugins import nbblib.progutils as progutils import nbblib.vcs as vcs import nbblib.bs as bs __all__ = ['Commander'] def adjust_doc(doc): """Remove common whitespace at beginning of doc string lines""" if not doc: return doc i = 0 for i in range(len(doc)): if doc[i] not in " \t": break prefix = doc[:i] rest_doc = doc[i:] almost_doc = rest_doc.replace("\n%s" % prefix, "\n") i = -1 while almost_doc[i] == '\n': i = i - 1 return almost_doc[:i] __all__.append('CommandLineError') class CommandLineError(Exception): def __init__(self, message): super(CommandLineError, self).__init__() self.msg = message def __str__(self): return "Command line error: %s" % self.msg __all__.append('UnknownCommand') class UnknownCommand(Exception): def __init__(self, context, cmd): super(UnknownCommand, self).__init__() self.prog = context.prog self.cmd = cmd def __str__(self): return "Unknown %(prog)s command '%(cmd)s'" % self.__dict__ ######################################################################## # Command plugin system ######################################################################## __all__.append('Command') class Command(object): """ Mount point for plugins which refer to commands that can be performed. Plugins implementing this reference should provide the following interface: name attribute The text to be displayed, describing the version control system summary attribute Short (less than 50 chars) command summary line usage attribute Usage string (defaults to '') validate_args(*args, **kwargs) function Must raise CommandLineError() if it encounters invalid arguments in cmdargs run() function Actually run the function FFF(*args, **kwargs) *args are the arguments from the command line **kwargs are additional parameters from within the program """ __metaclass__ = plugins.GenericPluginMeta usage = '' def __init__(self, context, *args, **kwargs): super(Command, self).__init__() self.validate_args(*args, **kwargs) self.args = args self.kwargs = kwargs self.context = context @plugins.abstractmethod def run(self): """Run the command""" pass @plugins.abstractmethod def validate_args(self, *cmdargs, **kwargs): """Validate command line arguments: Abstract method. May make use of self.context. """ pass def validate_args_none(self, *cmdargs, **kwargs): """Validate command line arguments: no argument at all""" logging.debug("Command: %s", self.name) logging.debug("*cmdargs: %s", cmdargs) logging.debug("**kwargs: %s", kwargs) if len(cmdargs) > 0: raise CommandLineError("'%s' command takes no parameters", self.name) logging.debug("Command match!") return True def validate_args_any(self, *cmdargs, **kwargs): """Validate command line arguments: accept any argument""" logging.debug("Command: %s", self.name) logging.debug("*cmdargs: %s", cmdargs) logging.debug("**kwargs: %s", kwargs) logging.debug("Command match!") return True def __str__(self): return "Command(%s, %s)" % (self.cmd_name, self.cmdargs) class HelpCommand(Command): """\ If the optional is given, print the help for . Else, print a list of commands and general help. """ name = 'help' summary = 'print help text' usage = '[]' def validate_args(self, *args, **kwargs): if len(args) == 1 and args[0] not in Command.plugins.keys(): raise CommandLineError("'%s' is an invalid command name", args[0]) elif len(args) > 1: raise CommandLineError("'%s' command only takes one optional parameter", self.name) def _print_command_list(self): print "List of commands:" keys = Command.plugins.keys() if not keys: raise Exception("No commands found. Please lart the developer.") keys.sort() keys2 = Command.plugins.keys() keys2.sort(lambda a,b: cmp(len(b),len(a))) print "keys ", keys print "keys2", keys2 fmt = "\t%%-%ds\t%%s" % len(keys2[0]) for k in keys: print fmt % (k, Command.plugins[k].summary) def _print_command_help(self, cmd): """print help for command cmd""" c = Command.plugins[cmd] print "Purpose:", c.summary if c.usage: print "Usage: ", self.context.prog, cmd, c.usage else: print "Usage: ", self.context.prog, cmd if hasattr(c, '__doc__'): if c.__doc__: print print adjust_doc(c.__doc__) def run(self): if len(self.args) == 0: self._print_command_list() elif len(self.args) == 1: self._print_command_help(self.args[0]) else: assert(False) class InternalConfigCommand(Command): name = 'internal-config' summary = 'print internal program configuration' validate_args = Command.validate_args_none def run(self): print "Source tree types:", ", ".join(vcs.VCSourceTree.plugins.keys()) print "Build system types:", ", ".join(bs.BSSourceTree.plugins.keys()) print "Commands:", ", ".join(Command.plugins.keys()) class SourceClassCommand(Command): """Base class for commands acting on source trees""" name = None # abstract command class def __init__(self, context, *args, **kwargs): super(SourceClassCommand, self).__init__(context, *args, **kwargs) srcdir = os.getcwd() absdir = os.path.abspath(srcdir) self.vcs_sourcetree = vcs.VCSourceTree.detect(context, absdir) logging.debug("vcs_sourcetree %s", self.vcs_sourcetree) self.bs_sourcetree = bs.BSSourceTree.detect(context, self.vcs_sourcetree) logging.debug("bs_sourcetree %s", self.bs_sourcetree) cfg = self.vcs_sourcetree.config for x in ('srcdir', 'builddir', 'installdir'): logging.info("CONFIG %s %s", x, getattr(cfg, x)) class DetectCommand(Command): name = None def __init__(self, context, *args, **kwargs): super(DetectCommand, self).__init__(context, *args, **kwargs) self.srcdir = os.getcwd() self.absdir = os.path.abspath(self.srcdir) validate_args = Command.validate_args_none class DetectVCSCommand(DetectCommand): name = "detect-vcs" summary = "detect source tree VCS" def __init__(self, context, *args, **kwargs): super(DetectVCSCommand, self).__init__(context, *args, **kwargs) self.vcs_sourcetree = vcs.VCSourceTree.detect(self.context, self.absdir) logging.debug("vcs_sourcetree %s", self.vcs_sourcetree) def run(self): if self.vcs_sourcetree: print 'VCS:', self.vcs_sourcetree.name, self.vcs_sourcetree.tree_root else: print 'VCS:', 'Not detected' validate_args = Command.validate_args_none class DetectBSCommand(DetectCommand): name = "detect-bs" summary = "detect source tree BS" def __init__(self, context, *args, **kwargs): super(DetectBSCommand, self).__init__(context, *args, **kwargs) self.vcs_sourcetree = vcs.VCSourceTree.detect(self.context, self.absdir) logging.debug("vcs_sourcetree %s", self.vcs_sourcetree) self.bs_sourcetree = bs.BSSourceTree.detect(self.context, self.vcs_sourcetree) logging.debug("bs_sourcetree %s", self.bs_sourcetree) def run(self): if self.bs_sourcetree: print 'BS:', self.bs_sourcetree.name, self.bs_sourcetree.tree_root else: print 'BS:', 'Not detected' validate_args = Command.validate_args_none class BuildTestCommand(SourceClassCommand): name = 'build-test' summary = 'simple build test' def run(self): self.bs_sourcetree.init() self.bs_sourcetree.configure() self.bs_sourcetree.build() self.bs_sourcetree.install() validate_args = Command.validate_args_none class InitCommand(SourceClassCommand): name = 'init' summary = 'initialize buildsystem' validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.init() class ConfigureCommand(SourceClassCommand): name = 'configure' summary = 'configure buildsystem' validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.configure() class BuildCommand(SourceClassCommand): name = 'build' summary = 'build from source' validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.build() class InstallCommand(SourceClassCommand): name = 'install' summary = 'install the built things' validate_args = Command.validate_args_none def run(self): self.bs_sourcetree.install() class MakeCommand(SourceClassCommand): name = 'make' summary = 'run make in builddir' validate_args = Command.validate_args_any def run(self): os.chdir(self.bs_sourcetree.config.builddir) progutils.prog_run(["make"] + list(self.args), self.context) class ConfigCommand(SourceClassCommand): name = 'config' summary = 'set/get config values' usage = '(srcdir|builddir|installdir)' def validate_args(self, *args, **kwargs): items = ('srcdir', 'builddir', 'installdir', ) if len(args) == 0: raise CommandLineError("'%s' command requires at least one parameter (%s)", self.name, ', '.join(items)) elif len(args) == 1 and args[0] in items: pass elif len(args) == 2 and args[0] in items: if args[0] in ('srcdir', ): raise CommandLineError("'%s' command cannot change 'srcdir'", self.name) else: pass else: raise CommandLineError("'%s' requires less or different parameters", self.name) def run(self): git_get_items = ('builddir', 'installdir', 'srcdir') git_set_items = ('builddir', 'installdir', ) if len(self.args) == 1: if self.args[0] in git_get_items: print getattr(self.vcs_sourcetree.config, self.args[0]) else: assert(False) elif len(self.args) == 2: if self.args[0] == 'builddir': self.vcs_sourcetree.config.builddir = self.args[1] elif self.args[0] == 'installdir': self.vcs_sourcetree.config.installdir = self.args[1] else: assert(False) else: assert(False) class Commander(object): def __init__(self, context, cmd, *cmdargs): self.context = context logging.debug("Commander() %s %s", cmd, cmdargs) if cmd in Command.plugins: self.command = Command.plugins[cmd](context, *cmdargs) else: raise UnknownCommand(context, cmd) def run(self): self.command.run() # End of file.