diff --git a/aksetup_helper.py b/aksetup_helper.py new file mode 100644 index 0000000000000000000000000000000000000000..f90d085284f00e6a6cd2a44e6b9bd63a857485f3 --- /dev/null +++ b/aksetup_helper.py @@ -0,0 +1,960 @@ +import setuptools # noqa +from setuptools import Extension +import sys +from setuptools.command.build_ext import ( # noqa: N812 + build_ext as BaseBuildExtCommand) + + +def count_down_delay(delay): + from time import sleep + while delay: + sys.stdout.write("Continuing in %d seconds... \r" % delay) + sys.stdout.flush() + delay -= 1 + sleep(1) + print("") + + +DASH_SEPARATOR = 75 * "-" + + +def setup(*args, **kwargs): + from setuptools import setup + try: + setup(*args, **kwargs) + except KeyboardInterrupt: + raise + except SystemExit: + raise + except Exception: + print(DASH_SEPARATOR) + print("Sorry, your build failed. Try rerunning configure.py with " + "different options.") + print(DASH_SEPARATOR) + raise + + +def get_numpy_incpath(): + 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") + + +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_additional_include_dirs(self): + return [get_numpy_incpath()] + + def get_include_dirs(self): + return self._include_dirs + self.get_additional_include_dirs() + + def set_include_dirs(self, value): + self._include_dirs = value + + def del_include_dirs(self): + pass + + include_dirs = property(get_include_dirs, set_include_dirs, del_include_dirs) + + +class ExtensionUsingNumpy(Extension): + """Unlike :class:`NumpyExtension`, this class does not require numpy to be + importable upon extension module creation, allowing ``setup_requires=["numpy"]`` + to work. On the other hand, it requires the use of:: + + setup(..., + cmdclass={'build_ext': NumpyBuildExtCommand}) + + or + + setup(..., + cmdclass={'build_ext': PybindBuildExtCommand}) + """ + + +class NumpyBuildExtCommand(BaseBuildExtCommand): + def build_extension(self, extension): + # We add the numpy include dir right before building the + # extension, in order to avoid having to import numpy when + # the setup script is imported, which would prevent + # installation before manual installation of numpy. + if isinstance(extension, ExtensionUsingNumpy): + numpy_incpath = get_numpy_incpath() + if numpy_incpath not in extension.include_dirs: + extension.include_dirs.append(numpy_incpath) + + BaseBuildExtCommand.build_extension(self, extension) + + +# {{{ tools + +def flatten(lst): + """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 lst: + 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, warn_about_no_config=True): + 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() + and warn_about_no_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("*************************************************************") + + count_down_delay(delay=10) + + config = expand_options(schema.read_config()) + schema.update_config_from_and_modify_command_line(config, sys.argv) + return config + + +def hack_distutils(debug=False, fast_link=True, what_opt=3): + # 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 + + 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: + if what_opt is None: + pass + else: + cflags.append("-O%s" % what_opt) + cflags.append("-DNDEBUG") + + cvars['OPT'] = str.join(' ', cflags) + if "BASECFLAGS" in cvars: + cvars["CFLAGS"] = cvars["BASECFLAGS"] + " " + cvars["OPT"] + else: + assert "CFLAGS" in cvars + + 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): + return expand_str(v, options) + elif isinstance(v, list): + result = [] + for i in v: + try: + exp_i = expand_value(i, options) + except Exception: + pass + else: + result.append(exp_i) + + return result + else: + return v + + +def expand_options(options): + return dict( + (k, expand_value(v, options)) for k, v in options.items()) + + +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") + + 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 = {} + infile = open(filename, "r") + try: + contents = infile.read() + finally: + infile.close() + + exec(compile(contents, filename, "exec"), filevars) + + for key, value in filevars.items(): + if key in self.optdict: + result[key] = value + + return result + + def update_conf_file(self, filename, config): + result = {} + filevars = {} + + try: + exec(compile(open(filename, "r").read(), filename, "exec"), filevars) + except IOError: + pass + + if "__builtins__" in filevars: + del filevars["__builtins__"] + + for key, value in config.items(): + if value is not None: + filevars[key] = value + + keys = list(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 update_from_python_snippet(self, config, py_snippet, filename): + filevars = {} + exec(compile(py_snippet, filename, "exec"), filevars) + + for key, value in filevars.items(): + if key in self.optdict: + config[key] = value + elif key == "__builtins__": + pass + else: + raise KeyError("invalid config key in %s: %s" % ( + filename, key)) + + def update_config_from_and_modify_command_line(self, config, argv): + cfg_prefix = "--conf:" + + i = 0 + while i < len(argv): + arg = argv[i] + + if arg.startswith(cfg_prefix): + del argv[i] + self.update_from_python_snippet( + config, arg[len(cfg_prefix):], "") + else: + i += 1 + + return config + + def read_config(self): + import os + cfile = self.get_conf_file() + + result = self.get_default_config_with_files() + if os.access(cfile, os.R_OK): + with open(cfile, "r") as inf: + py_snippet = inf.read() + self.update_from_python_snippet(result, py_snippet, cfile) + + 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) + return result + + def write_config(self, config): + 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): + if not isinstance(self.default, bool): + raise ValueError("Switch options must have a default") + + if default is None: + default = self.default + + option_name = self.as_option() + + if default: + option_name = "no-" + option_name + action = "store_false" + else: + action = "store_true" + + parser.add_option( + "--" + option_name, + 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).replace(",", r"\,") 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: + import re + sep = re.compile(r"(?