summaryrefslogtreecommitdiff
path: root/xnt
diff options
context:
space:
mode:
authorkennyballou <kballou@onyx.boisestate.edu>2013-03-09 18:33:32 -0700
committerkennyballou <kballou@onyx.boisestate.edu>2013-03-09 18:33:32 -0700
commit532f5d98c15f091a270e68450112e91ad085f6bf (patch)
tree140620a5a5e1e7284976521294c53bb354bbd2c7 /xnt
parentaeb0672db3f2a4ddb4812344398831180a723d0c (diff)
downloadxnt-532f5d98c15f091a270e68450112e91ad085f6bf.tar.gz
xnt-532f5d98c15f091a270e68450112e91ad085f6bf.tar.xz
Refactor/ Move Program flow to use `argparse`
Diffstat (limited to 'xnt')
-rw-r--r--xnt/basecommand.py32
-rw-r--r--xnt/cmdoptions.py31
-rw-r--r--xnt/commands/__init__.py41
-rw-r--r--xnt/commands/help.py42
-rw-r--r--xnt/commands/listtargets.py56
-rw-r--r--xnt/commands/target.py78
-rw-r--r--xnt/commands/version.py34
-rw-r--r--xnt/tasks.py6
-rw-r--r--xnt/tests/xenantargparsertests.py132
-rw-r--r--xnt/version.py2
-rw-r--r--xnt/xenant.py126
11 files changed, 234 insertions, 346 deletions
diff --git a/xnt/basecommand.py b/xnt/basecommand.py
deleted file mode 100644
index 6433918..0000000
--- a/xnt/basecommand.py
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env python
-"""Basecommand class for xnt commands"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-class Command(object):
- """Base Command Class Definition"""
- name = None
- usage = None
- hidden = False
- need_build = False
-
- def __init__(self):
- pass
-
- def run(self, arguments=None):
- """Invoke the Command"""
- pass
diff --git a/xnt/cmdoptions.py b/xnt/cmdoptions.py
deleted file mode 100644
index 529f060..0000000
--- a/xnt/cmdoptions.py
+++ /dev/null
@@ -1,31 +0,0 @@
-#!/usr/bin/env python
-"""Xenant Options Defintion
-
-All available options for Xenant (and there actions)
-"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-import logging
-
-def __flip_verbose_flag():
- """Turn on logging for xnt (and submodules)"""
- logging.getLogger("xnt").setLevel(logging.INFO)
-
-OPTIONS = {
- "-v": __flip_verbose_flag,
-}
diff --git a/xnt/commands/__init__.py b/xnt/commands/__init__.py
deleted file mode 100644
index 583558b..0000000
--- a/xnt/commands/__init__.py
+++ /dev/null
@@ -1,41 +0,0 @@
-#!/usr/bin/env python
-"""Commands Module"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from xnt.commands.help import HelpCommand
-from xnt.commands.listtargets import ListTargetsCommand
-from xnt.commands.version import VersionCommand
-from xnt.commands.target import TargetCommand
-
-COMMANDS = {
- HelpCommand.name: HelpCommand,
- ListTargetsCommand.name: ListTargetsCommand,
- VersionCommand.name: VersionCommand,
- TargetCommand.name: TargetCommand,
-}
-
-def get_summaries(ignore_hidden=True):
- """Return a list of summaries about each command"""
- items = []
-
- for name, command_class in COMMANDS.items():
- if ignore_hidden and command_class.hidden:
- continue
- items.append((name, command_class.summary))
-
- return sorted(items)
diff --git a/xnt/commands/help.py b/xnt/commands/help.py
deleted file mode 100644
index 79584be..0000000
--- a/xnt/commands/help.py
+++ /dev/null
@@ -1,42 +0,0 @@
-#!/usr/bin/env python
-"""Xnt Help Command"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from xnt.basecommand import Command
-from xnt.status_codes import SUCCESS
-
-class HelpCommand(Command):
- """Help Command"""
- name = 'help'
- usage = """"""
- summary = 'Print Usage Summary'
- needs_build = False
-
- def run(self, arguments=None):
- """Invoke Help"""
- from xnt.commands import get_summaries
- from xnt import __version__, __license__
- commands = get_summaries()
- print(__version__)
- print(__license__)
- print("Available Commands:")
- for name, summary in commands:
- print(name)
- print("\t" + summary)
-
- return SUCCESS
diff --git a/xnt/commands/listtargets.py b/xnt/commands/listtargets.py
deleted file mode 100644
index 2885905..0000000
--- a/xnt/commands/listtargets.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-"""List Targets Xnt Command"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from xnt.basecommand import Command
-from xnt.status_codes import SUCCESS, ERROR
-import logging
-
-LOGGER = logging.getLogger(__name__)
-
-class ListTargetsCommand(Command):
- """List Targets Command"""
- name = 'list-targets'
- usage = """"""
- summary = "Prints targets in build file"
- needs_build = True
-
- def __init__(self, build):
- """Initialization"""
- Command.__init__(self)
- self.build = build
-
- def run(self, arguments=None):
- """Invoke ListTargets"""
- LOGGER.debug("build is null? %s", self.build == None)
- try:
- for attr in dir(self.build):
- LOGGER.debug("Attribute %s:", attr)
- try:
- func = getattr(self.build, attr)
- if func.decorator == "target":
- print(attr + ":")
- if func.__doc__:
- print(func.__doc__)
- print("\n")
- except AttributeError:
- pass
- except AttributeError as ex:
- LOGGER.error(ex)
- return ERROR
- return SUCCESS
diff --git a/xnt/commands/target.py b/xnt/commands/target.py
deleted file mode 100644
index 0520c74..0000000
--- a/xnt/commands/target.py
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/usr/bin/env python
-"""(Generic) Target Xnt Command for invoking build targets"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from xnt.basecommand import Command
-from xnt.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR
-import logging
-
-LOGGER = logging.getLogger("xnt")
-
-class TargetCommand(Command):
- """Target Command"""
- name = '<target>'
- usage = """"""
- summary = "Invokes target(s) in build.py"
- needs_build = True
-
- def __init__(self, build):
- """Initialization"""
- Command.__init__(self)
- self.build = build
-
- def run(self, targets=None, props=None): #pylint: disable-msg=W0221
- """Invoke Target Command"""
- if targets:
- for target in targets:
- error_code = self.call_target(target, props)
- if error_code:
- return error_code
- return SUCCESS
- else:
- return self.call_target("default", props)
-
- def call_target(self, target_name, props):
- """Invoke build target"""
- def process_params(params, buildproperties=None):
- """Parse the passed properties and append to build properties"""
- properties = buildproperties if buildproperties is not None else {}
- for param in params:
- name, value = param[2:].split("=")
- properties[name] = value
- return properties
- def __get_properties():
- """Return the properties dictionary of the build module"""
- try:
- return getattr(self.build, "properties")
- except AttributeError:
- return None
- try:
- if props and len(props) > 0:
- setattr(self.build,
- "properties",
- process_params(props, __get_properties()))
- target = getattr(self.build, target_name)
- error_code = target()
- return error_code if error_code else 0
- except AttributeError:
- LOGGER.warning("There was no target: %s", target_name)
- return ERROR
- except Exception as ex:
- LOGGER.error(ex)
- return UNKNOWN_ERROR
-
diff --git a/xnt/commands/version.py b/xnt/commands/version.py
deleted file mode 100644
index 9c2e302..0000000
--- a/xnt/commands/version.py
+++ /dev/null
@@ -1,34 +0,0 @@
-#!/usr/bin/env python
-"""Version Xnt Command"""
-
-# Xnt -- A Wrapper Build Tool
-# Copyright (C) 2012 Kenny Ballou
-
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-from xnt.basecommand import Command
-from xnt.status_codes import SUCCESS
-
-class VersionCommand(Command):
- """Version Command"""
- name = 'version'
- usage = """"""
- summary = "Print Version of Xnt"
- needs_build = False
-
- def run(self, arguments=None):
- """Invoke Version"""
- from xnt import __version__
- print(__version__)
- return SUCCESS
diff --git a/xnt/tasks.py b/xnt/tasks.py
index 7a3e79d..679701c 100644
--- a/xnt/tasks.py
+++ b/xnt/tasks.py
@@ -127,12 +127,10 @@ def xntcall(path, targets=None, props=None):
param: targets - list of targets to execute
param: props - dictionary of properties to pass to the build module
"""
- import xnt.xenant
- from xnt.commands.target import TargetCommand
+ from xnt.xenant import invoke_build, load_build
cwd = os.getcwd()
- command = TargetCommand(xnt.xenant.load_build(path))
os.chdir(path)
- error_code = command.run(targets=targets, props=props)
+ error_code = invoke_build(load_build(path), targets=targets, props=props)
os.chdir(cwd)
return error_code
diff --git a/xnt/tests/xenantargparsertests.py b/xnt/tests/xenantargparsertests.py
new file mode 100644
index 0000000..3a22b33
--- /dev/null
+++ b/xnt/tests/xenantargparsertests.py
@@ -0,0 +1,132 @@
+#!/usr/bin/env python
+"""Xenant Arg Parser Tests"""
+
+# Xnt -- A Wrapper Build Tool
+# Copyright (C) 2012 Kenny Ballou
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
+
+import xnt.xenant
+import unittest
+
+#pylint: disable-msg=C0103
+class XenantArgParserTests(unittest.TestCase):
+ """Test Cases for Xenant Args Parsing"""
+
+ def test_nil_args(self):
+ """Test the empty case (no arguements)"""
+ args_in = []
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 0)
+
+ def test_single_target(self):
+ """Test the single target case"""
+ args_in = ["my_target"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 1)
+
+ def test_verbose(self):
+ """Test verbose flag"""
+ args_in = ["-v"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertTrue(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 0)
+
+ def test_list_targets_short(self):
+ """Test list targets flag, short hand"""
+ args_in = ["-l"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertTrue(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 0)
+
+ def test_list_targets_long(self):
+ """Test list targets flag, long hand"""
+ args_in = ["--list-targets"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertTrue(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 0)
+
+ def test_single_verbose(self):
+ """Test the verbose single case"""
+ args_in = ["-v", "my_verbose_target"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertTrue(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 1)
+
+ def test_multi_target(self):
+ """Test the verbose single case"""
+ args_in = ["my_first_target", "my_second_target"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNone(args["properties"])
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 2)
+
+ def test_properties_no_target(self):
+ """Test property parsing"""
+ args_in = ["-Dmyvar=myvalue"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNotNone(args["properties"])
+ self.assertEqual(len(args["properties"]), 1)
+ self.assertEqual(args["properties"][0], "myvar=myvalue")
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 0)
+
+ def test_more_properties(self):
+ """Test property parsing"""
+ args_in = ["-Dmyvar=myvalue", "-Dmyothervar=myothervalue"]
+ args = xnt.xenant.parse_args(args_in)
+ self.assertIsNotNone(args)
+ self.assertFalse(args["verbose"])
+ self.assertFalse(args["list-targets"])
+ self.assertIsNotNone(args["properties"])
+ self.assertEqual(len(args["properties"]), 2)
+ self.assertEqual(args["properties"][0], "myvar=myvalue")
+ self.assertEqual(args["properties"][1], "myothervar=myothervalue")
+ self.assertIsNotNone(args["targets"])
+ self.assertEqual(len(args["targets"]), 0)
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/xnt/version.py b/xnt/version.py
index 3562358..77cf994 100644
--- a/xnt/version.py
+++ b/xnt/version.py
@@ -17,5 +17,5 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-__version_info__ = (0, 5, 2)
+__version_info__ = (0, 5, 3)
__version__ = '.'.join(list(str(i) for i in __version_info__ if True))
diff --git a/xnt/xenant.py b/xnt/xenant.py
index e67ef7c..9e5a3df 100644
--- a/xnt/xenant.py
+++ b/xnt/xenant.py
@@ -21,9 +21,9 @@ import os
import sys
import time
import logging
-from xnt.cmdoptions import OPTIONS
-from xnt.commands import COMMANDS
-from xnt.commands.target import TargetCommand
+import argparse
+from xnt import __version__
+from xnt.status_codes import SUCCESS, ERROR, UNKNOWN_ERROR
logging.basicConfig(format="%(asctime)s:%(levelname)s:%(message)s")
LOGGER = logging.Logger(name=__name__)
@@ -32,30 +32,15 @@ LOGGER.addHandler(logging.StreamHandler())
def main():
"""Xnt Entry Point"""
start_time = time.time()
- params = list(p for p in sys.argv[1:] if p.startswith('-D'))
- flags = list(o for o in sys.argv[1:]
- if o.startswith('-') and o not in params)
- cmds = list(c for c in sys.argv[1:]
- if c not in flags and c not in params)
- #Loop flags and apply them
- for flag in flags:
- if flag in OPTIONS:
- OPTIONS[flag]()
- else:
- LOGGER.debug("%s is not a vaild option", flag)
- #run things
- cmd_found = False
- for cmd in cmds:
- if cmd in COMMANDS:
- cmd_found = True
- if COMMANDS[cmd].needs_build:
- command = COMMANDS[cmd](load_build())
- else:
- command = COMMANDS[cmd]()
- error_code = command.run()
- if cmd_found == False:
- command = TargetCommand(load_build())
- error_code = command.run(targets=cmds, props=params)
+ args = parse_args(sys.argv[1:])
+ if args["verbose"]:
+ LOGGER.setLevel(logging.DEBUG)
+ if args["list-targets"]:
+ error_code = list_targets(load_build())
+ else:
+ error_code = invoke_build(load_build(),
+ args["targets"],
+ args["properties"])
elapsed_time = time.time() - start_time
LOGGER.info("Execution time: %.3f", elapsed_time)
if error_code != 0:
@@ -91,5 +76,92 @@ def load_build(path=""):
del sys.modules["build"]
os.chdir(cwd)
+def invoke_build(build, targets=None, props=None):
+ """Invoke Build with `targets` passing `props`"""
+ def call_target(target_name, props):
+ """Call target on build module"""
+ def process_params(params, existing_props=None):
+ """Parse and separate properties and append to build module"""
+ properties = existing_props if existing_props is not None else {}
+ for param in params:
+ name, value = param.split("=")
+ properties[name] = value
+ return properties
+ def __get_properties():
+ """Return the properties dictionary of the build module"""
+ try:
+ return getattr(build, "PROPERTIES")
+ except AttributeError:
+ LOGGER.warning("Build file specifies no properties")
+ return None
+ try:
+ if props and len(props) > 0:
+ setattr(build,
+ "PROPERTIES",
+ process_params(props, __get_properties()))
+ target = getattr(build, target_name)
+ error_code = target()
+ return error_code if error_code else 0
+ except AttributeError:
+ LOGGER.error("There was no target: %s", target_name)
+ return ERROR
+ except Exception as ex:
+ LOGGER.critical(ex)
+ return UNKNOWN_ERROR
+ if targets and len(targets) > 0:
+ for target in targets:
+ error_code = call_target(target, props)
+ if error_code:
+ return error_code
+ return SUCCESS
+ else:
+ return call_target("default", props)
+
+def list_targets(build):
+ """List targets (and doctstrings) of the provided build module"""
+ try:
+ for attr in dir(build):
+ try:
+ func = getattr(build, attr)
+ if func.decorator == "target":
+ print(attr + ":")
+ if func.__doc__:
+ print(func.__doc__)
+ print("")
+ except AttributeError:
+ pass
+ except AttributeError as ex:
+ LOGGER.error(ex)
+ return ERROR
+ return SUCCESS
+
+def parse_args(args_in):
+ """Parse and group arguments"""
+ parser = argparse.ArgumentParser(prog="Xnt")
+ parser.add_argument("-v", "--verbose",
+ help="Enable verbose output",
+ action="store_true",
+ dest="verbose")
+ parser.add_argument(
+ "--version",
+ action="version",
+ version=__version__,
+ help="Print Xnt Version and quit")
+ parser.add_argument("-l", "--list-targets",
+ action="store_true",
+ dest="list-targets",
+ help="Print build targets")
+ # Properties Group
+ params_group = parser.add_argument_group("Properties")
+ params_group.add_argument(
+ "-D", dest="properties", action="append",
+ help="Property values to be passed to the build module")
+ target_group = parser.add_argument_group("Targets")
+
+ # Targets Group
+ target_group.add_argument("targets", nargs=argparse.REMAINDER,
+ help="Name(s) of targets to invoke")
+ return vars(parser.parse_args(args_in))
+
if __name__ == "__main__":
main()