annotate DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/distutils/npy_pkg_config.py @ 133:4acb5d8d80b6 tip

Don't fail environmental check if README.md exists (but .txt and no-suffix don't)
author Chris Cannam
date Tue, 30 Jul 2019 12:25:44 +0100
parents 2a2c65a20a8b
children
rev   line source
Chris@87 1 from __future__ import division, absolute_import, print_function
Chris@87 2
Chris@87 3 import sys
Chris@87 4 import re
Chris@87 5 import os
Chris@87 6 import shlex
Chris@87 7
Chris@87 8 if sys.version_info[0] < 3:
Chris@87 9 from ConfigParser import SafeConfigParser, NoOptionError
Chris@87 10 else:
Chris@87 11 from configparser import ConfigParser, SafeConfigParser, NoOptionError
Chris@87 12
Chris@87 13 __all__ = ['FormatError', 'PkgNotFound', 'LibraryInfo', 'VariableSet',
Chris@87 14 'read_config', 'parse_flags']
Chris@87 15
Chris@87 16 _VAR = re.compile('\$\{([a-zA-Z0-9_-]+)\}')
Chris@87 17
Chris@87 18 class FormatError(IOError):
Chris@87 19 """
Chris@87 20 Exception thrown when there is a problem parsing a configuration file.
Chris@87 21
Chris@87 22 """
Chris@87 23 def __init__(self, msg):
Chris@87 24 self.msg = msg
Chris@87 25
Chris@87 26 def __str__(self):
Chris@87 27 return self.msg
Chris@87 28
Chris@87 29 class PkgNotFound(IOError):
Chris@87 30 """Exception raised when a package can not be located."""
Chris@87 31 def __init__(self, msg):
Chris@87 32 self.msg = msg
Chris@87 33
Chris@87 34 def __str__(self):
Chris@87 35 return self.msg
Chris@87 36
Chris@87 37 def parse_flags(line):
Chris@87 38 """
Chris@87 39 Parse a line from a config file containing compile flags.
Chris@87 40
Chris@87 41 Parameters
Chris@87 42 ----------
Chris@87 43 line : str
Chris@87 44 A single line containing one or more compile flags.
Chris@87 45
Chris@87 46 Returns
Chris@87 47 -------
Chris@87 48 d : dict
Chris@87 49 Dictionary of parsed flags, split into relevant categories.
Chris@87 50 These categories are the keys of `d`:
Chris@87 51
Chris@87 52 * 'include_dirs'
Chris@87 53 * 'library_dirs'
Chris@87 54 * 'libraries'
Chris@87 55 * 'macros'
Chris@87 56 * 'ignored'
Chris@87 57
Chris@87 58 """
Chris@87 59 lexer = shlex.shlex(line)
Chris@87 60 lexer.whitespace_split = True
Chris@87 61
Chris@87 62 d = {'include_dirs': [], 'library_dirs': [], 'libraries': [],
Chris@87 63 'macros': [], 'ignored': []}
Chris@87 64 def next_token(t):
Chris@87 65 if t.startswith('-I'):
Chris@87 66 if len(t) > 2:
Chris@87 67 d['include_dirs'].append(t[2:])
Chris@87 68 else:
Chris@87 69 t = lexer.get_token()
Chris@87 70 d['include_dirs'].append(t)
Chris@87 71 elif t.startswith('-L'):
Chris@87 72 if len(t) > 2:
Chris@87 73 d['library_dirs'].append(t[2:])
Chris@87 74 else:
Chris@87 75 t = lexer.get_token()
Chris@87 76 d['library_dirs'].append(t)
Chris@87 77 elif t.startswith('-l'):
Chris@87 78 d['libraries'].append(t[2:])
Chris@87 79 elif t.startswith('-D'):
Chris@87 80 d['macros'].append(t[2:])
Chris@87 81 else:
Chris@87 82 d['ignored'].append(t)
Chris@87 83 return lexer.get_token()
Chris@87 84
Chris@87 85 t = lexer.get_token()
Chris@87 86 while t:
Chris@87 87 t = next_token(t)
Chris@87 88
Chris@87 89 return d
Chris@87 90
Chris@87 91 def _escape_backslash(val):
Chris@87 92 return val.replace('\\', '\\\\')
Chris@87 93
Chris@87 94 class LibraryInfo(object):
Chris@87 95 """
Chris@87 96 Object containing build information about a library.
Chris@87 97
Chris@87 98 Parameters
Chris@87 99 ----------
Chris@87 100 name : str
Chris@87 101 The library name.
Chris@87 102 description : str
Chris@87 103 Description of the library.
Chris@87 104 version : str
Chris@87 105 Version string.
Chris@87 106 sections : dict
Chris@87 107 The sections of the configuration file for the library. The keys are
Chris@87 108 the section headers, the values the text under each header.
Chris@87 109 vars : class instance
Chris@87 110 A `VariableSet` instance, which contains ``(name, value)`` pairs for
Chris@87 111 variables defined in the configuration file for the library.
Chris@87 112 requires : sequence, optional
Chris@87 113 The required libraries for the library to be installed.
Chris@87 114
Chris@87 115 Notes
Chris@87 116 -----
Chris@87 117 All input parameters (except "sections" which is a method) are available as
Chris@87 118 attributes of the same name.
Chris@87 119
Chris@87 120 """
Chris@87 121 def __init__(self, name, description, version, sections, vars, requires=None):
Chris@87 122 self.name = name
Chris@87 123 self.description = description
Chris@87 124 if requires:
Chris@87 125 self.requires = requires
Chris@87 126 else:
Chris@87 127 self.requires = []
Chris@87 128 self.version = version
Chris@87 129 self._sections = sections
Chris@87 130 self.vars = vars
Chris@87 131
Chris@87 132 def sections(self):
Chris@87 133 """
Chris@87 134 Return the section headers of the config file.
Chris@87 135
Chris@87 136 Parameters
Chris@87 137 ----------
Chris@87 138 None
Chris@87 139
Chris@87 140 Returns
Chris@87 141 -------
Chris@87 142 keys : list of str
Chris@87 143 The list of section headers.
Chris@87 144
Chris@87 145 """
Chris@87 146 return list(self._sections.keys())
Chris@87 147
Chris@87 148 def cflags(self, section="default"):
Chris@87 149 val = self.vars.interpolate(self._sections[section]['cflags'])
Chris@87 150 return _escape_backslash(val)
Chris@87 151
Chris@87 152 def libs(self, section="default"):
Chris@87 153 val = self.vars.interpolate(self._sections[section]['libs'])
Chris@87 154 return _escape_backslash(val)
Chris@87 155
Chris@87 156 def __str__(self):
Chris@87 157 m = ['Name: %s' % self.name]
Chris@87 158 m.append('Description: %s' % self.description)
Chris@87 159 if self.requires:
Chris@87 160 m.append('Requires:')
Chris@87 161 else:
Chris@87 162 m.append('Requires: %s' % ",".join(self.requires))
Chris@87 163 m.append('Version: %s' % self.version)
Chris@87 164
Chris@87 165 return "\n".join(m)
Chris@87 166
Chris@87 167 class VariableSet(object):
Chris@87 168 """
Chris@87 169 Container object for the variables defined in a config file.
Chris@87 170
Chris@87 171 `VariableSet` can be used as a plain dictionary, with the variable names
Chris@87 172 as keys.
Chris@87 173
Chris@87 174 Parameters
Chris@87 175 ----------
Chris@87 176 d : dict
Chris@87 177 Dict of items in the "variables" section of the configuration file.
Chris@87 178
Chris@87 179 """
Chris@87 180 def __init__(self, d):
Chris@87 181 self._raw_data = dict([(k, v) for k, v in d.items()])
Chris@87 182
Chris@87 183 self._re = {}
Chris@87 184 self._re_sub = {}
Chris@87 185
Chris@87 186 self._init_parse()
Chris@87 187
Chris@87 188 def _init_parse(self):
Chris@87 189 for k, v in self._raw_data.items():
Chris@87 190 self._init_parse_var(k, v)
Chris@87 191
Chris@87 192 def _init_parse_var(self, name, value):
Chris@87 193 self._re[name] = re.compile(r'\$\{%s\}' % name)
Chris@87 194 self._re_sub[name] = value
Chris@87 195
Chris@87 196 def interpolate(self, value):
Chris@87 197 # Brute force: we keep interpolating until there is no '${var}' anymore
Chris@87 198 # or until interpolated string is equal to input string
Chris@87 199 def _interpolate(value):
Chris@87 200 for k in self._re.keys():
Chris@87 201 value = self._re[k].sub(self._re_sub[k], value)
Chris@87 202 return value
Chris@87 203 while _VAR.search(value):
Chris@87 204 nvalue = _interpolate(value)
Chris@87 205 if nvalue == value:
Chris@87 206 break
Chris@87 207 value = nvalue
Chris@87 208
Chris@87 209 return value
Chris@87 210
Chris@87 211 def variables(self):
Chris@87 212 """
Chris@87 213 Return the list of variable names.
Chris@87 214
Chris@87 215 Parameters
Chris@87 216 ----------
Chris@87 217 None
Chris@87 218
Chris@87 219 Returns
Chris@87 220 -------
Chris@87 221 names : list of str
Chris@87 222 The names of all variables in the `VariableSet` instance.
Chris@87 223
Chris@87 224 """
Chris@87 225 return list(self._raw_data.keys())
Chris@87 226
Chris@87 227 # Emulate a dict to set/get variables values
Chris@87 228 def __getitem__(self, name):
Chris@87 229 return self._raw_data[name]
Chris@87 230
Chris@87 231 def __setitem__(self, name, value):
Chris@87 232 self._raw_data[name] = value
Chris@87 233 self._init_parse_var(name, value)
Chris@87 234
Chris@87 235 def parse_meta(config):
Chris@87 236 if not config.has_section('meta'):
Chris@87 237 raise FormatError("No meta section found !")
Chris@87 238
Chris@87 239 d = {}
Chris@87 240 for name, value in config.items('meta'):
Chris@87 241 d[name] = value
Chris@87 242
Chris@87 243 for k in ['name', 'description', 'version']:
Chris@87 244 if not k in d:
Chris@87 245 raise FormatError("Option %s (section [meta]) is mandatory, "
Chris@87 246 "but not found" % k)
Chris@87 247
Chris@87 248 if not 'requires' in d:
Chris@87 249 d['requires'] = []
Chris@87 250
Chris@87 251 return d
Chris@87 252
Chris@87 253 def parse_variables(config):
Chris@87 254 if not config.has_section('variables'):
Chris@87 255 raise FormatError("No variables section found !")
Chris@87 256
Chris@87 257 d = {}
Chris@87 258
Chris@87 259 for name, value in config.items("variables"):
Chris@87 260 d[name] = value
Chris@87 261
Chris@87 262 return VariableSet(d)
Chris@87 263
Chris@87 264 def parse_sections(config):
Chris@87 265 return meta_d, r
Chris@87 266
Chris@87 267 def pkg_to_filename(pkg_name):
Chris@87 268 return "%s.ini" % pkg_name
Chris@87 269
Chris@87 270 def parse_config(filename, dirs=None):
Chris@87 271 if dirs:
Chris@87 272 filenames = [os.path.join(d, filename) for d in dirs]
Chris@87 273 else:
Chris@87 274 filenames = [filename]
Chris@87 275
Chris@87 276 if sys.version[:3] > '3.1':
Chris@87 277 # SafeConfigParser is deprecated in py-3.2 and renamed to ConfigParser
Chris@87 278 config = ConfigParser()
Chris@87 279 else:
Chris@87 280 config = SafeConfigParser()
Chris@87 281
Chris@87 282 n = config.read(filenames)
Chris@87 283 if not len(n) >= 1:
Chris@87 284 raise PkgNotFound("Could not find file(s) %s" % str(filenames))
Chris@87 285
Chris@87 286 # Parse meta and variables sections
Chris@87 287 meta = parse_meta(config)
Chris@87 288
Chris@87 289 vars = {}
Chris@87 290 if config.has_section('variables'):
Chris@87 291 for name, value in config.items("variables"):
Chris@87 292 vars[name] = _escape_backslash(value)
Chris@87 293
Chris@87 294 # Parse "normal" sections
Chris@87 295 secs = [s for s in config.sections() if not s in ['meta', 'variables']]
Chris@87 296 sections = {}
Chris@87 297
Chris@87 298 requires = {}
Chris@87 299 for s in secs:
Chris@87 300 d = {}
Chris@87 301 if config.has_option(s, "requires"):
Chris@87 302 requires[s] = config.get(s, 'requires')
Chris@87 303
Chris@87 304 for name, value in config.items(s):
Chris@87 305 d[name] = value
Chris@87 306 sections[s] = d
Chris@87 307
Chris@87 308 return meta, vars, sections, requires
Chris@87 309
Chris@87 310 def _read_config_imp(filenames, dirs=None):
Chris@87 311 def _read_config(f):
Chris@87 312 meta, vars, sections, reqs = parse_config(f, dirs)
Chris@87 313 # recursively add sections and variables of required libraries
Chris@87 314 for rname, rvalue in reqs.items():
Chris@87 315 nmeta, nvars, nsections, nreqs = _read_config(pkg_to_filename(rvalue))
Chris@87 316
Chris@87 317 # Update var dict for variables not in 'top' config file
Chris@87 318 for k, v in nvars.items():
Chris@87 319 if not k in vars:
Chris@87 320 vars[k] = v
Chris@87 321
Chris@87 322 # Update sec dict
Chris@87 323 for oname, ovalue in nsections[rname].items():
Chris@87 324 if ovalue:
Chris@87 325 sections[rname][oname] += ' %s' % ovalue
Chris@87 326
Chris@87 327 return meta, vars, sections, reqs
Chris@87 328
Chris@87 329 meta, vars, sections, reqs = _read_config(filenames)
Chris@87 330
Chris@87 331 # FIXME: document this. If pkgname is defined in the variables section, and
Chris@87 332 # there is no pkgdir variable defined, pkgdir is automatically defined to
Chris@87 333 # the path of pkgname. This requires the package to be imported to work
Chris@87 334 if not 'pkgdir' in vars and "pkgname" in vars:
Chris@87 335 pkgname = vars["pkgname"]
Chris@87 336 if not pkgname in sys.modules:
Chris@87 337 raise ValueError("You should import %s to get information on %s" %
Chris@87 338 (pkgname, meta["name"]))
Chris@87 339
Chris@87 340 mod = sys.modules[pkgname]
Chris@87 341 vars["pkgdir"] = _escape_backslash(os.path.dirname(mod.__file__))
Chris@87 342
Chris@87 343 return LibraryInfo(name=meta["name"], description=meta["description"],
Chris@87 344 version=meta["version"], sections=sections, vars=VariableSet(vars))
Chris@87 345
Chris@87 346 # Trivial cache to cache LibraryInfo instances creation. To be really
Chris@87 347 # efficient, the cache should be handled in read_config, since a same file can
Chris@87 348 # be parsed many time outside LibraryInfo creation, but I doubt this will be a
Chris@87 349 # problem in practice
Chris@87 350 _CACHE = {}
Chris@87 351 def read_config(pkgname, dirs=None):
Chris@87 352 """
Chris@87 353 Return library info for a package from its configuration file.
Chris@87 354
Chris@87 355 Parameters
Chris@87 356 ----------
Chris@87 357 pkgname : str
Chris@87 358 Name of the package (should match the name of the .ini file, without
Chris@87 359 the extension, e.g. foo for the file foo.ini).
Chris@87 360 dirs : sequence, optional
Chris@87 361 If given, should be a sequence of directories - usually including
Chris@87 362 the NumPy base directory - where to look for npy-pkg-config files.
Chris@87 363
Chris@87 364 Returns
Chris@87 365 -------
Chris@87 366 pkginfo : class instance
Chris@87 367 The `LibraryInfo` instance containing the build information.
Chris@87 368
Chris@87 369 Raises
Chris@87 370 ------
Chris@87 371 PkgNotFound
Chris@87 372 If the package is not found.
Chris@87 373
Chris@87 374 See Also
Chris@87 375 --------
Chris@87 376 misc_util.get_info, misc_util.get_pkg_info
Chris@87 377
Chris@87 378 Examples
Chris@87 379 --------
Chris@87 380 >>> npymath_info = np.distutils.npy_pkg_config.read_config('npymath')
Chris@87 381 >>> type(npymath_info)
Chris@87 382 <class 'numpy.distutils.npy_pkg_config.LibraryInfo'>
Chris@87 383 >>> print npymath_info
Chris@87 384 Name: npymath
Chris@87 385 Description: Portable, core math library implementing C99 standard
Chris@87 386 Requires:
Chris@87 387 Version: 0.1 #random
Chris@87 388
Chris@87 389 """
Chris@87 390 try:
Chris@87 391 return _CACHE[pkgname]
Chris@87 392 except KeyError:
Chris@87 393 v = _read_config_imp(pkg_to_filename(pkgname), dirs)
Chris@87 394 _CACHE[pkgname] = v
Chris@87 395 return v
Chris@87 396
Chris@87 397 # TODO:
Chris@87 398 # - implements version comparison (modversion + atleast)
Chris@87 399
Chris@87 400 # pkg-config simple emulator - useful for debugging, and maybe later to query
Chris@87 401 # the system
Chris@87 402 if __name__ == '__main__':
Chris@87 403 import sys
Chris@87 404 from optparse import OptionParser
Chris@87 405 import glob
Chris@87 406
Chris@87 407 parser = OptionParser()
Chris@87 408 parser.add_option("--cflags", dest="cflags", action="store_true",
Chris@87 409 help="output all preprocessor and compiler flags")
Chris@87 410 parser.add_option("--libs", dest="libs", action="store_true",
Chris@87 411 help="output all linker flags")
Chris@87 412 parser.add_option("--use-section", dest="section",
Chris@87 413 help="use this section instead of default for options")
Chris@87 414 parser.add_option("--version", dest="version", action="store_true",
Chris@87 415 help="output version")
Chris@87 416 parser.add_option("--atleast-version", dest="min_version",
Chris@87 417 help="Minimal version")
Chris@87 418 parser.add_option("--list-all", dest="list_all", action="store_true",
Chris@87 419 help="Minimal version")
Chris@87 420 parser.add_option("--define-variable", dest="define_variable",
Chris@87 421 help="Replace variable with the given value")
Chris@87 422
Chris@87 423 (options, args) = parser.parse_args(sys.argv)
Chris@87 424
Chris@87 425 if len(args) < 2:
Chris@87 426 raise ValueError("Expect package name on the command line:")
Chris@87 427
Chris@87 428 if options.list_all:
Chris@87 429 files = glob.glob("*.ini")
Chris@87 430 for f in files:
Chris@87 431 info = read_config(f)
Chris@87 432 print("%s\t%s - %s" % (info.name, info.name, info.description))
Chris@87 433
Chris@87 434 pkg_name = args[1]
Chris@87 435 import os
Chris@87 436 d = os.environ.get('NPY_PKG_CONFIG_PATH')
Chris@87 437 if d:
Chris@87 438 info = read_config(pkg_name, ['numpy/core/lib/npy-pkg-config', '.', d])
Chris@87 439 else:
Chris@87 440 info = read_config(pkg_name, ['numpy/core/lib/npy-pkg-config', '.'])
Chris@87 441
Chris@87 442 if options.section:
Chris@87 443 section = options.section
Chris@87 444 else:
Chris@87 445 section = "default"
Chris@87 446
Chris@87 447 if options.define_variable:
Chris@87 448 m = re.search('([\S]+)=([\S]+)', options.define_variable)
Chris@87 449 if not m:
Chris@87 450 raise ValueError("--define-variable option should be of " \
Chris@87 451 "the form --define-variable=foo=bar")
Chris@87 452 else:
Chris@87 453 name = m.group(1)
Chris@87 454 value = m.group(2)
Chris@87 455 info.vars[name] = value
Chris@87 456
Chris@87 457 if options.cflags:
Chris@87 458 print(info.cflags(section))
Chris@87 459 if options.libs:
Chris@87 460 print(info.libs(section))
Chris@87 461 if options.version:
Chris@87 462 print(info.version)
Chris@87 463 if options.min_version:
Chris@87 464 print(info.version >= options.min_version)