diff --git a/waflib/Context.py b/waflib/Context.py index 9321fb7a..17820bc1 100644 --- a/waflib/Context.py +++ b/waflib/Context.py @@ -3,7 +3,7 @@ # Thomas Nagy, 2010-2016 (ita) """ -Classes and functions required for waf commands +Classes and functions enabling the command system """ import os, re, imp, sys @@ -54,7 +54,7 @@ waf_dir = '' g_module = None """ -Module representing the main wscript file (see :py:const:`waflib.Context.run_dir`) +Module representing the top-level wscript file (see :py:const:`waflib.Context.run_dir`) """ STDOUT = 1 @@ -69,15 +69,17 @@ are added automatically by a metaclass. def create_context(cmd_name, *k, **kw): """ - Create a new :py:class:`waflib.Context.Context` instance corresponding to the given command. + 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 + :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` """ global classes for x in classes: @@ -89,8 +91,9 @@ def create_context(cmd_name, *k, **kw): class store_context(type): """ - Metaclass for storing the command classes into the list :py:const:`waflib.Context.classes` - Context classes must provide an attribute 'cmd' representing the command to execute + 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, dict): super(store_context, cls).__init__(name, bases, dict) @@ -111,7 +114,7 @@ class store_context(type): classes.insert(0, cls) ctx = store_context('ctx', (object,), {}) -"""Base class for the :py:class:`waflib.Context.Context` classes""" +"""Base class for all :py:class:`waflib.Context.Context` classes""" class Context(ctx): """ @@ -122,7 +125,7 @@ class Context(ctx): def foo(ctx): print(ctx.__class__.__name__) # waflib.Context.Context - Subclasses must define the attribute 'cmd': + Subclasses must define the class attributes 'cmd' and 'fun': :param cmd: command to execute as in ``waf cmd`` :type cmd: string @@ -140,7 +143,7 @@ class Context(ctx): tools = {} """ - A cache for modules (wscript files) read by :py:meth:`Context.Context.load` + A module cache for wscript files; see :py:meth:`Context.Context.load` """ def __init__(self, **kw): @@ -152,7 +155,7 @@ class Context(ctx): # 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.__module__ = 'waflib.Node' self.node_class.ctx = self self.root = self.node_class('', None) @@ -165,7 +168,7 @@ class Context(ctx): def finalize(self): """ - Use to free resources such as open files potentially held by the logger + Called to free resources such as logger files """ try: logger = self.logger @@ -177,11 +180,11 @@ class Context(ctx): def load(self, tool_list, *k, **kw): """ - Load a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun` from it. - A ``tooldir`` value may be provided as a list of module paths. + 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 - :param tool_list: list of Waf tools to use """ tools = Utils.to_list(tool_list) path = Utils.to_list(kw.get('tooldir', '')) @@ -195,15 +198,17 @@ class Context(ctx): def execute(self): """ - Execute the command. Redefine this method in subclasses. + Here, it calls the function name in the top-level wscript file. Most subclasses + redefine this method to provide additional functionality. """ global g_module 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 node given is set - as an attribute ``self.cur_script``, and as the current path ``self.path`` + 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` @@ -215,7 +220,7 @@ class Context(ctx): def post_recurse(self, node): """ - Restore ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates. + 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` @@ -226,10 +231,13 @@ class Context(ctx): def recurse(self, dirs, name=None, mandatory=True, once=True, encoding=None): """ - Run user code from the supplied list of directories. + 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. + 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 @@ -290,18 +298,22 @@ class Context(ctx): def exec_command(self, cmd, **kw): """ - Execute a command and return the exit status. If the context has the attribute 'log', - capture and log the process stderr/stdout for logging purposes:: + Runs an external process and returns the exit status:: def run(tsk): ret = tsk.generator.bld.exec_command('touch foo.txt') return ret - This method captures the standard/error outputs (Issue 1101), but it does not return the values - unlike :py:meth:`waflib.Context.Context.cmd_and_log` + 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 """ subprocess = Utils.subprocess kw['shell'] = isinstance(cmd, str) @@ -358,7 +370,7 @@ class Context(ctx): def cmd_and_log(self, cmd, **kw): """ - Execute a command and return stdout/stderr if the execution is successful. + Executes a proces 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:: @@ -372,7 +384,13 @@ class Context(ctx): 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: 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; stdout/stderr/returncode are bound to the exception object """ subprocess = Utils.subprocess kw['shell'] = isinstance(cmd, str) @@ -442,7 +460,8 @@ class Context(ctx): def fatal(self, msg, ex=None): """ - Raise a configuration error to interrupt the execution immediately:: + 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') @@ -451,6 +470,7 @@ class Context(ctx): :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)) @@ -462,13 +482,13 @@ class Context(ctx): def to_log(self, msg): """ - Log some information to the logger (if present), or to stderr. If the message is empty, - it is not printed:: + Logs information to the logger (if present), or to stderr. + Empty messages are not printed:: def build(bld): bld.to_log('starting the build') - When in doubt, override this method, or provide a logger on the context class. + Provide a logger on the context class or override this methid if necessary. :param msg: message :type msg: string @@ -484,7 +504,7 @@ class Context(ctx): def msg(self, *k, **kw): """ - Print a configuration message of the form ``msg: result``. + 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:: @@ -520,7 +540,7 @@ class Context(ctx): def start_msg(self, *k, **kw): """ - Print the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg` + Prints the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg` """ if kw.get('quiet'): return @@ -543,7 +563,7 @@ class Context(ctx): Logs.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='') def end_msg(self, *k, **kw): - """Print the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`""" + """Prints the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`""" if kw.get('quiet'): return self.in_msg -= 1 @@ -573,6 +593,17 @@ class Context(ctx): 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 + """ global waf_dir if os.path.isdir(waf_dir): lst = self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var) @@ -598,13 +629,13 @@ class Context(ctx): cache_modules = {} """ -Dictionary holding already loaded modules, keyed by their absolute path. +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): """ - Load a source file as a python module. + 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 @@ -624,17 +655,17 @@ def load_module(path, encoding=None): 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) + 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): """ - Import a Waf tool (python module), and store it in the dict :py:const:`waflib.Context.Context.tools` + Importx 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 @@ -648,8 +679,9 @@ def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True): else: tool = tool.replace('++', 'xx') - origSysPath = sys.path - if not with_sys_path: sys.path = [] + if not with_sys_path: + back_path = sys.path + sys.path = [] try: if tooldir: assert isinstance(tooldir, list) @@ -679,5 +711,6 @@ def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True): Context.tools[tool] = ret return ret finally: - if not with_sys_path: sys.path += origSysPath + if not with_sys_path: + sys.path += back_path diff --git a/waflib/Logs.py b/waflib/Logs.py index 328d36c0..2a8db982 100644 --- a/waflib/Logs.py +++ b/waflib/Logs.py @@ -1,6 +1,6 @@ #!/usr/bin/env python # encoding: utf-8 -# Thomas Nagy, 2005-2010 (ita) +# Thomas Nagy, 2005-2016 (ita) """ logging, colors, terminal width and pretty-print @@ -81,8 +81,9 @@ get_term_cols.__doc__ = """ """ def get_color(cl): - if not colors_lst['USE']: return '' - return colors_lst.get(cl, '') + if colors_lst['USE']: + return colors_lst.get(cl, '') + return '' class color_dict(object): """attribute-based color access, eg: colors.PINK""" diff --git a/waflib/fixpy2.py b/waflib/fixpy2.py index 3a6bbed1..befc1be4 100644 --- a/waflib/fixpy2.py +++ b/waflib/fixpy2.py @@ -2,23 +2,19 @@ # encoding: utf-8 # Thomas Nagy, 2010-2015 (ita) -""" -burn a book, save a tree -""" - import os all_modifs = {} def fixdir(dir): - """call all the substitution functions on the waf folders""" + """Call all substitution functions on Waf folders""" global all_modifs for k in all_modifs: for v in all_modifs[k]: modif(os.path.join(dir, 'waflib'), k, v) def modif(dir, name, fun): - """execute a substitution function""" + """Call a substitution function""" if name == '*': lst = [] for y in '. Tools extras'.split():