This commit is contained in:
Thomas Nagy 2016-06-25 02:38:26 +02:00
parent 21e9be8cdb
commit ccc77cd9f5
No known key found for this signature in database
GPG Key ID: 67A565EDFDF90E64
3 changed files with 82 additions and 52 deletions

View File

@ -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

View File

@ -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"""

View File

@ -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():