2
0
mirror of https://gitlab.com/ita1024/waf.git synced 2024-11-29 21:41:44 +01:00
waf/waflib/Tools/python.py

649 lines
22 KiB
Python
Raw Normal View History

2011-09-10 11:13:51 +02:00
#!/usr/bin/env python
# encoding: utf-8
2015-01-01 16:24:54 +01:00
# Thomas Nagy, 2007-2015 (ita)
2011-09-10 11:13:51 +02:00
# Gustavo Carneiro (gjc), 2007
"""
Support for Python, detect the headers and libraries and provide
*use* variables to link C/C++ programs against them::
def options(opt):
opt.load('compiler_c python')
def configure(conf):
conf.load('compiler_c python')
conf.check_python_version((2,4,2))
conf.check_python_headers()
def build(bld):
bld.program(features='pyembed', source='a.c', target='myprog')
bld.shlib(features='pyext', source='b.c', target='mylib')
"""
import os, sys
from waflib import Errors, Logs, Node, Options, Task, Utils
2011-09-10 11:13:51 +02:00
from waflib.TaskGen import extension, before_method, after_method, feature
from waflib.Configure import conf
FRAG = '''
#include <Python.h>
#ifdef __cplusplus
extern "C" {
#endif
void Py_Initialize(void);
void Py_Finalize(void);
#ifdef __cplusplus
}
#endif
2012-08-24 22:53:07 +02:00
int main(int argc, char **argv)
2011-09-10 11:13:51 +02:00
{
2012-11-04 10:29:36 +01:00
(void)argc; (void)argv;
2011-09-10 11:13:51 +02:00
Py_Initialize();
Py_Finalize();
return 0;
}
'''
"""
Piece of C/C++ code used in :py:func:`waflib.Tools.python.check_python_headers`
"""
INST = '''
import sys, py_compile
py_compile.compile(sys.argv[1], sys.argv[2], sys.argv[3], True)
2011-09-10 11:13:51 +02:00
'''
"""
2017-11-27 19:35:59 +01:00
Piece of Python code used in :py:class:`waflib.Tools.python.pyo` and :py:class:`waflib.Tools.python.pyc` for byte-compiling python files
2011-09-10 11:13:51 +02:00
"""
2014-07-25 18:15:23 +02:00
@before_method('process_source')
@feature('py')
def feature_py(self):
2011-09-10 11:13:51 +02:00
"""
Create tasks to byte-compile .py files and install them, if requested
2011-09-10 11:13:51 +02:00
"""
2014-07-25 18:15:23 +02:00
self.install_path = getattr(self, 'install_path', '${PYTHONDIR}')
install_from = getattr(self, 'install_from', None)
if install_from and not isinstance(install_from, Node.Node):
install_from = self.path.find_dir(install_from)
self.install_from = install_from
2011-09-10 11:13:51 +02:00
2014-07-25 18:15:23 +02:00
ver = self.env.PYTHON_VERSION
if not ver:
self.bld.fatal('Installing python files requires PYTHON_VERSION, try conf.check_python_version')
2011-09-10 11:13:51 +02:00
2014-07-25 18:15:23 +02:00
if int(ver.replace('.', '')) > 31:
self.install_32 = True
2011-09-10 11:13:51 +02:00
@extension('.py')
def process_py(self, node):
"""
Add signature of .py file, so it will be byte-compiled when necessary
"""
assert(hasattr(self, 'install_path')), 'add features="py" for target "%s" in "%s/wscript".' % (self.target, self.path.abspath())
self.install_from = getattr(self, 'install_from', None)
relative_trick = getattr(self, 'relative_trick', True)
if self.install_from:
assert isinstance(self.install_from, Node.Node), \
'add features="py" for target "%s" in "%s/wscript" (%s).' % (self.target, self.path.abspath(), type(self.install_from))
2014-07-25 18:15:23 +02:00
# where to install the python file
if self.install_path:
if self.install_from:
self.add_install_files(install_to=self.install_path, install_from=node, cwd=self.install_from, relative_trick=relative_trick)
2014-07-25 18:15:23 +02:00
else:
self.add_install_files(install_to=self.install_path, install_from=node, relative_trick=relative_trick)
2014-07-25 18:15:23 +02:00
lst = []
if self.env.PYC:
lst.append('pyc')
if self.env.PYO:
lst.append('pyo')
2011-09-10 11:13:51 +02:00
2014-07-25 18:15:23 +02:00
if self.install_path:
if self.install_from:
target_dir = node.path_from(self.install_from) if relative_trick else node.name
pyd = Utils.subst_vars("%s/%s" % (self.install_path, target_dir), self.env)
2014-07-25 18:15:23 +02:00
else:
target_dir = node.path_from(self.path) if relative_trick else node.name
pyd = Utils.subst_vars("%s/%s" % (self.install_path, target_dir), self.env)
2014-07-25 18:15:23 +02:00
else:
pyd = node.abspath()
for ext in lst:
if self.env.PYTAG and not self.env.NOPYCACHE:
2014-07-25 18:15:23 +02:00
# __pycache__ installation for python 3.2 - PEP 3147
name = node.name[:-3]
pyobj = node.parent.get_bld().make_node('__pycache__').make_node("%s.%s.%s" % (name, self.env.PYTAG, ext))
pyobj.parent.mkdir()
else:
pyobj = node.change_ext(".%s" % ext)
tsk = self.create_task(ext, node, pyobj)
tsk.pyd = pyd
if self.install_path:
self.add_install_files(install_to=os.path.dirname(pyd), install_from=pyobj, cwd=node.parent.get_bld(), relative_trick=relative_trick)
2014-07-25 18:15:23 +02:00
class pyc(Task.Task):
"""
Byte-compiling python files
"""
color = 'PINK'
def __str__(self):
node = self.outputs[0]
return node.path_from(node.ctx.launch_node())
def run(self):
cmd = [Utils.subst_vars('${PYTHON}', self.env), '-c', INST, self.inputs[0].abspath(), self.outputs[0].abspath(), self.pyd]
ret = self.generator.bld.exec_command(cmd)
return ret
2014-07-25 18:15:23 +02:00
class pyo(Task.Task):
2011-09-10 11:13:51 +02:00
"""
Byte-compiling python files
2011-09-10 11:13:51 +02:00
"""
color = 'PINK'
def __str__(self):
node = self.outputs[0]
return node.path_from(node.ctx.launch_node())
def run(self):
2014-07-25 18:15:23 +02:00
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]
ret = self.generator.bld.exec_command(cmd)
return ret
2011-09-10 11:13:51 +02:00
@feature('pyext')
@before_method('propagate_uselib_vars', 'apply_link')
@after_method('apply_bundle')
def init_pyext(self):
"""
Change the values of *cshlib_PATTERN* and *cxxshlib_PATTERN* to remove the
*lib* prefix from library names.
"""
self.uselib = self.to_list(getattr(self, 'uselib', []))
if not 'PYEXT' in self.uselib:
self.uselib.append('PYEXT')
# override shlib_PATTERN set by the osx module
self.env.cshlib_PATTERN = self.env.cxxshlib_PATTERN = self.env.macbundle_PATTERN = self.env.pyext_PATTERN
self.env.fcshlib_PATTERN = self.env.dshlib_PATTERN = self.env.pyext_PATTERN
2011-09-10 11:13:51 +02:00
2012-03-18 19:05:54 +01:00
try:
if not self.install_path:
return
except AttributeError:
self.install_path = '${PYTHONARCHDIR}'
2011-09-10 11:13:51 +02:00
@feature('pyext')
@before_method('apply_link', 'apply_bundle')
def set_bundle(self):
"""Mac-specific pyext extension that enables bundles from c_osx.py"""
2012-01-05 21:01:11 +01:00
if Utils.unversioned_sys_platform() == 'darwin':
2011-09-10 11:13:51 +02:00
self.mac_bundle = True
@before_method('propagate_uselib_vars')
@feature('pyembed')
def init_pyembed(self):
"""
Add the PYEMBED variable.
"""
self.uselib = self.to_list(getattr(self, 'uselib', []))
if not 'PYEMBED' in self.uselib:
self.uselib.append('PYEMBED')
@conf
2021-09-30 21:30:49 +02:00
def get_sysconfig_variable(self, variable):
"""
Spawn a new python process to dump configuration variables
:param variable: variable to print
:type variable: string
:return: the variable value
:rtype: string
"""
env = dict(os.environ)
try:
del env['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool
except KeyError:
pass
cmd = self.env.PYTHON + ["-c", "import sysconfig; print(sysconfig.get_config_var('{}'))".format(variable)]
out = self.cmd_and_log(cmd, env=env).strip()
if out == "None":
return ""
else:
return out
@conf
def get_sysconfig_variables(self, variables):
2011-09-10 11:13:51 +02:00
"""
2011-12-17 23:07:25 +01:00
Spawn a new python process to dump configuration variables
2011-09-10 11:13:51 +02:00
:param variables: variables to print
:type variables: list of string
:return: the variable values
:rtype: list of string
"""
2021-09-30 21:30:49 +02:00
return [self.get_sysconfig_variable(variable=v) for v in variables]
@conf
def get_sysconfig_path(self, name):
"""
Spawn a new python process to dump configuration paths
:param name: path to print
:type variable: string
:return: the path value
:rtype: string
"""
env = dict(os.environ)
2011-09-10 11:13:51 +02:00
try:
2021-09-30 21:30:49 +02:00
del env['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool
2011-09-10 11:13:51 +02:00
except KeyError:
pass
2021-09-30 21:30:49 +02:00
cmd = self.env.PYTHON + ["-c", "import sysconfig; print(sysconfig.get_path('{}'))".format(name)]
out = self.cmd_and_log(cmd, env=env).strip()
if out == "None":
return ""
else:
return out
2011-09-10 11:13:51 +02:00
@conf
def test_pyembed(self, mode, msg='Testing pyembed configuration'):
self.check(header_name='Python.h', define_name='HAVE_PYEMBED', msg=msg,
fragment=FRAG, errmsg='Could not build a python embedded interpreter',
features='%s %sprogram pyembed' % (mode, mode))
@conf
def test_pyext(self, mode, msg='Testing pyext configuration'):
self.check(header_name='Python.h', define_name='HAVE_PYEXT', msg=msg,
fragment=FRAG, errmsg='Could not build python extensions',
features='%s %sshlib pyext' % (mode, mode))
@conf
def python_cross_compile(self, features='pyembed pyext'):
"""
For cross-compilation purposes, it is possible to bypass the normal detection and set the flags that you want:
PYTHON_VERSION='3.4' PYTAG='cpython34' pyext_PATTERN="%s.so" PYTHON_LDFLAGS='-lpthread -ldl' waf configure
The following variables are used:
PYTHON_VERSION required
PYTAG required
PYTHON_LDFLAGS required
pyext_PATTERN required
PYTHON_PYEXT_LDFLAGS
PYTHON_PYEMBED_LDFLAGS
"""
features = Utils.to_list(features)
if not ('PYTHON_LDFLAGS' in self.environ or 'PYTHON_PYEXT_LDFLAGS' in self.environ or 'PYTHON_PYEMBED_LDFLAGS' in self.environ):
return False
for x in 'PYTHON_VERSION PYTAG pyext_PATTERN'.split():
if not x in self.environ:
self.fatal('Please set %s in the os environment' % x)
else:
self.env[x] = self.environ[x]
xx = self.env.CXX_NAME and 'cxx' or 'c'
if 'pyext' in features:
2016-04-19 22:00:21 +02:00
flags = self.environ.get('PYTHON_PYEXT_LDFLAGS', self.environ.get('PYTHON_LDFLAGS'))
if flags is None:
self.fatal('No flags provided through PYTHON_PYEXT_LDFLAGS as required')
else:
self.parse_flags(flags, 'PYEXT')
self.test_pyext(xx)
if 'pyembed' in features:
2016-04-19 22:00:21 +02:00
flags = self.environ.get('PYTHON_PYEMBED_LDFLAGS', self.environ.get('PYTHON_LDFLAGS'))
if flags is None:
self.fatal('No flags provided through PYTHON_PYEMBED_LDFLAGS as required')
else:
self.parse_flags(flags, 'PYEMBED')
self.test_pyembed(xx)
return True
2011-09-10 11:13:51 +02:00
@conf
def check_python_headers(conf, features='pyembed pyext'):
2011-09-10 11:13:51 +02:00
"""
2021-09-30 21:30:49 +02:00
Check for headers and libraries necessary to extend or embed python by using the module *sysconfig*.
2011-09-10 11:13:51 +02:00
On success the environment variables xxx_PYEXT and xxx_PYEMBED are added:
* PYEXT: for compiling python extensions
* PYEMBED: for embedding a python interpreter
"""
features = Utils.to_list(features)
assert ('pyembed' in features) or ('pyext' in features), "check_python_headers features must include 'pyembed' and/or 'pyext'"
2012-07-03 15:42:17 +02:00
env = conf.env
2016-06-25 23:54:12 +02:00
if not env.CC_NAME and not env.CXX_NAME:
2011-09-10 11:13:51 +02:00
conf.fatal('load a compiler first (gcc, g++, ..)')
# bypass all the code below for cross-compilation
if conf.python_cross_compile(features):
return
2016-06-25 23:54:12 +02:00
if not env.PYTHON_VERSION:
2011-09-10 11:13:51 +02:00
conf.check_python_version()
pybin = env.PYTHON
2011-09-10 11:13:51 +02:00
if not pybin:
2012-07-03 15:42:17 +02:00
conf.fatal('Could not find the python executable')
2011-09-10 11:13:51 +02:00
# so we actually do all this for compatibility reasons and for obtaining pyext_PATTERN below
v = 'prefix SO EXT_SUFFIX LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS LDVERSION'.split()
2011-09-10 11:13:51 +02:00
try:
2021-09-30 21:30:49 +02:00
lst = conf.get_sysconfig_variables(variables=v)
2011-09-10 11:13:51 +02:00
except RuntimeError:
conf.fatal("Python development headers not found (-v for details).")
vals = ['%s = %r' % (x, y) for (x, y) in zip(v, lst)]
conf.to_log("Configuration returned from %r:\n%s\n" % (pybin, '\n'.join(vals)))
2011-09-10 11:13:51 +02:00
dct = dict(zip(v, lst))
x = 'MACOSX_DEPLOYMENT_TARGET'
if dct[x]:
env[x] = conf.environ[x] = str(dct[x])
env.pyext_PATTERN = '%s' + (dct['EXT_SUFFIX'] or dct['SO']) # SO is deprecated in 3.5 and removed in 3.11
2011-09-10 11:13:51 +02:00
# Try to get pythonX.Y-config
2016-06-25 23:54:12 +02:00
num = '.'.join(env.PYTHON_VERSION.split('.')[:2])
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)
if env.PYTHON_CONFIG:
2018-10-23 12:38:41 +02:00
# check python-config output only once
if conf.env.HAVE_PYTHON_H:
return
# python2.6-config requires 3 runs
all_flags = [['--cflags', '--libs', '--ldflags']]
if sys.hexversion < 0x2070000:
all_flags = [[k] for k in all_flags[0]]
xx = env.CXX_NAME and 'cxx' or 'c'
if 'pyembed' in features:
for flags in all_flags:
# Python 3.8 has different flags for pyembed, needs --embed
embedflags = flags + ['--embed']
try:
conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(embedflags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=embedflags)
except conf.errors.ConfigurationError:
# However Python < 3.8 doesn't accept --embed, so we need a fallback
conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(flags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=flags)
try:
conf.test_pyembed(xx)
except conf.errors.ConfigurationError:
# python bug 7352
if dct['Py_ENABLE_SHARED'] and dct['LIBDIR']:
env.append_unique('LIBPATH_PYEMBED', [dct['LIBDIR']])
conf.test_pyembed(xx)
else:
raise
2011-09-10 11:13:51 +02:00
if 'pyext' in features:
for flags in all_flags:
conf.check_cfg(msg='Asking python-config for pyext %r flags' % ' '.join(flags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEXT', args=flags)
try:
conf.test_pyext(xx)
except conf.errors.ConfigurationError:
# python bug 7352
if dct['Py_ENABLE_SHARED'] and dct['LIBDIR']:
env.append_unique('LIBPATH_PYEXT', [dct['LIBDIR']])
conf.test_pyext(xx)
else:
raise
2011-09-10 11:13:51 +02:00
conf.define('HAVE_PYTHON_H', 1)
return
# No python-config, do something else on windows systems
all_flags = dct['LDFLAGS'] + ' ' + dct['CFLAGS']
conf.parse_flags(all_flags, 'PYEMBED')
2011-09-10 11:13:51 +02:00
all_flags = dct['LDFLAGS'] + ' ' + dct['LDSHARED'] + ' ' + dct['CFLAGS']
conf.parse_flags(all_flags, 'PYEXT')
result = None
if not dct["LDVERSION"]:
2016-06-25 23:54:12 +02:00
dct["LDVERSION"] = env.PYTHON_VERSION
2011-09-10 11:13:51 +02:00
# further simplification will be complicated
2016-06-25 23:54:12 +02:00
for name in ('python' + dct['LDVERSION'], 'python' + env.PYTHON_VERSION + 'm', 'python' + env.PYTHON_VERSION.replace('.', '')):
# LIBPATH_PYEMBED is already set; see if it works.
2016-06-25 23:54:12 +02:00
if not result and env.LIBPATH_PYEMBED:
path = env.LIBPATH_PYEMBED
conf.to_log("\n\n# Trying default LIBPATH_PYEMBED: %r\n" % path)
result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in LIBPATH_PYEMBED' % name)
if not result and dct['LIBDIR']:
path = [dct['LIBDIR']]
conf.to_log("\n\n# try again with -L$python_LIBDIR: %r\n" % path)
result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in LIBDIR' % name)
if not result and dct['LIBPL']:
path = [dct['LIBPL']]
conf.to_log("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n")
result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in python_LIBPL' % name)
if not result:
path = [os.path.join(dct['prefix'], "libs")]
conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY rather than pythonX.Y (win32)\n")
result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in $prefix/libs' % name)
if not result:
path = [os.path.normpath(os.path.join(dct['INCLUDEPY'], '..', 'libs'))]
conf.to_log("\n\n# try again with -L$INCLUDEPY/../libs, and pythonXY rather than pythonX.Y (win32)\n")
result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in $INCLUDEPY/../libs' % name)
if result:
break # do not forget to set LIBPATH_PYEMBED
if result:
2016-06-25 23:54:12 +02:00
env.LIBPATH_PYEMBED = path
env.append_value('LIB_PYEMBED', [name])
else:
conf.to_log("\n\n### LIB NOT FOUND\n")
# under certain conditions, python extensions must link to
# python libraries, not just python embedding programs.
if Utils.is_win32 or dct['Py_ENABLE_SHARED']:
2016-06-25 23:54:12 +02:00
env.LIBPATH_PYEXT = env.LIBPATH_PYEMBED
env.LIB_PYEXT = env.LIB_PYEMBED
2021-09-30 21:30:49 +02:00
conf.to_log("Include path for Python extensions (found via sysconfig module): %r\n" % (dct['INCLUDEPY'],))
2016-06-25 23:54:12 +02:00
env.INCLUDES_PYEXT = [dct['INCLUDEPY']]
env.INCLUDES_PYEMBED = [dct['INCLUDEPY']]
# Code using the Python API needs to be compiled with -fno-strict-aliasing
2016-06-25 23:54:12 +02:00
if env.CC_NAME == 'gcc':
env.append_unique('CFLAGS_PYEMBED', ['-fno-strict-aliasing'])
env.append_unique('CFLAGS_PYEXT', ['-fno-strict-aliasing'])
2016-06-25 23:54:12 +02:00
if env.CXX_NAME == 'gcc':
env.append_unique('CXXFLAGS_PYEMBED', ['-fno-strict-aliasing'])
env.append_unique('CXXFLAGS_PYEXT', ['-fno-strict-aliasing'])
if env.CC_NAME == "msvc":
2021-09-30 21:30:49 +02:00
# From https://github.com/python/cpython/blob/main/Lib/distutils/msvccompiler.py
env.append_value('CFLAGS_PYEXT', [ '/nologo', '/Ox', '/MD', '/W3', '/GX', '/DNDEBUG'])
env.append_value('CXXFLAGS_PYEXT', [ '/nologo', '/Ox', '/MD', '/W3', '/GX', '/DNDEBUG'])
env.append_value('LINKFLAGS_PYEXT', ['/DLL', '/nologo', '/INCREMENTAL:NO'])
# See if it compiles
2021-09-30 21:30:49 +02:00
conf.check(header_name='Python.h', define_name='HAVE_PYTHON_H', uselib='PYEMBED', fragment=FRAG, errmsg='Broken python installation? Get python-config now!')
2011-09-10 11:13:51 +02:00
@conf
def check_python_version(conf, minver=None):
"""
Check if the python interpreter is found matching a given minimum version.
minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR' (eg. '2.4')
of the actual python version found, and PYTHONDIR and PYTHONARCHDIR
are defined, pointing to the site-packages directories appropriate for
2011-09-10 11:13:51 +02:00
this python version, where modules/packages/extensions should be
installed.
:param minver: minimum version
:type minver: tuple of int
"""
assert minver is None or isinstance(minver, tuple)
2016-06-25 23:54:12 +02:00
pybin = conf.env.PYTHON
2011-09-10 11:13:51 +02:00
if not pybin:
conf.fatal('could not find the python executable')
# Get python version string
cmd = pybin + ['-c', 'import sys\nfor x in sys.version_info: print(str(x))']
2016-03-19 14:46:22 +01:00
Logs.debug('python: Running python command %r', cmd)
2011-09-10 11:13:51 +02:00
lines = conf.cmd_and_log(cmd).split()
assert len(lines) == 5, "found %r lines, expected 5: %r" % (len(lines), lines)
2011-09-10 11:13:51 +02:00
pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4]))
2016-06-20 21:33:26 +02:00
# Compare python version with the minimum required
2011-09-10 11:13:51 +02:00
result = (minver is None) or (pyver_tuple >= minver)
if result:
# define useful environment variables
pyver = '.'.join([str(x) for x in pyver_tuple[:2]])
2016-06-25 23:54:12 +02:00
conf.env.PYTHON_VERSION = pyver
2011-09-10 11:13:51 +02:00
if 'PYTHONDIR' in conf.env:
# Check if --pythondir was specified
2016-06-25 23:54:12 +02:00
pydir = conf.env.PYTHONDIR
elif 'PYTHONDIR' in conf.environ:
# Check environment for PYTHONDIR
2011-09-10 11:13:51 +02:00
pydir = conf.environ['PYTHONDIR']
else:
pydir = conf.get_sysconfig_path('purelib')
2011-09-10 11:13:51 +02:00
if 'PYTHONARCHDIR' in conf.env:
# Check if --pythonarchdir was specified
2016-06-25 23:54:12 +02:00
pyarchdir = conf.env.PYTHONARCHDIR
elif 'PYTHONARCHDIR' in conf.environ:
# Check environment for PYTHONDIR
2011-09-10 11:13:51 +02:00
pyarchdir = conf.environ['PYTHONARCHDIR']
else:
# Finally, try to guess
pyarchdir = conf.get_sysconfig_path('platlib')
2021-09-30 21:30:49 +02:00
2011-09-10 11:13:51 +02:00
if not pyarchdir:
pyarchdir = pydir
if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist
conf.define('PYTHONDIR', pydir)
conf.define('PYTHONARCHDIR', pyarchdir)
2016-06-25 23:54:12 +02:00
conf.env.PYTHONDIR = pydir
conf.env.PYTHONARCHDIR = pyarchdir
2011-09-10 11:13:51 +02:00
# Feedback
pyver_full = '.'.join(map(str, pyver_tuple[:3]))
if minver is None:
conf.msg('Checking for python version', pyver_full)
else:
minver_str = '.'.join(map(str, minver))
conf.msg('Checking for python version >= %s' % (minver_str,), pyver_full, color=result and 'GREEN' or 'YELLOW')
2011-09-10 11:13:51 +02:00
if not result:
conf.fatal('The python version is too old, expecting %r' % (minver,))
PYTHON_MODULE_TEMPLATE = '''
2012-01-14 16:35:33 +01:00
import %s as current_module
version = getattr(current_module, '__version__', None)
if version is not None:
print(str(version))
2012-01-14 16:35:33 +01:00
else:
print('unknown version')
2011-09-10 11:13:51 +02:00
'''
@conf
2012-01-14 16:35:33 +01:00
def check_python_module(conf, module_name, condition=''):
2011-09-10 11:13:51 +02:00
"""
Check if the selected python interpreter can import the given python module::
def configure(conf):
conf.check_python_module('pygccxml')
2012-01-14 16:35:33 +01:00
conf.check_python_module('re', condition="ver > num(2, 0, 4) and ver <= num(3, 0, 0)")
2011-09-10 11:13:51 +02:00
:param module_name: module
:type module_name: string
"""
2016-04-26 19:35:46 +02:00
msg = "Checking for python module %r" % module_name
2012-01-14 16:35:33 +01:00
if condition:
msg = '%s (%s)' % (msg, condition)
conf.start_msg(msg)
2011-09-10 11:13:51 +02:00
try:
2016-06-25 23:54:12 +02:00
ret = conf.cmd_and_log(conf.env.PYTHON + ['-c', PYTHON_MODULE_TEMPLATE % module_name])
except Errors.WafError:
2011-09-10 11:13:51 +02:00
conf.end_msg(False)
conf.fatal('Could not find the python module %r' % module_name)
2012-01-14 16:35:33 +01:00
ret = ret.strip()
if condition:
conf.end_msg(ret)
if ret == 'unknown version':
conf.fatal('Could not check the %s version' % module_name)
def num(*k):
if isinstance(k[0], int):
return Utils.loose_version('.'.join([str(x) for x in k]))
2012-01-14 16:35:33 +01:00
else:
return Utils.loose_version(k[0])
d = {'num': num, 'ver': Utils.loose_version(ret)}
2012-01-14 16:35:33 +01:00
ev = eval(condition, {}, d)
if not ev:
conf.fatal('The %s version does not satisfy the requirements' % module_name)
else:
if ret == 'unknown version':
conf.end_msg(True)
else:
conf.end_msg(ret)
2011-09-10 11:13:51 +02:00
def configure(conf):
"""
Detect the python interpreter
"""
v = conf.env
2017-04-13 12:11:36 +02:00
if getattr(Options.options, 'pythondir', None):
2016-06-25 23:54:12 +02:00
v.PYTHONDIR = Options.options.pythondir
2017-04-13 12:11:36 +02:00
if getattr(Options.options, 'pythonarchdir', None):
2016-06-25 23:54:12 +02:00
v.PYTHONARCHDIR = Options.options.pythonarchdir
2017-04-13 12:11:36 +02:00
if getattr(Options.options, 'nopycache', None):
v.NOPYCACHE=Options.options.nopycache
2011-09-10 11:13:51 +02:00
2017-04-14 01:54:19 +02:00
if not v.PYTHON:
v.PYTHON = [getattr(Options.options, 'python', None) or sys.executable]
2017-04-14 01:54:19 +02:00
v.PYTHON = Utils.to_list(v.PYTHON)
conf.find_program('python', var='PYTHON')
2011-09-10 11:13:51 +02:00
2016-06-25 23:54:12 +02:00
v.PYFLAGS = ''
v.PYFLAGS_OPT = '-O'
2011-09-10 11:13:51 +02:00
2016-06-25 23:54:12 +02:00
v.PYC = getattr(Options.options, 'pyc', 1)
v.PYO = getattr(Options.options, 'pyo', 1)
2011-09-10 11:13:51 +02:00
2014-07-25 18:15:23 +02:00
try:
2019-08-14 22:05:45 +02:00
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()
2014-07-25 18:15:23 +02:00
except Errors.WafError:
pass
2011-09-10 11:13:51 +02:00
def options(opt):
"""
Add python-specific options
2011-09-10 11:13:51 +02:00
"""
pyopt=opt.add_option_group("Python Options")
pyopt.add_option('--nopyc', dest = 'pyc', action='store_false', default=1,
help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]')
pyopt.add_option('--nopyo', dest='pyo', action='store_false', default=1,
help='Do not install optimised compiled .pyo files (configuration) [Default:install]')
pyopt.add_option('--nopycache',dest='nopycache', action='store_true',
help='Do not use __pycache__ directory to install objects [Default:auto]')
pyopt.add_option('--python', dest="python",
help='python binary to be used [Default: %s]' % sys.executable)
pyopt.add_option('--pythondir', dest='pythondir',
help='Installation path for python modules (py, platform-independent .py and .pyc files)')
pyopt.add_option('--pythonarchdir', dest='pythonarchdir',
help='Installation path for python extension (pyext, platform-dependent .so or .dylib files)')
2014-07-25 18:15:23 +02:00