waf

FORK: waf with some random patches
git clone https://git.neptards.moe/neptards/waf.git
Log | Files | Refs | README

python.py (23264B)


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