# HG changeset patch # User M. George Hansen # Date 1308180085 36000 # Node ID 07ff8cf8a0f19d87a62123e829f008a1fd7cd4ad # Parent e97972cc7110dcac1c29cd21d183083710b9b2f2 Fixed WAF install paths issue on Windows. * Reorganized the waf_paths.py WAF tool so that install paths are correctly set on Windows. * Added ordereddict.py for use by the waf_paths.py WAF tool on python versions less than 2.7. * Renamed the waf script to waf.py so that Windows users get the benefits of the .py file extension. * Fixed a bug where the FifePath entry in parpg.cfg was not getting set to the default python site-package path. * Fixed a bug in the Windows parpg.bat launcher where quotation marks (") were screwing up the PYTHONPATH variable. diff -r e97972cc7110 -r 07ff8cf8a0f1 bin/parpg.bat.in --- a/bin/parpg.bat.in Fri Jun 10 11:57:39 2011 -1000 +++ b/bin/parpg.bat.in Wed Jun 15 13:21:25 2011 -1000 @@ -1,5 +1,5 @@ @ECHO OFF -SET PYTHONPATH=%PYTHONPATH%;"@PYTHONDIR@" +SET PYTHONPATH=%PYTHONPATH%;@PYTHONDIR@ CD %~dp0 -"@PYTHON@" -m parpg.main "@SYSCONFDIR@" %* +@PYTHON@ -m parpg.main "@SYSCONFDIR@" %* diff -r e97972cc7110 -r 07ff8cf8a0f1 ordereddict.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ordereddict.py Wed Jun 15 13:21:25 2011 -1000 @@ -0,0 +1,127 @@ +# Copyright (c) 2009 Raymond Hettinger +# +# Permission is hereby granted, free of charge, to any person +# obtaining a copy of this software and associated documentation files +# (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, +# publish, distribute, sublicense, and/or sell copies of the Software, +# and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be +# included in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +from UserDict import DictMixin + +class OrderedDict(dict, DictMixin): + + def __init__(self, *args, **kwds): + if len(args) > 1: + raise TypeError('expected at most 1 arguments, got %d' % len(args)) + try: + self.__end + except AttributeError: + self.clear() + self.update(*args, **kwds) + + def clear(self): + self.__end = end = [] + end += [None, end, end] # sentinel node for doubly linked list + self.__map = {} # key --> [key, prev, next] + dict.clear(self) + + def __setitem__(self, key, value): + if key not in self: + end = self.__end + curr = end[1] + curr[2] = end[1] = self.__map[key] = [key, curr, end] + dict.__setitem__(self, key, value) + + def __delitem__(self, key): + dict.__delitem__(self, key) + key, prev, next = self.__map.pop(key) + prev[2] = next + next[1] = prev + + def __iter__(self): + end = self.__end + curr = end[2] + while curr is not end: + yield curr[0] + curr = curr[2] + + def __reversed__(self): + end = self.__end + curr = end[1] + while curr is not end: + yield curr[0] + curr = curr[1] + + def popitem(self, last=True): + if not self: + raise KeyError('dictionary is empty') + if last: + key = reversed(self).next() + else: + key = iter(self).next() + value = self.pop(key) + return key, value + + def __reduce__(self): + items = [[k, self[k]] for k in self] + tmp = self.__map, self.__end + del self.__map, self.__end + inst_dict = vars(self).copy() + self.__map, self.__end = tmp + if inst_dict: + return (self.__class__, (items,), inst_dict) + return self.__class__, (items,) + + def keys(self): + return list(self) + + setdefault = DictMixin.setdefault + update = DictMixin.update + pop = DictMixin.pop + values = DictMixin.values + items = DictMixin.items + iterkeys = DictMixin.iterkeys + itervalues = DictMixin.itervalues + iteritems = DictMixin.iteritems + + def __repr__(self): + if not self: + return '%s()' % (self.__class__.__name__,) + return '%s(%r)' % (self.__class__.__name__, self.items()) + + def copy(self): + return self.__class__(self) + + @classmethod + def fromkeys(cls, iterable, value=None): + d = cls() + for key in iterable: + d[key] = value + return d + + def __eq__(self, other): + if isinstance(other, OrderedDict): + if len(self) != len(other): + return False + for p, q in zip(self.items(), other.items()): + if p != q: + return False + return True + return dict.__eq__(self, other) + + def __ne__(self, other): + return not self == other diff -r e97972cc7110 -r 07ff8cf8a0f1 waf Binary file waf has changed diff -r e97972cc7110 -r 07ff8cf8a0f1 waf.py Binary file waf.py has changed diff -r e97972cc7110 -r 07ff8cf8a0f1 waf_paths.py --- a/waf_paths.py Fri Jun 10 11:57:39 2011 -1000 +++ b/waf_paths.py Wed Jun 15 13:21:25 2011 -1000 @@ -1,73 +1,67 @@ #! /usr/bin/env python # encoding: utf-8 -import sys +import os +import platform +import re -from waflib import Utils, Options, Context +from waflib import Utils, Options, Context, Logs, Errors -option_values = { - 'bindir' : ('user executables', '${EXEC_PREFIX}/bin'), - 'sbindir' : ('system admin executables', '${EXEC_PREFIX}/sbin'), - 'libexecdir' : ('program executables', '${EXEC_PREFIX}/libexec'), - 'sharedstatedir': ('modifiable architecture-independent data', - '${PREFIX}/com'), - 'localstatedir' : ('modifiable single-machine data', '${PREFIX}/var'), - 'libdir' : ('object code libraries', '${EXEC_PREFIX}/lib'), - 'includedir' : ('C header files', '${PREFIX}/include'), - 'oldincludedir' : ('C header files for non-gcc', '/usr/include'), - 'datarootdir' : ('read-only arch.-independent data root', - '${PREFIX}/share'), - # datadir and sysconfdir definitions deviate from GNU standards by - # appending the ${APPNAME}, but it makes the install script simpler since - # we don't have to redefine install paths on Windows. - 'datadir' : ('read-only architecture-independent data', - '${DATAROOTDIR}/${APPNAME}'), - 'sysconfdir' : ('read-only single-machine data', - '${PREFIX}/etc/${APPNAME}'), - 'infodir' : ('info documentation', '${DATAROOTDIR}/info'), - 'localedir' : ('locale-dependent data', '${DATAROOTDIR}/locale'), - 'mandir' : ('man documentation', '${DATAROOTDIR}/man'), - 'docdir' : ('documentation root', '${DATAROOTDIR}/doc/${APPNAME}'), - 'htmldir' : ('html documentation', '${DOCDIR}'), - 'dvidir' : ('dvi documentation', '${DOCDIR}'), - 'pdfdir' : ('pdf documentation', '${DOCDIR}'), - 'psdir' : ('ps documentation', '${DOCDIR}'), -} +try: + from collections import OrderedDict +except ImportError: + # Python 2.6 doesn't have an OrderedDict, so we'll provide one. + from ordereddict import OrderedDict + +prefix_values = OrderedDict( + [ + ('prefix', ['installation prefix', '/usr/local']), + ('exec_prefix', + ['installation prefix for platform-dependent binary files', + '${PREFIX}']) + ] +) -if sys.platform == 'Windows': - option_values['PREFIX'][1] = '${PROGRAM_FILES}/${APPNAME}' - option_values['BINDIR'][1] = '${EXEC_PREFIX}' - option_values['SYSCONFDIR'][1] = '${PREFIX}' - option_values['DATAROOTDIR'][1] = '${PREFIX}' - option_values['DATADIR'][1] = '${DATAROOTDIR}/data' - option_values['DOCDIR'][1] = '${DATAROOTDIR}/doc' - -def get_param(varname, default): - return getattr(Options.options, varname, '') or default +option_values = OrderedDict( + [ + ('bindir', ['user executables', '${EXEC_PREFIX}/bin']), + ('sbindir', ['system admin executables', '${EXEC_PREFIX}/sbin']), + ('libexecdir', ['program executables', '${EXEC_PREFIX}/libexec']), + ('sharedstatedir', ['modifiable architecture-independent data', + '${PREFIX}/com']), + ('localstatedir', ['modifiable single-machine data', '${PREFIX}/var']), + ('libdir', ['object code libraries', '${EXEC_PREFIX}/lib']), + ('includedir', ['C header files', '${PREFIX}/include']), + ('oldincludedir', ['C header files for non-gcc', '/usr/include']), + ('datarootdir', ['read-only arch.-independent data root', + '${PREFIX}/share']), + # datadir and sysconfdir definitions deviate from GNU standards by + # appending the ${APPNAME}, but it makes the install script simpler + # since we don't have to redefine install paths on Windows. + ('datadir', ['read-only architecture-independent data', + '${DATAROOTDIR}/${APPNAME}']), + ('sysconfdir', ['read-only single-machine data', + '${PREFIX}/etc/${APPNAME}']), + ('infodir', ['info documentation', '${DATAROOTDIR}/info']), + ('localedir', ['locale-dependent data', '${DATAROOTDIR}/locale']), + ('mandir', ['man documentation', '${DATAROOTDIR}/man']), + ('docdir', ['documentation root', '${DATAROOTDIR}/doc/${APPNAME}']), + ('htmldir', ['html documentation', '${DOCDIR}']), + ('dvidir', ['dvi documentation', '${DOCDIR}']), + ('pdfdir', ['pdf documentation', '${DOCDIR}']), + ('psdir', ['ps documentation', '${DOCDIR}']), + ] +) -def configure(conf): - env = conf.env - env['LIBDIR'] = [] - env['BINDIR'] = [] - env['EXEC_PREFIX'] = get_param('EXEC_PREFIX', env['PREFIX']) - env['APPNAME'] = getattr(Context.g_module, 'APPNAME') - env['INSTALL_PATHS'] = ['PREFIX', 'EXEC_PREFIX'] - complete = False - iter = 0 - while not complete and iter < len(option_values) + 1: - iter += 1 - complete = True - for name in option_values: - help, default = option_values[name] - name = name.upper() - if not env[name]: - try: - env[name] = Utils.subst_vars(get_param(name, default), env) - except TypeError: - complete = False - env['INSTALL_PATHS'].append(name) - if not complete: - lst = [name for name in option_values if not env[name.upper()]] - raise Errors.WafError('Variable substitution failure %r' % lst) +if platform.system() == 'Windows': + prefix_values['prefix'][1] = '${PROGRAMFILES}/${APPNAME}' + + option_values['bindir'][1] = '${EXEC_PREFIX}' + option_values['sysconfdir'][1] = '${PREFIX}' + option_values['datarootdir'][1] = '${PREFIX}' + option_values['datadir'][1] = '${DATAROOTDIR}/data' + option_values['docdir'][1] = '${DATAROOTDIR}/doc' + del option_values['oldincludedir'] + del option_values['mandir'] def options(opt): inst_dir = opt.add_option_group( @@ -77,24 +71,75 @@ 'be specified using the "--prefix" option, for example ' '"--prefix=$HOME"' ) - for k in ('--prefix', '--destdir'): - option = opt.parser.get_option(k) - if option: - opt.parser.remove_option(k) - inst_dir.add_option(option) - inst_dir.add_option( - '--exec-prefix', - help='installation prefix [Default: ${PREFIX}]', - default=inst_dir.defaults['prefix'], - dest='EXEC_PREFIX', - ) - dirs_options = opt.add_option_group( + help_template = '{0} [Default: %default]' + for prefix_name in prefix_values.keys(): + # Remove any of the core WAF options if they conflict with our options. + if opt.parser.get_option(prefix_name): + opt.parser.remove_option(prefix_name) + help, default = prefix_values[prefix_name] + option_name = '--' + prefix_name + inst_dir.add_option(option_name, help=help_template.format(help), + default=default, dest=prefix_name) + + # Move the --destdir option to the inst_dir group for consistency. + destdir_option = opt.parser.get_option('destdir') + if destdir_option: + opt.parser.remove_option('destdir') + inst_dir.add_option(destdir_option) + + predef_dirs = opt.add_option_group( 'Pre-defined installation directories', '' ) for name in option_values: help, default = option_values[name] option_name = '--' + name - str_help = '{0} [Default: %default]'.format(help) - dirs_options.add_option(option_name, help=str_help, default=default, - dest=name.upper()) + predef_dirs.add_option(option_name, help=help_template.format(help), + default=default, dest=name) + +def configure(conf): + def set_env_paths(path_values): + errors_raised = False + index = 0 + print(path_values.keys(), path_values) + for option_name in path_values.keys(): + help, default = path_values[option_name] + upper_option_name = option_name.upper() + try: + env[upper_option_name] = Utils.subst_vars( + getattr(conf.options, option_name), + env, + ) + except TypeError: + errors_raised = True + env['INSTALL_PATHS'].append(upper_option_name) + if errors_raised: + failed_path_names = [name for name in option_values if + upper_option_name not in env] + else: + failed_path_names = [] + return failed_path_names + + env = conf.env + if platform.system() == 'Windows': + try: + env['PROGRAMFILES'] = os.environ['PROGRAMFILES'] + except KeyError: + Logs.warn('PROGRAMFILES environmental variable is not set') + if re.search(r'\$\{PROGRAMFILES\}', conf.options.prefix): + Logs.error( + 'unable to find path to Program Files direcotry, please ' + 'set the PROGRAMFILES environmental variable or ' + 'override installation prefix with --prefix' + ) + if 'APPNAME' not in env: + env['APPNAME'] = Context.g_module.APPNAME + env['INSTALL_PATHS'] = [] + failed_path_names = [] + failed_path_names.extend(set_env_paths(prefix_values)) + failed_path_names.extend(set_env_paths(option_values)) + if failed_path_names: + error_message = 'failed to expand install paths {0!r}' + raise Errors.ConfigurationError( + error_message.format(failed_path_names) + ) diff -r e97972cc7110 -r 07ff8cf8a0f1 wscript --- a/wscript Fri Jun 10 11:57:39 2011 -1000 +++ b/wscript Wed Jun 15 13:21:25 2011 -1000 @@ -1,7 +1,7 @@ #!/usr/bin/env python # encoding: utf-8 -import sys import os +import platform APPNAME = 'parpg' VERSION = '0.2.0' @@ -28,19 +28,22 @@ min_python_version = (2, 6) cnf.check_python_version(min_python_version) - cnf.env['FIFEPATH'] = \ - os.path.abspath(os.path.expanduser(cnf.options.fifepath)) or \ - cnf.env['PYTHONDIR'] + if not cnf.options.fifepath: + cnf.env['FIFEPATH'] = os.path.abspath( + os.path.expanduser(cnf.options.fifepath) + ) + else: + cnf.env['FIFEPATH'] = cnf.env['PYTHONDIR'] def build(bld): subst_vars = _get_subst_vars(bld) - if sys.platform == 'Windows': - launcher_template = 'bin/parpg.bat.in' - launcher = 'parpg.bat' + if platform.system() == 'Windows': + launcher_template = bld.path.find_node('bin/parpg.bat.in') + launcher = bld.path.find_or_declare('parpg.bat') else: - launcher_template = 'bin/parpg.sh.in' - launcher = 'parpg' + launcher_template = bld.path.find_node('bin/parpg.sh.in') + launcher = bld.path.find_or_declare('parpg') args = dict( features='subst', source=launcher_template, @@ -59,8 +62,8 @@ args = dict( features='subst', - source='parpg.cfg.in', - target='parpg.cfg', + source=bld.path.find_node('parpg.cfg.in'), + target=bld.path.find_or_declare('parpg.cfg'), install_path='${SYSCONFDIR}', chmod=0644, ) @@ -71,6 +74,7 @@ files=bld.path.find_node('data').ant_glob('**/*'), dest='${DATADIR}', relative_trick=True, + cwd=bld.path.find_node('data'), chmod=0644, )