Chris@87: from __future__ import division, absolute_import, print_function Chris@87: Chris@87: import os Chris@87: import re Chris@87: import sys Chris@87: import imp Chris@87: import copy Chris@87: import glob Chris@87: import atexit Chris@87: import tempfile Chris@87: import subprocess Chris@87: import shutil Chris@87: Chris@87: import distutils Chris@87: from distutils.errors import DistutilsError Chris@87: Chris@87: try: Chris@87: set Chris@87: except NameError: Chris@87: from sets import Set as set Chris@87: Chris@87: from numpy.distutils.compat import get_exception Chris@87: Chris@87: __all__ = ['Configuration', 'get_numpy_include_dirs', 'default_config_dict', Chris@87: 'dict_append', 'appendpath', 'generate_config_py', Chris@87: 'get_cmd', 'allpath', 'get_mathlibs', Chris@87: 'terminal_has_colors', 'red_text', 'green_text', 'yellow_text', Chris@87: 'blue_text', 'cyan_text', 'cyg2win32', 'mingw32', 'all_strings', Chris@87: 'has_f_sources', 'has_cxx_sources', 'filter_sources', Chris@87: 'get_dependencies', 'is_local_src_dir', 'get_ext_source_files', Chris@87: 'get_script_files', 'get_lib_source_files', 'get_data_files', Chris@87: 'dot_join', 'get_frame', 'minrelpath', 'njoin', Chris@87: 'is_sequence', 'is_string', 'as_list', 'gpaths', 'get_language', Chris@87: 'quote_args', 'get_build_architecture', 'get_info', 'get_pkg_info'] Chris@87: Chris@87: class InstallableLib(object): Chris@87: """ Chris@87: Container to hold information on an installable library. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: name : str Chris@87: Name of the installed library. Chris@87: build_info : dict Chris@87: Dictionary holding build information. Chris@87: target_dir : str Chris@87: Absolute path specifying where to install the library. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: Configuration.add_installed_library Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: The three parameters are stored as attributes with the same names. Chris@87: Chris@87: """ Chris@87: def __init__(self, name, build_info, target_dir): Chris@87: self.name = name Chris@87: self.build_info = build_info Chris@87: self.target_dir = target_dir Chris@87: Chris@87: def quote_args(args): Chris@87: # don't used _nt_quote_args as it does not check if Chris@87: # args items already have quotes or not. Chris@87: args = list(args) Chris@87: for i in range(len(args)): Chris@87: a = args[i] Chris@87: if ' ' in a and a[0] not in '"\'': Chris@87: args[i] = '"%s"' % (a) Chris@87: return args Chris@87: Chris@87: def allpath(name): Chris@87: "Convert a /-separated pathname to one using the OS's path separator." Chris@87: splitted = name.split('/') Chris@87: return os.path.join(*splitted) Chris@87: Chris@87: def rel_path(path, parent_path): Chris@87: """Return path relative to parent_path. Chris@87: """ Chris@87: pd = os.path.abspath(parent_path) Chris@87: apath = os.path.abspath(path) Chris@87: if len(apath)= 0 Chris@87: and curses.tigetnum("pairs") >= 0 Chris@87: and ((curses.tigetstr("setf") is not None Chris@87: and curses.tigetstr("setb") is not None) Chris@87: or (curses.tigetstr("setaf") is not None Chris@87: and curses.tigetstr("setab") is not None) Chris@87: or curses.tigetstr("scp") is not None)): Chris@87: return 1 Chris@87: except Exception: Chris@87: pass Chris@87: return 0 Chris@87: Chris@87: if terminal_has_colors(): Chris@87: _colour_codes = dict(black=0, red=1, green=2, yellow=3, Chris@87: blue=4, magenta=5, cyan=6, white=7, default=9) Chris@87: def colour_text(s, fg=None, bg=None, bold=False): Chris@87: seq = [] Chris@87: if bold: Chris@87: seq.append('1') Chris@87: if fg: Chris@87: fgcode = 30 + _colour_codes.get(fg.lower(), 0) Chris@87: seq.append(str(fgcode)) Chris@87: if bg: Chris@87: bgcode = 40 + _colour_codes.get(fg.lower(), 7) Chris@87: seq.append(str(bgcode)) Chris@87: if seq: Chris@87: return '\x1b[%sm%s\x1b[0m' % (';'.join(seq), s) Chris@87: else: Chris@87: return s Chris@87: else: Chris@87: def colour_text(s, fg=None, bg=None): Chris@87: return s Chris@87: Chris@87: def default_text(s): Chris@87: return colour_text(s, 'default') Chris@87: def red_text(s): Chris@87: return colour_text(s, 'red') Chris@87: def green_text(s): Chris@87: return colour_text(s, 'green') Chris@87: def yellow_text(s): Chris@87: return colour_text(s, 'yellow') Chris@87: def cyan_text(s): Chris@87: return colour_text(s, 'cyan') Chris@87: def blue_text(s): Chris@87: return colour_text(s, 'blue') Chris@87: Chris@87: ######################### Chris@87: Chris@87: def cyg2win32(path): Chris@87: if sys.platform=='cygwin' and path.startswith('/cygdrive'): Chris@87: path = path[10] + ':' + os.path.normcase(path[11:]) Chris@87: return path Chris@87: Chris@87: def mingw32(): Chris@87: """Return true when using mingw32 environment. Chris@87: """ Chris@87: if sys.platform=='win32': Chris@87: if os.environ.get('OSTYPE', '')=='msys': Chris@87: return True Chris@87: if os.environ.get('MSYSTEM', '')=='MINGW32': Chris@87: return True Chris@87: return False Chris@87: Chris@87: def msvc_runtime_library(): Chris@87: "Return name of MSVC runtime library if Python was built with MSVC >= 7" Chris@87: msc_pos = sys.version.find('MSC v.') Chris@87: if msc_pos != -1: Chris@87: msc_ver = sys.version[msc_pos+6:msc_pos+10] Chris@87: lib = {'1300': 'msvcr70', # MSVC 7.0 Chris@87: '1310': 'msvcr71', # MSVC 7.1 Chris@87: '1400': 'msvcr80', # MSVC 8 Chris@87: '1500': 'msvcr90', # MSVC 9 (VS 2008) Chris@87: '1600': 'msvcr100', # MSVC 10 (aka 2010) Chris@87: }.get(msc_ver, None) Chris@87: else: Chris@87: lib = None Chris@87: return lib Chris@87: Chris@87: Chris@87: ######################### Chris@87: Chris@87: #XXX need support for .C that is also C++ Chris@87: cxx_ext_match = re.compile(r'.*[.](cpp|cxx|cc)\Z', re.I).match Chris@87: fortran_ext_match = re.compile(r'.*[.](f90|f95|f77|for|ftn|f)\Z', re.I).match Chris@87: f90_ext_match = re.compile(r'.*[.](f90|f95)\Z', re.I).match Chris@87: f90_module_name_match = re.compile(r'\s*module\s*(?P[\w_]+)', re.I).match Chris@87: def _get_f90_modules(source): Chris@87: """Return a list of Fortran f90 module names that Chris@87: given source file defines. Chris@87: """ Chris@87: if not f90_ext_match(source): Chris@87: return [] Chris@87: modules = [] Chris@87: f = open(source, 'r') Chris@87: for line in f: Chris@87: m = f90_module_name_match(line) Chris@87: if m: Chris@87: name = m.group('name') Chris@87: modules.append(name) Chris@87: # break # XXX can we assume that there is one module per file? Chris@87: f.close() Chris@87: return modules Chris@87: Chris@87: def is_string(s): Chris@87: return isinstance(s, str) Chris@87: Chris@87: def all_strings(lst): Chris@87: """Return True if all items in lst are string objects. """ Chris@87: for item in lst: Chris@87: if not is_string(item): Chris@87: return False Chris@87: return True Chris@87: Chris@87: def is_sequence(seq): Chris@87: if is_string(seq): Chris@87: return False Chris@87: try: Chris@87: len(seq) Chris@87: except: Chris@87: return False Chris@87: return True Chris@87: Chris@87: def is_glob_pattern(s): Chris@87: return is_string(s) and ('*' in s or '?' is s) Chris@87: Chris@87: def as_list(seq): Chris@87: if is_sequence(seq): Chris@87: return list(seq) Chris@87: else: Chris@87: return [seq] Chris@87: Chris@87: def get_language(sources): Chris@87: # not used in numpy/scipy packages, use build_ext.detect_language instead Chris@87: """Determine language value (c,f77,f90) from sources """ Chris@87: language = None Chris@87: for source in sources: Chris@87: if isinstance(source, str): Chris@87: if f90_ext_match(source): Chris@87: language = 'f90' Chris@87: break Chris@87: elif fortran_ext_match(source): Chris@87: language = 'f77' Chris@87: return language Chris@87: Chris@87: def has_f_sources(sources): Chris@87: """Return True if sources contains Fortran files """ Chris@87: for source in sources: Chris@87: if fortran_ext_match(source): Chris@87: return True Chris@87: return False Chris@87: Chris@87: def has_cxx_sources(sources): Chris@87: """Return True if sources contains C++ files """ Chris@87: for source in sources: Chris@87: if cxx_ext_match(source): Chris@87: return True Chris@87: return False Chris@87: Chris@87: def filter_sources(sources): Chris@87: """Return four lists of filenames containing Chris@87: C, C++, Fortran, and Fortran 90 module sources, Chris@87: respectively. Chris@87: """ Chris@87: c_sources = [] Chris@87: cxx_sources = [] Chris@87: f_sources = [] Chris@87: fmodule_sources = [] Chris@87: for source in sources: Chris@87: if fortran_ext_match(source): Chris@87: modules = _get_f90_modules(source) Chris@87: if modules: Chris@87: fmodule_sources.append(source) Chris@87: else: Chris@87: f_sources.append(source) Chris@87: elif cxx_ext_match(source): Chris@87: cxx_sources.append(source) Chris@87: else: Chris@87: c_sources.append(source) Chris@87: return c_sources, cxx_sources, f_sources, fmodule_sources Chris@87: Chris@87: Chris@87: def _get_headers(directory_list): Chris@87: # get *.h files from list of directories Chris@87: headers = [] Chris@87: for d in directory_list: Chris@87: head = glob.glob(os.path.join(d, "*.h")) #XXX: *.hpp files?? Chris@87: headers.extend(head) Chris@87: return headers Chris@87: Chris@87: def _get_directories(list_of_sources): Chris@87: # get unique directories from list of sources. Chris@87: direcs = [] Chris@87: for f in list_of_sources: Chris@87: d = os.path.split(f) Chris@87: if d[0] != '' and not d[0] in direcs: Chris@87: direcs.append(d[0]) Chris@87: return direcs Chris@87: Chris@87: def get_dependencies(sources): Chris@87: #XXX scan sources for include statements Chris@87: return _get_headers(_get_directories(sources)) Chris@87: Chris@87: def is_local_src_dir(directory): Chris@87: """Return true if directory is local directory. Chris@87: """ Chris@87: if not is_string(directory): Chris@87: return False Chris@87: abs_dir = os.path.abspath(directory) Chris@87: c = os.path.commonprefix([os.getcwd(), abs_dir]) Chris@87: new_dir = abs_dir[len(c):].split(os.sep) Chris@87: if new_dir and not new_dir[0]: Chris@87: new_dir = new_dir[1:] Chris@87: if new_dir and new_dir[0]=='build': Chris@87: return False Chris@87: new_dir = os.sep.join(new_dir) Chris@87: return os.path.isdir(new_dir) Chris@87: Chris@87: def general_source_files(top_path): Chris@87: pruned_directories = {'CVS':1, '.svn':1, 'build':1} Chris@87: prune_file_pat = re.compile(r'(?:[~#]|\.py[co]|\.o)$') Chris@87: for dirpath, dirnames, filenames in os.walk(top_path, topdown=True): Chris@87: pruned = [ d for d in dirnames if d not in pruned_directories ] Chris@87: dirnames[:] = pruned Chris@87: for f in filenames: Chris@87: if not prune_file_pat.search(f): Chris@87: yield os.path.join(dirpath, f) Chris@87: Chris@87: def general_source_directories_files(top_path): Chris@87: """Return a directory name relative to top_path and Chris@87: files contained. Chris@87: """ Chris@87: pruned_directories = ['CVS', '.svn', 'build'] Chris@87: prune_file_pat = re.compile(r'(?:[~#]|\.py[co]|\.o)$') Chris@87: for dirpath, dirnames, filenames in os.walk(top_path, topdown=True): Chris@87: pruned = [ d for d in dirnames if d not in pruned_directories ] Chris@87: dirnames[:] = pruned Chris@87: for d in dirnames: Chris@87: dpath = os.path.join(dirpath, d) Chris@87: rpath = rel_path(dpath, top_path) Chris@87: files = [] Chris@87: for f in os.listdir(dpath): Chris@87: fn = os.path.join(dpath, f) Chris@87: if os.path.isfile(fn) and not prune_file_pat.search(fn): Chris@87: files.append(fn) Chris@87: yield rpath, files Chris@87: dpath = top_path Chris@87: rpath = rel_path(dpath, top_path) Chris@87: filenames = [os.path.join(dpath, f) for f in os.listdir(dpath) \ Chris@87: if not prune_file_pat.search(f)] Chris@87: files = [f for f in filenames if os.path.isfile(f)] Chris@87: yield rpath, files Chris@87: Chris@87: Chris@87: def get_ext_source_files(ext): Chris@87: # Get sources and any include files in the same directory. Chris@87: filenames = [] Chris@87: sources = [_m for _m in ext.sources if is_string(_m)] Chris@87: filenames.extend(sources) Chris@87: filenames.extend(get_dependencies(sources)) Chris@87: for d in ext.depends: Chris@87: if is_local_src_dir(d): Chris@87: filenames.extend(list(general_source_files(d))) Chris@87: elif os.path.isfile(d): Chris@87: filenames.append(d) Chris@87: return filenames Chris@87: Chris@87: def get_script_files(scripts): Chris@87: scripts = [_m for _m in scripts if is_string(_m)] Chris@87: return scripts Chris@87: Chris@87: def get_lib_source_files(lib): Chris@87: filenames = [] Chris@87: sources = lib[1].get('sources', []) Chris@87: sources = [_m for _m in sources if is_string(_m)] Chris@87: filenames.extend(sources) Chris@87: filenames.extend(get_dependencies(sources)) Chris@87: depends = lib[1].get('depends', []) Chris@87: for d in depends: Chris@87: if is_local_src_dir(d): Chris@87: filenames.extend(list(general_source_files(d))) Chris@87: elif os.path.isfile(d): Chris@87: filenames.append(d) Chris@87: return filenames Chris@87: Chris@87: def get_shared_lib_extension(is_python_ext=False): Chris@87: """Return the correct file extension for shared libraries. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: is_python_ext : bool, optional Chris@87: Whether the shared library is a Python extension. Default is False. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: so_ext : str Chris@87: The shared library extension. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: For Python shared libs, `so_ext` will typically be '.so' on Linux and OS X, Chris@87: and '.pyd' on Windows. For Python >= 3.2 `so_ext` has a tag prepended on Chris@87: POSIX systems according to PEP 3149. For Python 3.2 this is implemented on Chris@87: Linux, but not on OS X. Chris@87: Chris@87: """ Chris@87: confvars = distutils.sysconfig.get_config_vars() Chris@87: # SO is deprecated in 3.3.1, use EXT_SUFFIX instead Chris@87: so_ext = confvars.get('EXT_SUFFIX', None) Chris@87: if so_ext is None: Chris@87: so_ext = confvars.get('SO', '') Chris@87: Chris@87: if not is_python_ext: Chris@87: # hardcode known values, config vars (including SHLIB_SUFFIX) are Chris@87: # unreliable (see #3182) Chris@87: # darwin, windows and debug linux are wrong in 3.3.1 and older Chris@87: if (sys.platform.startswith('linux') or Chris@87: sys.platform.startswith('gnukfreebsd')): Chris@87: so_ext = '.so' Chris@87: elif sys.platform.startswith('darwin'): Chris@87: so_ext = '.dylib' Chris@87: elif sys.platform.startswith('win'): Chris@87: so_ext = '.dll' Chris@87: else: Chris@87: # fall back to config vars for unknown platforms Chris@87: # fix long extension for Python >=3.2, see PEP 3149. Chris@87: if 'SOABI' in confvars: Chris@87: # Does nothing unless SOABI config var exists Chris@87: so_ext = so_ext.replace('.' + confvars.get('SOABI'), '', 1) Chris@87: Chris@87: return so_ext Chris@87: Chris@87: def get_data_files(data): Chris@87: if is_string(data): Chris@87: return [data] Chris@87: sources = data[1] Chris@87: filenames = [] Chris@87: for s in sources: Chris@87: if hasattr(s, '__call__'): Chris@87: continue Chris@87: if is_local_src_dir(s): Chris@87: filenames.extend(list(general_source_files(s))) Chris@87: elif is_string(s): Chris@87: if os.path.isfile(s): Chris@87: filenames.append(s) Chris@87: else: Chris@87: print('Not existing data file:', s) Chris@87: else: Chris@87: raise TypeError(repr(s)) Chris@87: return filenames Chris@87: Chris@87: def dot_join(*args): Chris@87: return '.'.join([a for a in args if a]) Chris@87: Chris@87: def get_frame(level=0): Chris@87: """Return frame object from call stack with given level. Chris@87: """ Chris@87: try: Chris@87: return sys._getframe(level+1) Chris@87: except AttributeError: Chris@87: frame = sys.exc_info()[2].tb_frame Chris@87: for _ in range(level+1): Chris@87: frame = frame.f_back Chris@87: return frame Chris@87: Chris@87: Chris@87: ###################### Chris@87: Chris@87: class Configuration(object): Chris@87: Chris@87: _list_keys = ['packages', 'ext_modules', 'data_files', 'include_dirs', Chris@87: 'libraries', 'headers', 'scripts', 'py_modules', Chris@87: 'installed_libraries', 'define_macros'] Chris@87: _dict_keys = ['package_dir', 'installed_pkg_config'] Chris@87: _extra_keys = ['name', 'version'] Chris@87: Chris@87: numpy_include_dirs = [] Chris@87: Chris@87: def __init__(self, Chris@87: package_name=None, Chris@87: parent_name=None, Chris@87: top_path=None, Chris@87: package_path=None, Chris@87: caller_level=1, Chris@87: setup_name='setup.py', Chris@87: **attrs): Chris@87: """Construct configuration instance of a package. Chris@87: Chris@87: package_name -- name of the package Chris@87: Ex.: 'distutils' Chris@87: parent_name -- name of the parent package Chris@87: Ex.: 'numpy' Chris@87: top_path -- directory of the toplevel package Chris@87: Ex.: the directory where the numpy package source sits Chris@87: package_path -- directory of package. Will be computed by magic from the Chris@87: directory of the caller module if not specified Chris@87: Ex.: the directory where numpy.distutils is Chris@87: caller_level -- frame level to caller namespace, internal parameter. Chris@87: """ Chris@87: self.name = dot_join(parent_name, package_name) Chris@87: self.version = None Chris@87: Chris@87: caller_frame = get_frame(caller_level) Chris@87: self.local_path = get_path_from_frame(caller_frame, top_path) Chris@87: # local_path -- directory of a file (usually setup.py) that Chris@87: # defines a configuration() function. Chris@87: # local_path -- directory of a file (usually setup.py) that Chris@87: # defines a configuration() function. Chris@87: if top_path is None: Chris@87: top_path = self.local_path Chris@87: self.local_path = '' Chris@87: if package_path is None: Chris@87: package_path = self.local_path Chris@87: elif os.path.isdir(njoin(self.local_path, package_path)): Chris@87: package_path = njoin(self.local_path, package_path) Chris@87: if not os.path.isdir(package_path or '.'): Chris@87: raise ValueError("%r is not a directory" % (package_path,)) Chris@87: self.top_path = top_path Chris@87: self.package_path = package_path Chris@87: # this is the relative path in the installed package Chris@87: self.path_in_package = os.path.join(*self.name.split('.')) Chris@87: Chris@87: self.list_keys = self._list_keys[:] Chris@87: self.dict_keys = self._dict_keys[:] Chris@87: Chris@87: for n in self.list_keys: Chris@87: v = copy.copy(attrs.get(n, [])) Chris@87: setattr(self, n, as_list(v)) Chris@87: Chris@87: for n in self.dict_keys: Chris@87: v = copy.copy(attrs.get(n, {})) Chris@87: setattr(self, n, v) Chris@87: Chris@87: known_keys = self.list_keys + self.dict_keys Chris@87: self.extra_keys = self._extra_keys[:] Chris@87: for n in attrs.keys(): Chris@87: if n in known_keys: Chris@87: continue Chris@87: a = attrs[n] Chris@87: setattr(self, n, a) Chris@87: if isinstance(a, list): Chris@87: self.list_keys.append(n) Chris@87: elif isinstance(a, dict): Chris@87: self.dict_keys.append(n) Chris@87: else: Chris@87: self.extra_keys.append(n) Chris@87: Chris@87: if os.path.exists(njoin(package_path, '__init__.py')): Chris@87: self.packages.append(self.name) Chris@87: self.package_dir[self.name] = package_path Chris@87: Chris@87: self.options = dict( Chris@87: ignore_setup_xxx_py = False, Chris@87: assume_default_configuration = False, Chris@87: delegate_options_to_subpackages = False, Chris@87: quiet = False, Chris@87: ) Chris@87: Chris@87: caller_instance = None Chris@87: for i in range(1, 3): Chris@87: try: Chris@87: f = get_frame(i) Chris@87: except ValueError: Chris@87: break Chris@87: try: Chris@87: caller_instance = eval('self', f.f_globals, f.f_locals) Chris@87: break Chris@87: except NameError: Chris@87: pass Chris@87: if isinstance(caller_instance, self.__class__): Chris@87: if caller_instance.options['delegate_options_to_subpackages']: Chris@87: self.set_options(**caller_instance.options) Chris@87: Chris@87: self.setup_name = setup_name Chris@87: Chris@87: def todict(self): Chris@87: """ Chris@87: Return a dictionary compatible with the keyword arguments of distutils Chris@87: setup function. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: >>> setup(**config.todict()) #doctest: +SKIP Chris@87: """ Chris@87: Chris@87: self._optimize_data_files() Chris@87: d = {} Chris@87: known_keys = self.list_keys + self.dict_keys + self.extra_keys Chris@87: for n in known_keys: Chris@87: a = getattr(self, n) Chris@87: if a: Chris@87: d[n] = a Chris@87: return d Chris@87: Chris@87: def info(self, message): Chris@87: if not self.options['quiet']: Chris@87: print(message) Chris@87: Chris@87: def warn(self, message): Chris@87: sys.stderr.write('Warning: %s' % (message,)) Chris@87: Chris@87: def set_options(self, **options): Chris@87: """ Chris@87: Configure Configuration instance. Chris@87: Chris@87: The following options are available: Chris@87: - ignore_setup_xxx_py Chris@87: - assume_default_configuration Chris@87: - delegate_options_to_subpackages Chris@87: - quiet Chris@87: Chris@87: """ Chris@87: for key, value in options.items(): Chris@87: if key in self.options: Chris@87: self.options[key] = value Chris@87: else: Chris@87: raise ValueError('Unknown option: '+key) Chris@87: Chris@87: def get_distribution(self): Chris@87: """Return the distutils distribution object for self.""" Chris@87: from numpy.distutils.core import get_distribution Chris@87: return get_distribution() Chris@87: Chris@87: def _wildcard_get_subpackage(self, subpackage_name, Chris@87: parent_name, Chris@87: caller_level = 1): Chris@87: l = subpackage_name.split('.') Chris@87: subpackage_path = njoin([self.local_path]+l) Chris@87: dirs = [_m for _m in glob.glob(subpackage_path) if os.path.isdir(_m)] Chris@87: config_list = [] Chris@87: for d in dirs: Chris@87: if not os.path.isfile(njoin(d, '__init__.py')): Chris@87: continue Chris@87: if 'build' in d.split(os.sep): Chris@87: continue Chris@87: n = '.'.join(d.split(os.sep)[-len(l):]) Chris@87: c = self.get_subpackage(n, Chris@87: parent_name = parent_name, Chris@87: caller_level = caller_level+1) Chris@87: config_list.extend(c) Chris@87: return config_list Chris@87: Chris@87: def _get_configuration_from_setup_py(self, setup_py, Chris@87: subpackage_name, Chris@87: subpackage_path, Chris@87: parent_name, Chris@87: caller_level = 1): Chris@87: # In case setup_py imports local modules: Chris@87: sys.path.insert(0, os.path.dirname(setup_py)) Chris@87: try: Chris@87: fo_setup_py = open(setup_py, 'U') Chris@87: setup_name = os.path.splitext(os.path.basename(setup_py))[0] Chris@87: n = dot_join(self.name, subpackage_name, setup_name) Chris@87: setup_module = imp.load_module('_'.join(n.split('.')), Chris@87: fo_setup_py, Chris@87: setup_py, Chris@87: ('.py', 'U', 1)) Chris@87: fo_setup_py.close() Chris@87: if not hasattr(setup_module, 'configuration'): Chris@87: if not self.options['assume_default_configuration']: Chris@87: self.warn('Assuming default configuration '\ Chris@87: '(%s does not define configuration())'\ Chris@87: % (setup_module)) Chris@87: config = Configuration(subpackage_name, parent_name, Chris@87: self.top_path, subpackage_path, Chris@87: caller_level = caller_level + 1) Chris@87: else: Chris@87: pn = dot_join(*([parent_name] + subpackage_name.split('.')[:-1])) Chris@87: args = (pn,) Chris@87: def fix_args_py2(args): Chris@87: if setup_module.configuration.__code__.co_argcount > 1: Chris@87: args = args + (self.top_path,) Chris@87: return args Chris@87: def fix_args_py3(args): Chris@87: if setup_module.configuration.__code__.co_argcount > 1: Chris@87: args = args + (self.top_path,) Chris@87: return args Chris@87: if sys.version_info[0] < 3: Chris@87: args = fix_args_py2(args) Chris@87: else: Chris@87: args = fix_args_py3(args) Chris@87: config = setup_module.configuration(*args) Chris@87: if config.name!=dot_join(parent_name, subpackage_name): Chris@87: self.warn('Subpackage %r configuration returned as %r' % \ Chris@87: (dot_join(parent_name, subpackage_name), config.name)) Chris@87: finally: Chris@87: del sys.path[0] Chris@87: return config Chris@87: Chris@87: def get_subpackage(self,subpackage_name, Chris@87: subpackage_path=None, Chris@87: parent_name=None, Chris@87: caller_level = 1): Chris@87: """Return list of subpackage configurations. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: subpackage_name : str or None Chris@87: Name of the subpackage to get the configuration. '*' in Chris@87: subpackage_name is handled as a wildcard. Chris@87: subpackage_path : str Chris@87: If None, then the path is assumed to be the local path plus the Chris@87: subpackage_name. If a setup.py file is not found in the Chris@87: subpackage_path, then a default configuration is used. Chris@87: parent_name : str Chris@87: Parent name. Chris@87: """ Chris@87: if subpackage_name is None: Chris@87: if subpackage_path is None: Chris@87: raise ValueError( Chris@87: "either subpackage_name or subpackage_path must be specified") Chris@87: subpackage_name = os.path.basename(subpackage_path) Chris@87: Chris@87: # handle wildcards Chris@87: l = subpackage_name.split('.') Chris@87: if subpackage_path is None and '*' in subpackage_name: Chris@87: return self._wildcard_get_subpackage(subpackage_name, Chris@87: parent_name, Chris@87: caller_level = caller_level+1) Chris@87: assert '*' not in subpackage_name, repr((subpackage_name, subpackage_path, parent_name)) Chris@87: if subpackage_path is None: Chris@87: subpackage_path = njoin([self.local_path] + l) Chris@87: else: Chris@87: subpackage_path = njoin([subpackage_path] + l[:-1]) Chris@87: subpackage_path = self.paths([subpackage_path])[0] Chris@87: setup_py = njoin(subpackage_path, self.setup_name) Chris@87: if not self.options['ignore_setup_xxx_py']: Chris@87: if not os.path.isfile(setup_py): Chris@87: setup_py = njoin(subpackage_path, Chris@87: 'setup_%s.py' % (subpackage_name)) Chris@87: if not os.path.isfile(setup_py): Chris@87: if not self.options['assume_default_configuration']: Chris@87: self.warn('Assuming default configuration '\ Chris@87: '(%s/{setup_%s,setup}.py was not found)' \ Chris@87: % (os.path.dirname(setup_py), subpackage_name)) Chris@87: config = Configuration(subpackage_name, parent_name, Chris@87: self.top_path, subpackage_path, Chris@87: caller_level = caller_level+1) Chris@87: else: Chris@87: config = self._get_configuration_from_setup_py( Chris@87: setup_py, Chris@87: subpackage_name, Chris@87: subpackage_path, Chris@87: parent_name, Chris@87: caller_level = caller_level + 1) Chris@87: if config: Chris@87: return [config] Chris@87: else: Chris@87: return [] Chris@87: Chris@87: def add_subpackage(self,subpackage_name, Chris@87: subpackage_path=None, Chris@87: standalone = False): Chris@87: """Add a sub-package to the current Configuration instance. Chris@87: Chris@87: This is useful in a setup.py script for adding sub-packages to a Chris@87: package. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: subpackage_name : str Chris@87: name of the subpackage Chris@87: subpackage_path : str Chris@87: if given, the subpackage path such as the subpackage is in Chris@87: subpackage_path / subpackage_name. If None,the subpackage is Chris@87: assumed to be located in the local path / subpackage_name. Chris@87: standalone : bool Chris@87: """ Chris@87: Chris@87: if standalone: Chris@87: parent_name = None Chris@87: else: Chris@87: parent_name = self.name Chris@87: config_list = self.get_subpackage(subpackage_name, subpackage_path, Chris@87: parent_name = parent_name, Chris@87: caller_level = 2) Chris@87: if not config_list: Chris@87: self.warn('No configuration returned, assuming unavailable.') Chris@87: for config in config_list: Chris@87: d = config Chris@87: if isinstance(config, Configuration): Chris@87: d = config.todict() Chris@87: assert isinstance(d, dict), repr(type(d)) Chris@87: Chris@87: self.info('Appending %s configuration to %s' \ Chris@87: % (d.get('name'), self.name)) Chris@87: self.dict_append(**d) Chris@87: Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: self.warn('distutils distribution has been initialized,'\ Chris@87: ' it may be too late to add a subpackage '+ subpackage_name) Chris@87: Chris@87: def add_data_dir(self, data_path): Chris@87: """Recursively add files under data_path to data_files list. Chris@87: Chris@87: Recursively add files under data_path to the list of data_files to be Chris@87: installed (and distributed). The data_path can be either a relative Chris@87: path-name, or an absolute path-name, or a 2-tuple where the first Chris@87: argument shows where in the install directory the data directory Chris@87: should be installed to. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: data_path : seq or str Chris@87: Argument can be either Chris@87: Chris@87: * 2-sequence (, ) Chris@87: * path to data directory where python datadir suffix defaults Chris@87: to package dir. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: Rules for installation paths: Chris@87: foo/bar -> (foo/bar, foo/bar) -> parent/foo/bar Chris@87: (gun, foo/bar) -> parent/gun Chris@87: foo/* -> (foo/a, foo/a), (foo/b, foo/b) -> parent/foo/a, parent/foo/b Chris@87: (gun, foo/*) -> (gun, foo/a), (gun, foo/b) -> gun Chris@87: (gun/*, foo/*) -> parent/gun/a, parent/gun/b Chris@87: /foo/bar -> (bar, /foo/bar) -> parent/bar Chris@87: (gun, /foo/bar) -> parent/gun Chris@87: (fun/*/gun/*, sun/foo/bar) -> parent/fun/foo/gun/bar Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: For example suppose the source directory contains fun/foo.dat and Chris@87: fun/bar/car.dat:: Chris@87: Chris@87: >>> self.add_data_dir('fun') #doctest: +SKIP Chris@87: >>> self.add_data_dir(('sun', 'fun')) #doctest: +SKIP Chris@87: >>> self.add_data_dir(('gun', '/full/path/to/fun'))#doctest: +SKIP Chris@87: Chris@87: Will install data-files to the locations:: Chris@87: Chris@87: / Chris@87: fun/ Chris@87: foo.dat Chris@87: bar/ Chris@87: car.dat Chris@87: sun/ Chris@87: foo.dat Chris@87: bar/ Chris@87: car.dat Chris@87: gun/ Chris@87: foo.dat Chris@87: car.dat Chris@87: """ Chris@87: if is_sequence(data_path): Chris@87: d, data_path = data_path Chris@87: else: Chris@87: d = None Chris@87: if is_sequence(data_path): Chris@87: [self.add_data_dir((d, p)) for p in data_path] Chris@87: return Chris@87: if not is_string(data_path): Chris@87: raise TypeError("not a string: %r" % (data_path,)) Chris@87: if d is None: Chris@87: if os.path.isabs(data_path): Chris@87: return self.add_data_dir((os.path.basename(data_path), data_path)) Chris@87: return self.add_data_dir((data_path, data_path)) Chris@87: paths = self.paths(data_path, include_non_existing=False) Chris@87: if is_glob_pattern(data_path): Chris@87: if is_glob_pattern(d): Chris@87: pattern_list = allpath(d).split(os.sep) Chris@87: pattern_list.reverse() Chris@87: # /a/*//b/ -> /a/*/b Chris@87: rl = list(range(len(pattern_list)-1)); rl.reverse() Chris@87: for i in rl: Chris@87: if not pattern_list[i]: Chris@87: del pattern_list[i] Chris@87: # Chris@87: for path in paths: Chris@87: if not os.path.isdir(path): Chris@87: print('Not a directory, skipping', path) Chris@87: continue Chris@87: rpath = rel_path(path, self.local_path) Chris@87: path_list = rpath.split(os.sep) Chris@87: path_list.reverse() Chris@87: target_list = [] Chris@87: i = 0 Chris@87: for s in pattern_list: Chris@87: if is_glob_pattern(s): Chris@87: if i>=len(path_list): Chris@87: raise ValueError('cannot fill pattern %r with %r' \ Chris@87: % (d, path)) Chris@87: target_list.append(path_list[i]) Chris@87: else: Chris@87: assert s==path_list[i], repr((s, path_list[i], data_path, d, path, rpath)) Chris@87: target_list.append(s) Chris@87: i += 1 Chris@87: if path_list[i:]: Chris@87: self.warn('mismatch of pattern_list=%s and path_list=%s'\ Chris@87: % (pattern_list, path_list)) Chris@87: target_list.reverse() Chris@87: self.add_data_dir((os.sep.join(target_list), path)) Chris@87: else: Chris@87: for path in paths: Chris@87: self.add_data_dir((d, path)) Chris@87: return Chris@87: assert not is_glob_pattern(d), repr(d) Chris@87: Chris@87: dist = self.get_distribution() Chris@87: if dist is not None and dist.data_files is not None: Chris@87: data_files = dist.data_files Chris@87: else: Chris@87: data_files = self.data_files Chris@87: Chris@87: for path in paths: Chris@87: for d1, f in list(general_source_directories_files(path)): Chris@87: target_path = os.path.join(self.path_in_package, d, d1) Chris@87: data_files.append((target_path, f)) Chris@87: Chris@87: def _optimize_data_files(self): Chris@87: data_dict = {} Chris@87: for p, files in self.data_files: Chris@87: if p not in data_dict: Chris@87: data_dict[p] = set() Chris@87: for f in files: Chris@87: data_dict[p].add(f) Chris@87: self.data_files[:] = [(p, list(files)) for p, files in data_dict.items()] Chris@87: Chris@87: def add_data_files(self,*files): Chris@87: """Add data files to configuration data_files. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: files : sequence Chris@87: Argument(s) can be either Chris@87: Chris@87: * 2-sequence (,) Chris@87: * paths to data files where python datadir prefix defaults Chris@87: to package dir. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: The form of each element of the files sequence is very flexible Chris@87: allowing many combinations of where to get the files from the package Chris@87: and where they should ultimately be installed on the system. The most Chris@87: basic usage is for an element of the files argument sequence to be a Chris@87: simple filename. This will cause that file from the local path to be Chris@87: installed to the installation path of the self.name package (package Chris@87: path). The file argument can also be a relative path in which case the Chris@87: entire relative path will be installed into the package directory. Chris@87: Finally, the file can be an absolute path name in which case the file Chris@87: will be found at the absolute path name but installed to the package Chris@87: path. Chris@87: Chris@87: This basic behavior can be augmented by passing a 2-tuple in as the Chris@87: file argument. The first element of the tuple should specify the Chris@87: relative path (under the package install directory) where the Chris@87: remaining sequence of files should be installed to (it has nothing to Chris@87: do with the file-names in the source distribution). The second element Chris@87: of the tuple is the sequence of files that should be installed. The Chris@87: files in this sequence can be filenames, relative paths, or absolute Chris@87: paths. For absolute paths the file will be installed in the top-level Chris@87: package installation directory (regardless of the first argument). Chris@87: Filenames and relative path names will be installed in the package Chris@87: install directory under the path name given as the first element of Chris@87: the tuple. Chris@87: Chris@87: Rules for installation paths: Chris@87: Chris@87: #. file.txt -> (., file.txt)-> parent/file.txt Chris@87: #. foo/file.txt -> (foo, foo/file.txt) -> parent/foo/file.txt Chris@87: #. /foo/bar/file.txt -> (., /foo/bar/file.txt) -> parent/file.txt Chris@87: #. *.txt -> parent/a.txt, parent/b.txt Chris@87: #. foo/*.txt -> parent/foo/a.txt, parent/foo/b.txt Chris@87: #. */*.txt -> (*, */*.txt) -> parent/c/a.txt, parent/d/b.txt Chris@87: #. (sun, file.txt) -> parent/sun/file.txt Chris@87: #. (sun, bar/file.txt) -> parent/sun/file.txt Chris@87: #. (sun, /foo/bar/file.txt) -> parent/sun/file.txt Chris@87: #. (sun, *.txt) -> parent/sun/a.txt, parent/sun/b.txt Chris@87: #. (sun, bar/*.txt) -> parent/sun/a.txt, parent/sun/b.txt Chris@87: #. (sun/*, */*.txt) -> parent/sun/c/a.txt, parent/d/b.txt Chris@87: Chris@87: An additional feature is that the path to a data-file can actually be Chris@87: a function that takes no arguments and returns the actual path(s) to Chris@87: the data-files. This is useful when the data files are generated while Chris@87: building the package. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: Add files to the list of data_files to be included with the package. Chris@87: Chris@87: >>> self.add_data_files('foo.dat', Chris@87: ... ('fun', ['gun.dat', 'nun/pun.dat', '/tmp/sun.dat']), Chris@87: ... 'bar/cat.dat', Chris@87: ... '/full/path/to/can.dat') #doctest: +SKIP Chris@87: Chris@87: will install these data files to:: Chris@87: Chris@87: / Chris@87: foo.dat Chris@87: fun/ Chris@87: gun.dat Chris@87: nun/ Chris@87: pun.dat Chris@87: sun.dat Chris@87: bar/ Chris@87: car.dat Chris@87: can.dat Chris@87: Chris@87: where is the package (or sub-package) Chris@87: directory such as '/usr/lib/python2.4/site-packages/mypackage' ('C: Chris@87: \\Python2.4 \\Lib \\site-packages \\mypackage') or Chris@87: '/usr/lib/python2.4/site- packages/mypackage/mysubpackage' ('C: Chris@87: \\Python2.4 \\Lib \\site-packages \\mypackage \\mysubpackage'). Chris@87: """ Chris@87: Chris@87: if len(files)>1: Chris@87: for f in files: Chris@87: self.add_data_files(f) Chris@87: return Chris@87: assert len(files)==1 Chris@87: if is_sequence(files[0]): Chris@87: d, files = files[0] Chris@87: else: Chris@87: d = None Chris@87: if is_string(files): Chris@87: filepat = files Chris@87: elif is_sequence(files): Chris@87: if len(files)==1: Chris@87: filepat = files[0] Chris@87: else: Chris@87: for f in files: Chris@87: self.add_data_files((d, f)) Chris@87: return Chris@87: else: Chris@87: raise TypeError(repr(type(files))) Chris@87: Chris@87: if d is None: Chris@87: if hasattr(filepat, '__call__'): Chris@87: d = '' Chris@87: elif os.path.isabs(filepat): Chris@87: d = '' Chris@87: else: Chris@87: d = os.path.dirname(filepat) Chris@87: self.add_data_files((d, files)) Chris@87: return Chris@87: Chris@87: paths = self.paths(filepat, include_non_existing=False) Chris@87: if is_glob_pattern(filepat): Chris@87: if is_glob_pattern(d): Chris@87: pattern_list = d.split(os.sep) Chris@87: pattern_list.reverse() Chris@87: for path in paths: Chris@87: path_list = path.split(os.sep) Chris@87: path_list.reverse() Chris@87: path_list.pop() # filename Chris@87: target_list = [] Chris@87: i = 0 Chris@87: for s in pattern_list: Chris@87: if is_glob_pattern(s): Chris@87: target_list.append(path_list[i]) Chris@87: i += 1 Chris@87: else: Chris@87: target_list.append(s) Chris@87: target_list.reverse() Chris@87: self.add_data_files((os.sep.join(target_list), path)) Chris@87: else: Chris@87: self.add_data_files((d, paths)) Chris@87: return Chris@87: assert not is_glob_pattern(d), repr((d, filepat)) Chris@87: Chris@87: dist = self.get_distribution() Chris@87: if dist is not None and dist.data_files is not None: Chris@87: data_files = dist.data_files Chris@87: else: Chris@87: data_files = self.data_files Chris@87: Chris@87: data_files.append((os.path.join(self.path_in_package, d), paths)) Chris@87: Chris@87: ### XXX Implement add_py_modules Chris@87: Chris@87: def add_define_macros(self, macros): Chris@87: """Add define macros to configuration Chris@87: Chris@87: Add the given sequence of macro name and value duples to the beginning Chris@87: of the define_macros list This list will be visible to all extension Chris@87: modules of the current package. Chris@87: """ Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: if not hasattr(dist, 'define_macros'): Chris@87: dist.define_macros = [] Chris@87: dist.define_macros.extend(macros) Chris@87: else: Chris@87: self.define_macros.extend(macros) Chris@87: Chris@87: Chris@87: def add_include_dirs(self,*paths): Chris@87: """Add paths to configuration include directories. Chris@87: Chris@87: Add the given sequence of paths to the beginning of the include_dirs Chris@87: list. This list will be visible to all extension modules of the Chris@87: current package. Chris@87: """ Chris@87: include_dirs = self.paths(paths) Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: if dist.include_dirs is None: Chris@87: dist.include_dirs = [] Chris@87: dist.include_dirs.extend(include_dirs) Chris@87: else: Chris@87: self.include_dirs.extend(include_dirs) Chris@87: Chris@87: def add_headers(self,*files): Chris@87: """Add installable headers to configuration. Chris@87: Chris@87: Add the given sequence of files to the beginning of the headers list. Chris@87: By default, headers will be installed under // directory. If an item of files Chris@87: is a tuple, then its first argument specifies the actual installation Chris@87: location relative to the path. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: files : str or seq Chris@87: Argument(s) can be either: Chris@87: Chris@87: * 2-sequence (,) Chris@87: * path(s) to header file(s) where python includedir suffix will Chris@87: default to package name. Chris@87: """ Chris@87: headers = [] Chris@87: for path in files: Chris@87: if is_string(path): Chris@87: [headers.append((self.name, p)) for p in self.paths(path)] Chris@87: else: Chris@87: if not isinstance(path, (tuple, list)) or len(path) != 2: Chris@87: raise TypeError(repr(path)) Chris@87: [headers.append((path[0], p)) for p in self.paths(path[1])] Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: if dist.headers is None: Chris@87: dist.headers = [] Chris@87: dist.headers.extend(headers) Chris@87: else: Chris@87: self.headers.extend(headers) Chris@87: Chris@87: def paths(self,*paths,**kws): Chris@87: """Apply glob to paths and prepend local_path if needed. Chris@87: Chris@87: Applies glob.glob(...) to each path in the sequence (if needed) and Chris@87: pre-pends the local_path if needed. Because this is called on all Chris@87: source lists, this allows wildcard characters to be specified in lists Chris@87: of sources for extension modules and libraries and scripts and allows Chris@87: path-names be relative to the source directory. Chris@87: Chris@87: """ Chris@87: include_non_existing = kws.get('include_non_existing', True) Chris@87: return gpaths(paths, Chris@87: local_path = self.local_path, Chris@87: include_non_existing=include_non_existing) Chris@87: Chris@87: def _fix_paths_dict(self, kw): Chris@87: for k in kw.keys(): Chris@87: v = kw[k] Chris@87: if k in ['sources', 'depends', 'include_dirs', 'library_dirs', Chris@87: 'module_dirs', 'extra_objects']: Chris@87: new_v = self.paths(v) Chris@87: kw[k] = new_v Chris@87: Chris@87: def add_extension(self,name,sources,**kw): Chris@87: """Add extension to configuration. Chris@87: Chris@87: Create and add an Extension instance to the ext_modules list. This Chris@87: method also takes the following optional keyword arguments that are Chris@87: passed on to the Extension constructor. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: name : str Chris@87: name of the extension Chris@87: sources : seq Chris@87: list of the sources. The list of sources may contain functions Chris@87: (called source generators) which must take an extension instance Chris@87: and a build directory as inputs and return a source file or list of Chris@87: source files or None. If None is returned then no sources are Chris@87: generated. If the Extension instance has no sources after Chris@87: processing all source generators, then no extension module is Chris@87: built. Chris@87: include_dirs : Chris@87: define_macros : Chris@87: undef_macros : Chris@87: library_dirs : Chris@87: libraries : Chris@87: runtime_library_dirs : Chris@87: extra_objects : Chris@87: extra_compile_args : Chris@87: extra_link_args : Chris@87: extra_f77_compile_args : Chris@87: extra_f90_compile_args : Chris@87: export_symbols : Chris@87: swig_opts : Chris@87: depends : Chris@87: The depends list contains paths to files or directories that the Chris@87: sources of the extension module depend on. If any path in the Chris@87: depends list is newer than the extension module, then the module Chris@87: will be rebuilt. Chris@87: language : Chris@87: f2py_options : Chris@87: module_dirs : Chris@87: extra_info : dict or list Chris@87: dict or list of dict of keywords to be appended to keywords. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: The self.paths(...) method is applied to all lists that may contain Chris@87: paths. Chris@87: """ Chris@87: ext_args = copy.copy(kw) Chris@87: ext_args['name'] = dot_join(self.name, name) Chris@87: ext_args['sources'] = sources Chris@87: Chris@87: if 'extra_info' in ext_args: Chris@87: extra_info = ext_args['extra_info'] Chris@87: del ext_args['extra_info'] Chris@87: if isinstance(extra_info, dict): Chris@87: extra_info = [extra_info] Chris@87: for info in extra_info: Chris@87: assert isinstance(info, dict), repr(info) Chris@87: dict_append(ext_args,**info) Chris@87: Chris@87: self._fix_paths_dict(ext_args) Chris@87: Chris@87: # Resolve out-of-tree dependencies Chris@87: libraries = ext_args.get('libraries', []) Chris@87: libnames = [] Chris@87: ext_args['libraries'] = [] Chris@87: for libname in libraries: Chris@87: if isinstance(libname, tuple): Chris@87: self._fix_paths_dict(libname[1]) Chris@87: Chris@87: # Handle library names of the form libname@relative/path/to/library Chris@87: if '@' in libname: Chris@87: lname, lpath = libname.split('@', 1) Chris@87: lpath = os.path.abspath(njoin(self.local_path, lpath)) Chris@87: if os.path.isdir(lpath): Chris@87: c = self.get_subpackage(None, lpath, Chris@87: caller_level = 2) Chris@87: if isinstance(c, Configuration): Chris@87: c = c.todict() Chris@87: for l in [l[0] for l in c.get('libraries', [])]: Chris@87: llname = l.split('__OF__', 1)[0] Chris@87: if llname == lname: Chris@87: c.pop('name', None) Chris@87: dict_append(ext_args,**c) Chris@87: break Chris@87: continue Chris@87: libnames.append(libname) Chris@87: Chris@87: ext_args['libraries'] = libnames + ext_args['libraries'] Chris@87: ext_args['define_macros'] = \ Chris@87: self.define_macros + ext_args.get('define_macros', []) Chris@87: Chris@87: from numpy.distutils.core import Extension Chris@87: ext = Extension(**ext_args) Chris@87: self.ext_modules.append(ext) Chris@87: Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: self.warn('distutils distribution has been initialized,'\ Chris@87: ' it may be too late to add an extension '+name) Chris@87: return ext Chris@87: Chris@87: def add_library(self,name,sources,**build_info): Chris@87: """ Chris@87: Add library to configuration. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: name : str Chris@87: Name of the extension. Chris@87: sources : sequence Chris@87: List of the sources. The list of sources may contain functions Chris@87: (called source generators) which must take an extension instance Chris@87: and a build directory as inputs and return a source file or list of Chris@87: source files or None. If None is returned then no sources are Chris@87: generated. If the Extension instance has no sources after Chris@87: processing all source generators, then no extension module is Chris@87: built. Chris@87: build_info : dict, optional Chris@87: The following keys are allowed: Chris@87: Chris@87: * depends Chris@87: * macros Chris@87: * include_dirs Chris@87: * extra_compiler_args Chris@87: * extra_f77_compiler_args Chris@87: * extra_f90_compiler_args Chris@87: * f2py_options Chris@87: * language Chris@87: Chris@87: """ Chris@87: self._add_library(name, sources, None, build_info) Chris@87: Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: self.warn('distutils distribution has been initialized,'\ Chris@87: ' it may be too late to add a library '+ name) Chris@87: Chris@87: def _add_library(self, name, sources, install_dir, build_info): Chris@87: """Common implementation for add_library and add_installed_library. Do Chris@87: not use directly""" Chris@87: build_info = copy.copy(build_info) Chris@87: name = name #+ '__OF__' + self.name Chris@87: build_info['sources'] = sources Chris@87: Chris@87: # Sometimes, depends is not set up to an empty list by default, and if Chris@87: # depends is not given to add_library, distutils barfs (#1134) Chris@87: if not 'depends' in build_info: Chris@87: build_info['depends'] = [] Chris@87: Chris@87: self._fix_paths_dict(build_info) Chris@87: Chris@87: # Add to libraries list so that it is build with build_clib Chris@87: self.libraries.append((name, build_info)) Chris@87: Chris@87: def add_installed_library(self, name, sources, install_dir, build_info=None): Chris@87: """ Chris@87: Similar to add_library, but the specified library is installed. Chris@87: Chris@87: Most C libraries used with `distutils` are only used to build python Chris@87: extensions, but libraries built through this method will be installed Chris@87: so that they can be reused by third-party packages. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: name : str Chris@87: Name of the installed library. Chris@87: sources : sequence Chris@87: List of the library's source files. See `add_library` for details. Chris@87: install_dir : str Chris@87: Path to install the library, relative to the current sub-package. Chris@87: build_info : dict, optional Chris@87: The following keys are allowed: Chris@87: Chris@87: * depends Chris@87: * macros Chris@87: * include_dirs Chris@87: * extra_compiler_args Chris@87: * extra_f77_compiler_args Chris@87: * extra_f90_compiler_args Chris@87: * f2py_options Chris@87: * language Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: None Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: add_library, add_npy_pkg_config, get_info Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: The best way to encode the options required to link against the specified Chris@87: C libraries is to use a "libname.ini" file, and use `get_info` to Chris@87: retrieve the required options (see `add_npy_pkg_config` for more Chris@87: information). Chris@87: Chris@87: """ Chris@87: if not build_info: Chris@87: build_info = {} Chris@87: Chris@87: install_dir = os.path.join(self.package_path, install_dir) Chris@87: self._add_library(name, sources, install_dir, build_info) Chris@87: self.installed_libraries.append(InstallableLib(name, build_info, install_dir)) Chris@87: Chris@87: def add_npy_pkg_config(self, template, install_dir, subst_dict=None): Chris@87: """ Chris@87: Generate and install a npy-pkg config file from a template. Chris@87: Chris@87: The config file generated from `template` is installed in the Chris@87: given install directory, using `subst_dict` for variable substitution. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: template : str Chris@87: The path of the template, relatively to the current package path. Chris@87: install_dir : str Chris@87: Where to install the npy-pkg config file, relatively to the current Chris@87: package path. Chris@87: subst_dict : dict, optional Chris@87: If given, any string of the form ``@key@`` will be replaced by Chris@87: ``subst_dict[key]`` in the template file when installed. The install Chris@87: prefix is always available through the variable ``@prefix@``, since the Chris@87: install prefix is not easy to get reliably from setup.py. Chris@87: Chris@87: See also Chris@87: -------- Chris@87: add_installed_library, get_info Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: This works for both standard installs and in-place builds, i.e. the Chris@87: ``@prefix@`` refer to the source directory for in-place builds. Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: :: Chris@87: Chris@87: config.add_npy_pkg_config('foo.ini.in', 'lib', {'foo': bar}) Chris@87: Chris@87: Assuming the foo.ini.in file has the following content:: Chris@87: Chris@87: [meta] Chris@87: Name=@foo@ Chris@87: Version=1.0 Chris@87: Description=dummy description Chris@87: Chris@87: [default] Chris@87: Cflags=-I@prefix@/include Chris@87: Libs= Chris@87: Chris@87: The generated file will have the following content:: Chris@87: Chris@87: [meta] Chris@87: Name=bar Chris@87: Version=1.0 Chris@87: Description=dummy description Chris@87: Chris@87: [default] Chris@87: Cflags=-Iprefix_dir/include Chris@87: Libs= Chris@87: Chris@87: and will be installed as foo.ini in the 'lib' subpath. Chris@87: Chris@87: """ Chris@87: if subst_dict is None: Chris@87: subst_dict = {} Chris@87: basename = os.path.splitext(template)[0] Chris@87: template = os.path.join(self.package_path, template) Chris@87: Chris@87: if self.name in self.installed_pkg_config: Chris@87: self.installed_pkg_config[self.name].append((template, install_dir, Chris@87: subst_dict)) Chris@87: else: Chris@87: self.installed_pkg_config[self.name] = [(template, install_dir, Chris@87: subst_dict)] Chris@87: Chris@87: Chris@87: def add_scripts(self,*files): Chris@87: """Add scripts to configuration. Chris@87: Chris@87: Add the sequence of files to the beginning of the scripts list. Chris@87: Scripts will be installed under the /bin/ directory. Chris@87: Chris@87: """ Chris@87: scripts = self.paths(files) Chris@87: dist = self.get_distribution() Chris@87: if dist is not None: Chris@87: if dist.scripts is None: Chris@87: dist.scripts = [] Chris@87: dist.scripts.extend(scripts) Chris@87: else: Chris@87: self.scripts.extend(scripts) Chris@87: Chris@87: def dict_append(self,**dict): Chris@87: for key in self.list_keys: Chris@87: a = getattr(self, key) Chris@87: a.extend(dict.get(key, [])) Chris@87: for key in self.dict_keys: Chris@87: a = getattr(self, key) Chris@87: a.update(dict.get(key, {})) Chris@87: known_keys = self.list_keys + self.dict_keys + self.extra_keys Chris@87: for key in dict.keys(): Chris@87: if key not in known_keys: Chris@87: a = getattr(self, key, None) Chris@87: if a and a==dict[key]: continue Chris@87: self.warn('Inheriting attribute %r=%r from %r' \ Chris@87: % (key, dict[key], dict.get('name', '?'))) Chris@87: setattr(self, key, dict[key]) Chris@87: self.extra_keys.append(key) Chris@87: elif key in self.extra_keys: Chris@87: self.info('Ignoring attempt to set %r (from %r to %r)' \ Chris@87: % (key, getattr(self, key), dict[key])) Chris@87: elif key in known_keys: Chris@87: # key is already processed above Chris@87: pass Chris@87: else: Chris@87: raise ValueError("Don't know about key=%r" % (key)) Chris@87: Chris@87: def __str__(self): Chris@87: from pprint import pformat Chris@87: known_keys = self.list_keys + self.dict_keys + self.extra_keys Chris@87: s = '<'+5*'-' + '\n' Chris@87: s += 'Configuration of '+self.name+':\n' Chris@87: known_keys.sort() Chris@87: for k in known_keys: Chris@87: a = getattr(self, k, None) Chris@87: if a: Chris@87: s += '%s = %s\n' % (k, pformat(a)) Chris@87: s += 5*'-' + '>' Chris@87: return s Chris@87: Chris@87: def get_config_cmd(self): Chris@87: """ Chris@87: Returns the numpy.distutils config command instance. Chris@87: """ Chris@87: cmd = get_cmd('config') Chris@87: cmd.ensure_finalized() Chris@87: cmd.dump_source = 0 Chris@87: cmd.noisy = 0 Chris@87: old_path = os.environ.get('PATH') Chris@87: if old_path: Chris@87: path = os.pathsep.join(['.', old_path]) Chris@87: os.environ['PATH'] = path Chris@87: return cmd Chris@87: Chris@87: def get_build_temp_dir(self): Chris@87: """ Chris@87: Return a path to a temporary directory where temporary files should be Chris@87: placed. Chris@87: """ Chris@87: cmd = get_cmd('build') Chris@87: cmd.ensure_finalized() Chris@87: return cmd.build_temp Chris@87: Chris@87: def have_f77c(self): Chris@87: """Check for availability of Fortran 77 compiler. Chris@87: Chris@87: Use it inside source generating function to ensure that Chris@87: setup distribution instance has been initialized. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: True if a Fortran 77 compiler is available (because a simple Fortran 77 Chris@87: code was able to be compiled successfully). Chris@87: """ Chris@87: simple_fortran_subroutine = ''' Chris@87: subroutine simple Chris@87: end Chris@87: ''' Chris@87: config_cmd = self.get_config_cmd() Chris@87: flag = config_cmd.try_compile(simple_fortran_subroutine, lang='f77') Chris@87: return flag Chris@87: Chris@87: def have_f90c(self): Chris@87: """Check for availability of Fortran 90 compiler. Chris@87: Chris@87: Use it inside source generating function to ensure that Chris@87: setup distribution instance has been initialized. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: True if a Fortran 90 compiler is available (because a simple Fortran Chris@87: 90 code was able to be compiled successfully) Chris@87: """ Chris@87: simple_fortran_subroutine = ''' Chris@87: subroutine simple Chris@87: end Chris@87: ''' Chris@87: config_cmd = self.get_config_cmd() Chris@87: flag = config_cmd.try_compile(simple_fortran_subroutine, lang='f90') Chris@87: return flag Chris@87: Chris@87: def append_to(self, extlib): Chris@87: """Append libraries, include_dirs to extension or library item. Chris@87: """ Chris@87: if is_sequence(extlib): Chris@87: lib_name, build_info = extlib Chris@87: dict_append(build_info, Chris@87: libraries=self.libraries, Chris@87: include_dirs=self.include_dirs) Chris@87: else: Chris@87: from numpy.distutils.core import Extension Chris@87: assert isinstance(extlib, Extension), repr(extlib) Chris@87: extlib.libraries.extend(self.libraries) Chris@87: extlib.include_dirs.extend(self.include_dirs) Chris@87: Chris@87: def _get_svn_revision(self, path): Chris@87: """Return path's SVN revision number. Chris@87: """ Chris@87: revision = None Chris@87: m = None Chris@87: cwd = os.getcwd() Chris@87: try: Chris@87: os.chdir(path or '.') Chris@87: p = subprocess.Popen(['svnversion'], shell=True, Chris@87: stdout=subprocess.PIPE, stderr=None, Chris@87: close_fds=True) Chris@87: sout = p.stdout Chris@87: m = re.match(r'(?P\d+)', sout.read()) Chris@87: except: Chris@87: pass Chris@87: os.chdir(cwd) Chris@87: if m: Chris@87: revision = int(m.group('revision')) Chris@87: return revision Chris@87: if sys.platform=='win32' and os.environ.get('SVN_ASP_DOT_NET_HACK', None): Chris@87: entries = njoin(path, '_svn', 'entries') Chris@87: else: Chris@87: entries = njoin(path, '.svn', 'entries') Chris@87: if os.path.isfile(entries): Chris@87: f = open(entries) Chris@87: fstr = f.read() Chris@87: f.close() Chris@87: if fstr[:5] == '\d+)"', fstr) Chris@87: if m: Chris@87: revision = int(m.group('revision')) Chris@87: else: # non-xml entries file --- check to be sure that Chris@87: m = re.search(r'dir[\n\r]+(?P\d+)', fstr) Chris@87: if m: Chris@87: revision = int(m.group('revision')) Chris@87: return revision Chris@87: Chris@87: def _get_hg_revision(self, path): Chris@87: """Return path's Mercurial revision number. Chris@87: """ Chris@87: revision = None Chris@87: m = None Chris@87: cwd = os.getcwd() Chris@87: try: Chris@87: os.chdir(path or '.') Chris@87: p = subprocess.Popen(['hg identify --num'], shell=True, Chris@87: stdout=subprocess.PIPE, stderr=None, Chris@87: close_fds=True) Chris@87: sout = p.stdout Chris@87: m = re.match(r'(?P\d+)', sout.read()) Chris@87: except: Chris@87: pass Chris@87: os.chdir(cwd) Chris@87: if m: Chris@87: revision = int(m.group('revision')) Chris@87: return revision Chris@87: branch_fn = njoin(path, '.hg', 'branch') Chris@87: branch_cache_fn = njoin(path, '.hg', 'branch.cache') Chris@87: Chris@87: if os.path.isfile(branch_fn): Chris@87: branch0 = None Chris@87: f = open(branch_fn) Chris@87: revision0 = f.read().strip() Chris@87: f.close() Chris@87: Chris@87: branch_map = {} Chris@87: for line in file(branch_cache_fn, 'r'): Chris@87: branch1, revision1 = line.split()[:2] Chris@87: if revision1==revision0: Chris@87: branch0 = branch1 Chris@87: try: Chris@87: revision1 = int(revision1) Chris@87: except ValueError: Chris@87: continue Chris@87: branch_map[branch1] = revision1 Chris@87: Chris@87: revision = branch_map.get(branch0) Chris@87: return revision Chris@87: Chris@87: Chris@87: def get_version(self, version_file=None, version_variable=None): Chris@87: """Try to get version string of a package. Chris@87: Chris@87: Return a version string of the current package or None if the version Chris@87: information could not be detected. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: This method scans files named Chris@87: __version__.py, _version.py, version.py, and Chris@87: __svn_version__.py for string variables version, __version\__, and Chris@87: _version, until a version number is found. Chris@87: """ Chris@87: version = getattr(self, 'version', None) Chris@87: if version is not None: Chris@87: return version Chris@87: Chris@87: # Get version from version file. Chris@87: if version_file is None: Chris@87: files = ['__version__.py', Chris@87: self.name.split('.')[-1]+'_version.py', Chris@87: 'version.py', Chris@87: '__svn_version__.py', Chris@87: '__hg_version__.py'] Chris@87: else: Chris@87: files = [version_file] Chris@87: if version_variable is None: Chris@87: version_vars = ['version', Chris@87: '__version__', Chris@87: self.name.split('.')[-1]+'_version'] Chris@87: else: Chris@87: version_vars = [version_variable] Chris@87: for f in files: Chris@87: fn = njoin(self.local_path, f) Chris@87: if os.path.isfile(fn): Chris@87: info = (open(fn), fn, ('.py', 'U', 1)) Chris@87: name = os.path.splitext(os.path.basename(fn))[0] Chris@87: n = dot_join(self.name, name) Chris@87: try: Chris@87: version_module = imp.load_module('_'.join(n.split('.')),*info) Chris@87: except ImportError: Chris@87: msg = get_exception() Chris@87: self.warn(str(msg)) Chris@87: version_module = None Chris@87: if version_module is None: Chris@87: continue Chris@87: Chris@87: for a in version_vars: Chris@87: version = getattr(version_module, a, None) Chris@87: if version is not None: Chris@87: break Chris@87: if version is not None: Chris@87: break Chris@87: Chris@87: if version is not None: Chris@87: self.version = version Chris@87: return version Chris@87: Chris@87: # Get version as SVN or Mercurial revision number Chris@87: revision = self._get_svn_revision(self.local_path) Chris@87: if revision is None: Chris@87: revision = self._get_hg_revision(self.local_path) Chris@87: Chris@87: if revision is not None: Chris@87: version = str(revision) Chris@87: self.version = version Chris@87: Chris@87: return version Chris@87: Chris@87: def make_svn_version_py(self, delete=True): Chris@87: """Appends a data function to the data_files list that will generate Chris@87: __svn_version__.py file to the current package directory. Chris@87: Chris@87: Generate package __svn_version__.py file from SVN revision number, Chris@87: it will be removed after python exits but will be available Chris@87: when sdist, etc commands are executed. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: If __svn_version__.py existed before, nothing is done. Chris@87: Chris@87: This is Chris@87: intended for working with source directories that are in an SVN Chris@87: repository. Chris@87: """ Chris@87: target = njoin(self.local_path, '__svn_version__.py') Chris@87: revision = self._get_svn_revision(self.local_path) Chris@87: if os.path.isfile(target) or revision is None: Chris@87: return Chris@87: else: Chris@87: def generate_svn_version_py(): Chris@87: if not os.path.isfile(target): Chris@87: version = str(revision) Chris@87: self.info('Creating %s (version=%r)' % (target, version)) Chris@87: f = open(target, 'w') Chris@87: f.write('version = %r\n' % (version)) Chris@87: f.close() Chris@87: Chris@87: import atexit Chris@87: def rm_file(f=target,p=self.info): Chris@87: if delete: Chris@87: try: os.remove(f); p('removed '+f) Chris@87: except OSError: pass Chris@87: try: os.remove(f+'c'); p('removed '+f+'c') Chris@87: except OSError: pass Chris@87: Chris@87: atexit.register(rm_file) Chris@87: Chris@87: return target Chris@87: Chris@87: self.add_data_files(('', generate_svn_version_py())) Chris@87: Chris@87: def make_hg_version_py(self, delete=True): Chris@87: """Appends a data function to the data_files list that will generate Chris@87: __hg_version__.py file to the current package directory. Chris@87: Chris@87: Generate package __hg_version__.py file from Mercurial revision, Chris@87: it will be removed after python exits but will be available Chris@87: when sdist, etc commands are executed. Chris@87: Chris@87: Notes Chris@87: ----- Chris@87: If __hg_version__.py existed before, nothing is done. Chris@87: Chris@87: This is intended for working with source directories that are Chris@87: in an Mercurial repository. Chris@87: """ Chris@87: target = njoin(self.local_path, '__hg_version__.py') Chris@87: revision = self._get_hg_revision(self.local_path) Chris@87: if os.path.isfile(target) or revision is None: Chris@87: return Chris@87: else: Chris@87: def generate_hg_version_py(): Chris@87: if not os.path.isfile(target): Chris@87: version = str(revision) Chris@87: self.info('Creating %s (version=%r)' % (target, version)) Chris@87: f = open(target, 'w') Chris@87: f.write('version = %r\n' % (version)) Chris@87: f.close() Chris@87: Chris@87: import atexit Chris@87: def rm_file(f=target,p=self.info): Chris@87: if delete: Chris@87: try: os.remove(f); p('removed '+f) Chris@87: except OSError: pass Chris@87: try: os.remove(f+'c'); p('removed '+f+'c') Chris@87: except OSError: pass Chris@87: Chris@87: atexit.register(rm_file) Chris@87: Chris@87: return target Chris@87: Chris@87: self.add_data_files(('', generate_hg_version_py())) Chris@87: Chris@87: def make_config_py(self,name='__config__'): Chris@87: """Generate package __config__.py file containing system_info Chris@87: information used during building the package. Chris@87: Chris@87: This file is installed to the Chris@87: package installation directory. Chris@87: Chris@87: """ Chris@87: self.py_modules.append((self.name, name, generate_config_py)) Chris@87: Chris@87: Chris@87: def get_info(self,*names): Chris@87: """Get resources information. Chris@87: Chris@87: Return information (from system_info.get_info) for all of the names in Chris@87: the argument list in a single dictionary. Chris@87: """ Chris@87: from .system_info import get_info, dict_append Chris@87: info_dict = {} Chris@87: for a in names: Chris@87: dict_append(info_dict,**get_info(a)) Chris@87: return info_dict Chris@87: Chris@87: Chris@87: def get_cmd(cmdname, _cache={}): Chris@87: if cmdname not in _cache: Chris@87: import distutils.core Chris@87: dist = distutils.core._setup_distribution Chris@87: if dist is None: Chris@87: from distutils.errors import DistutilsInternalError Chris@87: raise DistutilsInternalError( Chris@87: 'setup distribution instance not initialized') Chris@87: cmd = dist.get_command_obj(cmdname) Chris@87: _cache[cmdname] = cmd Chris@87: return _cache[cmdname] Chris@87: Chris@87: def get_numpy_include_dirs(): Chris@87: # numpy_include_dirs are set by numpy/core/setup.py, otherwise [] Chris@87: include_dirs = Configuration.numpy_include_dirs[:] Chris@87: if not include_dirs: Chris@87: import numpy Chris@87: include_dirs = [ numpy.get_include() ] Chris@87: # else running numpy/core/setup.py Chris@87: return include_dirs Chris@87: Chris@87: def get_npy_pkg_dir(): Chris@87: """Return the path where to find the npy-pkg-config directory.""" Chris@87: # XXX: import here for bootstrapping reasons Chris@87: import numpy Chris@87: d = os.path.join(os.path.dirname(numpy.__file__), Chris@87: 'core', 'lib', 'npy-pkg-config') Chris@87: return d Chris@87: Chris@87: def get_pkg_info(pkgname, dirs=None): Chris@87: """ Chris@87: Return library info for the given package. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: pkgname : str Chris@87: Name of the package (should match the name of the .ini file, without Chris@87: the extension, e.g. foo for the file foo.ini). Chris@87: dirs : sequence, optional Chris@87: If given, should be a sequence of additional directories where to look Chris@87: for npy-pkg-config files. Those directories are searched prior to the Chris@87: NumPy directory. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: pkginfo : class instance Chris@87: The `LibraryInfo` instance containing the build information. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: PkgNotFound Chris@87: If the package is not found. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: Configuration.add_npy_pkg_config, Configuration.add_installed_library, Chris@87: get_info Chris@87: Chris@87: """ Chris@87: from numpy.distutils.npy_pkg_config import read_config Chris@87: Chris@87: if dirs: Chris@87: dirs.append(get_npy_pkg_dir()) Chris@87: else: Chris@87: dirs = [get_npy_pkg_dir()] Chris@87: return read_config(pkgname, dirs) Chris@87: Chris@87: def get_info(pkgname, dirs=None): Chris@87: """ Chris@87: Return an info dict for a given C library. Chris@87: Chris@87: The info dict contains the necessary options to use the C library. Chris@87: Chris@87: Parameters Chris@87: ---------- Chris@87: pkgname : str Chris@87: Name of the package (should match the name of the .ini file, without Chris@87: the extension, e.g. foo for the file foo.ini). Chris@87: dirs : sequence, optional Chris@87: If given, should be a sequence of additional directories where to look Chris@87: for npy-pkg-config files. Those directories are searched prior to the Chris@87: NumPy directory. Chris@87: Chris@87: Returns Chris@87: ------- Chris@87: info : dict Chris@87: The dictionary with build information. Chris@87: Chris@87: Raises Chris@87: ------ Chris@87: PkgNotFound Chris@87: If the package is not found. Chris@87: Chris@87: See Also Chris@87: -------- Chris@87: Configuration.add_npy_pkg_config, Configuration.add_installed_library, Chris@87: get_pkg_info Chris@87: Chris@87: Examples Chris@87: -------- Chris@87: To get the necessary information for the npymath library from NumPy: Chris@87: Chris@87: >>> npymath_info = np.distutils.misc_util.get_info('npymath') Chris@87: >>> npymath_info #doctest: +SKIP Chris@87: {'define_macros': [], 'libraries': ['npymath'], 'library_dirs': Chris@87: ['.../numpy/core/lib'], 'include_dirs': ['.../numpy/core/include']} Chris@87: Chris@87: This info dict can then be used as input to a `Configuration` instance:: Chris@87: Chris@87: config.add_extension('foo', sources=['foo.c'], extra_info=npymath_info) Chris@87: Chris@87: """ Chris@87: from numpy.distutils.npy_pkg_config import parse_flags Chris@87: pkg_info = get_pkg_info(pkgname, dirs) Chris@87: Chris@87: # Translate LibraryInfo instance into a build_info dict Chris@87: info = parse_flags(pkg_info.cflags()) Chris@87: for k, v in parse_flags(pkg_info.libs()).items(): Chris@87: info[k].extend(v) Chris@87: Chris@87: # add_extension extra_info argument is ANAL Chris@87: info['define_macros'] = info['macros'] Chris@87: del info['macros'] Chris@87: del info['ignored'] Chris@87: Chris@87: return info Chris@87: Chris@87: def is_bootstrapping(): Chris@87: if sys.version_info[0] >= 3: Chris@87: import builtins Chris@87: else: Chris@87: import __builtin__ as builtins Chris@87: Chris@87: try: Chris@87: builtins.__NUMPY_SETUP__ Chris@87: return True Chris@87: except AttributeError: Chris@87: return False Chris@87: __NUMPY_SETUP__ = False Chris@87: Chris@87: Chris@87: ######################### Chris@87: Chris@87: def default_config_dict(name = None, parent_name = None, local_path=None): Chris@87: """Return a configuration dictionary for usage in Chris@87: configuration() function defined in file setup_.py. Chris@87: """ Chris@87: import warnings Chris@87: warnings.warn('Use Configuration(%r,%r,top_path=%r) instead of '\ Chris@87: 'deprecated default_config_dict(%r,%r,%r)' Chris@87: % (name, parent_name, local_path, Chris@87: name, parent_name, local_path, Chris@87: )) Chris@87: c = Configuration(name, parent_name, local_path) Chris@87: return c.todict() Chris@87: Chris@87: Chris@87: def dict_append(d, **kws): Chris@87: for k, v in kws.items(): Chris@87: if k in d: Chris@87: ov = d[k] Chris@87: if isinstance(ov, str): Chris@87: d[k] = v Chris@87: else: Chris@87: d[k].extend(v) Chris@87: else: Chris@87: d[k] = v Chris@87: Chris@87: def appendpath(prefix, path): Chris@87: if os.path.sep != '/': Chris@87: prefix = prefix.replace('/', os.path.sep) Chris@87: path = path.replace('/', os.path.sep) Chris@87: drive = '' Chris@87: if os.path.isabs(path): Chris@87: drive = os.path.splitdrive(prefix)[0] Chris@87: absprefix = os.path.splitdrive(os.path.abspath(prefix))[1] Chris@87: pathdrive, path = os.path.splitdrive(path) Chris@87: d = os.path.commonprefix([absprefix, path]) Chris@87: if os.path.join(absprefix[:len(d)], absprefix[len(d):]) != absprefix \ Chris@87: or os.path.join(path[:len(d)], path[len(d):]) != path: Chris@87: # Handle invalid paths Chris@87: d = os.path.dirname(d) Chris@87: subpath = path[len(d):] Chris@87: if os.path.isabs(subpath): Chris@87: subpath = subpath[1:] Chris@87: else: Chris@87: subpath = path Chris@87: return os.path.normpath(njoin(drive + prefix, subpath)) Chris@87: Chris@87: def generate_config_py(target): Chris@87: """Generate config.py file containing system_info information Chris@87: used during building the package. Chris@87: Chris@87: Usage: Chris@87: config['py_modules'].append((packagename, '__config__',generate_config_py)) Chris@87: """ Chris@87: from numpy.distutils.system_info import system_info Chris@87: from distutils.dir_util import mkpath Chris@87: mkpath(os.path.dirname(target)) Chris@87: f = open(target, 'w') Chris@87: f.write('# This file is generated by %s\n' % (os.path.abspath(sys.argv[0]))) Chris@87: f.write('# It contains system_info results at the time of building this package.\n') Chris@87: f.write('__all__ = ["get_info","show"]\n\n') Chris@87: for k, i in system_info.saved_results.items(): Chris@87: f.write('%s=%r\n' % (k, i)) Chris@87: f.write(r''' Chris@87: def get_info(name): Chris@87: g = globals() Chris@87: return g.get(name, g.get(name + "_info", {})) Chris@87: Chris@87: def show(): Chris@87: for name,info_dict in globals().items(): Chris@87: if name[0] == "_" or type(info_dict) is not type({}): continue Chris@87: print(name + ":") Chris@87: if not info_dict: Chris@87: print(" NOT AVAILABLE") Chris@87: for k,v in info_dict.items(): Chris@87: v = str(v) Chris@87: if k == "sources" and len(v) > 200: Chris@87: v = v[:60] + " ...\n... " + v[-60:] Chris@87: print(" %s = %s" % (k,v)) Chris@87: ''') Chris@87: Chris@87: f.close() Chris@87: return target Chris@87: Chris@87: def msvc_version(compiler): Chris@87: """Return version major and minor of compiler instance if it is Chris@87: MSVC, raise an exception otherwise.""" Chris@87: if not compiler.compiler_type == "msvc": Chris@87: raise ValueError("Compiler instance is not msvc (%s)"\ Chris@87: % compiler.compiler_type) Chris@87: return compiler._MSVCCompiler__version Chris@87: Chris@87: if sys.version[:3] >= '2.5': Chris@87: def get_build_architecture(): Chris@87: from distutils.msvccompiler import get_build_architecture Chris@87: return get_build_architecture() Chris@87: else: Chris@87: #copied from python 2.5.1 distutils/msvccompiler.py Chris@87: def get_build_architecture(): Chris@87: """Return the processor architecture. Chris@87: Chris@87: Possible results are "Intel", "Itanium", or "AMD64". Chris@87: """ Chris@87: prefix = " bit (" Chris@87: i = sys.version.find(prefix) Chris@87: if i == -1: Chris@87: return "Intel" Chris@87: j = sys.version.find(")", i) Chris@87: return sys.version[i+len(prefix):j]