X-Git-Url: https://git.distorted.org.uk/~mdw/runlisp/blobdiff_plain/207202a314beb383030b3feb979afec12a27d5ce..8a060232a9ca770d898eb0dc9cf4b4e174bf3f8a:/mdwsetup.py diff --git a/mdwsetup.py b/mdwsetup.py index a1f56b6..55120fb 100644 --- a/mdwsetup.py +++ b/mdwsetup.py @@ -34,6 +34,13 @@ import distutils.core as DC import distutils.log as DL ###-------------------------------------------------------------------------- +### Compatibility hacks. + +def with_metaclass(meta, *supers): + return meta("#" % meta.__name__, + supers or (object,), dict()) + +###-------------------------------------------------------------------------- ### Random utilities. def uniquify(seq): @@ -73,9 +80,12 @@ def progoutput(command): The COMMAND must produce exactly one line of output, and must exit with status zero. """ - kid = SUB.Popen(command, stdout = SUB.PIPE) - out = kid.stdout.readline() - junk = kid.stdout.read() + kid = SUB.Popen(command, stdout = SUB.PIPE, universal_newlines = True) + try: + out = kid.stdout.readline() + junk = kid.stdout.read(1) + finally: + kid.stdout.close() if junk != '': raise ValueError \ ("Child process `%s' produced unspected output %r" % (command, junk)) rc = kid.wait() @@ -97,22 +107,24 @@ def pkg_config(pkg, version): library-directory names are in LIBDIRS; and the library names themselves are in LIBS. """ - spec = '%s >= %s' % (pkg, version) + def weird(what, word): raise ValueError \ ("Unexpected `%s' item `%s' from package `%s'" % (what, word, pkg)) - for word in progoutput(['pkg-config', '--cflags', spec]).split(): - if word.startswith('-I'): - INCLUDEDIRS.append(word[2:]) - else: - weird('--cflags', word) - for word in progoutput(['pkg-config', '--libs', spec]).split(): - if word.startswith('-L'): - LIBDIRS.append(word[2:]) - elif word.startswith('-l'): - LIBS.append(word[2:]) - else: - weird('--libs', word) + + spec = '%s >= %s' % (pkg, version) + + try: cflags = OS.environ["%s_CFLAGS" % pkg] + except KeyError: cflags = progoutput(['pkg-config', '--cflags', spec]) + for word in cflags.split(): + if word.startswith('-I'): INCLUDEDIRS.append(word[2:]) + else: weird('CFLAGS', word) + try: libs = OS.environ["%s_LIBS" % pkg] + except KeyError: libs = progoutput(['pkg-config', '--libs', spec]) + for word in libs.split(): + if word.startswith('-L'): LIBDIRS.append(word[2:]) + elif word.startswith('-l'): LIBS.append(word[2:]) + else: weird('LIBS', word) ###-------------------------------------------------------------------------- ### Substituting variables in files. @@ -222,7 +234,7 @@ class CommandClass (type): else: CMDS[name] = c return c -class Command (DC.Command, object): +class Command (with_metaclass(CommandClass, DC.Command, object)): """ Base class for `mdwsetup' command classes. @@ -245,9 +257,9 @@ class distdir (Command): description = "print the distribution directory name to stdout" def run(me): d = me.distribution - print '%s-%s' % (d.get_name(), d.get_version()) + print('%s-%s' % (d.get_name(), d.get_version())) -class build_gen(Command): +class build_gen (Command): """ Generate files, according to the `genfiles'. @@ -276,7 +288,47 @@ class build (_build, Command): sub_commands = [('build_gen', lambda me: me.distribution.genfiles)] sub_commands += _build.sub_commands -class clean_gen(Command): +class test (Command): + """ + Run unit tests, according to the `unittests'. + + The `unittests' keyword argument to `setup' lists module names (or other + things acceptable to the `loadTestsFromNames' test-loader method) to be + run. The build library directory is prepended to the load path before + running the tests to ensure that the newly built modules are tested. If + `unittest_dir' is set, then this is appended to the load path so that test + modules can be found there. + """ + NAME = "test" + description = "run the included test suite" + + user_options = \ + [('build-lib=', 'b', "directory containing compiled moules"), + ('tests=', 't', "tests to run"), + ('verbose-test', 'V', "run tests verbosely")] + + def initialize_options(me): + me.build_lib = None + me.verbose_test = False + me.tests = None + def finalize_options(me): + me.set_undefined_options('build', ('build_lib', 'build_lib')) + def run(me): + import unittest as U + d = me.distribution + SYS.path = [me.build_lib] + SYS.path + if d.unittest_dir is not None: SYS.path.append(d.unittest_dir) + if me.tests is not None: tests = me.tests.split(",") + else: tests = d.unittests + suite = U.defaultTestLoader.loadTestsFromNames(tests) + runner = U.TextTestRunner(verbosity = me.verbose_test and 2 or 1) + if me.dry_run: return + result = runner.run(suite) + if result.errors or result.failures or \ + getattr(result, "unexpectedSuccesses", 0): + SYS.exit(2) + +class clean_gen (Command): """ Remove the generated files, as listed in `genfiles'. @@ -288,7 +340,7 @@ class clean_gen(Command): d = me.distribution for g in d.genfiles: g.clean(dry_run_p = me.dry_run) -class clean_others(Command): +class clean_others (Command): """ Remove the files listed in the `cleanfiles' argument to `setup'. """ @@ -339,6 +391,8 @@ class Dist (DC.Distribution): ## our enhanced commands. def __init__(me, attrs = None): me.genfiles = [] + me.unittest_dir = None + me.unittests = [] me.cleanfiles = [] me._auto_version_p = False DC.Distribution.__init__(me, attrs)