diff options
Diffstat (limited to 'waflib/Context.py')
m--------- | waflib | 0 | ||||
-rw-r--r-- | waflib/Context.py | 737 |
2 files changed, 0 insertions, 737 deletions
diff --git a/waflib b/waflib new file mode 160000 +Subproject 2314e236ca6e7d94a26c3c17091da0f25f5867f diff --git a/waflib/Context.py b/waflib/Context.py deleted file mode 100644 index 876ea46b..00000000 --- a/waflib/Context.py +++ /dev/null @@ -1,737 +0,0 @@ -#!/usr/bin/env python -# encoding: utf-8 -# Thomas Nagy, 2010-2018 (ita) - -""" -Classes and functions enabling the command system -""" - -import os, re, imp, sys -from waflib import Utils, Errors, Logs -import waflib.Node - -# the following 3 constants are updated on each new release (do not touch) -HEXVERSION=0x2000f00 -"""Constant updated on new releases""" - -WAFVERSION="2.0.15" -"""Constant updated on new releases""" - -WAFREVISION="503db290b73ef738a495e0d116d6f8ee0b98dcc2" -"""Git revision when the waf version is updated""" - -ABI = 20 -"""Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)""" - -DBFILE = '.wafpickle-%s-%d-%d' % (sys.platform, sys.hexversion, ABI) -"""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""" - -g_module = None -""" -Module representing the top-level wscript file (see :py:const:`waflib.Context.run_dir`) -""" - -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): - """ - Returns a new :py:class:`waflib.Context.Context` instance corresponding to the given command. - Used in particular by :py:func:`waflib.Scripting.run_command` - - :param cmd_name: command name - :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 - :return: Context object - :rtype: :py:class:`waflib.Context.Context` - """ - 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): - """ - 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. - """ - def __init__(cls, name, bases, dct): - super(store_context, cls).__init__(name, bases, dct) - name = cls.__name__ - - if name in ('ctx', 'Context'): - 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,), {}) -"""Base class for all :py:class:`waflib.Context.Context` classes""" - -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 - - Subclasses must define the class attributes 'cmd' and 'fun': - - :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 - - """ - - errors = Errors - """ - Shortcut to :py:mod:`waflib.Errors` provided for convenience - """ - - tools = {} - """ - A module cache for wscript files; see :py:meth:`Context.Context.load` - """ - - 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 - self.node_class = type('Nod3', (waflib.Node.Node,), {}) - self.node_class.__module__ = 'waflib.Node' - 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 - - def finalize(self): - """ - Called to free resources such as logger files - """ - try: - logger = self.logger - except AttributeError: - pass - else: - Logs.free_logger(logger) - delattr(self, 'logger') - - def load(self, tool_list, *k, **kw): - """ - Loads a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun` - from it. A ``tooldir`` argument may be provided as a list of module paths. - - :param tool_list: list of Waf tool names to load - :type tool_list: list of string or space-separated string - """ - tools = Utils.to_list(tool_list) - path = Utils.to_list(kw.get('tooldir', '')) - with_sys_path = kw.get('with_sys_path', True) - - for t in tools: - module = load_tool(t, path, with_sys_path=with_sys_path) - fun = getattr(module, kw.get('name', self.fun), None) - if fun: - fun(self) - - def execute(self): - """ - Here, it calls the function name in the top-level wscript file. Most subclasses - redefine this method to provide additional functionality. - """ - self.recurse([os.path.dirname(g_module.root_path)]) - - def pre_recurse(self, node): - """ - 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`` - - :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): - """ - Restores ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates. - - :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): - """ - Runs user-provided functions from the supplied list of directories. - The directories can be either absolute, or relative to the directory - 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. - - :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 - except AttributeError: - 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: - function_code = node.read('r', encoding) - 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 - self.pre_recurse(node) - try: - wscript_module = load_module(node.abspath(), encoding=encoding) - 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())) - 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) - 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) - - def exec_command(self, cmd, **kw): - """ - Runs an external process and returns the exit status:: - - def run(tsk): - ret = tsk.generator.bld.exec_command('touch foo.txt') - return ret - - 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. - - :param cmd: command argument for subprocess.Popen - :type cmd: string or list - :param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate. - :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 - """ - subprocess = Utils.subprocess - kw['shell'] = isinstance(cmd, str) - self.log_command(cmd, kw) - - 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 - - if Logs.verbose and not kw['shell'] and not Utils.check_exe(cmd[0]): - 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'] - kw['stdin'] = subprocess.PIPE - del kw['input'] - - if 'cwd' in kw: - if not isinstance(kw['cwd'], str): - kw['cwd'] = kw['cwd'].abspath() - - encoding = kw.pop('decode_as', default_encoding) - - try: - ret, out, err = Utils.run_process(cmd, kw, cargs) - 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') - if self.logger: - self.logger.debug('out: %s', out) - else: - Logs.info(out, extra={'stream':sys.stdout, 'c1': ''}) - if err: - if not isinstance(err, str): - err = err.decode(encoding, errors='replace') - if self.logger: - self.logger.error('err: %s' % err) - else: - Logs.info(err, extra={'stream':sys.stderr, 'c1': ''}) - - return ret - - def cmd_and_log(self, cmd, **kw): - """ - Executes a process and returns stdout/stderr if the execution is successful. - 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):: - - 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) - try: - conf.cmd_and_log(['which', 'someapp'], output=waflib.Context.BOTH) - except Errors.WafError as e: - print(e.stdout, e.stderr) - - :param cmd: args for subprocess.Popen - :type cmd: list or string - :param kw: keyword arguments for subprocess.Popen. The parameters input/timeout will be passed to wait/communicate. - :type kw: dict - :returns: a tuple containing the contents of stdout and stderr - :rtype: string - :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 - """ - subprocess = Utils.subprocess - kw['shell'] = isinstance(cmd, str) - self.log_command(cmd, kw) - - quiet = kw.pop('quiet', None) - to_ret = kw.pop('output', STDOUT) - - if Logs.verbose and not kw['shell'] and not Utils.check_exe(cmd[0]): - raise Errors.WafError('Program %r not found!' % cmd[0]) - - 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'] - kw['stdin'] = subprocess.PIPE - del kw['input'] - - if 'cwd' in kw: - if not isinstance(kw['cwd'], str): - kw['cwd'] = kw['cwd'].abspath() - - encoding = kw.pop('decode_as', default_encoding) - - try: - ret, out, err = Utils.run_process(cmd, kw, cargs) - except Exception as e: - raise Errors.WafError('Execution failure: %s' % str(e), ex=e) - - if not isinstance(out, str): - out = out.decode(encoding, errors='replace') - if not isinstance(err, str): - err = err.decode(encoding, errors='replace') - - 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 - 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): - """ - Prints an error message in red and stops command execution; this is - usually used in the configuration section:: - - 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 - :raises: :py:class:`waflib.Errors.ConfigurationError` - """ - if self.logger: - self.logger.info('from %s: %s' % (self.path.abspath(), msg)) - try: - logfile = self.logger.handlers[0].baseFilename - except AttributeError: - pass - 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) - raise self.errors.ConfigurationError(msg, ex=ex) - - def to_log(self, msg): - """ - Logs information to the logger (if present), or to stderr. - Empty messages are not printed:: - - def build(bld): - bld.to_log('starting the build') - - Provide a logger on the context class or override this method if necessary. - - :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() - - - def msg(self, *k, **kw): - """ - Prints a configuration message of the form ``msg: result``. - The second part of the message will be in colors. The output - can be disabled easly by setting ``in_msg`` to a positive value:: - - 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 - """ - try: - msg = kw['msg'] - except KeyError: - msg = k[0] - - self.start_msg(msg, **kw) - - try: - result = kw['result'] - except KeyError: - result = k[1] - - color = kw.get('color') - if not isinstance(color, str): - color = result and 'GREEN' or 'YELLOW' - - self.end_msg(result, color, **kw) - - def start_msg(self, *k, **kw): - """ - Prints the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg` - """ - if kw.get('quiet'): - return - - msg = kw.get('msg') or k[0] - try: - if self.in_msg: - self.in_msg += 1 - return - except AttributeError: - 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): - """Prints the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`""" - if kw.get('quiet'): - return - self.in_msg -= 1 - if self.in_msg: - return - - result = kw.get('result') or k[0] - - defcolor = 'GREEN' - if result is True: - msg = 'ok' - elif not result: - msg = 'not found' - defcolor = 'YELLOW' - else: - msg = str(result) - - self.to_log(msg) - 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) - - def load_special_tools(self, var, ban=[]): - """ - 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` - - :param var: glob expression, for example 'cxx\\_\\*.py' - :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: - if not re.match('waflib/extras/%s' % var.replace('*', '.*'), var): - continue - f = os.path.basename(x) - doban = False - for b in ban: - r = b.replace('*', '.*') - if re.match(r, f): - doban = True - if not doban: - f = f.replace('.py', '') - load_tool(f) - -cache_modules = {} -""" -Dictionary holding already loaded modules (wscript), indexed by their absolute path. -The modules are added automatically by :py:func:`waflib.Context.load_module` -""" - -def load_module(path, encoding=None): - """ - Loads a wscript file as a python module. This method caches results in :py:attr:`waflib.Context.cache_modules` - - :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: - code = Utils.readf(path, m='r', encoding=encoding) - except EnvironmentError: - raise Errors.WafError('Could not read the file %r' % path) - - module_dir = os.path.dirname(path) - sys.path.insert(0, module_dir) - try: - exec(compile(code, path, 'exec'), module.__dict__) - finally: - sys.path.remove(module_dir) - - cache_modules[path] = module - return module - -def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True): - """ - Imports a Waf tool as a python module, and stores it in the dict :py:const:`waflib.Context.Context.tools` - - :type tool: string - :param tool: Name of the tool - :type tooldir: list - :param tooldir: List of directories to search for the tool module - :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 - """ - if tool == 'java': - tool = 'javaw' # jython - else: - tool = tool.replace('++', 'xx') - - 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) - ret = sys.modules[tool] - Context.tools[tool] = ret - return ret - else: - if not with_sys_path: - sys.path.insert(0, waf_dir) - 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: - if not with_sys_path: - sys.path += back_path - |