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 sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n"]).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