2
0
mirror of https://gitlab.com/ita1024/waf.git synced 2024-11-23 02:16:01 +01:00
waf/waflib/Context.py

752 lines
21 KiB
Python
Raw Normal View History

2011-09-10 11:13:51 +02:00
#!/usr/bin/env python
# encoding: utf-8
2018-01-01 20:53:49 +01:00
# Thomas Nagy, 2010-2018 (ita)
2011-09-10 11:13:51 +02:00
"""
2016-06-25 02:38:26 +02:00
Classes and functions enabling the command system
2011-09-10 11:13:51 +02:00
"""
2019-08-14 22:05:45 +02:00
import os, re, sys
2011-09-10 11:13:51 +02:00
from waflib import Utils, Errors, Logs
import waflib.Node
2019-08-14 22:05:45 +02:00
if sys.hexversion > 0x3040000:
import types
class imp(object):
new_module = lambda x: types.ModuleType(x)
else:
import imp
2011-09-10 11:13:51 +02:00
# the following 3 constants are updated on each new release (do not touch)
2019-11-03 10:44:09 +01:00
HEXVERSION=0x2010000
2011-09-10 11:13:51 +02:00
"""Constant updated on new releases"""
2019-11-03 10:44:09 +01:00
WAFVERSION="2.1.0"
2011-09-10 11:13:51 +02:00
"""Constant updated on new releases"""
2019-11-03 10:44:09 +01:00
WAFREVISION="4e171c87adf3401e817a7d434f82aa9510ff0d72"
2015-02-14 12:07:13 +01:00
"""Git revision when the waf version is updated"""
2011-09-10 11:13:51 +02:00
WAFNAME="waf"
"""Application name displayed on --help"""
ABI = 20
2011-09-10 11:13:51 +02:00
"""Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)"""
2012-07-30 18:35:17 +02:00
DBFILE = '.wafpickle-%s-%d-%d' % (sys.platform, sys.hexversion, ABI)
2011-09-10 11:13:51 +02:00
"""Name of the pickle file for storing the build data"""
APPNAME = 'APPNAME'
"""Default application name (used by ``waf dist``)"""
VERSION = 'VERSION'
"""Default application version (used by ``waf dist``)"""
TOP = 'top'
"""The variable name for the top-level directory in wscript files"""
OUT = 'out'
"""The variable name for the output directory in wscript files"""
WSCRIPT_FILE = 'wscript'
"""Name of the waf script files"""
launch_dir = ''
"""Directory from which waf has been called"""
run_dir = ''
"""Location of the wscript file to use as the entry point"""
top_dir = ''
"""Location of the project directory (top), if the project was configured"""
out_dir = ''
"""Location of the build directory (out), if the project was configured"""
waf_dir = ''
"""Directory containing the waf modules"""
default_encoding = Utils.console_encoding()
"""Encoding to use when reading outputs from other processes"""
2011-09-10 11:13:51 +02:00
g_module = None
"""
2016-06-25 02:38:26 +02:00
Module representing the top-level wscript file (see :py:const:`waflib.Context.run_dir`)
2011-09-10 11:13:51 +02:00
"""
STDOUT = 1
STDERR = -1
BOTH = 0
classes = []
"""
List of :py:class:`waflib.Context.Context` subclasses that can be used as waf commands. The classes
are added automatically by a metaclass.
"""
def create_context(cmd_name, *k, **kw):
"""
2016-06-25 02:38:26 +02:00
Returns a new :py:class:`waflib.Context.Context` instance corresponding to the given command.
2011-09-10 11:13:51 +02:00
Used in particular by :py:func:`waflib.Scripting.run_command`
2016-06-25 02:38:26 +02:00
:param cmd_name: command name
2011-09-10 11:13:51 +02:00
:type cmd_name: string
:param k: arguments to give to the context class initializer
:type k: list
:param k: keyword arguments to give to the context class initializer
:type k: dict
2016-06-25 02:38:26 +02:00
:return: Context object
:rtype: :py:class:`waflib.Context.Context`
2011-09-10 11:13:51 +02:00
"""
for x in classes:
if x.cmd == cmd_name:
return x(*k, **kw)
ctx = Context(*k, **kw)
ctx.fun = cmd_name
return ctx
class store_context(type):
"""
2016-06-25 02:38:26 +02:00
Metaclass that registers command classes into the list :py:const:`waflib.Context.classes`
Context classes must provide an attribute 'cmd' representing the command name, and a function
attribute 'fun' representing the function name that the command uses.
2011-09-10 11:13:51 +02:00
"""
2017-04-17 12:24:33 +02:00
def __init__(cls, name, bases, dct):
super(store_context, cls).__init__(name, bases, dct)
2011-09-10 11:13:51 +02:00
name = cls.__name__
2016-06-04 09:33:13 +02:00
if name in ('ctx', 'Context'):
2011-09-10 11:13:51 +02:00
return
try:
cls.cmd
except AttributeError:
raise Errors.WafError('Missing command for the context class %r (cmd)' % name)
if not getattr(cls, 'fun', None):
cls.fun = cls.cmd
classes.insert(0, cls)
ctx = store_context('ctx', (object,), {})
2016-06-25 02:38:26 +02:00
"""Base class for all :py:class:`waflib.Context.Context` classes"""
2011-09-10 11:13:51 +02:00
class Context(ctx):
"""
Default context for waf commands, and base class for new command contexts.
Context objects are passed to top-level functions::
def foo(ctx):
print(ctx.__class__.__name__) # waflib.Context.Context
2016-06-25 02:38:26 +02:00
Subclasses must define the class attributes 'cmd' and 'fun':
2011-09-10 11:13:51 +02:00
:param cmd: command to execute as in ``waf cmd``
:type cmd: string
:param fun: function name to execute when the command is called
:type fun: string
.. inheritance-diagram:: waflib.Context.Context waflib.Build.BuildContext waflib.Build.InstallContext waflib.Build.UninstallContext waflib.Build.StepContext waflib.Build.ListContext waflib.Configure.ConfigurationContext waflib.Scripting.Dist waflib.Scripting.DistCheck waflib.Build.CleanContext
:top-classes: waflib.Context.Context
2011-09-10 11:13:51 +02:00
"""
errors = Errors
"""
Shortcut to :py:mod:`waflib.Errors` provided for convenience
"""
tools = {}
"""
2016-06-25 02:38:26 +02:00
A module cache for wscript files; see :py:meth:`Context.Context.load`
2011-09-10 11:13:51 +02:00
"""
def __init__(self, **kw):
try:
rd = kw['run_dir']
except KeyError:
rd = run_dir
# binds the context to the nodes in use to avoid a context singleton
2016-06-05 00:23:57 +02:00
self.node_class = type('Nod3', (waflib.Node.Node,), {})
2016-06-25 02:38:26 +02:00
self.node_class.__module__ = 'waflib.Node'
2011-09-10 11:13:51 +02:00
self.node_class.ctx = self
self.root = self.node_class('', None)
self.cur_script = None
self.path = self.root.find_dir(rd)
self.stack_path = []
self.exec_dict = {'ctx':self, 'conf':self, 'bld':self, 'opt':self}
self.logger = None
2014-04-01 23:06:10 +02:00
def finalize(self):
"""
2016-06-25 02:38:26 +02:00
Called to free resources such as logger files
2014-04-01 23:06:10 +02:00
"""
try:
logger = self.logger
except AttributeError:
pass
else:
Logs.free_logger(logger)
delattr(self, 'logger')
def load(self, tool_list, **kw):
2011-09-10 11:13:51 +02:00
"""
Loads a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun` from it.
2011-09-10 11:13:51 +02:00
2016-06-25 02:38:26 +02:00
:param tool_list: list of Waf tool names to load
2011-09-10 11:13:51 +02:00
:type tool_list: list of string or space-separated string
:param tooldir: paths for the imports
:type tooldir: list of string
2011-09-10 11:13:51 +02:00
"""
tools = Utils.to_list(tool_list)
path = Utils.to_list(kw.get('tooldir', ''))
2015-08-28 22:43:38 +02:00
with_sys_path = kw.get('with_sys_path', True)
2011-09-10 11:13:51 +02:00
for t in tools:
module = load_tool(t, path, with_sys_path=with_sys_path)
2011-09-10 11:13:51 +02:00
fun = getattr(module, kw.get('name', self.fun), None)
if fun:
fun(self)
def execute(self):
"""
2016-06-25 02:38:26 +02:00
Here, it calls the function name in the top-level wscript file. Most subclasses
redefine this method to provide additional functionality.
2011-09-10 11:13:51 +02:00
"""
self.recurse([os.path.dirname(g_module.root_path)])
def pre_recurse(self, node):
"""
2016-06-25 02:38:26 +02:00
Method executed immediately before a folder is read by :py:meth:`waflib.Context.Context.recurse`.
The current script is bound as a Node object on ``self.cur_script``, and the current path
is bound to ``self.path``
2011-09-10 11:13:51 +02:00
:param node: script
:type node: :py:class:`waflib.Node.Node`
"""
self.stack_path.append(self.cur_script)
self.cur_script = node
self.path = node.parent
def post_recurse(self, node):
"""
2016-06-25 02:38:26 +02:00
Restores ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates.
2011-09-10 11:13:51 +02:00
:param node: script
:type node: :py:class:`waflib.Node.Node`
"""
self.cur_script = self.stack_path.pop()
if self.cur_script:
self.path = self.cur_script.parent
def recurse(self, dirs, name=None, mandatory=True, once=True, encoding=None):
2011-09-10 11:13:51 +02:00
"""
2016-06-25 02:38:26 +02:00
Runs user-provided functions from the supplied list of directories.
2011-09-10 11:13:51 +02:00
The directories can be either absolute, or relative to the directory
2016-06-25 02:38:26 +02:00
of the wscript file
The methods :py:meth:`waflib.Context.Context.pre_recurse` and
:py:meth:`waflib.Context.Context.post_recurse` are called immediately before
and after a script has been executed.
2011-09-10 11:13:51 +02:00
:param dirs: List of directories to visit
:type dirs: list of string or space-separated string
:param name: Name of function to invoke from the wscript
:type name: string
:param mandatory: whether sub wscript files are required to exist
:type mandatory: bool
:param once: read the script file once for a particular context
:type once: bool
"""
try:
cache = self.recurse_cache
2012-02-11 14:31:00 +01:00
except AttributeError:
2011-09-10 11:13:51 +02:00
cache = self.recurse_cache = {}
for d in Utils.to_list(dirs):
if not os.path.isabs(d):
# absolute paths only
d = os.path.join(self.path.abspath(), d)
WSCRIPT = os.path.join(d, WSCRIPT_FILE)
WSCRIPT_FUN = WSCRIPT + '_' + (name or self.fun)
node = self.root.find_node(WSCRIPT_FUN)
if node and (not once or node not in cache):
cache[node] = True
self.pre_recurse(node)
try:
2019-01-16 06:51:14 +01:00
function_code = node.read('r', encoding)
2011-09-10 11:13:51 +02:00
exec(compile(function_code, node.abspath(), 'exec'), self.exec_dict)
finally:
self.post_recurse(node)
elif not node:
node = self.root.find_node(WSCRIPT)
tup = (node, name or self.fun)
if node and (not once or tup not in cache):
cache[tup] = True
2011-09-10 11:13:51 +02:00
self.pre_recurse(node)
try:
wscript_module = load_module(node.abspath(), encoding=encoding)
2011-09-10 11:13:51 +02:00
user_function = getattr(wscript_module, (name or self.fun), None)
if not user_function:
if not mandatory:
continue
raise Errors.WafError('No function %r defined in %s' % (name or self.fun, node.abspath()))
2011-09-10 11:13:51 +02:00
user_function(self)
finally:
self.post_recurse(node)
elif not node:
if not mandatory:
continue
try:
os.listdir(d)
except OSError:
raise Errors.WafError('Cannot read the folder %r' % d)
2011-09-10 11:13:51 +02:00
raise Errors.WafError('No wscript file in directory %s' % d)
def log_command(self, cmd, kw):
if Logs.verbose:
fmt = os.environ.get('WAF_CMD_FORMAT')
if fmt == 'string':
if not isinstance(cmd, str):
cmd = Utils.shell_escape(cmd)
Logs.debug('runner: %r', cmd)
Logs.debug('runner_env: kw=%s', kw)
2011-09-10 11:13:51 +02:00
def exec_command(self, cmd, **kw):
"""
2016-06-25 02:38:26 +02:00
Runs an external process and returns the exit status::
2011-09-10 11:13:51 +02:00
def run(tsk):
ret = tsk.generator.bld.exec_command('touch foo.txt')
return ret
2016-06-25 02:38:26 +02:00
If the context has the attribute 'log', then captures and logs the process stderr/stdout.
Unlike :py:meth:`waflib.Context.Context.cmd_and_log`, this method does not return the
stdout/stderr values captured.
2011-09-10 11:13:51 +02:00
:param cmd: command argument for subprocess.Popen
2016-06-25 02:38:26 +02:00
:type cmd: string or list
:param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate.
2016-06-25 02:38:26 +02:00
:type kw: dict
:returns: process exit status
:rtype: integer
:raises: :py:class:`waflib.Errors.WafError` if an invalid executable is specified for a non-shell process
:raises: :py:class:`waflib.Errors.WafError` in case of execution failure
2011-09-10 11:13:51 +02:00
"""
subprocess = Utils.subprocess
kw['shell'] = isinstance(cmd, str)
self.log_command(cmd, kw)
2011-09-10 11:13:51 +02:00
2012-02-06 02:07:18 +01:00
if self.logger:
self.logger.info(cmd)
if 'stdout' not in kw:
kw['stdout'] = subprocess.PIPE
if 'stderr' not in kw:
kw['stderr'] = subprocess.PIPE
2012-02-06 02:07:18 +01:00
if Logs.verbose and not kw['shell'] and not Utils.check_exe(cmd[0], env=kw.get('env', os.environ)):
# This call isn't a shell command, and if the specified exe doesn't exist, check for a relative path being set
# with cwd and if so assume the caller knows what they're doing and don't pre-emptively fail
if not (cmd[0][0] == '.' and 'cwd' in kw):
raise Errors.WafError('Program %s not found!' % cmd[0])
cargs = {}
if 'timeout' in kw:
if sys.hexversion >= 0x3030000:
cargs['timeout'] = kw['timeout']
if not 'start_new_session' in kw:
kw['start_new_session'] = True
del kw['timeout']
if 'input' in kw:
if kw['input']:
cargs['input'] = kw['input']
2016-02-06 13:03:43 +01:00
kw['stdin'] = subprocess.PIPE
del kw['input']
2015-12-24 23:10:56 +01:00
if 'cwd' in kw:
if not isinstance(kw['cwd'], str):
kw['cwd'] = kw['cwd'].abspath()
encoding = kw.pop('decode_as', default_encoding)
2011-09-10 11:13:51 +02:00
try:
ret, out, err = Utils.run_process(cmd, kw, cargs)
2012-02-06 02:07:18 +01:00
except Exception as e:
raise Errors.WafError('Execution failure: %s' % str(e), ex=e)
if out:
if not isinstance(out, str):
out = out.decode(encoding, errors='replace')
2011-09-10 11:13:51 +02:00
if self.logger:
2016-03-19 14:46:22 +01:00
self.logger.debug('out: %s', out)
else:
2014-01-16 02:03:53 +01:00
Logs.info(out, extra={'stream':sys.stdout, 'c1': ''})
2012-02-06 02:07:18 +01:00
if err:
if not isinstance(err, str):
err = err.decode(encoding, errors='replace')
2012-02-06 02:07:18 +01:00
if self.logger:
self.logger.error('err: %s' % err)
else:
2014-01-16 02:03:53 +01:00
Logs.info(err, extra={'stream':sys.stderr, 'c1': ''})
2012-02-06 02:07:18 +01:00
return ret
2011-09-10 11:13:51 +02:00
def cmd_and_log(self, cmd, **kw):
"""
2017-02-15 08:01:15 +01:00
Executes a process and returns stdout/stderr if the execution is successful.
2011-09-10 11:13:51 +02:00
An exception is thrown when the exit status is non-0. In that case, both stderr and stdout
will be bound to the WafError object (configuration tests)::
2011-09-10 11:13:51 +02:00
def configure(conf):
out = conf.cmd_and_log(['echo', 'hello'], output=waflib.Context.STDOUT, quiet=waflib.Context.BOTH)
(out, err) = conf.cmd_and_log(['echo', 'hello'], output=waflib.Context.BOTH)
(out, err) = conf.cmd_and_log(cmd, input='\\n'.encode(), output=waflib.Context.STDOUT)
2011-09-10 11:13:51 +02:00
try:
conf.cmd_and_log(['which', 'someapp'], output=waflib.Context.BOTH)
except Errors.WafError as e:
2011-09-10 11:13:51 +02:00
print(e.stdout, e.stderr)
:param cmd: args for subprocess.Popen
2016-06-25 02:38:26 +02:00
:type cmd: list or string
:param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate.
2016-06-25 02:38:26 +02:00
:type kw: dict
:returns: a tuple containing the contents of stdout and stderr
:rtype: string
2016-06-25 02:38:26 +02:00
:raises: :py:class:`waflib.Errors.WafError` if an invalid executable is specified for a non-shell process
:raises: :py:class:`waflib.Errors.WafError` in case of execution failure; stdout/stderr/returncode are bound to the exception object
2011-09-10 11:13:51 +02:00
"""
subprocess = Utils.subprocess
kw['shell'] = isinstance(cmd, str)
self.log_command(cmd, kw)
2011-09-10 11:13:51 +02:00
2018-05-19 11:30:05 +02:00
quiet = kw.pop('quiet', None)
to_ret = kw.pop('output', STDOUT)
2011-09-10 11:13:51 +02:00
if Logs.verbose and not kw['shell'] and not Utils.check_exe(cmd[0], env=kw.get('env', os.environ)):
2016-06-05 00:23:57 +02:00
raise Errors.WafError('Program %r not found!' % cmd[0])
2011-09-10 11:13:51 +02:00
kw['stdout'] = kw['stderr'] = subprocess.PIPE
if quiet is None:
self.to_log(cmd)
cargs = {}
if 'timeout' in kw:
if sys.hexversion >= 0x3030000:
cargs['timeout'] = kw['timeout']
if not 'start_new_session' in kw:
kw['start_new_session'] = True
del kw['timeout']
if 'input' in kw:
if kw['input']:
cargs['input'] = kw['input']
2016-02-06 13:03:43 +01:00
kw['stdin'] = subprocess.PIPE
del kw['input']
2016-01-02 01:54:52 +01:00
if 'cwd' in kw:
if not isinstance(kw['cwd'], str):
kw['cwd'] = kw['cwd'].abspath()
encoding = kw.pop('decode_as', default_encoding)
2016-01-02 01:54:52 +01:00
2011-09-10 11:13:51 +02:00
try:
ret, out, err = Utils.run_process(cmd, kw, cargs)
2011-09-10 11:13:51 +02:00
except Exception as e:
2011-09-17 02:17:14 +02:00
raise Errors.WafError('Execution failure: %s' % str(e), ex=e)
2011-09-10 11:13:51 +02:00
if not isinstance(out, str):
out = out.decode(encoding, errors='replace')
2011-09-10 11:13:51 +02:00
if not isinstance(err, str):
err = err.decode(encoding, errors='replace')
2011-09-10 11:13:51 +02:00
if out and quiet != STDOUT and quiet != BOTH:
self.to_log('out: %s' % out)
if err and quiet != STDERR and quiet != BOTH:
self.to_log('err: %s' % err)
if ret:
e = Errors.WafError('Command %r returned %r' % (cmd, ret))
e.returncode = ret
2011-09-10 11:13:51 +02:00
e.stderr = err
e.stdout = out
raise e
if to_ret == BOTH:
return (out, err)
elif to_ret == STDERR:
return err
return out
def fatal(self, msg, ex=None):
"""
2016-06-25 02:38:26 +02:00
Prints an error message in red and stops command execution; this is
usually used in the configuration section::
2011-09-10 11:13:51 +02:00
def configure(conf):
conf.fatal('a requirement is missing')
:param msg: message to display
:type msg: string
:param ex: optional exception object
:type ex: exception
2016-06-25 02:38:26 +02:00
:raises: :py:class:`waflib.Errors.ConfigurationError`
2011-09-10 11:13:51 +02:00
"""
if self.logger:
self.logger.info('from %s: %s' % (self.path.abspath(), msg))
try:
2017-07-20 19:26:22 +02:00
logfile = self.logger.handlers[0].baseFilename
2016-05-14 12:15:15 +02:00
except AttributeError:
2011-09-10 11:13:51 +02:00
pass
2017-07-20 19:26:22 +02:00
else:
if os.environ.get('WAF_PRINT_FAILURE_LOG'):
# see #1930
msg = 'Log from (%s):\n%s\n' % (logfile, Utils.readf(logfile))
else:
msg = '%s\n(complete log in %s)' % (msg, logfile)
2011-09-10 11:13:51 +02:00
raise self.errors.ConfigurationError(msg, ex=ex)
def to_log(self, msg):
"""
2016-06-25 02:38:26 +02:00
Logs information to the logger (if present), or to stderr.
Empty messages are not printed::
2011-09-10 11:13:51 +02:00
def build(bld):
bld.to_log('starting the build')
2018-07-29 01:46:58 +02:00
Provide a logger on the context class or override this method if necessary.
2011-09-10 11:13:51 +02:00
:param msg: message
:type msg: string
"""
if not msg:
return
if self.logger:
self.logger.info(msg)
else:
sys.stderr.write(str(msg))
sys.stderr.flush()
2014-02-20 22:38:22 +01:00
def msg(self, *k, **kw):
2011-09-10 11:13:51 +02:00
"""
2016-06-25 02:38:26 +02:00
Prints a configuration message of the form ``msg: result``.
2011-09-10 11:13:51 +02:00
The second part of the message will be in colors. The output
2020-04-29 01:27:51 +02:00
can be disabled easily by setting ``in_msg`` to a positive value::
2011-09-10 11:13:51 +02:00
def configure(conf):
self.in_msg = 1
conf.msg('Checking for library foo', 'ok')
# no output
:param msg: message to display to the user
:type msg: string
:param result: result to display
:type result: string or boolean
:param color: color to use, see :py:const:`waflib.Logs.colors_lst`
:type color: string
"""
2014-02-20 22:38:22 +01:00
try:
msg = kw['msg']
except KeyError:
msg = k[0]
self.start_msg(msg, **kw)
2011-09-10 11:13:51 +02:00
2014-02-20 22:38:22 +01:00
try:
result = kw['result']
except KeyError:
result = k[1]
2016-04-19 22:00:21 +02:00
color = kw.get('color')
2011-09-10 11:13:51 +02:00
if not isinstance(color, str):
color = result and 'GREEN' or 'YELLOW'
self.end_msg(result, color, **kw)
2011-09-10 11:13:51 +02:00
def start_msg(self, *k, **kw):
2011-09-10 11:13:51 +02:00
"""
2016-06-25 02:38:26 +02:00
Prints the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg`
2011-09-10 11:13:51 +02:00
"""
2016-04-19 22:00:21 +02:00
if kw.get('quiet'):
return
2016-04-19 22:00:21 +02:00
msg = kw.get('msg') or k[0]
2011-09-10 11:13:51 +02:00
try:
if self.in_msg:
self.in_msg += 1
return
2012-02-11 14:31:00 +01:00
except AttributeError:
2011-09-10 11:13:51 +02:00
self.in_msg = 0
self.in_msg += 1
try:
self.line_just = max(self.line_just, len(msg))
except AttributeError:
self.line_just = max(40, len(msg))
for x in (self.line_just * '-', msg):
self.to_log(x)
Logs.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='')
def end_msg(self, *k, **kw):
2016-06-25 02:38:26 +02:00
"""Prints the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`"""
2016-04-19 22:00:21 +02:00
if kw.get('quiet'):
return
2011-09-10 11:13:51 +02:00
self.in_msg -= 1
if self.in_msg:
return
2016-04-19 22:00:21 +02:00
result = kw.get('result') or k[0]
2011-09-10 11:13:51 +02:00
defcolor = 'GREEN'
2017-04-22 22:08:00 +02:00
if result is True:
2011-09-10 11:13:51 +02:00
msg = 'ok'
2017-04-22 22:08:00 +02:00
elif not result:
2011-09-10 11:13:51 +02:00
msg = 'not found'
defcolor = 'YELLOW'
else:
msg = str(result)
self.to_log(msg)
2014-05-17 00:11:34 +02:00
try:
color = kw['color']
except KeyError:
if len(k) > 1 and k[1] in Logs.colors_lst:
# compatibility waf 1.7
color = k[1]
else:
color = defcolor
Logs.pprint(color, msg)
2011-09-10 11:13:51 +02:00
def load_special_tools(self, var, ban=[]):
2019-01-05 16:16:48 +01:00
"""
2016-06-25 02:38:26 +02:00
Loads third-party extensions modules for certain programming languages
by trying to list certain files in the extras/ directory. This method
is typically called once for a programming language group, see for
example :py:mod:`waflib.Tools.compiler_c`
2019-01-05 16:16:48 +01:00
:param var: glob expression, for example 'cxx\\_\\*.py'
2016-06-25 02:38:26 +02:00
:type var: string
:param ban: list of exact file names to exclude
:type ban: list of string
"""
if os.path.isdir(waf_dir):
lst = self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var)
for x in lst:
if not x.name in ban:
load_tool(x.name.replace('.py', ''))
else:
from zipfile import PyZipFile
waflibs = PyZipFile(waf_dir)
lst = waflibs.namelist()
for x in lst:
2016-06-05 00:23:57 +02:00
if not re.match('waflib/extras/%s' % var.replace('*', '.*'), var):
continue
f = os.path.basename(x)
doban = False
for b in ban:
2016-06-05 00:23:57 +02:00
r = b.replace('*', '.*')
if re.match(r, f):
doban = True
if not doban:
f = f.replace('.py', '')
load_tool(f)
2011-09-10 11:13:51 +02:00
cache_modules = {}
"""
2016-06-25 02:38:26 +02:00
Dictionary holding already loaded modules (wscript), indexed by their absolute path.
2011-09-10 11:13:51 +02:00
The modules are added automatically by :py:func:`waflib.Context.load_module`
"""
def load_module(path, encoding=None):
2011-09-10 11:13:51 +02:00
"""
2016-06-25 02:38:26 +02:00
Loads a wscript file as a python module. This method caches results in :py:attr:`waflib.Context.cache_modules`
2011-09-10 11:13:51 +02:00
:param path: file path
:type path: string
:return: Loaded Python module
:rtype: module
"""
try:
return cache_modules[path]
except KeyError:
pass
module = imp.new_module(WSCRIPT_FILE)
try:
2019-01-16 06:51:14 +01:00
code = Utils.readf(path, m='r', encoding=encoding)
2014-10-30 19:09:53 +01:00
except EnvironmentError:
2011-09-10 11:13:51 +02:00
raise Errors.WafError('Could not read the file %r' % path)
module_dir = os.path.dirname(path)
sys.path.insert(0, module_dir)
2016-06-25 02:38:26 +02:00
try:
exec(compile(code, path, 'exec'), module.__dict__)
finally:
sys.path.remove(module_dir)
2011-09-10 11:13:51 +02:00
cache_modules[path] = module
return module
2015-08-28 22:43:38 +02:00
def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True):
2011-09-10 11:13:51 +02:00
"""
2019-01-05 16:20:45 +01:00
Imports a Waf tool as a python module, and stores it in the dict :py:const:`waflib.Context.Context.tools`
2011-09-10 11:13:51 +02:00
:type tool: string
:param tool: Name of the tool
:type tooldir: list
:param tooldir: List of directories to search for the tool module
2015-08-28 22:43:38 +02:00
:type with_sys_path: boolean
:param with_sys_path: whether or not to search the regular sys.path, besides waf_dir and potentially given tooldirs
2011-09-10 11:13:51 +02:00
"""
2012-06-09 09:42:11 +02:00
if tool == 'java':
tool = 'javaw' # jython
else:
2012-06-10 17:51:55 +02:00
tool = tool.replace('++', 'xx')
2011-09-10 11:13:51 +02:00
2016-06-25 02:38:26 +02:00
if not with_sys_path:
back_path = sys.path
sys.path = []
try:
if tooldir:
assert isinstance(tooldir, list)
sys.path = tooldir + sys.path
try:
__import__(tool)
except ImportError as e:
e.waf_sys_path = list(sys.path)
raise
finally:
for d in tooldir:
sys.path.remove(d)
2011-09-10 11:13:51 +02:00
ret = sys.modules[tool]
Context.tools[tool] = ret
return ret
else:
if not with_sys_path:
sys.path.insert(0, waf_dir)
2011-09-10 11:13:51 +02:00
try:
for x in ('waflib.Tools.%s', 'waflib.extras.%s', 'waflib.%s', '%s'):
try:
__import__(x % tool)
break
except ImportError:
x = None
else: # raise an exception
__import__(tool)
except ImportError as e:
e.waf_sys_path = list(sys.path)
raise
finally:
if not with_sys_path:
sys.path.remove(waf_dir)
ret = sys.modules[x % tool]
Context.tools[tool] = ret
return ret
finally:
2016-06-25 02:38:26 +02:00
if not with_sys_path:
sys.path += back_path
2011-09-10 11:13:51 +02:00