Browse Source
git-subtree-dir: waflib git-subtree-split: 982416b8a6c6728e200243e1be3ab60435c08830zrythm_meson

commit
c2b0355b0d
182 changed files with 41453 additions and 0 deletions
@ -0,0 +1,25 @@
@@ -0,0 +1,25 @@
|
||||
Redistribution and use in source and binary forms, with or without |
||||
modification, are permitted provided that the following conditions |
||||
are met: |
||||
|
||||
1. Redistributions of source code must retain the above copyright |
||||
notice, this list of conditions and the following disclaimer. |
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright |
||||
notice, this list of conditions and the following disclaimer in the |
||||
documentation and/or other materials provided with the distribution. |
||||
|
||||
3. The name of the author may not be used to endorse or promote products |
||||
derived from this software without specific prior written permission. |
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR |
||||
IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
||||
DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
||||
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
||||
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
||||
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING |
||||
IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
||||
POSSIBILITY OF SUCH DAMAGE. |
@ -0,0 +1,361 @@
@@ -0,0 +1,361 @@
|
||||
#!/usr/bin/env python |
||||
# encoding: utf-8 |
||||
# Thomas Nagy, 2005-2018 (ita) |
||||
|
||||
""" |
||||
|
||||
ConfigSet: a special dict |
||||
|
||||
The values put in :py:class:`ConfigSet` must be serializable (dicts, lists, strings) |
||||
""" |
||||
|
||||
import copy, re, os |
||||
from waflib import Logs, Utils |
||||
re_imp = re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$', re.M) |
||||
|
||||
class ConfigSet(object): |
||||
""" |
||||
A copy-on-write dict with human-readable serialized format. The serialization format |
||||
is human-readable (python-like) and performed by using eval() and repr(). |
||||
For high performance prefer pickle. Do not store functions as they are not serializable. |
||||
|
||||
The values can be accessed by attributes or by keys:: |
||||
|
||||
from waflib.ConfigSet import ConfigSet |
||||
env = ConfigSet() |
||||
env.FOO = 'test' |
||||
env['FOO'] = 'test' |
||||
""" |
||||
__slots__ = ('table', 'parent') |
||||
def __init__(self, filename=None): |
||||
self.table = {} |
||||
""" |
||||
Internal dict holding the object values |
||||
""" |
||||
#self.parent = None |
||||
|
||||
if filename: |
||||
self.load(filename) |
||||
|
||||
def __contains__(self, key): |
||||
""" |
||||
Enables the *in* syntax:: |
||||
|
||||
if 'foo' in env: |
||||
print(env['foo']) |
||||
""" |
||||
if key in self.table: |
||||
return True |
||||
try: |
||||
return self.parent.__contains__(key) |
||||
except AttributeError: |
||||
return False # parent may not exist |
||||
|
||||
def keys(self): |
||||
"""Dict interface""" |
||||
keys = set() |
||||
cur = self |
||||
while cur: |
||||
keys.update(cur.table.keys()) |
||||
cur = getattr(cur, 'parent', None) |
||||
keys = list(keys) |
||||
keys.sort() |
||||
return keys |
||||
|
||||
def __iter__(self): |
||||
return iter(self.keys()) |
||||
|
||||
def __str__(self): |
||||
"""Text representation of the ConfigSet (for debugging purposes)""" |
||||
return "\n".join(["%r %r" % (x, self.__getitem__(x)) for x in self.keys()]) |
||||
|
||||
def __getitem__(self, key): |
||||
""" |
||||
Dictionary interface: get value from key:: |
||||
|
||||
def configure(conf): |
||||
conf.env['foo'] = {} |
||||
print(env['foo']) |
||||
""" |
||||
try: |
||||
while 1: |
||||
x = self.table.get(key) |
||||
if not x is None: |
||||
return x |
||||
self = self.parent |
||||
except AttributeError: |
||||
return [] |
||||
|
||||
def __setitem__(self, key, value): |
||||
""" |
||||
Dictionary interface: set value from key |
||||
""" |
||||
self.table[key] = value |
||||
|
||||
def __delitem__(self, key): |
||||
""" |
||||
Dictionary interface: mark the value as missing |
||||
""" |
||||
self[key] = [] |
||||
|
||||
def __getattr__(self, name): |
||||
""" |
||||
Attribute access provided for convenience. The following forms are equivalent:: |
||||
|
||||
def configure(conf): |
||||
conf.env.value |
||||
conf.env['value'] |
||||
""" |
||||
if name in self.__slots__: |
||||
return object.__getattribute__(self, name) |
||||
else: |
||||
return self[name] |
||||
|
||||
def __setattr__(self, name, value): |
||||
""" |
||||
Attribute access provided for convenience. The following forms are equivalent:: |
||||
|
||||
def configure(conf): |
||||
conf.env.value = x |
||||
env['value'] = x |
||||
""" |
||||
if name in self.__slots__: |
||||
object.__setattr__(self, name, value) |
||||
else: |
||||
self[name] = value |
||||
|
||||
def __delattr__(self, name): |
||||
""" |
||||
Attribute access provided for convenience. The following forms are equivalent:: |
||||
|
||||
def configure(conf): |
||||
del env.value |
||||
del env['value'] |
||||
""" |
||||
if name in self.__slots__: |
||||
object.__delattr__(self, name) |
||||
else: |
||||
del self[name] |
||||
|
||||
def derive(self): |
||||
""" |
||||
Returns a new ConfigSet deriving from self. The copy returned |
||||
will be a shallow copy:: |
||||
|
||||
from waflib.ConfigSet import ConfigSet |
||||
env = ConfigSet() |
||||
env.append_value('CFLAGS', ['-O2']) |
||||
child = env.derive() |
||||
child.CFLAGS.append('test') # warning! this will modify 'env' |
||||
child.CFLAGS = ['-O3'] # new list, ok |
||||
child.append_value('CFLAGS', ['-O3']) # ok |
||||
|
||||
Use :py:func:`ConfigSet.detach` to detach the child from the parent. |
||||
""" |
||||
newenv = ConfigSet() |
||||
newenv.parent = self |
||||
return newenv |
||||
|
||||
def detach(self): |
||||
""" |
||||
Detaches this instance from its parent (if present) |
||||
|
||||
Modifying the parent :py:class:`ConfigSet` will not change the current object |
||||
Modifying this :py:class:`ConfigSet` will not modify the parent one. |
||||
""" |
||||
tbl = self.get_merged_dict() |
||||
try: |
||||
delattr(self, 'parent') |
||||
except AttributeError: |
||||
pass |
||||
else: |
||||
keys = tbl.keys() |
||||
for x in keys: |
||||
tbl[x] = copy.deepcopy(tbl[x]) |
||||
self.table = tbl |
||||
return self |
||||
|
||||
def get_flat(self, key): |
||||
""" |
||||
Returns a value as a string. If the input is a list, the value returned is space-separated. |
||||
|
||||
:param key: key to use |
||||
:type key: string |
||||
""" |
||||
s = self[key] |
||||
if isinstance(s, str): |
||||
return s |
||||
return ' '.join(s) |
||||
|
||||
def _get_list_value_for_modification(self, key): |
||||
""" |
||||
Returns a list value for further modification. |
||||
|
||||
The list may be modified inplace and there is no need to do this afterwards:: |
||||
|
||||
self.table[var] = value |
||||
""" |
||||
try: |
||||
value = self.table[key] |
||||
except KeyError: |
||||
try: |
||||
value = self.parent[key] |
||||
except AttributeError: |
||||
value = [] |
||||
else: |
||||
if isinstance(value, list): |
||||
# force a copy |
||||
value = value[:] |
||||
else: |
||||
value = [value] |
||||
self.table[key] = value |
||||
else: |
||||
if not isinstance(value, list): |
||||
self.table[key] = value = [value] |
||||
return value |
||||
|
||||
def append_value(self, var, val): |
||||
""" |
||||
Appends a value to the specified config key:: |
||||
|
||||
def build(bld): |
||||
bld.env.append_value('CFLAGS', ['-O2']) |
||||
|
||||
The value must be a list or a tuple |
||||
""" |
||||
if isinstance(val, str): # if there were string everywhere we could optimize this |
||||
val = [val] |
||||
current_value = self._get_list_value_for_modification(var) |
||||
current_value.extend(val) |
||||
|
||||
def prepend_value(self, var, val): |
||||
""" |
||||
Prepends a value to the specified item:: |
||||
|
||||
def configure(conf): |
||||
conf.env.prepend_value('CFLAGS', ['-O2']) |
||||
|
||||
The value must be a list or a tuple |
||||
""" |
||||
if isinstance(val, str): |
||||
val = [val] |
||||
self.table[var] = val + self._get_list_value_for_modification(var) |
||||
|
||||
def append_unique(self, var, val): |
||||
""" |
||||
Appends a value to the specified item only if it's not already present:: |
||||
|
||||
def build(bld): |
||||
bld.env.append_unique('CFLAGS', ['-O2', '-g']) |
||||
|
||||
The value must be a list or a tuple |
||||
""" |
||||
if isinstance(val, str): |
||||
val = [val] |
||||
current_value = self._get_list_value_for_modification(var) |
||||
|
||||
for x in val: |
||||
if x not in current_value: |
||||
current_value.append(x) |
||||
|
||||
def get_merged_dict(self): |
||||
""" |
||||
Computes the merged dictionary from the fusion of self and all its parent |
||||
|
||||
:rtype: a ConfigSet object |
||||
""" |
||||
table_list = [] |
||||
env = self |
||||
while 1: |
||||
table_list.insert(0, env.table) |
||||
try: |
||||
env = env.parent |
||||
except AttributeError: |
||||
break |
||||
merged_table = {} |
||||
for table in table_list: |
||||
merged_table.update(table) |
||||
return merged_table |
||||
|
||||
def store(self, filename): |
||||
""" |
||||
Serializes the :py:class:`ConfigSet` data to a file. See :py:meth:`ConfigSet.load` for reading such files. |
||||
|
||||
:param filename: file to use |
||||
:type filename: string |
||||
""" |
||||
try: |
||||
os.makedirs(os.path.split(filename)[0]) |
||||
except OSError: |
||||
pass |
||||
|
||||
buf = [] |
||||
merged_table = self.get_merged_dict() |
||||
keys = list(merged_table.keys()) |
||||
keys.sort() |
||||
|
||||
try: |
||||
fun = ascii |
||||
except NameError: |
||||
fun = repr |
||||
|
||||
for k in keys: |
||||
if k != 'undo_stack': |
||||
buf.append('%s = %s\n' % (k, fun(merged_table[k]))) |
||||
Utils.writef(filename, ''.join(buf)) |
||||
|
||||
def load(self, filename): |
||||
""" |
||||
Restores contents from a file (current values are not cleared). Files are written using :py:meth:`ConfigSet.store`. |
||||
|
||||
:param filename: file to use |
||||
:type filename: string |
||||
""" |
||||
tbl = self.table |
||||
code = Utils.readf(filename, m='rU') |
||||
for m in re_imp.finditer(code): |
||||
g = m.group |
||||
tbl[g(2)] = eval(g(3)) |
||||
Logs.debug('env: %s', self.table) |
||||
|
||||
def update(self, d): |
||||
""" |
||||
Dictionary interface: replace values with the ones from another dict |
||||
|
||||
:param d: object to use the value from |
||||
:type d: dict-like object |
||||
""" |
||||
self.table.update(d) |
||||
|
||||
def stash(self): |
||||
""" |
||||
Stores the object state to provide transactionality semantics:: |
||||
|
||||
env = ConfigSet() |
||||
env.stash() |
||||
try: |
||||
env.append_value('CFLAGS', '-O3') |
||||
call_some_method(env) |
||||
finally: |
||||
env.revert() |
||||
|
||||
The history is kept in a stack, and is lost during the serialization by :py:meth:`ConfigSet.store` |
||||
""" |
||||
orig = self.table |
||||
tbl = self.table = self.table.copy() |
||||
for x in tbl.keys(): |
||||
tbl[x] = copy.deepcopy(tbl[x]) |
||||
self.undo_stack = self.undo_stack + [orig] |
||||
|
||||
def commit(self): |
||||
""" |
||||
Commits transactional changes. See :py:meth:`ConfigSet.stash` |
||||
""" |
||||
self.undo_stack.pop(-1) |
||||
|
||||
def revert(self): |
||||
""" |
||||
Reverts the object to a previous state. See :py:meth:`ConfigSet.stash` |
||||
""" |
||||
self.table = self.undo_stack.pop(-1) |
||||
|
@ -0,0 +1,638 @@
@@ -0,0 +1,638 @@
|
||||
#!/usr/bin/env python |
||||
# encoding: utf-8 |
||||
# Thomas Nagy, 2005-2018 (ita) |
||||
|
||||
""" |
||||
Configuration system |
||||
|
||||
A :py:class:`waflib.Configure.ConfigurationContext` instance is created when ``waf configure`` is called, it is used to: |
||||
|
||||
* create data dictionaries (ConfigSet instances) |
||||
* store the list of modules to import |
||||
* hold configuration routines such as ``find_program``, etc |
||||
""" |
||||
|
||||
import os, re, shlex, shutil, sys, time, traceback |
||||
from waflib import ConfigSet, Utils, Options, Logs, Context, Build, Errors |
||||
|
||||
WAF_CONFIG_LOG = 'config.log' |
||||
"""Name of the configuration log file""" |
||||
|
||||
autoconfig = False |
||||
"""Execute the configuration automatically""" |
||||
|
||||
conf_template = '''# project %(app)s configured on %(now)s by |
||||
# waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s) |
||||
# using %(args)s |
||||
#''' |
||||
|
||||
class ConfigurationContext(Context.Context): |
||||
'''configures the project''' |
||||
|
||||
cmd = 'configure' |
||||
|
||||
error_handlers = [] |
||||
""" |
||||
Additional functions to handle configuration errors |
||||
""" |
||||
|
||||
def __init__(self, **kw): |
||||
super(ConfigurationContext, self).__init__(**kw) |
||||
self.environ = dict(os.environ) |
||||
self.all_envs = {} |
||||
|
||||
self.top_dir = None |
||||
self.out_dir = None |
||||
|
||||
self.tools = [] # tools loaded in the configuration, and that will be loaded when building |
||||
|
||||
self.hash = 0 |
||||
self.files = [] |
||||
|
||||
self.tool_cache = [] |
||||
|
||||
self.setenv('') |
||||
|
||||
def setenv(self, name, env=None): |
||||
""" |
||||
Set a new config set for conf.env. If a config set of that name already exists, |
||||
recall it without modification. |
||||
|
||||
The name is the filename prefix to save to ``c4che/NAME_cache.py``, and it |
||||
is also used as *variants* by the build commands. |
||||
Though related to variants, whatever kind of data may be stored in the config set:: |
||||
|
||||
def configure(cfg): |
||||
cfg.env.ONE = 1 |
||||
cfg.setenv('foo') |
||||
cfg.env.ONE = 2 |
||||
|
||||
def build(bld): |
||||
2 == bld.env_of_name('foo').ONE |
||||
|
||||
:param name: name of the configuration set |
||||
:type name: string |
||||
:param env: ConfigSet to copy, or an empty ConfigSet is created |
||||
:type env: :py:class:`waflib.ConfigSet.ConfigSet` |
||||
""" |
||||
if name not in self.all_envs or env: |
||||
if not env: |
||||
env = ConfigSet.ConfigSet() |
||||
self.prepare_env(env) |
||||
else: |
||||
env = env.derive() |
||||
self.all_envs[name] = env |
||||
self.variant = name |
||||
|
||||
def get_env(self): |
||||
"""Getter for the env property""" |
||||
return self.all_envs[self.variant] |
||||
def set_env(self, val): |
||||
"""Setter for the env property""" |
||||
self.all_envs[self.variant] = val |
||||
|
||||
env = property(get_env, set_env) |
||||
|
||||
def init_dirs(self): |
||||
""" |
||||
Initialize the project directory and the build directory |
||||
""" |
||||
|
||||
top = self.top_dir |
||||
if not top: |
||||
top = Options.options.top |
||||
if not top: |
||||
top = getattr(Context.g_module, Context.TOP, None) |
||||
if not top: |
||||
top = self.path.abspath() |
||||
top = os.path.abspath(top) |
||||
|
||||
self.srcnode = (os.path.isabs(top) and self.root or self.path).find_dir(top) |
||||
assert(self.srcnode) |
||||
|
||||
out = self.out_dir |
||||
if not out: |
||||
out = Options.options.out |
||||
if not out: |
||||
out = getattr(Context.g_module, Context.OUT, None) |
||||
if not out: |
||||
out = Options.lockfile.replace('.lock-waf_%s_' % sys.platform, '').replace('.lock-waf', '') |
||||
|
||||
# someone can be messing with symlinks |
||||
out = os.path.realpath(out) |
||||
|
||||
self.bldnode = (os.path.isabs(out) and self.root or self.path).make_node(out) |
||||
self.bldnode.mkdir() |
||||
|
||||
if not os.path.isdir(self.bldnode.abspath()): |
||||
conf.fatal('Could not create the build directory %s' % self.bldnode.abspath()) |
||||
|
||||
def execute(self): |
||||
""" |
||||
See :py:func:`waflib.Context.Context.execute` |
||||
""" |
||||
self.init_dirs() |
||||
|
||||
self.cachedir = self.bldnode.make_node(Build.CACHE_DIR) |
||||
self.cachedir.mkdir() |
||||
|
||||
path = os.path.join(self.bldnode.abspath(), WAF_CONFIG_LOG) |
||||
self.logger = Logs.make_logger(path, 'cfg') |
||||
|
||||
app = getattr(Context.g_module, 'APPNAME', '') |
||||
if app: |
||||
ver = getattr(Context.g_module, 'VERSION', '') |
||||
if ver: |
||||
app = "%s (%s)" % (app, ver) |
||||
|
||||
params = {'now': time.ctime(), 'pyver': sys.hexversion, 'systype': sys.platform, 'args': " ".join(sys.argv), 'wafver': Context.WAFVERSION, 'abi': Context.ABI, 'app': app} |
||||
self.to_log(conf_template % params) |
||||
self.msg('Setting top to', self.srcnode.abspath()) |
||||
self.msg('Setting out to', self.bldnode.abspath()) |
||||
|
||||
if id(self.srcnode) == id(self.bldnode): |
||||
Logs.warn('Setting top == out') |
||||
elif id(self.path) != id(self.srcnode): |
||||
if self.srcnode.is_child_of(self.path): |
||||
Logs.warn('Are you certain that you do not want to set top="." ?') |
||||
|
||||
super(ConfigurationContext, self).execute() |
||||
|
||||
self.store() |
||||
|
||||
Context.top_dir = self.srcnode.abspath() |
||||
Context.out_dir = self.bldnode.abspath() |
||||
|
||||
# this will write a configure lock so that subsequent builds will |
||||
# consider the current path as the root directory (see prepare_impl). |
||||
# to remove: use 'waf distclean' |
||||
env = ConfigSet.ConfigSet() |
||||
env.argv = sys.argv |
||||
env.options = Options.options.__dict__ |
||||
env.config_cmd = self.cmd |
||||
|
||||
env.run_dir = Context.run_dir |
||||
env.top_dir = Context.top_dir |
||||
env.out_dir = Context.out_dir |
||||
|
||||
# conf.hash & conf.files hold wscript files paths and hash |
||||
# (used only by Configure.autoconfig) |
||||
env.hash = self.hash |
||||
env.files = self.files |
||||
env.environ = dict(self.environ) |
||||
|
||||
if not (self.env.NO_LOCK_IN_RUN or env.environ.get('NO_LOCK_IN_RUN') or getattr(Options.options, 'no_lock_in_run')): |
||||
env.store(os.path.join(Context.run_dir, Options.lockfile)) |
||||
if not (self.env.NO_LOCK_IN_TOP or env.environ.get('NO_LOCK_IN_TOP') or getattr(Options.options, 'no_lock_in_top')): |
||||
env.store(os.path.join(Context.top_dir, Options.lockfile)) |
||||
if not (self.env.NO_LOCK_IN_OUT or env.environ.get('NO_LOCK_IN_OUT') or getattr(Options.options, 'no_lock_in_out')): |
||||
env.store(os.path.join(Context.out_dir, Options.lockfile)) |
||||
|
||||
def prepare_env(self, env): |
||||
""" |
||||
Insert *PREFIX*, *BINDIR* and *LIBDIR* values into ``env`` |
||||
|
||||
:type env: :py:class:`waflib.ConfigSet.ConfigSet` |
||||
:param env: a ConfigSet, usually ``conf.env`` |
||||
""" |
||||
if not env.PREFIX: |
||||
if Options.options.prefix or Utils.is_win32: |
||||
env.PREFIX = Options.options.prefix |
||||
else: |
||||
env.PREFIX = '/' |
||||
if not env.BINDIR: |
||||
if Options.options.bindir: |
||||
env.BINDIR = Options.options.bindir |
||||
else: |
||||
env.BINDIR = Utils.subst_vars('${PREFIX}/bin', env) |
||||
if not env.LIBDIR: |
||||
if Options.options.libdir: |
||||
env.LIBDIR = Options.options.libdir |
||||
else: |
||||
env.LIBDIR = Utils.subst_vars('${PREFIX}/lib%s' % Utils.lib64(), env) |
||||
|
||||
def store(self): |
||||
"""Save the config results into the cache file""" |
||||
n = self.cachedir.make_node('build.config.py') |
||||
n.write('version = 0x%x\ntools = %r\n' % (Context.HEXVERSION, self.tools)) |
||||
|
||||
if not self.all_envs: |
||||
self.fatal('nothing to store in the configuration context!') |
||||
|
||||
for key in self.all_envs: |
||||
tmpenv = self.all_envs[key] |
||||
tmpenv.store(os.path.join(self.cachedir.abspath(), key + Build.CACHE_SUFFIX)) |
||||
|
||||
def load(self, tool_list, tooldir=None, funs=None, with_sys_path=True, cache=False): |
||||
""" |
||||
Load Waf tools, which will be imported whenever a build is started. |
||||
|
||||
:param tool_list: waf tools to import |
||||
:type tool_list: list of string |
||||
:param tooldir: paths for the imports |
||||
:type tooldir: list of string |
||||
:param funs: functions to execute from the waf tools |
||||
:type funs: list of string |
||||
:param cache: whether to prevent the tool from running twice |
||||
:type cache: bool |
||||
""" |
||||
|
||||
tools = Utils.to_list(tool_list) |
||||
if tooldir: |
||||
tooldir = Utils.to_list(tooldir) |
||||
for tool in tools: |
||||
# avoid loading the same tool more than once with the same functions |
||||
# used by composite projects |
||||
|
||||
if cache: |
||||
mag = (tool, id(self.env), tooldir, funs) |
||||
if mag in self.tool_cache: |
||||
self.to_log('(tool %s is already loaded, skipping)' % tool) |
||||
continue |
||||
self.tool_cache.append(mag) |
||||
|
||||
module = None |
||||
try: |
||||
module = Context.load_tool(tool, tooldir, ctx=self, with_sys_path=with_sys_path) |
||||
except ImportError as e: |
||||
self.fatal('Could not load the Waf tool %r from %r\n%s' % (tool, getattr(e, 'waf_sys_path', sys.path), e)) |
||||
except Exception as e: |
||||
self.to_log('imp %r (%r & %r)' % (tool, tooldir, funs)) |
||||
self.to_log(traceback.format_exc()) |
||||
raise |
||||
|
||||
if funs is not None: |
||||
self.eval_rules(funs) |
||||
else: |
||||
func = getattr(module, 'configure', None) |
||||
if func: |
||||
if type(func) is type(Utils.readf): |
||||
func(self) |
||||
else: |
||||
self.eval_rules(func) |
||||
|
||||
self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs}) |
||||
|
||||
def post_recurse(self, node): |
||||
""" |
||||
Records the path and a hash of the scripts visited, see :py:meth:`waflib.Context.Context.post_recurse` |
||||
|
||||
:param node: script |
||||
:type node: :py:class:`waflib.Node.Node` |
||||
""" |
||||
super(ConfigurationContext, self).post_recurse(node) |
||||
self.hash = Utils.h_list((self.hash, node.read('rb'))) |
||||
self.files.append(node.abspath()) |
||||
|
||||
def eval_rules(self, rules): |
||||
""" |
||||
Execute configuration tests provided as list of functions to run |
||||
|
||||
:param rules: list of configuration method names |
||||
:type rules: list of string |
||||
""" |
||||
self.rules = Utils.to_list(rules) |
||||
for x in self.rules: |
||||
f = getattr(self, x) |
||||
if not f: |
||||
self.fatal('No such configuration function %r' % x) |
||||
f() |
||||
|
||||
def conf(f): |
||||
""" |
||||
Decorator: attach new configuration functions to :py:class:`waflib.Build.BuildContext` and |
||||
:py:class:`waflib.Configure.ConfigurationContext`. The methods bound will accept a parameter |
||||
named 'mandatory' to disable the configuration errors:: |
||||
|
||||
def configure(conf): |
||||
conf.find_program('abc', mandatory=False) |
||||
|
||||
:param f: method to bind |
||||
:type f: function |
||||
""" |
||||
def fun(*k, **kw): |
||||
mandatory = kw.pop('mandatory', True) |
||||
try: |
||||
return f(*k, **kw) |
||||
except Errors.ConfigurationError: |
||||
if mandatory: |
||||
raise |
||||
|
||||
fun.__name__ = f.__name__ |
||||
setattr(ConfigurationContext, f.__name__, fun) |
||||
setattr(Build.BuildContext, f.__name__, fun) |
||||
return f |
||||
|
||||
@conf |
||||
def add_os_flags(self, var, dest=None, dup=False): |
||||
""" |
||||
Import operating system environment values into ``conf.env`` dict:: |
||||
|
||||
def configure(conf): |
||||
conf.add_os_flags('CFLAGS') |
||||
|
||||
:param var: variable to use |
||||
:type var: string |
||||
:param dest: destination variable, by default the same as var |
||||
:type dest: string |
||||
:param dup: add the same set of flags again |
||||
:type dup: bool |
||||
""" |
||||
try: |
||||
flags = shlex.split(self.environ[var]) |
||||
except KeyError: |
||||
return |
||||
if dup or ''.join(flags) not in ''.join(Utils.to_list(self.env[dest or var])): |
||||
self.env.append_value(dest or var, flags) |
||||
|
||||
@conf |
||||
def cmd_to_list(self, cmd): |
||||
""" |
||||
Detect if a command is written in pseudo shell like ``ccache g++`` and return a list. |
||||
|
||||
:param cmd: command |
||||
:type cmd: a string or a list of string |
||||
""" |
||||
if isinstance(cmd, str): |
||||
if os.path.isfile(cmd): |
||||
# do not take any risk |
||||
return [cmd] |
||||
if os.sep == '/': |
||||
return shlex.split(cmd) |
||||
else: |
||||
try: |
||||
return shlex.split(cmd, posix=False) |
||||
except TypeError: |
||||
# Python 2.5 on windows? |
||||
return shlex.split(cmd) |
||||
return cmd |
||||
|
||||
@conf |
||||
def check_waf_version(self, mini='1.9.99', maxi='2.1.0', **kw): |
||||
""" |
||||
Raise a Configuration error if the Waf version does not strictly match the given bounds:: |
||||
|
||||
conf.check_waf_version(mini='1.9.99', maxi='2.1.0') |
||||
|
||||
:type mini: number, tuple or string |
||||
:param mini: Minimum required version |
||||
:type maxi: number, tuple or string |
||||
:param maxi: Maximum allowed version |
||||
""" |
||||
self.start_msg('Checking for waf version in %s-%s' % (str(mini), str(maxi)), **kw) |
||||
ver = Context.HEXVERSION |
||||
if Utils.num2ver(mini) > ver: |
||||
self.fatal('waf version should be at least %r (%r found)' % (Utils.num2ver(mini), ver)) |
||||
if Utils.num2ver(maxi) < ver: |
||||
self.fatal('waf version should be at most %r (%r found)' % (Utils.num2ver(maxi), ver)) |
||||
self.end_msg('ok', **kw) |
||||
|
||||
@conf |
||||
def find_file(self, filename, path_list=[]): |
||||
""" |
||||
Find a file in a list of paths |
||||
|
||||
:param filename: name of the file to search for |
||||
:param path_list: list of directories to search |
||||
:return: the first matching filename; else a configuration exception is raised |
||||
""" |
||||
for n in Utils.to_list(filename): |
||||
for d in Utils.to_list(path_list): |
||||
p = os.path.expanduser(os.path.join(d, n)) |
||||
if os.path.exists(p): |
||||
return p |
||||
self.fatal('Could not find %r' % filename) |
||||
|
||||
@conf |
||||
def find_program(self, filename, **kw): |
||||
""" |
||||
Search for a program on the operating system |
||||
|
||||
When var is used, you may set os.environ[var] to help find a specific program version, for example:: |
||||
|
||||
$ CC='ccache gcc' waf configure |
||||
|
||||
:param path_list: paths to use for searching |
||||
:type param_list: list of string |
||||
:param var: store the result to conf.env[var] where var defaults to filename.upper() if not provided; the result is stored as a list of strings |
||||
:type var: string |
||||
:param value: obtain the program from the value passed exclusively |
||||
:type value: list or string (list is preferred) |
||||
:param exts: list of extensions for the binary (do not add an extension for portability) |
||||
:type exts: list of string |
||||
:param msg: name to display in the log, by default filename is used |
||||
:type msg: string |
||||
:param interpreter: interpreter for the program |
||||
:type interpreter: ConfigSet variable key |
||||
:raises: :py:class:`waflib.Errors.ConfigurationError` |
||||
""" |
||||
|
||||
exts = kw.get('exts', Utils.is_win32 and '.exe,.com,.bat,.cmd' or ',.sh,.pl,.py') |
||||
|
||||
environ = kw.get('environ', getattr(self, 'environ', os.environ)) |
||||
|
||||
ret = '' |
||||
|
||||
filename = Utils.to_list(filename) |
||||
msg = kw.get('msg', ', '.join(filename)) |
||||
|
||||
var = kw.get('var', '') |
||||
if not var: |
||||
var = re.sub(r'[-.]', '_', filename[0].upper()) |
||||
|
||||
path_list = kw.get('path_list', '') |
||||
if path_list: |
||||
path_list = Utils.to_list(path_list) |
||||
else: |
||||
path_list = environ.get('PATH', '').split(os.pathsep) |
||||
|
||||
if kw.get('value'): |
||||
# user-provided in command-line options and passed to find_program |
||||
ret = self.cmd_to_list(kw['value']) |
||||
elif environ.get(var): |
||||
# user-provided in the os environment |
||||
ret = self.cmd_to_list(environ[var]) |
||||
elif self.env[var]: |
||||
# a default option in the wscript file |
||||
ret = self.cmd_to_list(self.env[var]) |
||||
else: |
||||
if not ret: |
||||
ret = self.find_binary(filename, exts.split(','), path_list) |
||||
if not ret and Utils.winreg: |
||||
ret = Utils.get_registry_app_path(Utils.winreg.HKEY_CURRENT_USER, filename) |
||||
if not ret and Utils.winreg: |
||||
ret = Utils.get_registry_app_path(Utils.winreg.HKEY_LOCAL_MACHINE, filename) |
||||
ret = self.cmd_to_list(ret) |
||||
|
||||
if ret: |
||||
if len(ret) == 1: |
||||
retmsg = ret[0] |
||||
else: |
||||
retmsg = ret |
||||
else: |
||||
retmsg = False |
||||
|
||||
self.msg('Checking for program %r' % msg, retmsg, **kw) |
||||
if not kw.get('quiet'): |
||||
self.to_log('find program=%r paths=%r var=%r -> %r' % (filename, path_list, var, ret)) |
||||
|
||||
if not ret: |
||||
self.fatal(kw.get('errmsg', '') or 'Could not find the program %r' % filename) |
||||
|
||||
interpreter = kw.get('interpreter') |
||||
if interpreter is None: |
||||
if not Utils.check_exe(ret[0], env=environ): |
||||
self.fatal('Program %r is not executable' % ret) |
||||
self.env[var] = ret |
||||
else: |
||||
self.env[var] = self.env[interpreter] + ret |
||||
|
||||
return ret |
||||
|
||||
@conf |
||||
def find_binary(self, filenames, exts, paths): |
||||
for f in filenames: |
||||
for ext in exts: |
||||
exe_name = f + ext |
||||
if os.path.isabs(exe_name): |
||||
if os.path.isfile(exe_name): |
||||
return exe_name |
||||
else: |
||||
for path in paths: |
||||
x = os.path.expanduser(os.path.join(path, exe_name)) |
||||
if os.path.isfile(x): |
||||
return x |
||||
return None |
||||
|
||||
@conf |
||||
def run_build(self, *k, **kw): |
||||
""" |
||||
Create a temporary build context to execute a build. A reference to that build |
||||
context is kept on self.test_bld for debugging purposes, and you should not rely |
||||
on it too much (read the note on the cache below). |
||||
The parameters given in the arguments to this function are passed as arguments for |
||||
a single task generator created in the build. Only three parameters are obligatory: |
||||
|
||||
:param features: features to pass to a task generator created in the build |
||||
:type features: list of string |
||||
:param compile_filename: file to create for the compilation (default: *test.c*) |
||||
:type compile_filename: string |
||||
:param code: code to write in the filename to compile |
||||
:type code: string |
||||
|
||||
Though this function returns *0* by default, the build may set an attribute named *retval* on the |
||||
build context object to return a particular value. See :py:func:`waflib.Tools.c_config.test_exec_fun` for example. |
||||
|
||||
This function also provides a limited cache. To use it, provide the following option:: |
||||
|
||||
def options(opt): |
||||
opt.add_option('--confcache', dest='confcache', default=0, |
||||
action='count', help='Use a configuration cache') |
||||
|
||||
And execute the configuration with the following command-line:: |
||||
|
||||
$ waf configure --confcache |
||||
|
||||
""" |
||||
lst = [str(v) for (p, v) in kw.items() if p != 'env'] |
||||
h = Utils.h_list(lst) |
||||
dir = self.bldnode.abspath() + os.sep + (not Utils.is_win32 and '.' or '') + 'conf_check_' + Utils.to_hex(h) |
||||
|
||||
try: |
||||
os.makedirs(dir) |
||||
except OSError: |
||||
pass |
||||
|
||||
try: |
||||
os.stat(dir) |
||||
except OSError: |
||||
self.fatal('cannot use the configuration test folder %r' % dir) |
||||
|
||||
cachemode = getattr(Options.options, 'confcache', None) |
||||
if cachemode == 1: |
||||
try: |
||||
proj = ConfigSet.ConfigSet(os.path.join(dir, 'cache_run_build')) |
||||
except EnvironmentError: |
||||
pass |
||||
else: |
||||
ret = proj['cache_run_build'] |
||||
if isinstance(ret, str) and ret.startswith('Test does not build'): |
||||
self.fatal(ret) |
||||
return ret |
||||
|
||||
bdir = os.path.join(dir, 'testbuild') |
||||
|
||||
if not os.path.exists(bdir): |
||||
os.makedirs(bdir) |
||||
|
||||
cls_name = kw.get('run_build_cls') or getattr(self, 'run_build_cls', 'build') |
||||
self.test_bld = bld = Context.create_context(cls_name, top_dir=dir, out_dir=bdir) |
||||
bld.init_dirs() |
||||
bld.progress_bar = 0 |
||||
bld.targets = '*' |
||||
|
||||
bld.logger = self.logger |
||||
bld.all_envs.update(self.all_envs) # not really necessary |
||||
bld.env = kw['env'] |
||||
|
||||
bld.kw = kw |
||||
bld.conf = self |
||||
kw['build_fun'](bld) |
||||
ret = -1 |
||||
try: |
||||
try: |
||||
bld.compile() |
||||
except Errors.WafError: |
||||
ret = 'Test does not build: %s' % traceback.format_exc() |
||||
self.fatal(ret) |
||||
else: |
||||
ret = getattr(bld, 'retval', 0) |
||||
finally: |
||||
if cachemode == 1: |
||||
# cache the results each time |
||||
proj = ConfigSet.ConfigSet() |
||||
proj['cache_run_build'] = ret |
||||
proj.store(os.path.join(dir, 'cache_run_build')) |
||||
else: |
||||
shutil.rmtree(dir) |
||||
return ret |
||||
|
||||
@conf |
||||
def ret_msg(self, msg, args): |
||||
if isinstance(msg, str): |
||||
return msg |
||||
return msg(args) |
||||
|
||||
@conf |
||||
def test(self, *k, **kw): |
||||
|
||||
if not 'env' in kw: |
||||
kw['env'] = self.env.derive() |
||||
|
||||
# validate_c for example |
||||
if kw.get('validate'): |
||||
kw['validate'](kw) |
||||
|
||||
self.start_msg(kw['msg'], **kw) |
||||
ret = None |
||||
try: |
||||
ret = self.run_build(*k, **kw) |
||||
except self.errors.ConfigurationError: |
||||
self.end_msg(kw['errmsg'], 'YELLOW', **kw) |
||||
if Logs.verbose > 1: |
||||
raise |
||||
else: |
||||
self.fatal('The configuration failed') |
||||
else: |
||||
kw['success'] = ret |
||||
|
||||
if kw.get('post_check'): |
||||
ret = kw['post_check'](kw) |
||||
|
||||
if ret: |
||||
self.end_msg(kw['errmsg'], 'YELLOW', **kw) |
||||
self.fatal('The configuration failed %r' % ret) |
||||
else: |
||||
self.end_msg(self.ret_msg(kw['okmsg'], kw), **kw) |
||||
return ret |
||||
|
@ -0,0 +1,737 @@
@@ -0,0 +1,737 @@
|
||||
#!/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=0x2000b00 |
||||
"""Constant updated on new releases""" |
||||
|
||||
WAFVERSION="2.0.11" |
||||
"""Constant updated on new releases""" |
||||
|
||||
WAFREVISION="a97f6fb0941091b4966b625f15ec32fa783a8bec" |
||||
"""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('rU', 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'] |
||||