mirror of https://gitlab.com/ita1024/waf.git
Tools/python: Extending python tool
add: show more command-line arguments to control which python is used and where py/pyext files should be installed. The new options are in addition to the existing PYTHON, PYTHONDIR, and PYTHONARCHDIR environment variable, providing the help messages to the user, so it can easily change parameters without second-guessing of what are they. Environmental parameters override command line arguments (is it right?) updated: An extension to Configure.find_command to allow specification of the name for the searched command (using `msg` option). This is helpful when the search command is a huge list, as in the case of python's python-config case. updated: Do not specify prefix for `get_python_lib`, since this way it can result in a wrong PYTHONDIR and PYTHONARCHDIR on certain platforms (e.g., OSX) updated: Changing logic with byte-compilation of python files. Mow, files are compiled during the build stage, not install stage. During the install stage files are simply copied to the requested location. updated: If there is an error in python source code, byte-compilation phase will raise an exception and abort building process. Signed-off-by: Thomas Nagy <tnagy2pow10@gmail.com>
This commit is contained in:
parent
b32ab8c7c5
commit
9b75b843ae
|
@ -485,6 +485,8 @@ def find_program(self, filename, **kw):
|
|||
:type var: string
|
||||
:param ext: list of extensions for the binary (do not add an extension for portability)
|
||||
:type ext: list of string
|
||||
:param msg: name to display in the log, by default filename is used
|
||||
:type msg: string
|
||||
"""
|
||||
|
||||
exts = kw.get('exts', Utils.is_win32 and '.exe,.com,.bat,.cmd' or ',.sh,.pl,.py')
|
||||
|
@ -531,7 +533,8 @@ def find_program(self, filename, **kw):
|
|||
if not ret and Utils.winreg:
|
||||
ret = Utils.get_registry_app_path(Utils.winreg.HKEY_LOCAL_MACHINE, filename)
|
||||
|
||||
self.msg('Checking for program ' + ','.join(filename), ret or False)
|
||||
msg = kw.get('msg', ', '.join(filename))
|
||||
self.msg("Checking for program '%s'" % msg, ret or False)
|
||||
self.to_log('find program=%r paths=%r var=%r -> %r' % (filename, path_list, var, ret))
|
||||
|
||||
if not ret:
|
||||
|
|
|
@ -19,7 +19,7 @@ Support for Python, detect the headers and libraries and provide
|
|||
"""
|
||||
|
||||
import os, sys
|
||||
from waflib import Utils, Options, Errors, Logs
|
||||
from waflib import Utils, Options, Errors, Logs, Task, Node
|
||||
from waflib.TaskGen import extension, before_method, after_method, feature
|
||||
from waflib.Configure import conf
|
||||
|
||||
|
@ -47,92 +47,92 @@ 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])
|
||||
py_compile.compile(sys.argv[1], sys.argv[2], sys.argv[3], True)
|
||||
'''
|
||||
"""
|
||||
Piece of Python code used in :py:func:`waflib.Tools.python.install_pyfile` for installing python files
|
||||
Piece of Python code used in :py:func:`waflib.Tools.python.pytask` for byte-compiling python files
|
||||
"""
|
||||
|
||||
DISTUTILS_IMP = ['from distutils.sysconfig import get_config_var, get_python_lib']
|
||||
|
||||
@extension('.py')
|
||||
def process_py(self, node):
|
||||
"""
|
||||
Add a callback using :py:func:`waflib.Tools.python.install_pyfile` to install a python file
|
||||
"""
|
||||
try:
|
||||
if not self.bld.is_install:
|
||||
return
|
||||
except AttributeError:
|
||||
return
|
||||
|
||||
try:
|
||||
if not self.install_path:
|
||||
return
|
||||
except AttributeError:
|
||||
self.install_path = '${PYTHONDIR}'
|
||||
|
||||
# i wonder now why we wanted to do this after the build is over
|
||||
# issue #901: people want to preserve the structure of installed files
|
||||
def inst_py(ctx):
|
||||
install_from = getattr(self, 'install_from', None)
|
||||
if install_from:
|
||||
install_from = self.path.find_dir(install_from)
|
||||
install_pyfile(self, node, install_from)
|
||||
self.bld.add_post_fun(inst_py)
|
||||
|
||||
def install_pyfile(self, node, install_from=None):
|
||||
"""
|
||||
Execute the installation of a python file
|
||||
|
||||
:param node: python file
|
||||
:type node: :py:class:`waflib.Node.Node`
|
||||
"""
|
||||
|
||||
from_node = install_from or node.parent
|
||||
tsk = self.bld.install_as(self.install_path + '/' + node.path_from(from_node), node, postpone=False)
|
||||
path = tsk.get_install_path()
|
||||
|
||||
if self.bld.is_install < 0:
|
||||
Logs.info("+ removing byte compiled python files")
|
||||
for x in 'co':
|
||||
try:
|
||||
os.remove(path + x)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
if self.bld.is_install > 0:
|
||||
try:
|
||||
st1 = os.stat(path)
|
||||
except OSError:
|
||||
Logs.error('The python file is missing, this should not happen')
|
||||
|
||||
for x in ['c', 'o']:
|
||||
do_inst = self.env['PY' + x.upper()]
|
||||
try:
|
||||
st2 = os.stat(path + x)
|
||||
except OSError:
|
||||
pass
|
||||
else:
|
||||
if st1.st_mtime <= st2.st_mtime:
|
||||
do_inst = False
|
||||
|
||||
if do_inst:
|
||||
lst = (x == 'o') and [self.env['PYFLAGS_OPT']] or []
|
||||
(a, b, c) = (path, path + x, tsk.get_install_path(destdir=False) + x)
|
||||
argv = self.env['PYTHON'] + lst + ['-c', INST, a, b, c]
|
||||
Logs.info('+ byte compiling %r' % (path + x))
|
||||
env = self.env.env or None
|
||||
ret = Utils.subprocess.Popen(argv, env=env).wait()
|
||||
if ret:
|
||||
raise Errors.WafError('py%s compilation failed %r' % (x, path))
|
||||
|
||||
@feature('py')
|
||||
def feature_py(self):
|
||||
"""
|
||||
Dummy feature which does nothing
|
||||
Create tasks to byte-compile .py files and install them, if requested
|
||||
"""
|
||||
pass
|
||||
|
||||
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)
|
||||
|
||||
inputs = []
|
||||
for x in Utils.to_list (self.source):
|
||||
if isinstance (x, Node.Node):
|
||||
y = x
|
||||
else:
|
||||
y = self.path.find_resource (x)
|
||||
inputs.append (y)
|
||||
|
||||
if install_path:
|
||||
if install_from:
|
||||
pyd = Utils.subst_vars ("%s/%s" % (install_path, y.path_from (install_from)), self.env)
|
||||
else:
|
||||
pyd = Utils.subst_vars ("%s/%s" % (install_path, y.path_from (self.path)), self.env)
|
||||
else:
|
||||
pyd = y.abspath ()
|
||||
|
||||
for ext in ["pyc", "pyo"]:
|
||||
if install_from:
|
||||
pyobj = self.path.get_bld ().make_node (y
|
||||
.change_ext (".%s" % ext)
|
||||
.get_bld ()
|
||||
.path_from (install_from.get_bld ()))
|
||||
else:
|
||||
pyobj = self.path.get_bld ().make_node (y
|
||||
.change_ext (".%s" % ext)
|
||||
.name)
|
||||
pyobj.parent.mkdir ()
|
||||
|
||||
tsk = self.create_task (ext, y, pyobj)
|
||||
tsk.pyd = pyd
|
||||
|
||||
if install_path:
|
||||
self.bld.install_files (install_path, pyobj, cwd = self.path.get_bld (), relative_trick = True)
|
||||
|
||||
if install_path:
|
||||
if install_from:
|
||||
self.bld.install_files (install_path, inputs, cwd = install_from, relative_trick = True)
|
||||
else:
|
||||
self.bld.install_files (install_path, inputs)
|
||||
|
||||
@extension('.py')
|
||||
def process_py(self, node):
|
||||
"""
|
||||
Add signature of .py file, so it will be byte-compiled when necessary
|
||||
"""
|
||||
node.sig = Utils.h_file (node.abspath())
|
||||
|
||||
class pyc (Task.Task):
|
||||
"""
|
||||
Byte-compiling python files
|
||||
"""
|
||||
color = 'PINK'
|
||||
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
|
||||
|
||||
class pyo (Task.Task):
|
||||
"""
|
||||
Byte-compiling python files
|
||||
"""
|
||||
color = 'PINK'
|
||||
def run(self):
|
||||
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
|
||||
|
||||
@feature('pyext')
|
||||
@before_method('propagate_uselib_vars', 'apply_link')
|
||||
|
@ -311,7 +311,7 @@ def check_python_headers(conf):
|
|||
# We check that pythonX.Y-config exists, and if it exists we
|
||||
# use it to get only the includes, else fall back to distutils.
|
||||
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', mandatory=False)
|
||||
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)
|
||||
|
||||
includes = []
|
||||
if conf.env.PYTHON_CONFIG:
|
||||
|
@ -327,7 +327,7 @@ def check_python_headers(conf):
|
|||
env['INCLUDES_PYEMBED'] = includes
|
||||
else:
|
||||
conf.to_log("Include path for Python extensions "
|
||||
"(found via distutils module): %r\n" % (dct['INCLUDEPY'],))
|
||||
"(found via distutils module): %r\n" % (dct['INCLUDEPY'],))
|
||||
env['INCLUDES_PYEXT'] = [dct['INCLUDEPY']]
|
||||
env['INCLUDES_PYEMBED'] = [dct['INCLUDEPY']]
|
||||
|
||||
|
@ -406,27 +406,36 @@ def check_python_version(conf, minver=None):
|
|||
pyver = '.'.join([str(x) for x in pyver_tuple[:2]])
|
||||
conf.env['PYTHON_VERSION'] = pyver
|
||||
|
||||
if 'PYTHONDIR' in conf.environ:
|
||||
if 'PYTHONDIR' in conf.env:
|
||||
# Check if --pythondir was specified
|
||||
pydir = conf.env['PYTHONDIR']
|
||||
elif 'PYTHONDIR' in conf.environ:
|
||||
# Check environment for PYTHONDIR
|
||||
pydir = conf.environ['PYTHONDIR']
|
||||
else:
|
||||
# Finally, try to guess
|
||||
if Utils.is_win32:
|
||||
(python_LIBDEST, pydir) = conf.get_python_variables(
|
||||
["get_config_var('LIBDEST') or ''",
|
||||
"get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']])
|
||||
"get_python_lib(standard_lib=0) or ''"])
|
||||
else:
|
||||
python_LIBDEST = None
|
||||
(pydir,) = conf.get_python_variables( ["get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']])
|
||||
(pydir,) = conf.get_python_variables( ["get_python_lib(standard_lib=0) or ''"])
|
||||
if python_LIBDEST is None:
|
||||
if conf.env['LIBDIR']:
|
||||
python_LIBDEST = os.path.join(conf.env['LIBDIR'], "python" + pyver)
|
||||
else:
|
||||
python_LIBDEST = os.path.join(conf.env['PREFIX'], "lib", "python" + pyver)
|
||||
|
||||
|
||||
if 'PYTHONARCHDIR' in conf.environ:
|
||||
if 'PYTHONARCHDIR' in conf.env:
|
||||
# Check if --pythonarchdir was specified
|
||||
pyarchdir = conf.env['PYTHONARCHDIR']
|
||||
elif 'PYTHONARCHDIR' in conf.environ:
|
||||
# Check environment for PYTHONDIR
|
||||
pyarchdir = conf.environ['PYTHONARCHDIR']
|
||||
else:
|
||||
(pyarchdir, ) = conf.get_python_variables( ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']])
|
||||
# Finally, try to guess
|
||||
(pyarchdir, ) = conf.get_python_variables( ["get_python_lib(plat_specific=1, standard_lib=0) or ''"])
|
||||
if not pyarchdir:
|
||||
pyarchdir = pydir
|
||||
|
||||
|
@ -452,9 +461,9 @@ PYTHON_MODULE_TEMPLATE = '''
|
|||
import %s as current_module
|
||||
version = getattr(current_module, '__version__', None)
|
||||
if version is not None:
|
||||
print(str(version))
|
||||
print(str(version))
|
||||
else:
|
||||
print('unknown version')
|
||||
print('unknown version')
|
||||
'''
|
||||
|
||||
@conf
|
||||
|
@ -469,7 +478,7 @@ def check_python_module(conf, module_name, condition=''):
|
|||
:param module_name: module
|
||||
:type module_name: string
|
||||
"""
|
||||
msg = 'Python module %s' % module_name
|
||||
msg = "Checking for python module '%s'" % module_name
|
||||
if condition:
|
||||
msg = '%s (%s)' % (msg, condition)
|
||||
conf.start_msg(msg)
|
||||
|
@ -505,17 +514,20 @@ def configure(conf):
|
|||
"""
|
||||
Detect the python interpreter
|
||||
"""
|
||||
v = conf.env
|
||||
v['PYTHON']=Options.options.python or sys.executable
|
||||
if Options.options.pythondir:
|
||||
v['PYTHONDIR'] = Options.options.pythondir
|
||||
if Options.options.pythonarchdir:
|
||||
v['PYTHONARCHDIR'] = Options.options.pythonarchdir
|
||||
try:
|
||||
conf.find_program('python', var='PYTHON')
|
||||
except conf.errors.ConfigurationError:
|
||||
Logs.warn("could not find a python executable, setting to sys.executable '%s'" % sys.executable)
|
||||
conf.env.PYTHON = sys.executable
|
||||
|
||||
if conf.env.PYTHON != sys.executable:
|
||||
Logs.warn("python executable %r differs from system %r" % (conf.env.PYTHON, sys.executable))
|
||||
conf.env.PYTHON = conf.cmd_to_list(conf.env.PYTHON)
|
||||
|
||||
v = conf.env
|
||||
v['PYCMD'] = '"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"'
|
||||
v['PYFLAGS'] = ''
|
||||
v['PYFLAGS_OPT'] = '-O'
|
||||
|
@ -525,16 +537,16 @@ def configure(conf):
|
|||
|
||||
def options(opt):
|
||||
"""
|
||||
Add the options ``--nopyc`` and ``--nopyo``
|
||||
Add python-specific options
|
||||
"""
|
||||
opt.add_option('--nopyc',
|
||||
action='store_false',
|
||||
default=1,
|
||||
help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]',
|
||||
dest = 'pyc')
|
||||
opt.add_option('--nopyo',
|
||||
action='store_false',
|
||||
default=1,
|
||||
help='Do not install optimised compiled .pyo files (configuration) [Default:install]',
|
||||
dest='pyo')
|
||||
|
||||
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('--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)')
|
||||
|
|
Loading…
Reference in New Issue