Mercurial > hg > vamp-build-and-test
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) |