1#!/usr/bin/env python 2# encoding: utf-8 3# Thomas Nagy, 2007-2015 (ita) 4# Gustavo Carneiro (gjc), 2007 5 6""" 7Support for Python, detect the headers and libraries and provide 8*use* variables to link C/C++ programs against them:: 9 10 def options(opt): 11 opt.load('compiler_c python') 12 def configure(conf): 13 conf.load('compiler_c python') 14 conf.check_python_version((2,4,2)) 15 conf.check_python_headers() 16 def build(bld): 17 bld.program(features='pyembed', source='a.c', target='myprog') 18 bld.shlib(features='pyext', source='b.c', target='mylib') 19""" 20 21import os, sys 22from waflib import Errors, Logs, Node, Options, Task, Utils 23from waflib.TaskGen import extension, before_method, after_method, feature 24from waflib.Configure import conf 25 26FRAG = ''' 27#include <Python.h> 28#ifdef __cplusplus 29extern "C" { 30#endif 31 void Py_Initialize(void); 32 void Py_Finalize(void); 33#ifdef __cplusplus 34} 35#endif 36int main(int argc, char **argv) 37{ 38 (void)argc; (void)argv; 39 Py_Initialize(); 40 Py_Finalize(); 41 return 0; 42} 43''' 44""" 45Piece of C/C++ code used in :py:func:`waflib.Tools.python.check_python_headers` 46""" 47 48INST = ''' 49import sys, py_compile 50py_compile.compile(sys.argv[1], sys.argv[2], sys.argv[3], True) 51''' 52""" 53Piece of Python code used in :py:class:`waflib.Tools.python.pyo` and :py:class:`waflib.Tools.python.pyc` for byte-compiling python files 54""" 55 56DISTUTILS_IMP = ['from distutils.sysconfig import get_config_var, get_python_lib'] 57 58@before_method('process_source') 59@feature('py') 60def feature_py(self): 61 """ 62 Create tasks to byte-compile .py files and install them, if requested 63 """ 64 self.install_path = getattr(self, 'install_path', '${PYTHONDIR}') 65 install_from = getattr(self, 'install_from', None) 66 if install_from and not isinstance(install_from, Node.Node): 67 install_from = self.path.find_dir(install_from) 68 self.install_from = install_from 69 70 ver = self.env.PYTHON_VERSION 71 if not ver: 72 self.bld.fatal('Installing python files requires PYTHON_VERSION, try conf.check_python_version') 73 74 if int(ver.replace('.', '')) > 31: 75 self.install_32 = True 76 77@extension('.py') 78def process_py(self, node): 79 """ 80 Add signature of .py file, so it will be byte-compiled when necessary 81 """ 82 assert(hasattr(self, 'install_path')), 'add features="py" for target "%s" in "%s/wscript".' % (self.target, self.path.nice_path()) 83 self.install_from = getattr(self, 'install_from', None) 84 relative_trick = getattr(self, 'relative_trick', True) 85 if self.install_from: 86 assert isinstance(self.install_from, Node.Node), \ 87 'add features="py" for target "%s" in "%s/wscript" (%s).' % (self.target, self.path.nice_path(), type(self.install_from)) 88 89 # where to install the python file 90 if self.install_path: 91 if self.install_from: 92 self.add_install_files(install_to=self.install_path, install_from=node, cwd=self.install_from, relative_trick=relative_trick) 93 else: 94 self.add_install_files(install_to=self.install_path, install_from=node, relative_trick=relative_trick) 95 96 lst = [] 97 if self.env.PYC: 98 lst.append('pyc') 99 if self.env.PYO: 100 lst.append('pyo') 101 102 if self.install_path: 103 if self.install_from: 104 target_dir = node.path_from(self.install_from) if relative_trick else node.name 105 pyd = Utils.subst_vars("%s/%s" % (self.install_path, target_dir), self.env) 106 else: 107 target_dir = node.path_from(self.path) if relative_trick else node.name 108 pyd = Utils.subst_vars("%s/%s" % (self.install_path, target_dir), self.env) 109 else: 110 pyd = node.abspath() 111 112 for ext in lst: 113 if self.env.PYTAG and not self.env.NOPYCACHE: 114 # __pycache__ installation for python 3.2 - PEP 3147 115 name = node.name[:-3] 116 pyobj = node.parent.get_bld().make_node('__pycache__').make_node("%s.%s.%s" % (name, self.env.PYTAG, ext)) 117 pyobj.parent.mkdir() 118 else: 119 pyobj = node.change_ext(".%s" % ext) 120 121 tsk = self.create_task(ext, node, pyobj) 122 tsk.pyd = pyd 123 124 if self.install_path: 125 self.add_install_files(install_to=os.path.dirname(pyd), install_from=pyobj, cwd=node.parent.get_bld(), relative_trick=relative_trick) 126 127class pyc(Task.Task): 128 """ 129 Byte-compiling python files 130 """ 131 color = 'PINK' 132 def __str__(self): 133 node = self.outputs[0] 134 return node.path_from(node.ctx.launch_node()) 135 def run(self): 136 cmd = [Utils.subst_vars('${PYTHON}', self.env), '-c', INST, self.inputs[0].abspath(), self.outputs[0].abspath(), self.pyd] 137 ret = self.generator.bld.exec_command(cmd) 138 return ret 139 140class pyo(Task.Task): 141 """ 142 Byte-compiling python files 143 """ 144 color = 'PINK' 145 def __str__(self): 146 node = self.outputs[0] 147 return node.path_from(node.ctx.launch_node()) 148 def run(self): 149 cmd = [Utils.subst_vars('${PYTHON}', self.env), Utils.subst_vars('${PYFLAGS_OPT}', self.env), '-c', INST, self.inputs[0].abspath(), self.outputs[0].abspath(), self.pyd] 150 ret = self.generator.bld.exec_command(cmd) 151 return ret 152 153@feature('pyext') 154@before_method('propagate_uselib_vars', 'apply_link') 155@after_method('apply_bundle') 156def init_pyext(self): 157 """ 158 Change the values of *cshlib_PATTERN* and *cxxshlib_PATTERN* to remove the 159 *lib* prefix from library names. 160 """ 161 self.uselib = self.to_list(getattr(self, 'uselib', [])) 162 if not 'PYEXT' in self.uselib: 163 self.uselib.append('PYEXT') 164 # override shlib_PATTERN set by the osx module 165 self.env.cshlib_PATTERN = self.env.cxxshlib_PATTERN = self.env.macbundle_PATTERN = self.env.pyext_PATTERN 166 self.env.fcshlib_PATTERN = self.env.dshlib_PATTERN = self.env.pyext_PATTERN 167 168 try: 169 if not self.install_path: 170 return 171 except AttributeError: 172 self.install_path = '${PYTHONARCHDIR}' 173 174@feature('pyext') 175@before_method('apply_link', 'apply_bundle') 176def set_bundle(self): 177 """Mac-specific pyext extension that enables bundles from c_osx.py""" 178 if Utils.unversioned_sys_platform() == 'darwin': 179 self.mac_bundle = True 180 181@before_method('propagate_uselib_vars') 182@feature('pyembed') 183def init_pyembed(self): 184 """ 185 Add the PYEMBED variable. 186 """ 187 self.uselib = self.to_list(getattr(self, 'uselib', [])) 188 if not 'PYEMBED' in self.uselib: 189 self.uselib.append('PYEMBED') 190 191@conf 192def get_python_variables(self, variables, imports=None): 193 """ 194 Spawn a new python process to dump configuration variables 195 196 :param variables: variables to print 197 :type variables: list of string 198 :param imports: one import by element 199 :type imports: list of string 200 :return: the variable values 201 :rtype: list of string 202 """ 203 if not imports: 204 try: 205 imports = self.python_imports 206 except AttributeError: 207 imports = DISTUTILS_IMP 208 209 program = list(imports) # copy 210 program.append('') 211 for v in variables: 212 program.append("print(repr(%s))" % v) 213 os_env = dict(os.environ) 214 try: 215 del os_env['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool 216 except KeyError: 217 pass 218 219 try: 220 out = self.cmd_and_log(self.env.PYTHON + ['-c', '\n'.join(program)], env=os_env) 221 except Errors.WafError: 222 self.fatal('The distutils module is unusable: install "python-devel"?') 223 self.to_log(out) 224 return_values = [] 225 for s in out.splitlines(): 226 s = s.strip() 227 if not s: 228 continue 229 if s == 'None': 230 return_values.append(None) 231 elif (s[0] == "'" and s[-1] == "'") or (s[0] == '"' and s[-1] == '"'): 232 return_values.append(eval(s)) 233 elif s[0].isdigit(): 234 return_values.append(int(s)) 235 else: break 236 return return_values 237 238@conf 239def test_pyembed(self, mode, msg='Testing pyembed configuration'): 240 self.check(header_name='Python.h', define_name='HAVE_PYEMBED', msg=msg, 241 fragment=FRAG, errmsg='Could not build a python embedded interpreter', 242 features='%s %sprogram pyembed' % (mode, mode)) 243 244@conf 245def test_pyext(self, mode, msg='Testing pyext configuration'): 246 self.check(header_name='Python.h', define_name='HAVE_PYEXT', msg=msg, 247 fragment=FRAG, errmsg='Could not build python extensions', 248 features='%s %sshlib pyext' % (mode, mode)) 249 250@conf 251def python_cross_compile(self, features='pyembed pyext'): 252 """ 253 For cross-compilation purposes, it is possible to bypass the normal detection and set the flags that you want: 254 PYTHON_VERSION='3.4' PYTAG='cpython34' pyext_PATTERN="%s.so" PYTHON_LDFLAGS='-lpthread -ldl' waf configure 255 256 The following variables are used: 257 PYTHON_VERSION required 258 PYTAG required 259 PYTHON_LDFLAGS required 260 pyext_PATTERN required 261 PYTHON_PYEXT_LDFLAGS 262 PYTHON_PYEMBED_LDFLAGS 263 """ 264 features = Utils.to_list(features) 265 if not ('PYTHON_LDFLAGS' in self.environ or 'PYTHON_PYEXT_LDFLAGS' in self.environ or 'PYTHON_PYEMBED_LDFLAGS' in self.environ): 266 return False 267 268 for x in 'PYTHON_VERSION PYTAG pyext_PATTERN'.split(): 269 if not x in self.environ: 270 self.fatal('Please set %s in the os environment' % x) 271 else: 272 self.env[x] = self.environ[x] 273 274 xx = self.env.CXX_NAME and 'cxx' or 'c' 275 if 'pyext' in features: 276 flags = self.environ.get('PYTHON_PYEXT_LDFLAGS', self.environ.get('PYTHON_LDFLAGS')) 277 if flags is None: 278 self.fatal('No flags provided through PYTHON_PYEXT_LDFLAGS as required') 279 else: 280 self.parse_flags(flags, 'PYEXT') 281 self.test_pyext(xx) 282 if 'pyembed' in features: 283 flags = self.environ.get('PYTHON_PYEMBED_LDFLAGS', self.environ.get('PYTHON_LDFLAGS')) 284 if flags is None: 285 self.fatal('No flags provided through PYTHON_PYEMBED_LDFLAGS as required') 286 else: 287 self.parse_flags(flags, 'PYEMBED') 288 self.test_pyembed(xx) 289 return True 290 291@conf 292def check_python_headers(conf, features='pyembed pyext'): 293 """ 294 Check for headers and libraries necessary to extend or embed python by using the module *distutils*. 295 On success the environment variables xxx_PYEXT and xxx_PYEMBED are added: 296 297 * PYEXT: for compiling python extensions 298 * PYEMBED: for embedding a python interpreter 299 """ 300 features = Utils.to_list(features) 301 assert ('pyembed' in features) or ('pyext' in features), "check_python_headers features must include 'pyembed' and/or 'pyext'" 302 env = conf.env 303 if not env.CC_NAME and not env.CXX_NAME: 304 conf.fatal('load a compiler first (gcc, g++, ..)') 305 306 # bypass all the code below for cross-compilation 307 if conf.python_cross_compile(features): 308 return 309 310 if not env.PYTHON_VERSION: 311 conf.check_python_version() 312 313 pybin = env.PYTHON 314 if not pybin: 315 conf.fatal('Could not find the python executable') 316 317 # so we actually do all this for compatibility reasons and for obtaining pyext_PATTERN below 318 v = 'prefix SO LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split() 319 try: 320 lst = conf.get_python_variables(["get_config_var('%s') or ''" % x for x in v]) 321 except RuntimeError: 322 conf.fatal("Python development headers not found (-v for details).") 323 324 vals = ['%s = %r' % (x, y) for (x, y) in zip(v, lst)] 325 conf.to_log("Configuration returned from %r:\n%s\n" % (pybin, '\n'.join(vals))) 326 327 dct = dict(zip(v, lst)) 328 x = 'MACOSX_DEPLOYMENT_TARGET' 329 if dct[x]: 330 env[x] = conf.environ[x] = dct[x] 331 env.pyext_PATTERN = '%s' + dct['SO'] # not a mistake 332 333 334 # Try to get pythonX.Y-config 335 num = '.'.join(env.PYTHON_VERSION.split('.')[:2]) 336 conf.find_program([''.join(pybin) + '-config', 'python%s-config' % num, 'python-config-%s' % num, 'python%sm-config' % num], var='PYTHON_CONFIG', msg="python-config", mandatory=False) 337 338 if env.PYTHON_CONFIG: 339 # check python-config output only once 340 if conf.env.HAVE_PYTHON_H: 341 return 342 343 # python2.6-config requires 3 runs 344 all_flags = [['--cflags', '--libs', '--ldflags']] 345 if sys.hexversion < 0x2070000: 346 all_flags = [[k] for k in all_flags[0]] 347 348 xx = env.CXX_NAME and 'cxx' or 'c' 349 350 if 'pyembed' in features: 351 for flags in all_flags: 352 # Python 3.8 has different flags for pyembed, needs --embed 353 embedflags = flags + ['--embed'] 354 try: 355 conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(embedflags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=embedflags) 356 except conf.errors.ConfigurationError: 357 # However Python < 3.8 doesn't accept --embed, so we need a fallback 358 conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(flags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=flags) 359 360 try: 361 conf.test_pyembed(xx) 362 except conf.errors.ConfigurationError: 363 # python bug 7352 364 if dct['Py_ENABLE_SHARED'] and dct['LIBDIR']: 365 env.append_unique('LIBPATH_PYEMBED', [dct['LIBDIR']]) 366 conf.test_pyembed(xx) 367 else: 368 raise 369 370 if 'pyext' in features: 371 for flags in all_flags: 372 conf.check_cfg(msg='Asking python-config for pyext %r flags' % ' '.join(flags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEXT', args=flags) 373 374 try: 375 conf.test_pyext(xx) 376 except conf.errors.ConfigurationError: 377 # python bug 7352 378 if dct['Py_ENABLE_SHARED'] and dct['LIBDIR']: 379 env.append_unique('LIBPATH_PYEXT', [dct['LIBDIR']]) 380 conf.test_pyext(xx) 381 else: 382 raise 383 384 conf.define('HAVE_PYTHON_H', 1) 385 return 386 387 # No python-config, do something else on windows systems 388 all_flags = dct['LDFLAGS'] + ' ' + dct['CFLAGS'] 389 conf.parse_flags(all_flags, 'PYEMBED') 390 391 all_flags = dct['LDFLAGS'] + ' ' + dct['LDSHARED'] + ' ' + dct['CFLAGS'] 392 conf.parse_flags(all_flags, 'PYEXT') 393 394 result = None 395 if not dct["LDVERSION"]: 396 dct["LDVERSION"] = env.PYTHON_VERSION 397 398 # further simplification will be complicated 399 for name in ('python' + dct['LDVERSION'], 'python' + env.PYTHON_VERSION + 'm', 'python' + env.PYTHON_VERSION.replace('.', '')): 400 401 # LIBPATH_PYEMBED is already set; see if it works. 402 if not result and env.LIBPATH_PYEMBED: 403 path = env.LIBPATH_PYEMBED 404 conf.to_log("\n\n# Trying default LIBPATH_PYEMBED: %r\n" % path) 405 result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in LIBPATH_PYEMBED' % name) 406 407 if not result and dct['LIBDIR']: 408 path = [dct['LIBDIR']] 409 conf.to_log("\n\n# try again with -L$python_LIBDIR: %r\n" % path) 410 result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in LIBDIR' % name) 411 412 if not result and dct['LIBPL']: 413 path = [dct['LIBPL']] 414 conf.to_log("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n") 415 result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in python_LIBPL' % name) 416 417 if not result: 418 path = [os.path.join(dct['prefix'], "libs")] 419 conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n") 420 result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in $prefix/libs' % name) 421 422 if result: 423 break # do not forget to set LIBPATH_PYEMBED 424 425 if result: 426 env.LIBPATH_PYEMBED = path 427 env.append_value('LIB_PYEMBED', [name]) 428 else: 429 conf.to_log("\n\n### LIB NOT FOUND\n") 430 431 # under certain conditions, python extensions must link to 432 # python libraries, not just python embedding programs. 433 if Utils.is_win32 or dct['Py_ENABLE_SHARED']: 434 env.LIBPATH_PYEXT = env.LIBPATH_PYEMBED 435 env.LIB_PYEXT = env.LIB_PYEMBED 436 437 conf.to_log("Include path for Python extensions (found via distutils module): %r\n" % (dct['INCLUDEPY'],)) 438 env.INCLUDES_PYEXT = [dct['INCLUDEPY']] 439 env.INCLUDES_PYEMBED = [dct['INCLUDEPY']] 440 441 # Code using the Python API needs to be compiled with -fno-strict-aliasing 442 if env.CC_NAME == 'gcc': 443 env.append_unique('CFLAGS_PYEMBED', ['-fno-strict-aliasing']) 444 env.append_unique('CFLAGS_PYEXT', ['-fno-strict-aliasing']) 445 if env.CXX_NAME == 'gcc': 446 env.append_unique('CXXFLAGS_PYEMBED', ['-fno-strict-aliasing']) 447 env.append_unique('CXXFLAGS_PYEXT', ['-fno-strict-aliasing']) 448 449 if env.CC_NAME == "msvc": 450 from distutils.msvccompiler import MSVCCompiler 451 dist_compiler = MSVCCompiler() 452 dist_compiler.initialize() 453 env.append_value('CFLAGS_PYEXT', dist_compiler.compile_options) 454 env.append_value('CXXFLAGS_PYEXT', dist_compiler.compile_options) 455 env.append_value('LINKFLAGS_PYEXT', dist_compiler.ldflags_shared) 456 457 # See if it compiles 458 conf.check(header_name='Python.h', define_name='HAVE_PYTHON_H', uselib='PYEMBED', fragment=FRAG, errmsg='Distutils not installed? Broken python installation? Get python-config now!') 459 460@conf 461def check_python_version(conf, minver=None): 462 """ 463 Check if the python interpreter is found matching a given minimum version. 464 minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver. 465 466 If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR' (eg. '2.4') 467 of the actual python version found, and PYTHONDIR and PYTHONARCHDIR 468 are defined, pointing to the site-packages directories appropriate for 469 this python version, where modules/packages/extensions should be 470 installed. 471 472 :param minver: minimum version 473 :type minver: tuple of int 474 """ 475 assert minver is None or isinstance(minver, tuple) 476 pybin = conf.env.PYTHON 477 if not pybin: 478 conf.fatal('could not find the python executable') 479 480 # Get python version string 481 cmd = pybin + ['-c', 'import sys\nfor x in sys.version_info: print(str(x))'] 482 Logs.debug('python: Running python command %r', cmd) 483 lines = conf.cmd_and_log(cmd).split() 484 assert len(lines) == 5, "found %r lines, expected 5: %r" % (len(lines), lines) 485 pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4])) 486 487 # Compare python version with the minimum required 488 result = (minver is None) or (pyver_tuple >= minver) 489 490 if result: 491 # define useful environment variables 492 pyver = '.'.join([str(x) for x in pyver_tuple[:2]]) 493 conf.env.PYTHON_VERSION = pyver 494 495 if 'PYTHONDIR' in conf.env: 496 # Check if --pythondir was specified 497 pydir = conf.env.PYTHONDIR 498 elif 'PYTHONDIR' in conf.environ: 499 # Check environment for PYTHONDIR 500 pydir = conf.environ['PYTHONDIR'] 501 else: 502 # Finally, try to guess 503 if Utils.is_win32: 504 (python_LIBDEST, pydir) = conf.get_python_variables( 505 ["get_config_var('LIBDEST') or ''", 506 "get_python_lib(standard_lib=0) or ''"]) 507 else: 508 python_LIBDEST = None 509 (pydir,) = conf.get_python_variables( ["get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env.PREFIX]) 510 if python_LIBDEST is None: 511 if conf.env.LIBDIR: 512 python_LIBDEST = os.path.join(conf.env.LIBDIR, 'python' + pyver) 513 else: 514 python_LIBDEST = os.path.join(conf.env.PREFIX, 'lib', 'python' + pyver) 515 516 if 'PYTHONARCHDIR' in conf.env: 517 # Check if --pythonarchdir was specified 518 pyarchdir = conf.env.PYTHONARCHDIR 519 elif 'PYTHONARCHDIR' in conf.environ: 520 # Check environment for PYTHONDIR 521 pyarchdir = conf.environ['PYTHONARCHDIR'] 522 else: 523 # Finally, try to guess 524 (pyarchdir, ) = conf.get_python_variables( ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''" % conf.env.PREFIX]) 525 if not pyarchdir: 526 pyarchdir = pydir 527 528 if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist 529 conf.define('PYTHONDIR', pydir) 530 conf.define('PYTHONARCHDIR', pyarchdir) 531 532 conf.env.PYTHONDIR = pydir 533 conf.env.PYTHONARCHDIR = pyarchdir 534 535 # Feedback 536 pyver_full = '.'.join(map(str, pyver_tuple[:3])) 537 if minver is None: 538 conf.msg('Checking for python version', pyver_full) 539 else: 540 minver_str = '.'.join(map(str, minver)) 541 conf.msg('Checking for python version >= %s' % (minver_str,), pyver_full, color=result and 'GREEN' or 'YELLOW') 542 543 if not result: 544 conf.fatal('The python version is too old, expecting %r' % (minver,)) 545 546PYTHON_MODULE_TEMPLATE = ''' 547import %s as current_module 548version = getattr(current_module, '__version__', None) 549if version is not None: 550 print(str(version)) 551else: 552 print('unknown version') 553''' 554 555@conf 556def check_python_module(conf, module_name, condition=''): 557 """ 558 Check if the selected python interpreter can import the given python module:: 559 560 def configure(conf): 561 conf.check_python_module('pygccxml') 562 conf.check_python_module('re', condition="ver > num(2, 0, 4) and ver <= num(3, 0, 0)") 563 564 :param module_name: module 565 :type module_name: string 566 """ 567 msg = "Checking for python module %r" % module_name 568 if condition: 569 msg = '%s (%s)' % (msg, condition) 570 conf.start_msg(msg) 571 try: 572 ret = conf.cmd_and_log(conf.env.PYTHON + ['-c', PYTHON_MODULE_TEMPLATE % module_name]) 573 except Errors.WafError: 574 conf.end_msg(False) 575 conf.fatal('Could not find the python module %r' % module_name) 576 577 ret = ret.strip() 578 if condition: 579 conf.end_msg(ret) 580 if ret == 'unknown version': 581 conf.fatal('Could not check the %s version' % module_name) 582 583 from distutils.version import LooseVersion 584 def num(*k): 585 if isinstance(k[0], int): 586 return LooseVersion('.'.join([str(x) for x in k])) 587 else: 588 return LooseVersion(k[0]) 589 d = {'num': num, 'ver': LooseVersion(ret)} 590 ev = eval(condition, {}, d) 591 if not ev: 592 conf.fatal('The %s version does not satisfy the requirements' % module_name) 593 else: 594 if ret == 'unknown version': 595 conf.end_msg(True) 596 else: 597 conf.end_msg(ret) 598 599def configure(conf): 600 """ 601 Detect the python interpreter 602 """ 603 v = conf.env 604 if getattr(Options.options, 'pythondir', None): 605 v.PYTHONDIR = Options.options.pythondir 606 if getattr(Options.options, 'pythonarchdir', None): 607 v.PYTHONARCHDIR = Options.options.pythonarchdir 608 if getattr(Options.options, 'nopycache', None): 609 v.NOPYCACHE=Options.options.nopycache 610 611 if not v.PYTHON: 612 v.PYTHON = [getattr(Options.options, 'python', None) or sys.executable] 613 v.PYTHON = Utils.to_list(v.PYTHON) 614 conf.find_program('python', var='PYTHON') 615 616 v.PYFLAGS = '' 617 v.PYFLAGS_OPT = '-O' 618 619 v.PYC = getattr(Options.options, 'pyc', 1) 620 v.PYO = getattr(Options.options, 'pyo', 1) 621 622 try: 623 v.PYTAG = conf.cmd_and_log(conf.env.PYTHON + ['-c', "import imp;print(imp.get_tag())"]).strip() 624 except Errors.WafError: 625 pass 626 627def options(opt): 628 """ 629 Add python-specific options 630 """ 631 pyopt=opt.add_option_group("Python Options") 632 pyopt.add_option('--nopyc', dest = 'pyc', action='store_false', default=1, 633 help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]') 634 pyopt.add_option('--nopyo', dest='pyo', action='store_false', default=1, 635 help='Do not install optimised compiled .pyo files (configuration) [Default:install]') 636 pyopt.add_option('--nopycache',dest='nopycache', action='store_true', 637 help='Do not use __pycache__ directory to install objects [Default:auto]') 638 pyopt.add_option('--python', dest="python", 639 help='python binary to be used [Default: %s]' % sys.executable) 640 pyopt.add_option('--pythondir', dest='pythondir', 641 help='Installation path for python modules (py, platform-independent .py and .pyc files)') 642 pyopt.add_option('--pythonarchdir', dest='pythonarchdir', 643 help='Installation path for python extension (pyext, platform-dependent .so or .dylib files)') 644 645