summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorkennyballou <kballou@onyx.boisestate.edu>2013-02-21 10:42:22 -0700
committerkennyballou <kballou@onyx.boisestate.edu>2013-02-21 10:42:22 -0700
commitfe18807a9307a43d19c2d14489f48e6d0370d878 (patch)
tree50884483a0e9ea5238b3cfcc6d737e8ba20f39ed
parent082e85051d5f5c877090cbaf3ef9c011e952a8de (diff)
parent44ef20c131021c7bcb83079d0e56321b6f9c17b4 (diff)
downloadxnt-fe18807a9307a43d19c2d14489f48e6d0370d878.tar.gz
xnt-fe18807a9307a43d19c2d14489f48e6d0370d878.tar.xz
Merge branch 'rewrite_xenant'
-rw-r--r--README26
-rw-r--r--docs/source/buildfile.rst46
-rw-r--r--docs/source/xenant.rst32
-rw-r--r--setup.py2
-rw-r--r--xnt/__init__.py2
-rw-r--r--xnt/basecommand.py (renamed from xnt/tests/xenanttests.py)29
-rw-r--r--xnt/cmdoptions.py26
-rw-r--r--xnt/commands/__init__.py39
-rw-r--r--xnt/commands/help.py39
-rw-r--r--xnt/commands/listtargets.py51
-rw-r--r--xnt/commands/target.py70
-rw-r--r--xnt/commands/version.py32
-rw-r--r--xnt/status_codes.py21
-rw-r--r--xnt/xenant.py143
14 files changed, 401 insertions, 157 deletions
diff --git a/README b/README
index f94c8ca..6bd1329 100644
--- a/README
+++ b/README
@@ -9,6 +9,15 @@ A wrapper build tool
Release Notes
=============
+0.5.0:<tbd>
+-----------
+
+* Rewrite Command Parsing
+ * This change does incur some interface change. Namely,
+ ``--version`` is now ``version``, ``--usage`` is now ``help``
+ * All other commands and switches are the same
+ * See `Package Documentation`_ for more information
+
0.4.1:2012-01-25
----------------
@@ -197,15 +206,22 @@ Where ``[options]`` are one of the following:
* ``--version``: print version and quit
-And where ``[target]`` is any target method in your ``build.py`` file
-or:
+And where ``[target]+`` are any target(s) method in your ``build.py``
+file or:
* Nothing; if no target is specified, Xnt will attempt to invoke the
``default`` target
-* ``list-targets``: if this 'special' target is provided, Xnt will
- print all targets marked by the ``@target`` decorator and possibly
- their docstrings if they are defined
+Other Commands
+--------------
+
+* ``list-targets``: Xnt will print all targets marked by the
+ ``@target`` decorator and possibly their docstrings if they are
+ defined
+
+* ``version``: Print the current version of Xnt and quit
+
+* ``help``: Print summary information about Xnt and command usage
For more information about Xnt and the build in functions, see the
`Package Documentation`_.
diff --git a/docs/source/buildfile.rst b/docs/source/buildfile.rst
index a2fbf99..cec8e30 100644
--- a/docs/source/buildfile.rst
+++ b/docs/source/buildfile.rst
@@ -63,7 +63,7 @@ Next, we will look at a new target::
This is a standard definition of a Python function with a decorator.
First, the ``target`` decorator marks the function definition as a target (to
-be used by the ``list-targets`` command, see :ref:`specialTargets`). Next, we
+be used by the ``list-targets`` command, see :ref:`otherCommands`). Next, we
define the function; this function name *is* the name of the target. That is,
the name given to the function will be the name given to the command to invoke
this target. Further, we have the docstring; (this is also used by the
@@ -77,10 +77,32 @@ Return Values
=============
The targets you define can return an error code (or '0' for success) however
-you see fit. Doing this will give you a status message at the end of the
-invocation of Xnt that will inform you if the target ran successfully or not
-given your criteria (or will just say it succeeded if you don't specify a
-return value at all). For example::
+you see fit. Xnt will emit 'Failure' if the status code is *not* zero and will
+otherwise remain silent if the code is zero. Further, the status code returned
+by your target will be returned as the exit code of Xnt when finished
+executing.
+
+*Notice*, this allows Xnt to fail fast when attempting to execute multiple
+targets. That is, if you specify more than one target, Xnt will stop at the
+first failure.
+
+If you don't define a return value for a target, Xnt will assume success and
+return '0'.
+
+Examples
+--------
+
+Not defining the return value::
+
+ @target
+ def foo():
+ pass
+
+Will result in (no success message; other output may be shown)::
+
+ ...
+
+Returning success (no success message; other output may be shown)::
@target
def foo():
@@ -89,9 +111,19 @@ return value at all). For example::
Will result in::
...
- Success
-Most tasks have been updated to return error codes as well to that you can
+Returning failure (not 0)::
+
+ @target
+ def foo():
+ return 1
+
+Will result in::
+
+ ...
+ Failure
+
+Most tasks have been updated to return error codes as well so that you can
return what it returns. If you find any tasks that can be updated to behave
this way, please create an issue for it.
diff --git a/docs/source/xenant.rst b/docs/source/xenant.rst
index 5bd6235..e9456fa 100644
--- a/docs/source/xenant.rst
+++ b/docs/source/xenant.rst
@@ -38,16 +38,22 @@ example::
Will execute `target1` through `targetN` in order of listing.
-.. _specialTargets:
+.. _otherCommands:
-Special Targets
----------------
+Othes Commands
+--------------
+
+Xnt has a number of other commands that can be invoked besides those defined in
+the current directory's `build.py` file. One will need a build file to run. The
+others, however, do not.
+
+* ``help`` prints a summary message, including information about the version of
+ Xnt, license, and usage.
-"Special" targets (for lack of a better name) are targets that do not exist in
-the build script, but rather are a part of Xnt.
+Usage::
+
+ $ xnt help
-Thus far, I have only defined one "special" target, ``list-targets`` (I don't
-think this name is going to change again ...).
* ``list-targets`` does exactly what the name should suggest: it prints a list
of the targets found in the current directory's `build.py` script, along with
@@ -57,6 +63,12 @@ Usage::
$ xnt list-targets
+* ``version`` prints Xnt's installed version.
+
+Usage::
+
+ $ xnt version
+
.. _xntOptions:
Options
@@ -72,12 +84,6 @@ Where options can be any and all of the following (unless otherwise specified):
* ``-v``: add verbose output to the execution of Xnt
-* ``--version``: Print the version of Xnt and exit
-
-* ``--usage``: Print version, license, usage information and quit. [I've
- debatted between putting this as a special target and leaving it as an
- option.. not sure which is better...]
-
.. _xntPropertiesParameters:
Properties and Parameter Passing
diff --git a/setup.py b/setup.py
index 2d5bb00..50261b9 100644
--- a/setup.py
+++ b/setup.py
@@ -25,7 +25,7 @@ def read(fname):
setup(
name="Xnt",
- version="0.4.1",
+ version="0.5.0dev1",
author="Kenny Ballou",
author_email="kennethmgballou@gmail.com",
url="https://github.com/devnulltao/Xnt",
diff --git a/xnt/__init__.py b/xnt/__init__.py
index d96178a..762efdc 100644
--- a/xnt/__init__.py
+++ b/xnt/__init__.py
@@ -16,7 +16,7 @@
# 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__ = "Xnt 0.4.1"
+__version__ = "Xnt 0.5.0dev1"
__license__ = """
Xnt -- A Wrapper Build Tool
Copyright (C) 2012 Kenny Ballou
diff --git a/xnt/tests/xenanttests.py b/xnt/basecommand.py
index 043b40d..83bdf0a 100644
--- a/xnt/tests/xenanttests.py
+++ b/xnt/basecommand.py
@@ -1,4 +1,3 @@
-
#!/usr/bin/env python
# Xnt -- A Wrapper Build Tool
@@ -17,25 +16,17 @@
# 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 sys
import os
-import shutil
-import xnt
-import xnt.tasks
-import xnt.xenant as xenant
-import unittest
-
-
-class XenantTests(unittest.TestCase):
- def setUp(self):
- os.mkdir("temp")
+import sys
- def tearDown(self):
- shutil.rmtree("temp")
+class Command(object):
+ name = None
+ usage = None
+ hidden = False
+ need_build = False
- def test_version(self):
- actual = xenant.version()
- self.assertEqual(xnt.__version__, xenant.version())
+ def __init__(self):
+ pass
-if __name__ == "__main__":
- unittest.main()
+ def run(arguments=[]):
+ pass
diff --git a/xnt/cmdoptions.py b/xnt/cmdoptions.py
new file mode 100644
index 0000000..9f3c944
--- /dev/null
+++ b/xnt/cmdoptions.py
@@ -0,0 +1,26 @@
+#!/usr/bin/env python
+
+# 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 flipVerboseFlag():
+ logging.getLogger("xnt").setLevel(logging.INFO)
+
+options = {
+ "-v": flipVerboseFlag,
+}
diff --git a/xnt/commands/__init__.py b/xnt/commands/__init__.py
new file mode 100644
index 0000000..8d51cec
--- /dev/null
+++ b/xnt/commands/__init__.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# 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):
+ 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
new file mode 100644
index 0000000..35ec9bb
--- /dev/null
+++ b/xnt/commands/help.py
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+# 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):
+ name = 'help'
+ usage = """"""
+ summary = 'Print Usage Summary'
+ needs_build = False
+
+ def run(self, arguments=[]):
+ 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
new file mode 100644
index 0000000..4ecc645
--- /dev/null
+++ b/xnt/commands/listtargets.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+
+# 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):
+ name = 'list-targets'
+ usage = """"""
+ summary = "Prints targets in build file"
+ needs_build = True
+
+ def __init__(self, build):
+ self.build = build
+
+ def run(self, arguments=[]):
+ logger.debug("build is null? %s", self.build == None)
+ try:
+ for f in dir(self.build):
+ logger.debug("Attribute %s:", f)
+ try:
+ fa = getattr(self.build, f)
+ if fa.decorator == "target":
+ print(f + ":")
+ if fa.__doc__:
+ print(fa.__doc__)
+ print("\n")
+ except AttributeError:
+ pass
+ except Exception as ex:
+ logger.error(ex)
+ return ERROR
+ return SUCCESS
diff --git a/xnt/commands/target.py b/xnt/commands/target.py
new file mode 100644
index 0000000..ad43dd0
--- /dev/null
+++ b/xnt/commands/target.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python
+
+# 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):
+ name = '<target>'
+ usage = """"""
+ summary = "Invokes target(s) in build.py"
+ needs_build = True
+
+ def __init__(self, build):
+ self.build = build
+
+ def run(self, targets=[], props=[]):
+ if targets:
+ for t in targets:
+ ec = self.callTarget(t, props)
+ if ec:
+ return ec
+ return SUCCESS
+ else:
+ return self.callTarget("default", props)
+
+ def callTarget(self, targetName, props):
+ def processParams(params, buildProperties={}):
+ properties = buildProperties if buildProperties is not None else {}
+ for p in params:
+ name, value = p[2:].split("=")
+ properties[name] = value
+ return properties
+ def __getProperties():
+ try:
+ return getattr(self.build, "properties")
+ except AttributeError:
+ return None
+ try:
+ if len(props) > 0:
+ setattr(self.build,
+ "properties",
+ processParams(props, __getProperties()))
+ target = getattr(self.build, targetName)
+ ec = target()
+ return ec if ec else 0
+ except AttributeError:
+ logger.warning("There was no target: %s", targetName)
+ return ERROR
+ except Exception as ex:
+ logger.error(ex)
+ return UNKNOWN_ERROR
+
diff --git a/xnt/commands/version.py b/xnt/commands/version.py
new file mode 100644
index 0000000..350a739
--- /dev/null
+++ b/xnt/commands/version.py
@@ -0,0 +1,32 @@
+#!/usr/bin/env python
+
+# 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):
+ name = 'version'
+ usage = """"""
+ summary = "Print Version of Xnt"
+ needs_build = False
+
+ def run(arguments=[]):
+ from xnt import __version__
+ print(__version__)
+
+ return SUCCESS
diff --git a/xnt/status_codes.py b/xnt/status_codes.py
new file mode 100644
index 0000000..3549a14
--- /dev/null
+++ b/xnt/status_codes.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python
+
+# 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/>.
+
+SUCCESS = 0
+ERROR = 1
+UNKNOWN_ERROR = 2
diff --git a/xnt/xenant.py b/xnt/xenant.py
index b9f40ab..b0cf15e 100644
--- a/xnt/xenant.py
+++ b/xnt/xenant.py
@@ -20,125 +20,51 @@ import os
import sys
import time
import logging
+from xnt.cmdoptions import options
+from xnt.commands import commands
+from xnt.commands.target import TargetCommand
logging.basicConfig(format="%(asctime)s:%(levelname)s:%(message)s")
logger = logging.Logger(name=__name__)
logger.addHandler(logging.StreamHandler())
-def usageAction():
- print(usage());
- sys.exit(0)
-
-def versionAction():
- print(version())
- sys.exit(0)
-
-def verboseAction():
- logging.getLogger("xnt").setLevel(logging.INFO)
-
-actions = {
- "--usage": usageAction,
- "--version": versionAction,
- "-v" : verboseAction,
-}
-
def main():
start_time = time.time()
params = list(p for p in sys.argv[1:] if p.startswith('-D'))
- opts = list(o for o in sys.argv[1:]
+ flags = list(o for o in sys.argv[1:]
if o.startswith('-') and o not in params)
- targets = list(a for a in sys.argv[1:]
- if a not in opts and a not in params)
- for opt in opts:
- if opt in actions:
- actions[opt]()
+ 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 valid option", opt)
- exit_codes = []
- def invoke(target):
- return invokeBuild(__loadBuild(),
- target,
- params)
- if targets:
- for t in targets:
- exit_codes.append(invoke(t))
- else:
- exit_codes.append(invoke("default"))
+ 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](loadBuild())
+ else:
+ command = commands[cmd]()
+ ec = command.run()
+ if cmd_found == False:
+ command = TargetCommand(loadBuild())
+ ec = command.run(targets=cmds, props=params)
+ elapsed_time = time.time() - start_time
+ logger.info("Execution time: %.3f", elapsed_time)
+ if ec != 0:
+ logger.info("Failure")
from xnt.tasks import rm
rm("build.pyc",
"__pycache__")
- elapsed_time = time.time() - start_time
- logger.info("Execution time: %.3f", elapsed_time)
- ec = sum(exit_codes)
- logger.info("Success" if ec == 0 else "Failure")
if ec != 0:
sys.exit(ec)
-def invokeBuild(build, targetName, props=[]):
- def __getProperties():
- try:
- return getattr(build, "properties")
- except AttributeError:
- return None
-
- if targetName == "list-targets":
- return printTargets(build)
- try:
- if len(props) > 0:
- setattr(build, "properties", __processParams(props,
- __getProperties()))
- target = getattr(build, targetName)
- ec = target()
- return ec if ec else 0
- except AttributeError:
- logger.warning("There was no target: %s", targetName)
- return -2
- except Exception as e:
- logger.error(e)
- return -3
-
-def usage():
- import xnt
- endl = os.linesep
- usageText = \
- xnt.__version__ + endl + \
- xnt.__license__ + endl + \
- "Usage:\txnt [options] [target]" + endl + \
- "Where [target] is a target in your ``build.py`` file" + endl + \
- " And [options] is one of the falling:" + endl + \
- "\t-v: print verbose information about Xnt's running" + endl + \
- "\t--usage: Print this message" + endl + \
- "In addition to targets defined by your ``build.py`` file" + endl + \
- "\t``list-targets`` can be used in place of [targets] to" + endl + \
- "\t\tlist targets and docstrings defined in your ``build.py`` file" + \
- endl + \
- "\tIf no [target] is provided, Xnt will try the target: ``default``" \
- + endl
- return usageText
-
-def version():
- import xnt
- return xnt.__version__
-
-def printTargets(build):
- print(version())
- print("\n")
- try:
- for f in dir(build):
- try:
- fa = getattr(build, f)
- if fa.decorator == "target":
- print(f + ":")
- if fa.__doc__:
- print(fa.__doc__)
- print("\n")
- except AttributeError:
- pass
- except Exception as e:
- logger.error(e)
- return 1
-
-def __loadBuild(path=""):
+def loadBuild(path=""):
if not path:
path = os.getcwd()
else:
@@ -159,12 +85,7 @@ def __loadBuild(path=""):
del sys.modules["build"]
os.chdir(cwd)
-def __processParams(params, buildProperties={}):
- properties = buildProperties if buildProperties is not None else {}
- for p in params:
- name, value = p[2:].split("=")
- properties[name] = value
- return properties
-
if __name__ == "__main__":
- main()
+ ec = main()
+ if ec:
+ sys.exit(ec)