comparison DEPENDENCIES/mingw32/Python27/Lib/site-packages/numpy/distutils/npy_pkg_config.py @ 87:2a2c65a20a8b

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