import os, sys, inspect
-VERSION="2.0.8"
+VERSION="2.0.17"
REVISION="x"
GIT="x"
INSTALL="x"
os.environ['PYTHONUNBUFFERED'] = '1'
-if Context.HEXVERSION not in (0x2000800,):
+if Context.HEXVERSION not in (0x2001100,):
Logs.error('''
Please use the version of waf that comes with Samba, not
a system installed version. See http://wiki.samba.org/index.php/Waf
"""Amount of jobs to run in parallel"""
self.targets = Options.options.targets
- """List of targets to build (default: \*)"""
+ """List of targets to build (default: \\*)"""
self.keep = Options.options.keep
"""Whether the build should continue past errors"""
elif not ln.is_child_of(self.srcnode):
Logs.warn('CWD %s is not under %s, forcing --targets=* (run distclean?)', ln.abspath(), self.srcnode.abspath())
ln = self.srcnode
- for tg in self.groups[self.current_group]:
+
+ def is_post(tg, ln):
try:
p = tg.path
except AttributeError:
pass
else:
if p.is_child_of(ln):
- tgpost(tg)
+ return True
+
+ def is_post_group():
+ for i, g in enumerate(self.groups):
+ if i > self.current_group:
+ for tg in g:
+ if is_post(tg, ln):
+ return True
+
+ if self.post_mode == POST_LAZY and ln != self.srcnode:
+ # partial folder builds require all targets from a previous build group
+ if is_post_group():
+ ln = self.srcnode
+
+ for tg in self.groups[self.current_group]:
+ if is_post(tg, ln):
+ tgpost(tg)
def get_tasks_group(self, idx):
"""
:param dest: absolute path of the symlink
:type dest: :py:class:`waflib.Node.Node` or string (absolute path)
- :param src: link contents, which is a relative or abolute path which may exist or not
+ :param src: link contents, which is a relative or absolute path which may exist or not
:type src: string
:param env: configuration set for performing substitutions in dest
:type env: :py:class:`waflib.ConfigSet.ConfigSet`
"""
Returns the destination path where files will be installed, pre-pending `destdir`.
+ Relative paths will be interpreted relative to `PREFIX` if no `destdir` is given.
+
:rtype: string
"""
if isinstance(self.install_to, Node.Node):
dest = self.install_to.abspath()
else:
- dest = Utils.subst_vars(self.install_to, self.env)
+ dest = os.path.normpath(Utils.subst_vars(self.install_to, self.env))
+ if not os.path.isabs(dest):
+ dest = os.path.join(self.env.PREFIX, dest)
if destdir and Options.options.destdir:
dest = os.path.join(Options.options.destdir, os.path.splitdrive(dest)[1].lstrip(os.sep))
return dest
# same size and identical timestamps -> make no copy
if st1.st_mtime + 2 >= st2.st_mtime and st1.st_size == st2.st_size:
if not self.generator.bld.progress_bar:
- Logs.info('- install %s (from %s)', tgt, lbl)
+
+ c1 = Logs.colors.NORMAL
+ c2 = Logs.colors.BLUE
+
+ Logs.info('%s- install %s%s%s (from %s)', c1, c2, tgt, c1, lbl)
return False
if not self.generator.bld.progress_bar:
- Logs.info('+ install %s (from %s)', tgt, lbl)
+
+ c1 = Logs.colors.NORMAL
+ c2 = Logs.colors.BLUE
+
+ Logs.info('%s+ install %s%s%s (from %s)', c1, c2, tgt, c1, lbl)
# Give best attempt at making destination overwritable,
# like the 'install' utility used by 'make install' does.
"""
if os.path.islink(tgt) and os.readlink(tgt) == src:
if not self.generator.bld.progress_bar:
- Logs.info('- symlink %s (to %s)', tgt, src)
+ c1 = Logs.colors.NORMAL
+ c2 = Logs.colors.BLUE
+ Logs.info('%s- symlink %s%s%s (to %s)', c1, c2, tgt, c1, src)
else:
try:
os.remove(tgt)
except OSError:
pass
if not self.generator.bld.progress_bar:
- Logs.info('+ symlink %s (to %s)', tgt, src)
+ c1 = Logs.colors.NORMAL
+ c2 = Logs.colors.BLUE
+ Logs.info('%s+ symlink %s%s%s (to %s)', c1, c2, tgt, c1, src)
os.symlink(src, tgt)
self.fix_perms(tgt)
See :py:meth:`waflib.Build.inst.do_install`
"""
if not self.generator.bld.progress_bar:
- Logs.info('- remove %s', tgt)
+ c1 = Logs.colors.NORMAL
+ c2 = Logs.colors.BLUE
+ Logs.info('%s- remove %s%s%s', c1, c2, tgt, c1)
#self.uninstall.append(tgt)
try:
"""
try:
if not self.generator.bld.progress_bar:
- Logs.info('- remove %s', tgt)
+ c1 = Logs.colors.NORMAL
+ c2 = Logs.colors.BLUE
+ Logs.info('%s- remove %s%s%s', c1, c2, tgt, c1)
os.remove(tgt)
except OSError:
pass
lst = []
for env in self.all_envs.values():
lst.extend(self.root.find_or_declare(f) for f in env[CFG_FILES])
- for n in self.bldnode.ant_glob('**/*', excl='.lock* *conf_check_*/** config.log c4che/*', quiet=True):
+ excluded_dirs = '.lock* *conf_check_*/** config.log %s/*' % CACHE_DIR
+ for n in self.bldnode.ant_glob('**/*', excl=excluded_dirs, quiet=True):
if n in lst:
continue
n.delete()
import copy, re, os
from waflib import Logs, Utils
-re_imp = re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$', re.M)
+re_imp = re.compile(r'^(#)*?([^#=]*?)\ =\ (.*?)$', re.M)
class ConfigSet(object):
"""
:type filename: string
"""
tbl = self.table
- code = Utils.readf(filename, m='rU')
+ code = Utils.readf(filename, m='r')
for m in re_imp.finditer(code):
g = m.group
tbl[g(2)] = eval(g(3))
self.bldnode.mkdir()
if not os.path.isdir(self.bldnode.abspath()):
- conf.fatal('Could not create the build directory %s' % self.bldnode.abspath())
+ self.fatal('Could not create the build directory %s' % self.bldnode.abspath())
def execute(self):
"""
env.hash = self.hash
env.files = self.files
env.environ = dict(self.environ)
+ env.launch_dir = Context.launch_dir
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))
def eval_rules(self, rules):
"""
- Execute configuration tests provided as list of funcitons to run
+ Execute configuration tests provided as list of functions to run
:param rules: list of configuration method names
:type rules: list of string
import waflib.Node
# the following 3 constants are updated on each new release (do not touch)
-HEXVERSION=0x2000800
+HEXVERSION=0x2001100
"""Constant updated on new releases"""
-WAFVERSION="2.0.8"
+WAFVERSION="2.0.17"
"""Constant updated on new releases"""
-WAFREVISION="f78fbc32bb355a3291c9b5f79bbe0c8dfe81282a"
+WAFREVISION="6bc6cb599c702e985780e9f705b291b812123693"
"""Git revision when the waf version is updated"""
ABI = 20
cache[node] = True
self.pre_recurse(node)
try:
- function_code = node.read('rU', encoding)
+ function_code = node.read('r', encoding)
exec(compile(function_code, node.abspath(), 'exec'), self.exec_dict)
finally:
self.post_recurse(node)
def build(bld):
bld.to_log('starting the build')
- Provide a logger on the context class or override this methid if necessary.
+ Provide a logger on the context class or override this method if necessary.
:param msg: message
:type msg: string
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'
+ :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
module = imp.new_module(WSCRIPT_FILE)
try:
- code = Utils.readf(path, m='rU', encoding=encoding)
+ code = Utils.readf(path, m='r', encoding=encoding)
except EnvironmentError:
raise Errors.WafError('Could not read the file %r' % path)
def load_tool(tool, tooldir=None, ctx=None, with_sys_path=True):
"""
- Importx a Waf tool as a python module, and stores it in the dict :py:const:`waflib.Context.Context.tools`
+ 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
if rec.levelno >= logging.INFO:
# the goal of this is to format without the leading "Logs, hour" prefix
if rec.args:
- return msg % rec.args
+ try:
+ return msg % rec.args
+ except UnicodeDecodeError:
+ return msg.encode('utf-8') % rec.args
return msg
rec.msg = msg
def warn(*k, **kw):
"""
- Wraps logging.warn
+ Wraps logging.warning
"""
- log.warn(*k, **kw)
+ log.warning(*k, **kw)
def info(*k, **kw):
"""
if k == '**':
accu.append(k)
else:
- k = k.replace('.', '[.]').replace('*','.*').replace('?', '.').replace('+', '\\+')
+ k = k.replace('.', '[.]').replace('*', '.*').replace('?', '.').replace('+', '\\+')
k = '^%s$' % k
try:
exp = re.compile(k, flags=reflags)
:rtype: iterator
"""
dircont = self.listdir()
- dircont.sort()
try:
lst = set(self.children.keys())
return len(self.lst)
def __iter__(self):
return iter(self.lst)
+ def __str__(self):
+ return 'PriorityTasks: [%s]' % '\n '.join(str(x) for x in self.lst)
def clear(self):
self.lst = []
def append(self, task):
The reverse dependency graph of dependencies obtained from Task.run_after
"""
- self.spawner = Spawner(self)
+ self.spawner = None
"""
Coordinating daemon thread that spawns thread consumers
"""
+ if self.numjobs > 1:
+ self.spawner = Spawner(self)
def get_next_task(self):
"""
pass
else:
if cond:
+ # The most common reason is conflicting build order declaration
+ # for example: "X run_after Y" and "Y run_after X"
+ # Another can be changing "run_after" dependencies while the build is running
+ # for example: updating "tsk.run_after" in the "runnable_status" method
lst = []
for tsk in self.postponed:
deps = [id(x) for x in tsk.run_after if not x.hasrun]
self.outstanding.append(x)
break
else:
+ if self.stop or self.error:
+ break
raise Errors.WafError('Broken revdeps detected on %r' % self.incomplete)
else:
tasks = next(self.biter)
def mark_finished(self, tsk):
def try_unfreeze(x):
# DAG ancestors are likely to be in the incomplete set
+ # This assumes that the run_after contents have not changed
+ # after the build starts, else a deadlock may occur
if x in self.incomplete:
# TODO remove dependencies to free some memory?
# x.run_after.remove(tsk)
try_unfreeze(x)
del self.revdeps[tsk]
+ if hasattr(tsk, 'semaphore'):
+ sem = tsk.semaphore
+ try:
+ sem.release(tsk)
+ except KeyError:
+ # TODO
+ pass
+ else:
+ while sem.waiting and not sem.is_locked():
+ # take a frozen task, make it ready to run
+ x = sem.waiting.pop()
+ self._add_task(x)
+
def get_out(self):
"""
Waits for a Task that task consumers add to :py:attr:`waflib.Runner.Parallel.out` after execution.
:param tsk: task instance
:type tsk: :py:attr:`waflib.Task.Task`
"""
+ # TODO change in waf 2.1
self.ready.put(tsk)
+ def _add_task(self, tsk):
+ if hasattr(tsk, 'semaphore'):
+ sem = tsk.semaphore
+ try:
+ sem.acquire(tsk)
+ except IndexError:
+ sem.waiting.add(tsk)
+ return
+
+ self.count += 1
+ self.processed += 1
+ if self.numjobs == 1:
+ tsk.log_display(tsk.generator.bld)
+ try:
+ self.process_task(tsk)
+ finally:
+ self.out.put(tsk)
+ else:
+ self.add_task(tsk)
+
def process_task(self, tsk):
"""
Processes a task and attempts to stop the build in case of errors
st = self.task_status(tsk)
if st == Task.RUN_ME:
- self.count += 1
- self.processed += 1
-
- if self.numjobs == 1:
- tsk.log_display(tsk.generator.bld)
- try:
- self.process_task(tsk)
- finally:
- self.out.put(tsk)
- else:
- self.add_task(tsk)
+ self._add_task(tsk)
elif st == Task.ASK_LATER:
self.postpone(tsk)
elif st == Task.SKIP_ME:
if no_climb:
break
- if not Context.run_dir:
+ wscript = os.path.normpath(os.path.join(Context.run_dir, Context.WSCRIPT_FILE))
+ if not os.path.exists(wscript):
if options.whelp:
Logs.warn('These are the generic options (no wscript/project found)')
ctx.parser.print_help()
sys.exit(1)
try:
- set_main_module(os.path.normpath(os.path.join(Context.run_dir, Context.WSCRIPT_FILE)))
+ set_main_module(wscript)
except Errors.WafError as e:
Logs.pprint('RED', e.verbose_msg)
Logs.error(str(e))
ctx = Context.create_context('options')
ctx.execute()
if not Options.commands:
- Options.commands.append(default_cmd)
+ if isinstance(default_cmd, list):
+ Options.commands.extend(default_cmd)
+ else:
+ Options.commands.append(default_cmd)
if Options.options.whelp:
ctx.parser.print_help()
sys.exit(0)
pass
try:
- shutil.rmtree('c4che')
+ shutil.rmtree(Build.CACHE_DIR)
except OSError:
pass
cmd = env.config_cmd or 'configure'
if Configure.autoconfig == 'clobber':
tmp = Options.options.__dict__
+ launch_dir_tmp = Context.launch_dir
if env.options:
Options.options.__dict__ = env.options
+ Context.launch_dir = env.launch_dir
try:
run_command(cmd)
finally:
Options.options.__dict__ = tmp
+ Context.launch_dir = launch_dir_tmp
else:
run_command(cmd)
run_command(self.cmd)
bld = gen.bld
cwdx = tsk.get_cwd()
p = env.get_flat
+ def to_list(xx):
+ if isinstance(xx, str): return [xx]
+ return xx
tsk.last_cmd = cmd = \'\'\' %s \'\'\' % s
return tsk.exec_command(cmd, cwd=cwdx, env=env.env or None)
'''
return tsk.exec_command(lst, cwd=cwdx, env=env.env or None)
'''
+COMPILE_TEMPLATE_SIG_VARS = '''
+def f(tsk):
+ sig = tsk.generator.bld.hash_env_vars(tsk.env, tsk.vars)
+ tsk.m.update(sig)
+ env = tsk.env
+ gen = tsk.generator
+ bld = gen.bld
+ cwdx = tsk.get_cwd()
+ p = env.get_flat
+ buf = []
+ %s
+ tsk.m.update(repr(buf).encode())
+'''
+
classes = {}
"""
The metaclass :py:class:`waflib.Task.store_task_type` stores all class tasks
# change the name of run_str or it is impossible to subclass with a function
cls.run_str = None
cls.run = f
+ # process variables
cls.vars = list(set(cls.vars + dvars))
cls.vars.sort()
+ if cls.vars:
+ fun = compile_sig_vars(cls.vars)
+ if fun:
+ cls.sig_vars = fun
elif getattr(cls, 'run', None) and not 'hcode' in cls.__dict__:
# getattr(cls, 'hcode') would look in the upper classes
cls.hcode = Utils.h_cmd(cls.run)
class Task(evil):
"""
- This class deals with the filesystem (:py:class:`waflib.Node.Node`). The method :py:class:`waflib.Task.Task.runnable_status`
- uses a hash value (from :py:class:`waflib.Task.Task.signature`) which is persistent from build to build. When the value changes,
- the task has to be executed. The method :py:class:`waflib.Task.Task.post_run` will assign the task signature to the output
- nodes (if present).
+ Task objects represents actions to perform such as commands to execute by calling the `run` method.
+
+ Detecting when to execute a task occurs in the method :py:meth:`waflib.Task.Task.runnable_status`.
+
+ Detecting which tasks to execute is performed through a hash value returned by
+ :py:meth:`waflib.Task.Task.signature`. The task signature is persistent from build to build.
"""
vars = []
"""ConfigSet variables that should trigger a rebuild (class attribute used for :py:meth:`waflib.Task.Task.sig_vars`)"""
"""File extensions that objects of this task class may create"""
before = []
- """List of task class names to execute before instances of this class"""
+ """The instances of this class are executed before the instances of classes whose names are in this list"""
after = []
- """List of task class names to execute after instances of this class"""
+ """The instances of this class are executed after the instances of classes whose names are in this list"""
hcode = Utils.SIG_NIL
"""String representing an additional hash for the class representation"""
if hasattr(self, 'stderr'):
kw['stderr'] = self.stderr
- # workaround for command line length limit:
- # http://support.microsoft.com/kb/830473
- if not isinstance(cmd, str) and (len(repr(cmd)) >= 8192 if Utils.is_win32 else len(cmd) > 200000):
- cmd, args = self.split_argfile(cmd)
- try:
- (fd, tmp) = tempfile.mkstemp()
- os.write(fd, '\r\n'.join(args).encode())
- os.close(fd)
- if Logs.verbose:
- Logs.debug('argfile: @%r -> %r', tmp, args)
- return self.generator.bld.exec_command(cmd + ['@' + tmp], **kw)
- finally:
+ if not isinstance(cmd, str):
+ if Utils.is_win32:
+ # win32 compares the resulting length http://support.microsoft.com/kb/830473
+ too_long = sum([len(arg) for arg in cmd]) + len(cmd) > 8192
+ else:
+ # non-win32 counts the amount of arguments (200k)
+ too_long = len(cmd) > 200000
+
+ if too_long and getattr(self, 'allow_argsfile', True):
+ # Shunt arguments to a temporary file if the command is too long.
+ cmd, args = self.split_argfile(cmd)
try:
- os.remove(tmp)
- except OSError:
- # anti-virus and indexers can keep files open -_-
- pass
- else:
- return self.generator.bld.exec_command(cmd, **kw)
+ (fd, tmp) = tempfile.mkstemp()
+ os.write(fd, '\r\n'.join(args).encode())
+ os.close(fd)
+ if Logs.verbose:
+ Logs.debug('argfile: @%r -> %r', tmp, args)
+ return self.generator.bld.exec_command(cmd + ['@' + tmp], **kw)
+ finally:
+ try:
+ os.remove(tmp)
+ except OSError:
+ # anti-virus and indexers can keep files open -_-
+ pass
+ return self.generator.bld.exec_command(cmd, **kw)
def process(self):
"""
"""
Run this task only after the given *task*.
+ Calling this method from :py:meth:`waflib.Task.Task.runnable_status` may cause
+ build deadlocks; see :py:meth:`waflib.Tools.fc.fc.runnable_status` for details.
+
:param task: task
:type task: :py:class:`waflib.Task.Task`
"""
def sig_vars(self):
"""
Used by :py:meth:`waflib.Task.Task.signature`; it hashes :py:attr:`waflib.Task.Task.env` variables/values
+ When overriding this method, and if scriptlet expressions are used, make sure to follow
+ the code in :py:meth:`waflib.Task.Task.compile_sig_vars` to enable dependencies on scriptlet results.
+
+ This method may be replaced on subclasses by the metaclass to force dependencies on scriptlet code.
"""
sig = self.generator.bld.hash_env_vars(self.env, self.vars)
self.m.update(sig)
exec(c, dc)
return dc['f']
-re_cond = re.compile('(?P<var>\w+)|(?P<or>\|)|(?P<and>&)')
+re_cond = re.compile(r'(?P<var>\w+)|(?P<or>\|)|(?P<and>&)')
re_novar = re.compile(r'^(SRC|TGT)\W+.*?$')
reg_act = re.compile(r'(?P<backslash>\\)|(?P<dollar>\$\$)|(?P<subst>\$\{(?P<var>\w+)(?P<code>.*?)\})', re.M)
def compile_fun_shell(line):
return None
line = reg_act.sub(repl, line) or line
dvars = []
+ def add_dvar(x):
+ if x not in dvars:
+ dvars.append(x)
def replc(m):
# performs substitutions and populates dvars
return ' or '
else:
x = m.group('var')
- if x not in dvars:
- dvars.append(x)
+ add_dvar(x)
return 'env[%r]' % x
parm = []
app('" ".join([a.path_from(cwdx) for a in tsk.outputs])')
elif meth:
if meth.startswith(':'):
- if var not in dvars:
- dvars.append(var)
+ add_dvar(var)
m = meth[1:]
if m == 'SRC':
m = '[a.path_from(cwdx) for a in tsk.inputs]'
m = '[tsk.inputs%s]' % m[3:]
elif re_novar.match(m):
m = '[tsk.outputs%s]' % m[3:]
- elif m[:3] not in ('tsk', 'gen', 'bld'):
- dvars.append(meth[1:])
- m = '%r' % m
+ else:
+ add_dvar(m)
+ if m[:3] not in ('tsk', 'gen', 'bld'):
+ m = '%r' % m
app('" ".join(tsk.colon(%r, %s))' % (var, m))
elif meth.startswith('?'):
# In A?B|C output env.A if one of env.B or env.C is non-empty
expr = re_cond.sub(replc, meth[1:])
app('p(%r) if (%s) else ""' % (var, expr))
else:
- app('%s%s' % (var, meth))
+ call = '%s%s' % (var, meth)
+ add_dvar(call)
+ app(call)
else:
- if var not in dvars:
- dvars.append(var)
+ add_dvar(var)
app("p('%s')" % var)
if parm:
parm = "%% (%s) " % (',\n\t\t'.join(parm))
merge = False
app = buf.append
+ def add_dvar(x):
+ if x not in dvars:
+ dvars.append(x)
+
def replc(m):
# performs substitutions and populates dvars
if m.group('and'):
return ' or '
else:
x = m.group('var')
- if x not in dvars:
- dvars.append(x)
+ add_dvar(x)
return 'env[%r]' % x
for m in reg_act_noshell.finditer(line):
elif code:
if code.startswith(':'):
# a composed variable ${FOO:OUT}
- if not var in dvars:
- dvars.append(var)
+ add_dvar(var)
m = code[1:]
if m == 'SRC':
m = '[a.path_from(cwdx) for a in tsk.inputs]'
m = '[tsk.inputs%s]' % m[3:]
elif re_novar.match(m):
m = '[tsk.outputs%s]' % m[3:]
- elif m[:3] not in ('tsk', 'gen', 'bld'):
- dvars.append(m)
- m = '%r' % m
+ else:
+ add_dvar(m)
+ if m[:3] not in ('tsk', 'gen', 'bld'):
+ m = '%r' % m
app('tsk.colon(%r, %s)' % (var, m))
elif code.startswith('?'):
# In A?B|C output env.A if one of env.B or env.C is non-empty
app('to_list(env[%r] if (%s) else [])' % (var, expr))
else:
# plain code such as ${tsk.inputs[0].abspath()}
- app('gen.to_list(%s%s)' % (var, code))
+ call = '%s%s' % (var, code)
+ add_dvar(call)
+ app('to_list(%s)' % call)
else:
# a plain variable such as # a plain variable like ${AR}
app('to_list(env[%r])' % var)
- if not var in dvars:
- dvars.append(var)
+ add_dvar(var)
if merge:
tmp = 'merge(%s, %s)' % (buf[-2], buf[-1])
del buf[-1]
else:
return compile_fun_noshell(line)
+def compile_sig_vars(vars):
+ """
+ This method produces a sig_vars method suitable for subclasses that provide
+ scriptlet code in their run_str code.
+ If no such method can be created, this method returns None.
+
+ The purpose of the sig_vars method returned is to ensures
+ that rebuilds occur whenever the contents of the expression changes.
+ This is the case B below::
+
+ import time
+ # case A: regular variables
+ tg = bld(rule='echo ${FOO}')
+ tg.env.FOO = '%s' % time.time()
+ # case B
+ bld(rule='echo ${gen.foo}', foo='%s' % time.time())
+
+ :param vars: env variables such as CXXFLAGS or gen.foo
+ :type vars: list of string
+ :return: A sig_vars method relevant for dependencies if adequate, else None
+ :rtype: A function, or None in most cases
+ """
+ buf = []
+ for x in sorted(vars):
+ if x[:3] in ('tsk', 'gen', 'bld'):
+ buf.append('buf.append(%s)' % x)
+ if buf:
+ return funex(COMPILE_TEMPLATE_SIG_VARS % '\n\t'.join(buf))
+ return None
+
def task_factory(name, func=None, vars=None, color='GREEN', ext_in=[], ext_out=[], before=[], after=[], shell=False, scan=None):
"""
Returns a new task subclass with the function ``run`` compiled from the line given.
TaskBase = Task
"Provided for compatibility reasons, TaskBase should not be used"
+class TaskSemaphore(object):
+ """
+ Task semaphores provide a simple and efficient way of throttling the amount of
+ a particular task to run concurrently. The throttling value is capped
+ by the amount of maximum jobs, so for example, a `TaskSemaphore(10)`
+ has no effect in a `-j2` build.
+
+ Task semaphores are typically specified on the task class level::
+
+ class compile(waflib.Task.Task):
+ semaphore = waflib.Task.TaskSemaphore(2)
+ run_str = 'touch ${TGT}'
+
+ Task semaphores are meant to be used by the build scheduler in the main
+ thread, so there are no guarantees of thread safety.
+ """
+ def __init__(self, num):
+ """
+ :param num: maximum value of concurrent tasks
+ :type num: int
+ """
+ self.num = num
+ self.locking = set()
+ self.waiting = set()
+
+ def is_locked(self):
+ """Returns True if this semaphore cannot be acquired by more tasks"""
+ return len(self.locking) >= self.num
+
+ def acquire(self, tsk):
+ """
+ Mark the semaphore as used by the given task (not re-entrant).
+
+ :param tsk: task object
+ :type tsk: :py:class:`waflib.Task.Task`
+ :raises: :py:class:`IndexError` in case the resource is already acquired
+ """
+ if self.is_locked():
+ raise IndexError('Cannot lock more %r' % self.locking)
+ self.locking.add(tsk)
+
+ def release(self, tsk):
+ """
+ Mark the semaphore as unused by the given task.
+
+ :param tsk: task object
+ :type tsk: :py:class:`waflib.Task.Task`
+ :raises: :py:class:`KeyError` in case the resource is not acquired by the task
+ """
+ self.locking.remove(tsk)
+
else:
self.bld = kw['bld']
self.env = self.bld.env.derive()
- self.path = self.bld.path # emulate chdir when reading scripts
+ self.path = kw.get('path', self.bld.path) # by default, emulate chdir when reading scripts
# Provide a unique index per folder
# This is part of a measure to prevent output file name collisions
* chmod: permissions for the resulting files (integer value such as Utils.O755)
* shell: set to False to execute the command directly (default is True to use a shell)
* scan: scanner function
- * vars: list of variables to trigger rebuilts, such as CFLAGS
+ * vars: list of variables to trigger rebuilds, such as CFLAGS
* cls_str: string to display when executing the task
* cls_keyword: label to display when executing the task
* cache_rule: by default, try to re-use similar classes, set to False to disable
self.bld.prev = self
-re_m4 = re.compile('@(\w+)@', re.M)
+re_m4 = re.compile(r'@(\w+)@', re.M)
class subst_pc(Task.Task):
"""
:type atleast_pkgconfig_version: string
:param package: package name, for example *gtk+-2.0*
:type package: string
- :param uselib_store: if the test is successful, define HAVE\_*name*. It is also used to define *conf.env.FLAGS_name* variables.
+ :param uselib_store: if the test is successful, define HAVE\\_*name*. It is also used to define *conf.env.FLAGS_name* variables.
:type uselib_store: string
- :param modversion: if provided, return the version of the given module and define *name*\_VERSION
+ :param modversion: if provided, return the version of the given module and define *name*\\_VERSION
:type modversion: string
:param args: arguments to give to *package* when retrieving flags
:type args: list of string
ret = None
try:
ret = self.exec_cfg(kw)
- except self.errors.WafError:
+ except self.errors.WafError as e:
if 'errmsg' in kw:
self.end_msg(kw['errmsg'], 'YELLOW', **kw)
if Logs.verbose > 1:
- raise
- else:
- self.fatal('The configuration failed')
+ self.to_log('Command failure: %s' % e)
+ self.fatal('The configuration failed')
else:
if not ret:
ret = True
re.IGNORECASE | re.MULTILINE)
"""Match #include lines"""
-re_mac = re.compile("^[a-zA-Z_]\w*")
+re_mac = re.compile(r"^[a-zA-Z_]\w*")
"""Match macro definitions"""
re_fun = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*[(]')
"""Match macro functions"""
-re_pragma_once = re.compile('^\s*once\s*', re.IGNORECASE)
+re_pragma_once = re.compile(r'^\s*once\s*', re.IGNORECASE)
"""Match #pragma once statements"""
re_nl = re.compile('\\\\\r*\n', re.MULTILINE)
prec = {}
"""
-Operator precendence rules required for parsing expressions of the form::
+Operator precedence rules required for parsing expressions of the form::
#if 1 && 2 != 0
"""
# empty define, assign an empty token
return (v, [[], [('T','')]])
-re_include = re.compile('^\s*(<(?:.*)>|"(?:.*)")')
+re_include = re.compile(r'^\s*(<(?:.*)>|"(?:.*)")')
def extract_include(txt, defs):
"""
Process a line in the form::
tg = bld(features='includes', includes='.')
The folders only need to be relative to the current directory, the equivalent build directory is
- added automatically (for headers created in the build directory). This enable using a build directory
+ added automatically (for headers created in the build directory). This enables using a build directory
or not (``top == out``).
This method will add a list of nodes read by :py:func:`waflib.Tools.ccroot.to_incnodes` in ``tg.env.INCPATHS``,
nums = self.generator.vnum.split('.')
if self.env.DEST_BINFMT == 'pe':
# include the version in the dll file name,
- # the import lib file name stays unversionned.
+ # the import lib file name stays unversioned.
name = name + '-' + nums[0]
elif self.env.DEST_OS == 'openbsd':
pattern = '%s.%s' % (pattern, nums[0])
setattr(cls, 'run', wrap)
rm_tgt(stlink_task)
+@feature('skip_stlib_link_deps')
+@before_method('process_use')
+def apply_skip_stlib_link_deps(self):
+ """
+ This enables an optimization in the :py:func:wafilb.Tools.ccroot.processes_use: method that skips dependency and
+ link flag optimizations for targets that generate static libraries (via the :py:class:Tools.ccroot.stlink_task task).
+ The actual behavior is implemented in :py:func:wafilb.Tools.ccroot.processes_use: method so this feature only tells waf
+ to enable the new behavior.
+ """
+ self.env.SKIP_STLIB_LINK_DEPS = True
+
@feature('c', 'cxx', 'd', 'fc', 'asm')
@after_method('process_source')
def apply_link(self):
y = self.bld.get_tgen_by_name(x)
var = y.tmp_use_var
if var and link_task:
- if var == 'LIB' or y.tmp_use_stlib or x in names:
+ if self.env.SKIP_STLIB_LINK_DEPS and isinstance(link_task, stlink_task):
+ # If the skip_stlib_link_deps feature is enabled then we should
+ # avoid adding lib deps to the stlink_task instance.
+ pass
+ elif var == 'LIB' or y.tmp_use_stlib or x in names:
self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]])
self.link_task.dep_nodes.extend(y.link_task.outputs)
tmp_path = y.link_task.outputs[0].parent.path_from(self.get_cwd())
if getattr(self, 'install_task', None):
self.install_task.hasrun = Task.SKIPPED
+ self.install_task.no_errcheck_out = True
path = self.install_task.install_to
if self.env.DEST_OS == 'openbsd':
libname = self.link_task.outputs[0].name
self.allnames = []
- self.re_module = re.compile("module\s+([^;]+)")
- self.re_import = re.compile("import\s+([^;]+)")
+ self.re_module = re.compile(r"module\s+([^;]+)")
+ self.re_import = re.compile(r"import\s+([^;]+)")
self.re_import_bindings = re.compile("([^:]+):(.*)")
self.re_import_alias = re.compile("[^=]+=(.+)")
mod_name = self.re_module.search(code)
if mod_name:
- self.module = re.sub('\s+', '', mod_name.group(1)) # strip all whitespaces
+ self.module = re.sub(r'\s+', '', mod_name.group(1)) # strip all whitespaces
# go through the code, have a look at all import occurrences
import_iterator = self.re_import.finditer(code)
if import_iterator:
for import_match in import_iterator:
- import_match_str = re.sub('\s+', '', import_match.group(1)) # strip all whitespaces
+ import_match_str = re.sub(r'\s+', '', import_match.group(1)) # strip all whitespaces
# does this end with an import bindings declaration?
# (import bindings always terminate the list of imports)
Turns a module name into the right module file name.
Defaults to all lower case.
"""
- return {'lower' :name.lower() + '.mod',
- 'lower.MOD' :name.lower() + '.MOD',
- 'UPPER.mod' :name.upper() + '.mod',
- 'UPPER' :name.upper() + '.MOD'}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
+ if name.find(':') >= 0:
+ # Depending on a submodule!
+ separator = conf.env.FC_SUBMOD_SEPARATOR or '@'
+ # Ancestors of the submodule will be prefixed to the
+ # submodule name, separated by a colon.
+ modpath = name.split(':')
+ # Only the ancestor (actual) module and the submodule name
+ # will be used for the filename.
+ modname = modpath[0] + separator + modpath[-1]
+ suffix = conf.env.FC_SUBMOD_SUFFIX or '.smod'
+ else:
+ modname = name
+ suffix = '.mod'
+
+ return {'lower' :modname.lower() + suffix.lower(),
+ 'lower.MOD' :modname.lower() + suffix.upper(),
+ 'UPPER.mod' :modname.upper() + suffix.lower(),
+ 'UPPER' :modname.upper() + suffix.upper()}[conf.env.FC_MOD_CAPITALIZATION or 'lower']
def get_fortran_tasks(tsk):
"""
for k in ins.keys():
for a in ins[k]:
a.run_after.update(outs[k])
+ for x in outs[k]:
+ self.generator.bld.producer.revdeps[x].add(a)
# the scanner cannot output nodes, so we have to set them
# ourselves as task.dep_nodes (additional input nodes)
# ------------------------------------------------------------------------
GCC_DRIVER_LINE = re.compile('^Driving:')
-POSIX_STATIC_EXT = re.compile('\S+\.a')
-POSIX_LIB_FLAGS = re.compile('-l\S+')
+POSIX_STATIC_EXT = re.compile(r'\S+\.a')
+POSIX_LIB_FLAGS = re.compile(r'-l\S+')
@conf
def is_link_verbose(self, txt):
elif POSIX_LIB_FLAGS.match(token):
tmp_flags.append(token)
else:
- # ignore anything not explicitely taken into account
+ # ignore anything not explicitly taken into account
pass
t = lexer.get_token()
import re
-INC_REGEX = """(?:^|['">]\s*;)\s*(?:|#\s*)INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])"""
-USE_REGEX = """(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)"""
-MOD_REGEX = """(?:^|;)\s*MODULE(?!\s*PROCEDURE)(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)"""
+INC_REGEX = r"""(?:^|['">]\s*;)\s*(?:|#\s*)INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])"""
+USE_REGEX = r"""(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)"""
+MOD_REGEX = r"""(?:^|;)\s*MODULE(?!\s+(?:PROCEDURE|SUBROUTINE|FUNCTION))\s+(\w+)"""
+SMD_REGEX = r"""(?:^|;)\s*SUBMODULE\s*\(([\w:]+)\)\s*(\w+)"""
re_inc = re.compile(INC_REGEX, re.I)
re_use = re.compile(USE_REGEX, re.I)
re_mod = re.compile(MOD_REGEX, re.I)
+re_smd = re.compile(SMD_REGEX, re.I)
class fortran_parser(object):
"""
m = re_mod.search(line)
if m:
mods.append(m.group(1))
+ m = re_smd.search(line)
+ if m:
+ uses.append(m.group(1))
+ mods.append('{0}:{1}'.format(m.group(1),m.group(2)))
return (incs, uses, mods)
def start(self, node):
"""
List compiler versions by looking up registry keys
"""
- version_pattern = re.compile('^...?.?\....?.?')
+ version_pattern = re.compile(r'^...?.?\....?.?')
try:
all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\Fortran')
except OSError:
java -jar /path/to/jython.jar waf configure
[1] http://www.jython.org/
+
+Usage
+=====
+
+Load the "java" tool.
+
+def configure(conf):
+ conf.load('java')
+
+Java tools will be autodetected and eventually, if present, the quite
+standard JAVA_HOME environment variable will be used. The also standard
+CLASSPATH variable is used for library searching.
+
+In configuration phase checks can be done on the system environment, for
+example to check if a class is known in the classpath::
+
+ conf.check_java_class('java.io.FileOutputStream')
+
+or if the system supports JNI applications building::
+
+ conf.check_jni_headers()
+
+
+The java tool supports compiling java code, creating jar files and
+creating javadoc documentation. This can be either done separately or
+together in a single definition. For example to manage them separately::
+
+ bld(features = 'javac',
+ srcdir = 'src',
+ compat = '1.7',
+ use = 'animals',
+ name = 'cats-src',
+ )
+
+ bld(features = 'jar',
+ basedir = '.',
+ destfile = '../cats.jar',
+ name = 'cats',
+ use = 'cats-src'
+ )
+
+
+Or together by defining all the needed attributes::
+
+ bld(features = 'javac jar javadoc',
+ srcdir = 'src/', # folder containing the sources to compile
+ outdir = 'src', # folder where to output the classes (in the build directory)
+ compat = '1.6', # java compatibility version number
+ classpath = ['.', '..'],
+
+ # jar
+ basedir = 'src', # folder containing the classes and other files to package (must match outdir)
+ destfile = 'foo.jar', # do not put the destfile in the folder of the java classes!
+ use = 'NNN',
+ jaropts = ['-C', 'default/src/', '.'], # can be used to give files
+ manifest = 'src/Manifest.mf', # Manifest file to include
+
+ # javadoc
+ javadoc_package = ['com.meow' , 'com.meow.truc.bar', 'com.meow.truc.foo'],
+ javadoc_output = 'javadoc',
+ )
+
+External jar dependencies can be mapped to a standard waf "use" dependency by
+setting an environment variable with a CLASSPATH prefix in the configuration,
+for example::
+
+ conf.env.CLASSPATH_NNN = ['aaaa.jar', 'bbbb.jar']
+
+and then NNN can be freely used in rules as::
+
+ use = 'NNN',
+
+In the java tool the dependencies via use are not transitive by default, as
+this necessity depends on the code. To enable recursive dependency scanning
+use on a specific rule:
+
+ recurse_use = True
+
+Or build-wise by setting RECURSE_JAVA:
+
+ bld.env.RECURSE_JAVA = True
+
+Unit tests can be integrated in the waf unit test environment using the javatest extra.
"""
import os, shutil
from waflib import Task, Utils, Errors, Node
from waflib.Configure import conf
-from waflib.TaskGen import feature, before_method, after_method
+from waflib.TaskGen import feature, before_method, after_method, taskgen_method
from waflib.Tools import ccroot
ccroot.USELIB_VARS['javac'] = set(['CLASSPATH', 'JAVACFLAGS'])
if names:
tsk.env.append_value('JAVACFLAGS', ['-sourcepath', names])
+
+@taskgen_method
+def java_use_rec(self, name, **kw):
+ """
+ Processes recursively the *use* attribute for each referred java compilation
+ """
+ if name in self.tmp_use_seen:
+ return
+
+ self.tmp_use_seen.append(name)
+
+ try:
+ y = self.bld.get_tgen_by_name(name)
+ except Errors.WafError:
+ self.uselib.append(name)
+ return
+ else:
+ y.post()
+ # Add generated JAR name for CLASSPATH. Task ordering (set_run_after)
+ # is already guaranteed by ordering done between the single tasks
+ if hasattr(y, 'jar_task'):
+ self.use_lst.append(y.jar_task.outputs[0].abspath())
+ else:
+ if hasattr(y,'outdir'):
+ self.use_lst.append(y.outdir.abspath())
+ else:
+ self.use_lst.append(y.path.get_bld().abspath())
+
+ for x in self.to_list(getattr(y, 'use', [])):
+ self.java_use_rec(x)
+
@feature('javac')
@before_method('propagate_uselib_vars')
@after_method('apply_java')
"""
Processes the *use* attribute referring to other java compilations
"""
- lst = []
+ self.use_lst = []
+ self.tmp_use_seen = []
self.uselib = self.to_list(getattr(self, 'uselib', []))
names = self.to_list(getattr(self, 'use', []))
get = self.bld.get_tgen_by_name
for x in names:
try:
- y = get(x)
+ tg = get(x)
except Errors.WafError:
self.uselib.append(x)
else:
- y.post()
- if hasattr(y, 'jar_task'):
- lst.append(y.jar_task.outputs[0].abspath())
- self.javac_task.set_run_after(y.jar_task)
+ tg.post()
+ if hasattr(tg, 'jar_task'):
+ self.use_lst.append(tg.jar_task.outputs[0].abspath())
+ self.javac_task.set_run_after(tg.jar_task)
+ self.javac_task.dep_nodes.extend(tg.jar_task.outputs)
else:
- for tsk in y.tasks:
+ if hasattr(tg, 'outdir'):
+ base_node = tg.outdir.abspath()
+ else:
+ base_node = tg.path.get_bld()
+
+ self.use_lst.append(base_node.abspath())
+ self.javac_task.dep_nodes.extend([x for x in base_node.ant_glob(JAR_RE, remove=False, quiet=True)])
+
+ for tsk in tg.tasks:
self.javac_task.set_run_after(tsk)
- self.env.append_value('CLASSPATH', lst)
+
+ # If recurse use scan is enabled recursively add use attribute for each used one
+ if getattr(self, 'recurse_use', False) or self.bld.env.RECURSE_JAVA:
+ self.java_use_rec(x)
+
+ self.env.append_value('CLASSPATH', self.use_lst)
@feature('javac')
@after_method('apply_java', 'propagate_uselib_vars', 'use_javac_files')
return Task.ASK_LATER
if not self.inputs:
try:
- self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False) if id(x) != id(self.outputs[0])]
+ self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False, quiet=True) if id(x) != id(self.outputs[0])]
except Exception:
raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self))
return super(jar_create, self).runnable_status()
self.inputs = []
for x in self.srcdir:
if x.exists():
- self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False))
+ self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False, quiet=True))
return super(javac, self).runnable_status()
def post_run(self):
"""
List class files created
"""
- for node in self.generator.outdir.ant_glob('**/*.class'):
+ for node in self.generator.outdir.ant_glob('**/*.class', quiet=True):
self.generator.bld.node_sigs[node] = self.uid()
self.generator.bld.task_sigs[self.uid()] = self.cache_sig
self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, quiet=0)
def post_run(self):
- nodes = self.generator.javadoc_output.ant_glob('**')
+ nodes = self.generator.javadoc_output.ant_glob('**', quiet=True)
for node in nodes:
self.generator.bld.node_sigs[node] = self.uid()
self.generator.bld.task_sigs[self.uid()] = self.cache_sig
self.env.JAVA_HOME = [self.environ['JAVA_HOME']]
for x in 'javac java jar javadoc'.split():
- self.find_program(x, var=x.upper(), path_list=java_path)
+ self.find_program(x, var=x.upper(), path_list=java_path, mandatory=(x not in ('javadoc')))
if 'CLASSPATH' in self.environ:
v.CLASSPATH = self.environ['CLASSPATH']
# encoding: utf-8
"""
-Re-calculate md5 hashes of files only when the file times or the file
-size have changed.
+Re-calculate md5 hashes of files only when the file time have changed::
+
+ def options(opt):
+ opt.load('md5_tstamp')
The hashes can also reflect either the file contents (STRONGEST=True) or the
file time and file size.
def gather_msvc_detected_versions():
#Detected MSVC versions!
- version_pattern = re.compile('^(\d\d?\.\d\d?)(Exp)?$')
+ version_pattern = re.compile(r'^(\d\d?\.\d\d?)(Exp)?$')
detected_versions = []
for vcver,vcvar in (('VCExpress','Exp'), ('VisualStudio','')):
prefix = 'SOFTWARE\\Wow6432node\\Microsoft\\' + vcver
:param versions: list to modify
:type versions: list
"""
- version_pattern = re.compile('^v..?.?\...?.?')
+ version_pattern = re.compile(r'^v..?.?\...?.?')
try:
all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows')
except OSError:
:param versions: list to modify
:type versions: list
"""
- version_pattern = re.compile('^...?.?\....?.?')
+ version_pattern = re.compile(r'^...?.?\....?.?')
try:
all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\C++')
except OSError:
:param versions: list to modify
:type versions: list
"""
- version_pattern = re.compile('^...?.?\...?.?.?')
+ version_pattern = re.compile(r'^...?.?\...?.?.?')
try:
all_versions = Utils.winreg.OpenKey(Utils.winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Suites')
except OSError:
if not is_static and ltdict.get('library_names', ''):
dllnames=ltdict['library_names'].split()
dll=dllnames[0].lower()
- dll=re.sub('\.dll$', '', dll)
+ dll=re.sub(r'\.dll$', '', dll)
return (lt_libdir, dll, False)
elif ltdict.get('old_library', ''):
olib=ltdict['old_library']
@conf
def libname_msvc(self, libname, is_static=False):
lib = libname.lower()
- lib = re.sub('\.lib$','',lib)
+ lib = re.sub(r'\.lib$','',lib)
if lib in g_msvc_systemlibs:
return lib
for libn in libnames:
if os.path.exists(os.path.join(path, libn)):
Logs.debug('msvc: lib found: %s', os.path.join(path,libn))
- return re.sub('\.lib$', '',libn)
+ return re.sub(r'\.lib$', '',libn)
#if no lib can be found, just return the libname as msvc expects it
self.fatal('The library %r could not be found' % libname)
- return re.sub('\.lib$', '', libname)
+ return re.sub(r'\.lib$', '', libname)
@conf
def check_lib_msvc(self, libname, is_static=False, uselib_store=None):
if not is_static:
for f in self.env.LINKFLAGS:
d = f.lower()
- if d[1:] == 'debug':
+ if d[1:] in ('debug', 'debug:full', 'debug:fastlink'):
pdbnode = self.link_task.outputs[0].change_ext('.pdb')
self.link_task.outputs.append(pdbnode)
conf.find_program([''.join(pybin) + '-config', 'python%s-config' % num, 'python-config-%s' % num, 'python%sm-config' % num], var='PYTHON_CONFIG', msg="python-config", mandatory=False)
if env.PYTHON_CONFIG:
+ # check python-config output only once
+ if conf.env.HAVE_PYTHON_H:
+ return
+
# python2.6-config requires 3 runs
all_flags = [['--cflags', '--libs', '--ldflags']]
if sys.hexversion < 0x2070000:
if 'pyembed' in features:
for flags in all_flags:
- conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(flags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=flags)
+ # Python 3.8 has different flags for pyembed, needs --embed
+ embedflags = flags + ['--embed']
+ try:
+ conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(embedflags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=embedflags)
+ except conf.errors.ConfigurationError:
+ # However Python < 3.8 doesn't accept --embed, so we need a fallback
+ conf.check_cfg(msg='Asking python-config for pyembed %r flags' % ' '.join(flags), path=env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=flags)
try:
conf.test_pyembed(xx)
Check if the python interpreter is found matching a given minimum version.
minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver.
- If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR'
- (eg. '2.4') of the actual python version found, and PYTHONDIR is
- defined, pointing to the site-packages directory appropriate for
+ If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR' (eg. '2.4')
+ of the actual python version found, and PYTHONDIR and PYTHONARCHDIR
+ are defined, pointing to the site-packages directories appropriate for
this python version, where modules/packages/extensions should be
installed.
import os, sys, re
from waflib.Tools import cxx
-from waflib import Task, Utils, Options, Errors, Context
+from waflib import Build, Task, Utils, Options, Errors, Context
from waflib.TaskGen import feature, after_method, extension, before_method
from waflib.Configure import conf
from waflib import Logs
node = self.inputs[0]
bld = self.generator.bld
+ # skip on uninstall due to generated files
+ if bld.is_install == Build.UNINSTALL:
+ return
+
try:
# compute the signature once to know if there is a moc file to create
self.signature()
The additional parameters are:
- :param lang: list of translation files (\*.ts) to process
+ :param lang: list of translation files (\\*.ts) to process
:type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
- :param update: whether to process the C++ files to update the \*.ts files (use **waf --translate**)
+ :param update: whether to process the C++ files to update the \\*.ts files (use **waf --translate**)
:type update: bool
- :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
+ :param langname: if given, transform the \\*.ts files into a .qrc files to include in the binary file
:type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
"""
if getattr(self, 'lang', None):
if self.environ.get('QT5_FORCE_STATIC'):
pat = self.env.cxxstlib_PATTERN
if Utils.unversioned_sys_platform() == 'darwin':
- pat = "%s\.framework"
+ pat = r"%s\.framework"
re_qt = re.compile(pat%'Qt5?(?P<name>.*)'+'$')
for x in dirlst:
m = re_qt.match(x)
return self.exec_command(self.ut_exec)
def exec_command(self, cmd, **kw):
- Logs.debug('runner: %r', cmd)
+ self.generator.bld.log_command(cmd, kw)
if getattr(Options.options, 'dump_test_scripts', False):
script_code = SCRIPT_TEMPLATE % {
'python': sys.executable,
'cmd': cmd
}
script_file = self.inputs[0].abspath() + '_run.py'
- Utils.writef(script_file, script_code)
+ Utils.writef(script_file, script_code, encoding='utf-8')
os.chmod(script_file, Utils.O755)
if Logs.verbose > 1:
Logs.info('Test debug file written as %r' % script_file)
self.compiled_tasks = [rctask]
re_lines = re.compile(
- '(?:^[ \t]*(#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*?)\s*$)|'\
- '(?:^\w+[ \t]*(ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)[ \t]*(.*?)\s*$)',
+ r'(?:^[ \t]*(#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*?)\s*$)|'\
+ r'(?:^\w+[ \t]*(ICON|BITMAP|CURSOR|HTML|FONT|MESSAGETABLE|TYPELIB|REGISTRY|D3DFX)[ \t]*(.*?)\s*$)',
re.IGNORECASE | re.MULTILINE)
class rc_parser(c_preproc.c_parser):
from hashlib import md5
except ImportError:
try:
- from md5 import md5
+ from hashlib import sha1 as md5
except ImportError:
- # never fail to enable fixes from another module
+ # never fail to enable potential fixes from another module
pass
+else:
+ try:
+ md5().digest()
+ except ValueError:
+ # Fips? #2213
+ from hashlib import sha1 as md5
try:
import threading
next = __next__
-is_win32 = os.sep == '\\' or sys.platform == 'win32' # msys2
+is_win32 = os.sep == '\\' or sys.platform == 'win32' or os.name == 'nt' # msys2
"""
Whether this system is a Windows series
"""
if sys.platform == 'cygwin':
split_path = split_path_cygwin
elif is_win32:
- if os.environ.get('MSYSTEM'):
+ # Consider this an MSYSTEM environment if $MSYSTEM is set and python
+ # reports is executable from a unix like path on a windows host.
+ if os.environ.get('MSYSTEM') and sys.executable.startswith('/'):
split_path = split_path_msys
else:
split_path = split_path_win32
"""
return md5(repr(lst).encode()).digest()
+if sys.hexversion < 0x3000000:
+ def h_list_python2(lst):
+ return md5(repr(lst)).digest()
+ h_list_python2.__doc__ = h_list.__doc__
+ h_list = h_list_python2
+
def h_fun(fun):
"""
Hash functions
if s == 'cli' and os.name == 'nt':
# ironpython is only on windows as far as we know
return 'win32'
- return re.split('\d+$', s)[0]
+ return re.split(r'\d+$', s)[0]
def nada(*k, **kw):
"""
except IndexError:
filepath = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'processor.py'
cmd = [sys.executable, '-c', readf(filepath)]
- return subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0)
+ return subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0, close_fds=not is_win32)
def run_prefork_process(cmd, kwargs, cargs):
"""
'u': pop_cursor,
}
# Match either the escape sequence or text not containing escape sequence
- ansi_tokens = re.compile('(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))')
+ ansi_tokens = re.compile(r'(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))')
def write(self, text):
try:
wlock.acquire()
"""
import os, shutil
-from waflib import Errors, Task, TaskGen, Utils, Node
+from waflib import Errors, Task, TaskGen, Utils, Node, Logs
@TaskGen.before_method('process_source')
@TaskGen.feature('buildcopy')
raise Errors.WafError('buildcopy: File not found in src: %s'%os.path.join(*lst))
nodes = [ to_src_nodes(n) for n in getattr(self, 'buildcopy_source', getattr(self, 'source', [])) ]
+ if not nodes:
+ Logs.warn('buildcopy: No source files provided to buildcopy in %s (set `buildcopy_source` or `source`)',
+ self)
+ return
node_pairs = [(n, n.get_bld()) for n in nodes]
self.create_task('buildcopy', [n[0] for n in node_pairs], [n[1] for n in node_pairs], node_pairs=node_pairs)
-
class buildcopy(Task.Task):
"""
Copy for each pair `n` in `node_pairs`: n[0] -> n[1].
--- /dev/null
+#!/usr/bin/env python
+# encoding: utf-8
+# Krzysztof Kosiński 2014
+# DragoonX6 2018
+
+"""
+Detect the Clang C compiler
+This version is an attempt at supporting the -target and -sysroot flag of Clang.
+"""
+
+from waflib.Tools import ccroot, ar, gcc
+from waflib.Configure import conf
+import waflib.Context
+import waflib.extras.clang_cross_common
+
+def options(opt):
+ """
+ Target triplet for clang::
+ $ waf configure --clang-target-triple=x86_64-pc-linux-gnu
+ """
+ cc_compiler_opts = opt.add_option_group('Configuration options')
+ cc_compiler_opts.add_option('--clang-target-triple', default=None,
+ help='Target triple for clang',
+ dest='clang_target_triple')
+ cc_compiler_opts.add_option('--clang-sysroot', default=None,
+ help='Sysroot for clang',
+ dest='clang_sysroot')
+
+@conf
+def find_clang(conf):
+ """
+ Finds the program clang and executes it to ensure it really is clang
+ """
+
+ import os
+
+ cc = conf.find_program('clang', var='CC')
+
+ if conf.options.clang_target_triple != None:
+ conf.env.append_value('CC', ['-target', conf.options.clang_target_triple])
+
+ if conf.options.clang_sysroot != None:
+ sysroot = str()
+
+ if os.path.isabs(conf.options.clang_sysroot):
+ sysroot = conf.options.clang_sysroot
+ else:
+ sysroot = os.path.normpath(os.path.join(os.getcwd(), conf.options.clang_sysroot))
+
+ conf.env.append_value('CC', ['--sysroot', sysroot])
+
+ conf.get_cc_version(cc, clang=True)
+ conf.env.CC_NAME = 'clang'
+
+@conf
+def clang_modifier_x86_64_w64_mingw32(conf):
+ conf.gcc_modifier_win32()
+
+@conf
+def clang_modifier_i386_w64_mingw32(conf):
+ conf.gcc_modifier_win32()
+
+@conf
+def clang_modifier_x86_64_windows_msvc(conf):
+ conf.clang_modifier_msvc()
+
+ # Allow the user to override any flags if they so desire.
+ clang_modifier_user_func = getattr(conf, 'clang_modifier_x86_64_windows_msvc_user', None)
+ if clang_modifier_user_func:
+ clang_modifier_user_func()
+
+@conf
+def clang_modifier_i386_windows_msvc(conf):
+ conf.clang_modifier_msvc()
+
+ # Allow the user to override any flags if they so desire.
+ clang_modifier_user_func = getattr(conf, 'clang_modifier_i386_windows_msvc_user', None)
+ if clang_modifier_user_func:
+ clang_modifier_user_func()
+
+def configure(conf):
+ conf.find_clang()
+ conf.find_program(['llvm-ar', 'ar'], var='AR')
+ conf.find_ar()
+ conf.gcc_common_flags()
+ # Allow the user to provide flags for the target platform.
+ conf.gcc_modifier_platform()
+ # And allow more fine grained control based on the compiler's triplet.
+ conf.clang_modifier_target_triple()
+ conf.cc_load_tools()
+ conf.cc_add_flags()
+ conf.link_add_flags()
--- /dev/null
+#!/usr/bin/env python
+# encoding: utf-8
+# DragoonX6 2018
+
+"""
+Common routines for cross_clang.py and cross_clangxx.py
+"""
+
+from waflib.Configure import conf
+import waflib.Context
+
+def normalize_target_triple(target_triple):
+ target_triple = target_triple[:-1]
+ normalized_triple = target_triple.replace('--', '-unknown-')
+
+ if normalized_triple.startswith('-'):
+ normalized_triple = 'unknown' + normalized_triple
+
+ if normalized_triple.endswith('-'):
+ normalized_triple += 'unknown'
+
+ # Normalize MinGW builds to *arch*-w64-mingw32
+ if normalized_triple.endswith('windows-gnu'):
+ normalized_triple = normalized_triple[:normalized_triple.index('-')] + '-w64-mingw32'
+
+ # Strip the vendor when doing msvc builds, since it's unused anyway.
+ if normalized_triple.endswith('windows-msvc'):
+ normalized_triple = normalized_triple[:normalized_triple.index('-')] + '-windows-msvc'
+
+ return normalized_triple.replace('-', '_')
+
+@conf
+def clang_modifier_msvc(conf):
+ import os
+
+ """
+ Really basic setup to use clang in msvc mode.
+ We actually don't really want to do a lot, even though clang is msvc compatible
+ in this mode, that doesn't mean we're actually using msvc.
+ It's probably the best to leave it to the user, we can assume msvc mode if the user
+ uses the clang-cl frontend, but this module only concerns itself with the gcc-like frontend.
+ """
+ v = conf.env
+ v.cprogram_PATTERN = '%s.exe'
+
+ v.cshlib_PATTERN = '%s.dll'
+ v.implib_PATTERN = '%s.lib'
+ v.IMPLIB_ST = '-Wl,-IMPLIB:%s'
+ v.SHLIB_MARKER = []
+
+ v.CFLAGS_cshlib = []
+ v.LINKFLAGS_cshlib = ['-Wl,-DLL']
+ v.cstlib_PATTERN = '%s.lib'
+ v.STLIB_MARKER = []
+
+ del(v.AR)
+ conf.find_program(['llvm-lib', 'lib'], var='AR')
+ v.ARFLAGS = ['-nologo']
+ v.AR_TGT_F = ['-out:']
+
+ # Default to the linker supplied with llvm instead of link.exe or ld
+ v.LINK_CC = v.CC + ['-fuse-ld=lld', '-nostdlib']
+ v.CCLNK_TGT_F = ['-o']
+ v.def_PATTERN = '-Wl,-def:%s'
+
+ v.LINKFLAGS = []
+
+ v.LIB_ST = '-l%s'
+ v.LIBPATH_ST = '-Wl,-LIBPATH:%s'
+ v.STLIB_ST = '-l%s'
+ v.STLIBPATH_ST = '-Wl,-LIBPATH:%s'
+
+ CFLAGS_CRT_COMMON = [
+ '-Xclang', '--dependent-lib=oldnames',
+ '-Xclang', '-fno-rtti-data',
+ '-D_MT'
+ ]
+
+ v.CFLAGS_CRT_MULTITHREADED = CFLAGS_CRT_COMMON + [
+ '-Xclang', '-flto-visibility-public-std',
+ '-Xclang', '--dependent-lib=libcmt',
+ ]
+ v.CXXFLAGS_CRT_MULTITHREADED = v.CFLAGS_CRT_MULTITHREADED
+
+ v.CFLAGS_CRT_MULTITHREADED_DBG = CFLAGS_CRT_COMMON + [
+ '-D_DEBUG',
+ '-Xclang', '-flto-visibility-public-std',
+ '-Xclang', '--dependent-lib=libcmtd',
+ ]
+ v.CXXFLAGS_CRT_MULTITHREADED_DBG = v.CFLAGS_CRT_MULTITHREADED_DBG
+
+ v.CFLAGS_CRT_MULTITHREADED_DLL = CFLAGS_CRT_COMMON + [
+ '-D_DLL',
+ '-Xclang', '--dependent-lib=msvcrt'
+ ]
+ v.CXXFLAGS_CRT_MULTITHREADED_DLL = v.CFLAGS_CRT_MULTITHREADED_DLL
+
+ v.CFLAGS_CRT_MULTITHREADED_DLL_DBG = CFLAGS_CRT_COMMON + [
+ '-D_DLL',
+ '-D_DEBUG',
+ '-Xclang', '--dependent-lib=msvcrtd',
+ ]
+ v.CXXFLAGS_CRT_MULTITHREADED_DLL_DBG = v.CFLAGS_CRT_MULTITHREADED_DLL_DBG
+
+@conf
+def clang_modifier_target_triple(conf, cpp=False):
+ compiler = conf.env.CXX if cpp else conf.env.CC
+ output = conf.cmd_and_log(compiler + ['-dumpmachine'], output=waflib.Context.STDOUT)
+
+ modifier = ('clangxx' if cpp else 'clang') + '_modifier_'
+ clang_modifier_func = getattr(conf, modifier + normalize_target_triple(output), None)
+ if clang_modifier_func:
+ clang_modifier_func()
--- /dev/null
+#!/usr/bin/env python
+# encoding: utf-8
+# Thomas Nagy 2009-2018 (ita)
+# DragoonX6 2018
+
+"""
+Detect the Clang++ C++ compiler
+This version is an attempt at supporting the -target and -sysroot flag of Clang++.
+"""
+
+from waflib.Tools import ccroot, ar, gxx
+from waflib.Configure import conf
+import waflib.extras.clang_cross_common
+
+def options(opt):
+ """
+ Target triplet for clang++::
+ $ waf configure --clangxx-target-triple=x86_64-pc-linux-gnu
+ """
+ cxx_compiler_opts = opt.add_option_group('Configuration options')
+ cxx_compiler_opts.add_option('--clangxx-target-triple', default=None,
+ help='Target triple for clang++',
+ dest='clangxx_target_triple')
+ cxx_compiler_opts.add_option('--clangxx-sysroot', default=None,
+ help='Sysroot for clang++',
+ dest='clangxx_sysroot')
+
+@conf
+def find_clangxx(conf):
+ """
+ Finds the program clang++, and executes it to ensure it really is clang++
+ """
+
+ import os
+
+ cxx = conf.find_program('clang++', var='CXX')
+
+ if conf.options.clangxx_target_triple != None:
+ conf.env.append_value('CXX', ['-target', conf.options.clangxx_target_triple])
+
+ if conf.options.clangxx_sysroot != None:
+ sysroot = str()
+
+ if os.path.isabs(conf.options.clangxx_sysroot):
+ sysroot = conf.options.clangxx_sysroot
+ else:
+ sysroot = os.path.normpath(os.path.join(os.getcwd(), conf.options.clangxx_sysroot))
+
+ conf.env.append_value('CXX', ['--sysroot', sysroot])
+
+ conf.get_cc_version(cxx, clang=True)
+ conf.env.CXX_NAME = 'clang'
+
+@conf
+def clangxx_modifier_x86_64_w64_mingw32(conf):
+ conf.gcc_modifier_win32()
+
+@conf
+def clangxx_modifier_i386_w64_mingw32(conf):
+ conf.gcc_modifier_win32()
+
+@conf
+def clangxx_modifier_msvc(conf):
+ v = conf.env
+ v.cxxprogram_PATTERN = v.cprogram_PATTERN
+ v.cxxshlib_PATTERN = v.cshlib_PATTERN
+
+ v.CXXFLAGS_cxxshlib = []
+ v.LINKFLAGS_cxxshlib = v.LINKFLAGS_cshlib
+ v.cxxstlib_PATTERN = v.cstlib_PATTERN
+
+ v.LINK_CXX = v.CXX + ['-fuse-ld=lld', '-nostdlib']
+ v.CXXLNK_TGT_F = v.CCLNK_TGT_F
+
+@conf
+def clangxx_modifier_x86_64_windows_msvc(conf):
+ conf.clang_modifier_msvc()
+ conf.clangxx_modifier_msvc()
+
+ # Allow the user to override any flags if they so desire.
+ clang_modifier_user_func = getattr(conf, 'clangxx_modifier_x86_64_windows_msvc_user', None)
+ if clang_modifier_user_func:
+ clang_modifier_user_func()
+
+@conf
+def clangxx_modifier_i386_windows_msvc(conf):
+ conf.clang_modifier_msvc()
+ conf.clangxx_modifier_msvc()
+
+ # Allow the user to override any flags if they so desire.
+ clang_modifier_user_func = getattr(conf, 'clangxx_modifier_i386_windows_msvc_user', None)
+ if clang_modifier_user_func:
+ clang_modifier_user_func()
+
+def configure(conf):
+ conf.find_clangxx()
+ conf.find_program(['llvm-ar', 'ar'], var='AR')
+ conf.find_ar()
+ conf.gxx_common_flags()
+ # Allow the user to provide flags for the target platform.
+ conf.gxx_modifier_platform()
+ # And allow more fine grained control based on the compiler's triplet.
+ conf.clang_modifier_target_triple(cpp=True)
+ conf.cxx_load_tools()
+ conf.cxx_add_flags()
+ conf.link_add_flags()
--- /dev/null
+#!/usr/bin/env python
+# encoding: utf-8
+
+# Replaces the default formatter by one which understands MSVC output and colorizes it.
+# Modified from color_gcc.py
+
+__author__ = __maintainer__ = "Alibek Omarov <a1ba.omarov@gmail.com>"
+__copyright__ = "Alibek Omarov, 2019"
+
+import sys
+from waflib import Logs
+
+class ColorMSVCFormatter(Logs.formatter):
+ def __init__(self, colors):
+ self.colors = colors
+ Logs.formatter.__init__(self)
+
+ def parseMessage(self, line, color):
+ # Split messaage from 'disk:filepath: type: message'
+ arr = line.split(':', 3)
+ if len(arr) < 4:
+ return line
+
+ colored = self.colors.BOLD + arr[0] + ':' + arr[1] + ':' + self.colors.NORMAL
+ colored += color + arr[2] + ':' + self.colors.NORMAL
+ colored += arr[3]
+ return colored
+
+ def format(self, rec):
+ frame = sys._getframe()
+ while frame:
+ func = frame.f_code.co_name
+ if func == 'exec_command':
+ cmd = frame.f_locals.get('cmd')
+ if isinstance(cmd, list):
+ # Fix file case, it may be CL.EXE or cl.exe
+ argv0 = cmd[0].lower()
+ if 'cl.exe' in argv0:
+ lines = []
+ # This will not work with "localized" versions
+ # of MSVC
+ for line in rec.msg.splitlines():
+ if ': warning ' in line:
+ lines.append(self.parseMessage(line, self.colors.YELLOW))
+ elif ': error ' in line:
+ lines.append(self.parseMessage(line, self.colors.RED))
+ elif ': fatal error ' in line:
+ lines.append(self.parseMessage(line, self.colors.RED + self.colors.BOLD))
+ elif ': note: ' in line:
+ lines.append(self.parseMessage(line, self.colors.CYAN))
+ else:
+ lines.append(line)
+ rec.msg = "\n".join(lines)
+ frame = frame.f_back
+ return Logs.formatter.format(self, rec)
+
+def options(opt):
+ Logs.log.handlers[0].setFormatter(ColorMSVCFormatter(Logs.colors))
+
args.append('--enable=%s' % lib_enable)
for src in self.to_list(getattr(self, 'source', [])):
- args.append('%r' % src)
+ if not isinstance(src, str):
+ src = repr(src)
+ args.append(src)
for inc in self.to_incnodes(self.to_list(getattr(self, 'includes', []))):
- args.append('-I%r' % inc)
+ if not isinstance(inc, str):
+ inc = repr(inc)
+ args.append('-I%s' % inc)
for inc in self.to_incnodes(self.to_list(self.env.INCLUDES)):
- args.append('-I%r' % inc)
+ if not isinstance(inc, str):
+ inc = repr(inc)
+ args.append('-I%s' % inc)
return cmd + args
from __future__ import absolute_import
import sys, re
import logging
-import threading
-from waflib import Task, TaskGen, Logs, Options, Node
-try:
- import cpplint.cpplint as cpplint_tool
-except ImportError:
- try:
- import cpplint as cpplint_tool
- except ImportError:
- pass
+from waflib import Errors, Task, TaskGen, Logs, Options, Node, Utils
critical_errors = 0
CPPLINT_FORMAT = '[CPPLINT] %(filename)s:\nline %(linenum)s, severity %(confidence)s, category: %(category)s\n%(message)s\n'
-RE_EMACS = re.compile('(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
+RE_EMACS = re.compile(r'(?P<filename>.*):(?P<linenum>\d+): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]')
CPPLINT_RE = {
'waf': RE_EMACS,
'emacs': RE_EMACS,
- 'vs7': re.compile('(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
- 'eclipse': re.compile('(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
+ 'vs7': re.compile(r'(?P<filename>.*)\((?P<linenum>\d+)\): (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
+ 'eclipse': re.compile(r'(?P<filename>.*):(?P<linenum>\d+): warning: (?P<message>.*) \[(?P<category>.*)\] \[(?P<confidence>\d+)\]'),
}
+CPPLINT_STR = ('${CPPLINT} '
+ '--verbose=${CPPLINT_LEVEL} '
+ '--output=${CPPLINT_OUTPUT} '
+ '--filter=${CPPLINT_FILTERS} '
+ '--root=${CPPLINT_ROOT} '
+ '--linelength=${CPPLINT_LINE_LENGTH} ')
+
def options(opt):
opt.add_option('--cpplint-filters', type='string',
opt.add_option('--cpplint-break', default=5, type='int', dest='CPPLINT_BREAK',
help='break the build if error >= level (default: 5)')
opt.add_option('--cpplint-root', type='string',
- default=None, dest='CPPLINT_ROOT',
+ default='', dest='CPPLINT_ROOT',
help='root directory used to derive header guard')
opt.add_option('--cpplint-skip', action='store_true',
default=False, dest='CPPLINT_SKIP',
help='skip cpplint during build')
opt.add_option('--cpplint-output', type='string',
default='waf', dest='CPPLINT_OUTPUT',
- help='select output format (waf, emacs, vs7)')
+ help='select output format (waf, emacs, vs7, eclipse)')
def configure(conf):
- conf.start_msg('Checking cpplint')
try:
- cpplint_tool._cpplint_state
- conf.end_msg('ok')
- except NameError:
+ conf.find_program('cpplint', var='CPPLINT')
+ except Errors.ConfigurationError:
conf.env.CPPLINT_SKIP = True
- conf.end_msg('not found, skipping it.')
class cpplint_formatter(Logs.formatter, object):
class cpplint_wrapper(object):
- stream = None
- tasks_count = 0
- lock = threading.RLock()
-
def __init__(self, logger, threshold, fmt):
self.logger = logger
self.threshold = threshold
- self.error_count = 0
self.fmt = fmt
def __enter__(self):
- with cpplint_wrapper.lock:
- cpplint_wrapper.tasks_count += 1
- if cpplint_wrapper.tasks_count == 1:
- sys.stderr.flush()
- cpplint_wrapper.stream = sys.stderr
- sys.stderr = self
- return self
+ return self
def __exit__(self, exc_type, exc_value, traceback):
- with cpplint_wrapper.lock:
- cpplint_wrapper.tasks_count -= 1
- if cpplint_wrapper.tasks_count == 0:
- sys.stderr = cpplint_wrapper.stream
- sys.stderr.flush()
-
- def isatty(self):
- return True
+ if isinstance(exc_value, Utils.subprocess.CalledProcessError):
+ messages = [m for m in exc_value.output.splitlines()
+ if 'Done processing' not in m
+ and 'Total errors found' not in m]
+ for message in messages:
+ self.write(message)
+ return True
def write(self, message):
global critical_errors
def run(self):
global critical_errors
with cpplint_wrapper(get_cpplint_logger(self.env.CPPLINT_OUTPUT), self.env.CPPLINT_BREAK, self.env.CPPLINT_OUTPUT):
- if self.env.CPPLINT_OUTPUT != 'waf':
- cpplint_tool._SetOutputFormat(self.env.CPPLINT_OUTPUT)
- cpplint_tool._SetFilters(self.env.CPPLINT_FILTERS)
- cpplint_tool._line_length = self.env.CPPLINT_LINE_LENGTH
- cpplint_tool._root = self.env.CPPLINT_ROOT
- cpplint_tool.ProcessFile(self.inputs[0].abspath(), self.env.CPPLINT_LEVEL)
+ params = {key: str(self.env[key]) for key in self.env if 'CPPLINT_' in key}
+ if params['CPPLINT_OUTPUT'] is 'waf':
+ params['CPPLINT_OUTPUT'] = 'emacs'
+ params['CPPLINT'] = self.env.get_flat('CPPLINT')
+ cmd = Utils.subst_vars(CPPLINT_STR, params)
+ env = self.env.env or None
+ Utils.subprocess.check_output(cmd + self.inputs[0].abspath(),
+ stderr=Utils.subprocess.STDOUT,
+ env=env, shell=True)
return critical_errors
@TaskGen.extension('.h', '.hh', '.hpp', '.hxx')
cy_api_pat = re.compile(r'\s*?cdef\s*?(public|api)\w*')
re_cyt = re.compile(r"""
- (?:from\s+(\w+)\s+)? # optionally match "from foo" and capture foo
- c?import\s(\w+|[*]) # require "import bar" and capture bar
+ ^\s* # must begin with some whitespace characters
+ (?:from\s+(\w+)(?:\.\w+)*\s+)? # optionally match "from foo(.baz)" and capture foo
+ c?import\s(\w+|[*]) # require "import bar" and capture bar
""", re.M | re.VERBOSE)
@extension('.pyx')
node = self.inputs[0]
txt = node.read()
- mods = []
+ mods = set()
for m in re_cyt.finditer(txt):
if m.group(1): # matches "from foo import bar"
- mods.append(m.group(1))
+ mods.add(m.group(1))
else:
- mods.append(m.group(2))
+ mods.add(m.group(2))
Logs.debug('cython: mods %r', mods)
incs = getattr(self.generator, 'cython_includes', [])
found = []
missing = []
- for x in mods:
+ for x in sorted(mods):
for y in incs:
k = y.find_resource(x + '.pxd')
if k:
if not ctx.env.PYTHON:
ctx.fatal('Load the python tool first!')
ctx.find_program('cython', var='CYTHON')
- if ctx.options.cython_flags:
+ if hasattr(ctx.options, 'cython_flags'):
ctx.env.CYTHONFLAGS = ctx.options.cython_flags
TIMEOUT = 60
REQUIRES = 'requires.txt'
-re_com = re.compile('\s*#.*', re.M)
+re_com = re.compile(r'\s*#.*', re.M)
def total_version_order(num):
lst = num.split('.')
"""
import os, os.path, re
+from collections import OrderedDict
from waflib import Task, Utils, Node
from waflib.TaskGen import feature
re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
re_nl = re.compile('\r*\n', re.M)
def parse_doxy(txt):
- tbl = {}
+ '''
+ Parses a doxygen file.
+ Returns an ordered dictionary. We cannot return a default dictionary, as the
+ order in which the entries are reported does matter, especially for the
+ '@INCLUDE' lines.
+ '''
+ tbl = OrderedDict()
txt = re_rl.sub('', txt)
lines = re_nl.split(txt)
for x in lines:
@feature('doxygen')
def process_doxy(self):
if not getattr(self, 'doxyfile', None):
- self.generator.bld.fatal('no doxyfile??')
+ self.bld.fatal('no doxyfile variable specified??')
node = self.doxyfile
if not isinstance(node, Node.Node):
node = self.path.find_resource(node)
if not node:
- raise ValueError('doxygen file not found')
+ self.bld.fatal('doxygen file %s not found' % self.doxyfile)
# the task instance
dsk = self.create_task('doxygen', node)
if n.abspath() in scanned:
continue
- for i in re.findall('-include\("(.*)"\)\.', n.read()):
+ for i in re.findall(r'-include\("(.*)"\)\.', n.read()):
for d in task.erlc_incnodes:
r = d.find_node(i)
if r:
def options(opt):
opt.load('fast_partial')
-Assuptions:
+Assumptions:
* Mostly for C/C++/Fortran targets with link tasks (object-only targets are not handled)
+ try it in the folder generated by utils/genbench.py
* For full project builds: no --targets and no pruning from subfolders
* The installation phase is ignored
* `use=` dependencies are specified up front even across build groups
@conf
def crayftn_flags(conf):
v = conf.env
- v['_FCMODOUTFLAGS'] = ['-em', '-J.'] # enable module files and put them in the current directoy
+ v['_FCMODOUTFLAGS'] = ['-em', '-J.'] # enable module files and put them in the current directory
v['FCFLAGS_DEBUG'] = ['-m1'] # more verbose compiler warnings
v['FCFLAGS_fcshlib'] = ['-h pic']
v['LINKFLAGS_fcshlib'] = ['-h shared']
@conf
def sxfc_flags(conf):
v = conf.env
- v['_FCMODOUTFLAGS'] = [] # enable module files and put them in the current directoy
+ v['_FCMODOUTFLAGS'] = [] # enable module files and put them in the current directory
v['FCFLAGS_DEBUG'] = [] # more verbose compiler warnings
v['FCFLAGS_fcshlib'] = []
v['LINKFLAGS_fcshlib'] = []
--- /dev/null
+#! /usr/bin/env python
+# encoding: utf-8
+# Detection of the NEC Fortran compiler for Aurora Tsubasa
+
+import re
+from waflib.Tools import fc,fc_config,fc_scan
+from waflib.Configure import conf
+from waflib.Tools.compiler_fc import fc_compiler
+fc_compiler['linux'].append('fc_nfort')
+
+@conf
+def find_nfort(conf):
+ fc=conf.find_program(['nfort'],var='FC')
+ conf.get_nfort_version(fc)
+ conf.env.FC_NAME='NFORT'
+ conf.env.FC_MOD_CAPITALIZATION='lower'
+
+@conf
+def nfort_flags(conf):
+ v=conf.env
+ v['_FCMODOUTFLAGS']=[]
+ v['FCFLAGS_DEBUG']=[]
+ v['FCFLAGS_fcshlib']=[]
+ v['LINKFLAGS_fcshlib']=[]
+ v['FCSTLIB_MARKER']=''
+ v['FCSHLIB_MARKER']=''
+
+@conf
+def get_nfort_version(conf,fc):
+ version_re=re.compile(r"nfort\s*\(NFORT\)\s*(?P<major>\d+)\.(?P<minor>\d+)\.",re.I).search
+ cmd=fc+['--version']
+ out,err=fc_config.getoutput(conf,cmd,stdin=False)
+ if out:
+ match=version_re(out)
+ else:
+ match=version_re(err)
+ if not match:
+ return(False)
+ conf.fatal('Could not determine the NEC NFORT Fortran compiler version.')
+ else:
+ k=match.groupdict()
+ conf.env['FC_VERSION']=(k['major'],k['minor'])
+
+def configure(conf):
+ conf.find_nfort()
+ conf.find_program('nar',var='AR')
+ conf.add_os_flags('ARFLAGS')
+ if not conf.env.ARFLAGS:
+ conf.env.ARFLAGS=['rcs']
+ conf.fc_flags()
+ conf.fc_add_flags()
+ conf.nfort_flags()
names = []
return (nodes, names)
-re_o = re.compile("\.o$")
+re_o = re.compile(r"\.o$")
re_splitter = re.compile(r'(?<!\\)\s+') # split by space, except when spaces are escaped
def remove_makefile_rule_lhs(line):
except Errors.ConfigurationError:
pass
else:
- conf.env.append_value('CFLAGS', gccdeps_flags)
+ conf.env.append_value('CFLAGS', flags)
conf.env.append_unique('ENABLE_GCCDEPS', 'c')
if conf.env.CXX_NAME in supported_compilers:
except Errors.ConfigurationError:
pass
else:
- conf.env.append_value('CXXFLAGS', gccdeps_flags)
+ conf.env.append_value('CXXFLAGS', flags)
conf.env.append_unique('ENABLE_GCCDEPS', 'cxx')
def options(opt):
fu = re.compile('#(.*)\n')
txt = fu.sub('', txt)
- setregexp = re.compile('([sS][eE][tT]\s*\()\s*([^\s]+)\s+\"([^"]+)\"\)')
+ setregexp = re.compile(r'([sS][eE][tT]\s*\()\s*([^\s]+)\s+\"([^"]+)\"\)')
found = setregexp.findall(txt)
for (_, key, val) in found:
if taskgen.env.get_flat(flag).find(PREPROCESSOR_FLAG) < 0:
taskgen.env.append_value(flag, PREPROCESSOR_FLAG)
- # Figure out what casing conventions the user's shell used when
- # launching Waf
- (drive, _) = os.path.splitdrive(taskgen.bld.srcnode.abspath())
- taskgen.msvcdeps_drive_lowercase = drive == drive.lower()
-
def path_to_node(base_node, path, cached_nodes):
- # Take the base node and the path and return a node
- # Results are cached because searching the node tree is expensive
- # The following code is executed by threads, it is not safe, so a lock is needed...
- if getattr(path, '__hash__'):
- node_lookup_key = (base_node, path)
- else:
- # Not hashable, assume it is a list and join into a string
- node_lookup_key = (base_node, os.path.sep.join(path))
+ '''
+ Take the base node and the path and return a node
+ Results are cached because searching the node tree is expensive
+ The following code is executed by threads, it is not safe, so a lock is needed...
+ '''
+ # normalize the path because ant_glob() does not understand
+ # parent path components (..)
+ path = os.path.normpath(path)
+
+ # normalize the path case to increase likelihood of a cache hit
+ path = os.path.normcase(path)
+
+ # ant_glob interprets [] and () characters, so those must be replaced
+ path = path.replace('[', '?').replace(']', '?').replace('(', '[(]').replace(')', '[)]')
+
+ node_lookup_key = (base_node, path)
+
try:
- lock.acquire()
node = cached_nodes[node_lookup_key]
except KeyError:
- node = base_node.find_resource(path)
- cached_nodes[node_lookup_key] = node
- finally:
- lock.release()
+ # retry with lock on cache miss
+ with lock:
+ try:
+ node = cached_nodes[node_lookup_key]
+ except KeyError:
+ node_list = base_node.ant_glob([path], ignorecase=True, remove=False, quiet=True, regex=False)
+ node = cached_nodes[node_lookup_key] = node_list[0] if node_list else None
+
return node
def post_run(self):
unresolved_names = []
resolved_nodes = []
- lowercase = self.generator.msvcdeps_drive_lowercase
- correct_case_path = bld.path.abspath()
- correct_case_path_len = len(correct_case_path)
- correct_case_path_norm = os.path.normcase(correct_case_path)
-
# Dynamically bind to the cache
try:
cached_nodes = bld.cached_nodes
for path in self.msvcdeps_paths:
node = None
if os.path.isabs(path):
- # Force drive letter to match conventions of main source tree
- drive, tail = os.path.splitdrive(path)
-
- if os.path.normcase(path[:correct_case_path_len]) == correct_case_path_norm:
- # Path is in the sandbox, force it to be correct. MSVC sometimes returns a lowercase path.
- path = correct_case_path + path[correct_case_path_len:]
- else:
- # Check the drive letter
- if lowercase and (drive != drive.lower()):
- path = drive.lower() + tail
- elif (not lowercase) and (drive != drive.upper()):
- path = drive.upper() + tail
node = path_to_node(bld.root, path, cached_nodes)
else:
+ # when calling find_resource, make sure the path does not begin with '..'
base_node = bld.bldnode
- # when calling find_resource, make sure the path does not begin by '..'
path = [k for k in Utils.split_path(path) if k and k != '.']
while path[0] == '..':
- path = path[1:]
+ path.pop(0)
base_node = base_node.parent
+ path = os.sep.join(path)
node = path_to_node(base_node, path, cached_nodes)
raw_out = self.generator.bld.cmd_and_log(cmd + ['@' + tmp], **kw)
ret = 0
except Errors.WafError as e:
- raw_out = e.stdout
- ret = e.returncode
+ # Use e.msg if e.stdout is not set
+ raw_out = getattr(e, 'stdout', e.msg)
+
+ # Return non-zero error code even if we didn't
+ # get one from the exception object
+ ret = getattr(e, 'returncode', 1)
for line in raw_out.splitlines():
if line.startswith(INCLUDE_PATTERN):
EXT_MLC = ['.c']
EXT_ML = ['.ml']
-open_re = re.compile('^\s*open\s+([a-zA-Z]+)(;;){0,1}$', re.M)
+open_re = re.compile(r'^\s*open\s+([a-zA-Z]+)(;;){0,1}$', re.M)
foo = re.compile(r"""(\(\*)|(\*\))|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^()*"'\\]*)""", re.M)
def filter_comments(txt):
meh = [0]
# Thomas Nagy, 2007-2010 (ita)
"""
-Debugging helper for parallel compilation, outputs
-a file named pdebug.svg in the source directory::
+Debugging helper for parallel compilation.
+
+Copy it to your project and load it with::
def options(opt):
- opt.load('parallel_debug')
+ opt.load('parallel_debug', tooldir='.')
def build(bld):
...
+
+The build will then output a file named pdebug.svg in the source directory.
"""
import re, sys, threading, time, traceback
except Errors.WafError:
conf.fatal('Could not find pgi compiler %r' % cmd)
- version = re.findall('^COMPVER\s*=(.*)', out, re.M)
+ version = re.findall(r'^COMPVER\s*=(.*)', out, re.M)
if len(version) != 1:
conf.fatal('Could not determine the compiler version')
return version[0]
import re, os
from waflib.Task import Task
from waflib.TaskGen import extension
-from waflib import Errors, Context
+from waflib import Errors, Context, Logs
"""
A simple tool to integrate protocol buffers into your build system.
protoc_includes = ['inc']) # for protoc to search dependencies
+Protoc includes passed via protoc_includes are either relative to the taskgen
+or to the project and are searched in this order.
+
+Include directories external to the waf project can also be passed to the
+extra by using protoc_extincludes
+
+ protoc_extincludes = ['/usr/include/pblib']
Notes when using this tool:
"""
class protoc(Task):
- run_str = '${PROTOC} ${PROTOC_FL:PROTOC_FLAGS} ${PROTOC_ST:INCPATHS} ${PROTOC_ST:PROTOC_INCPATHS} ${SRC[0].bldpath()}'
+ run_str = '${PROTOC} ${PROTOC_FL:PROTOC_FLAGS} ${PROTOC_ST:INCPATHS} ${PROTOC_ST:PROTOC_INCPATHS} ${PROTOC_ST:PROTOC_EXTINCPATHS} ${SRC[0].bldpath()}'
color = 'BLUE'
ext_out = ['.h', 'pb.cc', '.py', '.java']
def scan(self):
if 'py' in self.generator.features or 'javac' in self.generator.features:
for incpath in getattr(self.generator, 'protoc_includes', []):
- search_nodes.append(self.generator.bld.path.find_node(incpath))
+ incpath_node = self.generator.path.find_node(incpath)
+ if incpath_node:
+ search_nodes.append(incpath_node)
+ else:
+ # Check if relative to top-level for extra tg dependencies
+ incpath_node = self.generator.bld.path.find_node(incpath)
+ if incpath_node:
+ search_nodes.append(incpath_node)
+ else:
+ raise Errors.WafError('protoc: include path %r does not exist' % incpath)
+
def parse_node(node):
if node in seen:
parse_node(node)
# Add also dependencies path to INCPATHS so protoc will find the included file
for deppath in nodes:
- self.env.append_value('INCPATHS', deppath.parent.bldpath())
+ self.env.append_unique('INCPATHS', deppath.parent.bldpath())
return (nodes, names)
@extension('.proto')
protoc_flags.append('--python_out=%s' % node.parent.get_bld().bldpath())
if 'javac' in self.features:
- pkgname, javapkg, javacn, nodename = None, None, None, None
- messages = []
-
- # .java file name is done with some rules depending on .proto file content:
- # -) package is either derived from option java_package if present
- # or from package directive
- # -) file name is either derived from option java_outer_classname if present
- # or the .proto file is converted to camelcase. If a message
- # is named the same then the behaviour depends on protoc version
- #
- # See also: https://developers.google.com/protocol-buffers/docs/reference/java-generated#invocation
-
- code = node.read().splitlines()
- for line in code:
- m = re.search(r'^package\s+(.*);', line)
- if m:
- pkgname = m.groups()[0]
- m = re.search(r'^option\s+(\S*)\s*=\s*"(\S*)";', line)
- if m:
- optname = m.groups()[0]
- if optname == 'java_package':
- javapkg = m.groups()[1]
- elif optname == 'java_outer_classname':
- javacn = m.groups()[1]
- if self.env.PROTOC_MAJOR > '2':
- m = re.search(r'^message\s+(\w*)\s*{*', line)
- if m:
- messages.append(m.groups()[0])
-
- if javapkg:
- nodename = javapkg
- elif pkgname:
- nodename = pkgname
- else:
- raise Errors.WafError('Cannot derive java name from protoc file')
-
- nodename = nodename.replace('.',os.sep) + os.sep
- if javacn:
- nodename += javacn + '.java'
- else:
- if self.env.PROTOC_MAJOR > '2' and node.abspath()[node.abspath().rfind(os.sep)+1:node.abspath().rfind('.')].title() in messages:
- nodename += node.abspath()[node.abspath().rfind(os.sep)+1:node.abspath().rfind('.')].title().replace('_','') + 'OuterClass.java'
- else:
- nodename += node.abspath()[node.abspath().rfind(os.sep)+1:node.abspath().rfind('.')].title().replace('_','') + '.java'
-
- java_node = node.parent.find_or_declare(nodename)
- out_nodes.append(java_node)
- protoc_flags.append('--java_out=%s' % node.parent.get_bld().bldpath())
-
# Make javac get also pick java code generated in build
if not node.parent.get_bld() in self.javac_task.srcdir:
self.javac_task.srcdir.append(node.parent.get_bld())
- if not out_nodes:
- raise Errors.WafError('Feature %r not supported by protoc extra' % self.features)
+ protoc_flags.append('--java_out=%s' % node.parent.get_bld().bldpath())
+ node.parent.get_bld().mkdir()
tsk = self.create_task('protoc', node, out_nodes)
tsk.env.append_value('PROTOC_FLAGS', protoc_flags)
# For C++ standard include files dirs are used,
# but this doesn't apply to Python for example
for incpath in getattr(self, 'protoc_includes', []):
- incdirs.append(self.path.find_node(incpath).bldpath())
+ incpath_node = self.path.find_node(incpath)
+ if incpath_node:
+ incdirs.append(incpath_node.bldpath())
+ else:
+ # Check if relative to top-level for extra tg dependencies
+ incpath_node = self.bld.path.find_node(incpath)
+ if incpath_node:
+ incdirs.append(incpath_node.bldpath())
+ else:
+ raise Errors.WafError('protoc: include path %r does not exist' % incpath)
+
tsk.env.PROTOC_INCPATHS = incdirs
+ # Include paths external to the waf project (ie. shared pb repositories)
+ tsk.env.PROTOC_EXTINCPATHS = getattr(self, 'protoc_extincludes', [])
+
# PR2115: protoc generates output of .proto files in nested
# directories by canonicalizing paths. To avoid this we have to pass
# as first include the full directory file of the .proto file
#!/usr/bin/env python
# encoding: utf-8
-# Federico Pellegrin, 2016-2018 (fedepell) adapted for Python
+# Federico Pellegrin, 2016-2019 (fedepell) adapted for Python
"""
This tool helps with finding Python Qt5 tools and libraries,
Add into the sources list also the qrc resources files or ui5
definition files and they will be translated into python code
-with the system tools (PyQt5, pyside2, PyQt4 are searched in this
+with the system tools (PyQt5, PySide2, PyQt4 are searched in this
order) and then compiled
"""
"""
The additional parameters are:
- :param lang: list of translation files (\*.ts) to process
+ :param lang: list of translation files (\\*.ts) to process
:type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
- :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
+ :param langname: if given, transform the \\*.ts files into a .qrc files to include in the binary file
:type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
"""
if getattr(self, 'lang', None):
@conf
def find_pyqt5_binaries(self):
"""
- Detects PyQt5 or pyside2 programs such as pyuic5/pyside2-uic, pyrcc5/pyside2-rcc
+ Detects PyQt5 or PySide2 programs such as pyuic5/pyside2-uic, pyrcc5/pyside2-rcc
"""
env = self.env
- if getattr(Options.options, 'want_pyside2', True):
+ if getattr(Options.options, 'want_pyqt5', True):
+ self.find_program(['pyuic5'], var='QT_PYUIC')
+ self.find_program(['pyrcc5'], var='QT_PYRCC')
+ self.find_program(['pylupdate5'], var='QT_PYLUPDATE')
+ elif getattr(Options.options, 'want_pyside2', True):
self.find_program(['pyside2-uic'], var='QT_PYUIC')
self.find_program(['pyside2-rcc'], var='QT_PYRCC')
self.find_program(['pyside2-lupdate'], var='QT_PYLUPDATE')
if not env.QT_PYUIC:
self.fatal('cannot find the uic compiler for python for qt5')
- if not env.QT_PYUIC:
+ if not env.QT_PYRCC:
self.fatal('cannot find the rcc compiler for python for qt5')
self.find_program(['lrelease-qt5', 'lrelease'], var='QT_LRELEASE')
Command-line options
"""
pyqt5opt=opt.add_option_group("Python QT5 Options")
- pyqt5opt.add_option('--pyqt5-pyside2', action='store_true', default=False, dest='want_pyside2', help='use pyside2 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after)')
+ pyqt5opt.add_option('--pyqt5-pyqt5', action='store_true', default=False, dest='want_pyqt5', help='use PyQt5 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after, PyQt4 last)')
+ pyqt5opt.add_option('--pyqt5-pyside2', action='store_true', default=False, dest='want_pyside2', help='use PySide2 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after, PyQt4 last)')
pyqt5opt.add_option('--pyqt5-pyqt4', action='store_true', default=False, dest='want_pyqt4', help='use PyQt4 bindings as python QT5 bindings (default PyQt5 is searched first, PySide2 after, PyQt4 last)')
The additional parameters are:
- :param lang: list of translation files (\*.ts) to process
+ :param lang: list of translation files (\\*.ts) to process
:type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension
- :param update: whether to process the C++ files to update the \*.ts files (use **waf --translate**)
+ :param update: whether to process the C++ files to update the \\*.ts files (use **waf --translate**)
:type update: bool
- :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file
+ :param langname: if given, transform the \\*.ts files into a .qrc files to include in the binary file
:type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension
"""
if getattr(self, 'lang', None):
Options.commands.remove(k)
def login_to_host(self, login):
- return re.sub('(\w+@)', '', login)
+ return re.sub(r'(\w+@)', '', login)
def variant_to_login(self, variant):
"""linux_32_debug -> search env.LINUX_32 and then env.LINUX"""
with open(**kwargs) as log:
log_tail = log.readlines()[-10:]
for line in log_tail:
- error_found = re.match("r\(([0-9]+)\)", line)
+ error_found = re.match(r"r\(([0-9]+)\)", line)
if error_found:
return error_found.group(1), ''.join(log_tail)
else:
--- /dev/null
+"""Support for Sphinx documentation
+
+This is a wrapper for sphinx-build program. Please note that sphinx-build supports only one output format which can
+passed to build via sphinx_output_format attribute. The default output format is html.
+
+Example wscript:
+
+def configure(cnf):
+ conf.load('sphinx')
+
+def build(bld):
+ bld(
+ features='sphinx',
+ sphinx_source='sources', # path to source directory
+ sphinx_options='-a -v', # sphinx-build program additional options
+ sphinx_output_format='man' # output format of sphinx documentation
+ )
+
+"""
+
+from waflib.Node import Node
+from waflib import Utils
+from waflib.Task import Task
+from waflib.TaskGen import feature, after_method
+
+
+def configure(cnf):
+ """Check if sphinx-build program is available and loads gnu_dirs tool."""
+ cnf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False)
+ cnf.load('gnu_dirs')
+
+
+@feature('sphinx')
+def build_sphinx(self):
+ """Builds sphinx sources.
+ """
+ if not self.env.SPHINX_BUILD:
+ self.bld.fatal('Program SPHINX_BUILD not defined.')
+ if not getattr(self, 'sphinx_source', None):
+ self.bld.fatal('Attribute sphinx_source not defined.')
+ if not isinstance(self.sphinx_source, Node):
+ self.sphinx_source = self.path.find_node(self.sphinx_source)
+ if not self.sphinx_source:
+ self.bld.fatal('Can\'t find sphinx_source: %r' % self.sphinx_source)
+
+ Utils.def_attrs(self, sphinx_output_format='html')
+ self.env.SPHINX_OUTPUT_FORMAT = self.sphinx_output_format
+ self.env.SPHINX_OPTIONS = getattr(self, 'sphinx_options', [])
+
+ for source_file in self.sphinx_source.ant_glob('**/*'):
+ self.bld.add_manual_dependency(self.sphinx_source, source_file)
+
+ sphinx_build_task = self.create_task('SphinxBuildingTask')
+ sphinx_build_task.set_inputs(self.sphinx_source)
+ sphinx_build_task.set_outputs(self.path.get_bld())
+
+ # the sphinx-build results are in <build + output_format> directory
+ sphinx_output_directory = self.path.get_bld().make_node(self.env.SPHINX_OUTPUT_FORMAT)
+ sphinx_output_directory.mkdir()
+ Utils.def_attrs(self, install_path=get_install_path(self))
+ self.add_install_files(install_to=self.install_path,
+ install_from=sphinx_output_directory.ant_glob('**/*'),
+ cwd=sphinx_output_directory,
+ relative_trick=True)
+
+
+def get_install_path(tg):
+ if tg.env.SPHINX_OUTPUT_FORMAT == 'man':
+ return tg.env.MANDIR
+ elif tg.env.SPHINX_OUTPUT_FORMAT == 'info':
+ return tg.env.INFODIR
+ else:
+ return tg.env.DOCDIR
+
+
+class SphinxBuildingTask(Task):
+ color = 'BOLD'
+ run_str = '${SPHINX_BUILD} -M ${SPHINX_OUTPUT_FORMAT} ${SRC} ${TGT} ${SPHINX_OPTIONS}'
+
+ def keyword(self):
+ return 'Compiling (%s)' % self.env.SPHINX_OUTPUT_FORMAT
SWIG_EXTS = ['.swig', '.i']
-re_module = re.compile('%module(?:\s*\(.*\))?\s+(.+)', re.M)
+re_module = re.compile(r'%module(?:\s*\(.*\))?\s+(.+)', re.M)
re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M)
-re_2 = re.compile('[#%]include [<"](.*)[">]', re.M)
+re_2 = re.compile(r'[#%](?:include|import(?:\(module=".*"\))+|python(?:begin|code)) [<"](.*)[">]', re.M)
class swig(Task.Task):
color = 'BLUE'
if self.env.DEST_BINFMT == 'pe': #gcc uses nm, and has a preceding _ on windows
re_nm = re.compile(r'(T|D)\s+_(?P<symbol>%s)\b' % reg)
elif self.env.DEST_BINFMT=='mac-o':
- re_nm=re.compile(r'(T|D)\s+(?P<symbol>_?%s)\b' % reg)
+ re_nm=re.compile(r'(T|D)\s+(?P<symbol>_?(%s))\b' % reg)
else:
re_nm = re.compile(r'(T|D)\s+(?P<symbol>%s)\b' % reg)
cmd = (self.env.NM or ['nm']) + ['-g', obj.abspath()]
local_repo = ''
"""Local repository containing additional Waf tools (plugins)"""
-remote_repo = 'https://raw.githubusercontent.com/waf-project/waf/master/'
+remote_repo = 'https://gitlab.com/ita1024/waf/raw/master/'
"""
Remote directory containing downloadable waf tools. The missing tools can be downloaded by using::
Represents a tree node in the XCode project plist file format.
When written to a file, all attributes of XCodeNode are stringified together with
its value. However, attributes starting with an underscore _ are ignored
-during that process and allows you to store arbitray values that are not supposed
+during that process and allows you to store arbitrary values that are not supposed
to be written out.
"""
class XCodeNode(object):
# fileRef is a reference to a PBXFileReference object
self.fileRef = fileRef
- # A map of key/value pairs for additionnal settings.
+ # A map of key/value pairs for additional settings.
self.settings = settings
def __hash__(self):
def create_target_dependency(self, target, name):
""" : param target : PXBNativeTarget """
proxy = PBXContainerItemProxy(self, target, name)
- dependecy = PBXTargetDependency(target, proxy)
- return dependecy
+ dependency = PBXTargetDependency(target, proxy)
+ return dependency
def write(self, file):
[cmd, kwargs, cargs] = cPickle.loads(base64.b64decode(txt))
cargs = cargs or {}
+ if not 'close_fds' in kwargs:
+ # workers have no fds
+ kwargs['close_fds'] = False
+
ret = 1
out, err, ex, trace = (None, None, None, None)
try: