Skip to content
Snippets Groups Projects
aksetup_helper.py 19.2 KiB
Newer Older
  • Learn to ignore specific revisions
  • # dealings with ez_setup ------------------------------------------------------
    
    import distribute_setup
    distribute_setup.use_setuptools()
    
    
    from setuptools import Extension
    
    def setup(*args, **kwargs):
        from setuptools import setup
        import traceback
        try:
            setup(*args, **kwargs)
        except KeyboardInterrupt:
            raise
        except SystemExit:
            raise
        except:
    
            print "----------------------------------------------------------------------------"
            print "Sorry, your build failed. Try rerunning configure.py with different options."
            print "----------------------------------------------------------------------------"
    
            raise
    
    
    
    
    class NumpyExtension(Extension):
        # nicked from 
        # http://mail.python.org/pipermail/distutils-sig/2007-September/008253.html
        # solution by Michael Hoffmann
        def __init__(self, *args, **kwargs):
            Extension.__init__(self, *args, **kwargs)
            self._include_dirs = self.include_dirs
            del self.include_dirs # restore overwritten property
    
        def get_numpy_incpath(self):
            from imp import find_module
            # avoid actually importing numpy, it screws up distutils
            file, pathname, descr = find_module("numpy")
            from os.path import join
            return join(pathname, "core", "include")
    
        @property
        def include_dirs(self):
            return self._include_dirs + [self.get_numpy_incpath()]
    
    
    
    
    class PyUblasExtension(NumpyExtension):
        def get_module_include_path(self, name):
            from imp import find_module
            file, pathname, descr = find_module(name)
    
            from os.path import join, exists
            installed_path = join(pathname, "..", "include")
            development_path = join(pathname, "..", "src", "cpp")
            if exists(installed_path):
                return installed_path
            elif exists(development_path):
                return development_path
            else:
                raise RuntimeError("could not find C include path for module '%s'" % name)
    
    
        @property
        def include_dirs(self):
            return self._include_dirs + [
                    self.get_numpy_incpath(),
                    self.get_module_include_path("pyublas"),
                    ]
    
    
    
    
    class HedgeExtension(PyUblasExtension):
        @property
        def include_dirs(self):
            return self._include_dirs + [
                    self.get_numpy_incpath(),
                    self.get_module_include_path("pyublas"),
                    self.get_module_include_path("hedge"),
                    ]
    
    
    
    
    # tools -----------------------------------------------------------------------
    def flatten(list):
        """For an iterable of sub-iterables, generate each member of each 
        sub-iterable in turn, i.e. a flattened version of that super-iterable.
    
        Example: Turn [[a,b,c],[d,e,f]] into [a,b,c,d,e,f].
        """
        for sublist in list:
            for j in sublist:
                yield j
    
    
    
    
    def humanize(sym_str):
        words = sym_str.lower().replace("_", " ").split(" ")
        return " ".join([word.capitalize() for word in words])
    
    
    
    
    # siteconf handling -----------------------------------------------------------
    def get_config(schema=None):
        if schema is None:
            from setup import get_config_schema
            schema = get_config_schema()
    
        if not schema.have_config() and not schema.have_global_config():
    
            print "*************************************************************"
            print "*** I have detected that you have not run configure.py."
            print "*************************************************************"
    
            print "*** Additionally, no global config files were found."
            print "*** I will go ahead with the default configuration."
            print "*** In all likelihood, this will not work out."
            print "*** "
            print "*** See README_SETUP.txt for more information."
            print "*** "
    
            print "*** If the build does fail, just re-run configure.py with the"
    
            print "*** correct arguments, and then retry. Good luck!"
    
            print "*************************************************************"
    
            print "*** HIT Ctrl-C NOW IF THIS IS NOT WHAT YOU WANT"
    
            print "*************************************************************"
    
    
            delay = 10
    
            from time import sleep
            import sys
            while delay:
                sys.stdout.write("Continuing in %d seconds...   \r" % delay)
                sys.stdout.flush()
                delay -= 1
                sleep(1)
    
        return schema.read_config()
    
    
    
    
    def hack_distutils(debug=False, fast_link=True):
        # hack distutils.sysconfig to eliminate debug flags
        # stolen from mpi4py
    
        def remove_prefixes(optlist, bad_prefixes):
            for bad_prefix in bad_prefixes:
                for i, flag in enumerate(optlist):
                    if flag.startswith(bad_prefix):
                        optlist.pop(i)
                        break
            return optlist
    
        import sys
        if not sys.platform.lower().startswith("win"):
            from distutils import sysconfig
    
            cvars = sysconfig.get_config_vars()
            cflags = cvars.get('OPT')
            if cflags:
                cflags = remove_prefixes(cflags.split(),
                        ['-g', '-O', '-Wstrict-prototypes', '-DNDEBUG'])
                if debug:
                    cflags.append("-g")
                else:
                    cflags.append("-O3")
                    cflags.append("-DNDEBUG")
                cvars['OPT'] = str.join(' ', cflags)
                cvars["CFLAGS"] = cvars["BASECFLAGS"] + " " + cvars["OPT"]
    
            if fast_link:
                for varname in ["LDSHARED", "BLDSHARED"]:
                    ldsharedflags = cvars.get(varname)
                    if ldsharedflags:
                        ldsharedflags = remove_prefixes(ldsharedflags.split(),
                                ['-Wl,-O'])
                        cvars[varname] = str.join(' ', ldsharedflags)
    
    
    
    # configure guts --------------------------------------------------------------
    def default_or(a, b):
        if a is None:
            return b
        else:
            return a
    
    
    
    def expand_str(s, options):
        import re
    
        def my_repl(match):
            sym = match.group(1)
            try:
                repl = options[sym]
            except KeyError:
                from os import environ
                repl = environ[sym]
    
            return expand_str(repl, options)
    
        return re.subn(r"\$\{([a-zA-Z0-9_]+)\}", my_repl, s)[0]
    
    def expand_value(v, options):
        if isinstance(v, (str, unicode)):
            return expand_str(v, options)
        elif isinstance(v, list):
            return [expand_value(i, options) for i in v]
        else:
            return v
    
    
    def expand_options(options):
        for k in options.keys():
            options[k] = expand_value(options[k], options)
        return options
    
    
    
    
    
    
    class ConfigSchema:
        def __init__(self, options, conf_file="siteconf.py", conf_dir="."):
            self.optdict = dict((opt.name, opt) for opt in options)
            self.options = options
            self.conf_dir = conf_dir
            self.conf_file = conf_file
    
            from os.path import expanduser
            self.user_conf_file = expanduser("~/.aksetup-defaults.py")
    
            import sys
            if not sys.platform.lower().startswith("win"):
                self.global_conf_file = "/etc/aksetup-defaults.py"
            else:
                self.global_conf_file = None
    
        def get_conf_file(self):
            import os
            return os.path.join(self.conf_dir, self.conf_file)
    
        def set_conf_dir(self, conf_dir):
            self.conf_dir = conf_dir
    
        def get_default_config(self):
            return dict((opt.name, opt.default) 
                    for opt in self.options)
            
        def read_config_from_pyfile(self, filename):
            result = {}
            filevars = {}
            execfile(filename, filevars)
    
            for key, value in filevars.iteritems():
                if key in self.optdict:
                    result[key] = value
    
            return result
    
        def update_conf_file(self, filename, config):
            result = {}
            filevars = {}
    
            try:
                execfile(filename, filevars)
            except IOError:
                pass
    
            del filevars["__builtins__"]
    
            for key, value in config.iteritems():
                if value is not None:
                    filevars[key] = value
    
            keys = filevars.keys()
            keys.sort()
    
            outf = open(filename, "w")
            for key in keys:
                outf.write("%s = %s\n" % (key, repr(filevars[key])))
            outf.close()
    
            return result
    
        def update_user_config(self, config):
            self.update_conf_file(self.user_conf_file, config)
    
        def update_global_config(self, config):
            if self.global_conf_file is not None:
                self.update_conf_file(self.global_conf_file, config)
    
        def get_default_config_with_files(self):
            result = self.get_default_config()
    
            import os
            
            confignames = []
            if self.global_conf_file is not None:
                confignames.append(self.global_conf_file)
            confignames.append(self.user_conf_file)
    
            for fn in confignames:
                if os.access(fn, os.R_OK):
                    result.update(self.read_config_from_pyfile(fn))
    
            return result
    
        def have_global_config(self):
            import os
            result = os.access(self.user_conf_file, os.R_OK)
    
            if self.global_conf_file is not None:
                result = result or os.access(self.global_conf_file, os.R_OK)
    
            return result
    
        def have_config(self):
            import os
            return os.access(self.get_conf_file(), os.R_OK)
    
        def read_config(self, warn_if_none=True):
            import os
            cfile = self.get_conf_file()
    
            result = self.get_default_config_with_files()
            if os.access(cfile, os.R_OK):
                filevars = {}
                execfile(cfile, filevars)
    
                for key, value in filevars.iteritems():
                    if key in self.optdict:
                        result[key] = value
                    elif key == "__builtins__":
                        pass
                    else:
                        raise KeyError, "invalid config key in %s: %s" % (
                                cfile, key)
    
            expand_options(result)
    
            return result
    
        def add_to_configparser(self, parser, def_config=None):
            if def_config is None:
                def_config = self.get_default_config_with_files()
    
            for opt in self.options:
                default = default_or(def_config.get(opt.name), opt.default)
                opt.add_to_configparser(parser, default)
    
        def get_from_configparser(self, options):
            result = {}
            for opt in self.options:
                result[opt.name] = opt.take_from_configparser(options)
            expand_options(result)
            return result
    
        def write_config(self, config):
            import os
            outf = open(self.get_conf_file(), "w")
            for opt in self.options:
                value = config[opt.name]
                if value is not None:
                    outf.write("%s = %s\n" % (opt.name, repr(config[opt.name])))
            outf.close()
    
        def make_substitutions(self, config):
            return dict((opt.name, opt.value_to_str(config[opt.name]))
                    for opt in self.options)
    
    
    
    
    
    
    
    
    class Option(object):
        def __init__(self, name, default=None, help=None):
            self.name = name
            self.default = default
            self.help = help
    
        def as_option(self):
            return self.name.lower().replace("_", "-")
    
        def metavar(self):
            last_underscore = self.name.rfind("_")
            return self.name[last_underscore+1:]
    
        def get_help(self, default):
            result = self.help
            if self.default:
                result += " (default: %s)" % self.value_to_str(
                        default_or(default, self.default))
            return result
    
        def value_to_str(self, default):
            return default 
    
        def add_to_configparser(self, parser, default=None):
            default = default_or(default, self.default)
            default_str = self.value_to_str(default)
            parser.add_option(
                "--" + self.as_option(), dest=self.name, 
                default=default_str,
                metavar=self.metavar(), help=self.get_help(default))
    
        def take_from_configparser(self, options):
            return getattr(options, self.name)
    
    class Switch(Option):
        def add_to_configparser(self, parser, default=None):
            option = self.as_option()
    
            if not isinstance(self.default, bool):
                raise ValueError, "Switch options must have a default"
    
            if default is None:
                default = self.default
    
            if default:
                action = "store_false"
            else:
                action = "store_true"
                
            parser.add_option(
                "--" + self.as_option(), 
                dest=self.name, 
                help=self.get_help(default), 
                default=default,
                action=action)
    
    class StringListOption(Option):
        def value_to_str(self, default):
            if default is None:
                return None
    
            return ",".join([str(el) for el in default])
    
        def get_help(self, default):
            return Option.get_help(self, default) + " (several ok)"
    
        def take_from_configparser(self, options):
            opt = getattr(options, self.name)
            if opt is None:
                return None
            else:
                if opt:
                    return opt.split(",")
                else:
                    return []
    
    
    class IncludeDir(StringListOption):
        def __init__(self, lib_name, default=None, human_name=None, help=None):
            StringListOption.__init__(self, "%s_INC_DIR" % lib_name, default,
                    help=help or ("Include directories for %s" 
                    % (human_name or humanize(lib_name))))
    
    class LibraryDir(StringListOption):
        def __init__(self, lib_name, default=None, human_name=None, help=None):
            StringListOption.__init__(self, "%s_LIB_DIR" % lib_name, default,
                    help=help or ("Library directories for %s"
                    % (human_name or humanize(lib_name))))
    
    class Libraries(StringListOption):
        def __init__(self, lib_name, default=None, human_name=None, help=None):
            StringListOption.__init__(self, "%s_LIBNAME" % lib_name, default,
                    help=help or ("Library names for %s (without lib or .so)" 
                    % (human_name or humanize(lib_name))))
    
    class BoostLibraries(Libraries):
        def __init__(self, lib_base_name):
            Libraries.__init__(self, "BOOST_%s" % lib_base_name.upper(), 
                    ["boost_%s-${BOOST_COMPILER}-mt" % lib_base_name],
                    help="Library names for Boost C++ %s library (without lib or .so)" 
                        % humanize(lib_base_name))
    
    
    def set_up_shipped_boost_if_requested(conf):
        """Set up the package to use a shipped version of Boost.
    
        Return a list of extra C files to build.
        """
        if conf["USE_SHIPPED_BOOST"]:
            from os.path import exists
            if not exists("bpl-subset/bpl_subset/boost/version.hpp"):
                raise RuntimeError("The shipped Boost library was not found.")
            conf["BOOST_INC_DIR"] = ["bpl-subset/bpl_subset"]
            conf["BOOST_LIB_DIR"] = []
            conf["BOOST_PYTHON_LIBNAME"] = []
    
            from glob import glob
            source_files = (glob("bpl-subset/bpl_subset/libs/*/*/*/*.cpp")
                    + glob("bpl-subset/bpl_subset/libs/*/*/*.cpp")
                    + glob("bpl-subset/bpl_subset/libs/*/*.cpp"))
    
            print len(source_files)
            source_files = [f for f in source_files
                    if not f.startswith("bpl-subset/bpl_subset/libs/thread/src")]
            print len(source_files)
    
            import sys
            if sys.platform == "nt":
                print glob(
                        "bpl-subset/bpl_subset/libs/thread/src/win32/*.cpp")
                source_files += glob(
                        "bpl-subset/bpl_subset/libs/thread/src/win32/*.cpp")
            else:
                print glob(
                        "bpl-subset/bpl_subset/libs/thread/src/pthread/*.cpp")
                source_files += glob(
                        "bpl-subset/bpl_subset/libs/thread/src/pthread/*.cpp")
    
            return (source_files, 
                    {"BOOST_MULTI_INDEX_DISABLE_SERIALIZATION": 1}
                    )
        else:
            return [], {}
    
    
    
    def make_boost_base_options():
        return [
            IncludeDir("BOOST", []),
            LibraryDir("BOOST", []),
            Option("BOOST_COMPILER", default="gcc43", 
                help="The compiler with which Boost C++ was compiled, e.g. gcc43"),
            ]
    
    
    
    
    
    
    
    def configure_frontend():
        from optparse import OptionParser
    
        from setup import get_config_schema
        schema = get_config_schema()
        if schema.have_config():
            print "************************************************************"
            print "*** I have detected that you have already run configure."
            print "*** I'm taking the configured values as defaults for this"
            print "*** configure run. If you don't want this, delete the file"
            print "*** %s." % schema.get_conf_file()
            print "************************************************************"
    
        import sys
    
        description = "generate a configuration file for this software package"
        parser = OptionParser(description=description)
        parser.add_option(
    	    "--python-exe", dest="python_exe", default=sys.executable,
    	    help="Which Python interpreter to use", metavar="PATH")
    
        parser.add_option("--prefix", default=None,
    	    help="Ignored")
        parser.add_option("--enable-shared", help="Ignored", action="store_false")
        parser.add_option("--disable-static", help="Ignored", action="store_false")
        parser.add_option("--update-user", help="Update user config file (%s)" % schema.user_conf_file, 
                action="store_true")
        parser.add_option("--update-global", 
                help="Update global config file (%s)" % schema.global_conf_file, 
                action="store_true")
    
        schema.add_to_configparser(parser, schema.read_config())
    
        options, args = parser.parse_args()
    
        config = schema.get_from_configparser(options)
        schema.write_config(config)
    
        if options.update_user:
            schema.update_user_config(config)
    
        if options.update_global:
            schema.update_global_config(config)
    
        import os
        if os.access("Makefile.in", os.F_OK):
            substs = schema.make_substitutions(config)
            substs["PYTHON_EXE"] = options.python_exe
    
            substitute(substs, "Makefile")
    
    
    
    
    def substitute(substitutions, fname):
        import re
        var_re = re.compile(r"\$\{([A-Za-z_0-9]+)\}")
        string_var_re = re.compile(r"\$str\{([A-Za-z_0-9]+)\}")
    
        fname_in = fname+".in"
        lines = open(fname_in, "r").readlines()
        new_lines = []
        for l in lines:
            made_change = True
            while made_change:
                made_change = False
                match = var_re.search(l)
                if match:
                    varname = match.group(1)
                    l = l[:match.start()] + str(substitutions[varname]) + l[match.end():]
                    made_change = True
    
                match = string_var_re.search(l)
                if match:
                    varname = match.group(1)
                    subst = substitutions[varname]
                    if subst is None:
                        subst = ""
                    else:
                        subst = '"%s"' % subst
    
                    l = l[:match.start()] + subst  + l[match.end():]
                    made_change = True
            new_lines.append(l)
        new_lines.insert(1, "# DO NOT EDIT THIS FILE -- it was generated by configure.py\n")
        import sys
        new_lines.insert(2, "# %s\n" % (" ".join(sys.argv)))
        open(fname, "w").write("".join(new_lines))
    
        from os import stat, chmod
        infile_stat_res = stat(fname_in)
        chmod(fname, infile_stat_res.st_mode)