mirror of
https://github.com/samba-team/samba.git
synced 2025-01-08 21:18:16 +03:00
waf: upgrade to 2.0.20
This contain an important change: "Fix gccdeps.scan() returning nodes that no longer exist on disk." https://gitlab.com/ita1024/waf/-/merge_requests/2293 Signed-off-by: David Mulder <dmulder@suse.com> Reviewed-by: Stefan Metzmacher <metze@samba.org>
This commit is contained in:
parent
896b7bbcf2
commit
5fc3a71d0f
2
buildtools/bin/waf
vendored
2
buildtools/bin/waf
vendored
@ -32,7 +32,7 @@ POSSIBILITY OF SUCH DAMAGE.
|
|||||||
|
|
||||||
import os, sys, inspect
|
import os, sys, inspect
|
||||||
|
|
||||||
VERSION="2.0.18"
|
VERSION="2.0.20"
|
||||||
REVISION="x"
|
REVISION="x"
|
||||||
GIT="x"
|
GIT="x"
|
||||||
INSTALL="x"
|
INSTALL="x"
|
||||||
|
@ -459,7 +459,7 @@ def RECURSE(ctx, directory):
|
|||||||
return
|
return
|
||||||
visited_dirs.add(key)
|
visited_dirs.add(key)
|
||||||
relpath = os.path.relpath(abspath, ctx.path.abspath())
|
relpath = os.path.relpath(abspath, ctx.path.abspath())
|
||||||
if ctxclass in ['tmp', 'OptionsContext', 'ConfigurationContext', 'BuildContext']:
|
if ctxclass in ['tmp', 'OptionsContext', 'ConfigurationContext', 'BuildContext', 'ClangDbContext']:
|
||||||
return ctx.recurse(relpath)
|
return ctx.recurse(relpath)
|
||||||
if 'waflib.extras.compat15' in sys.modules:
|
if 'waflib.extras.compat15' in sys.modules:
|
||||||
return ctx.recurse(relpath)
|
return ctx.recurse(relpath)
|
||||||
|
@ -5,6 +5,7 @@ from waflib import Build, Configure, Node, Utils, Options, Logs, TaskGen
|
|||||||
from waflib import ConfigSet
|
from waflib import ConfigSet
|
||||||
from waflib.TaskGen import feature, after
|
from waflib.TaskGen import feature, after
|
||||||
from waflib.Configure import conf, ConfigurationContext
|
from waflib.Configure import conf, ConfigurationContext
|
||||||
|
from waflib.extras import clang_compilation_database
|
||||||
|
|
||||||
from waflib.Tools.flex import decide_ext
|
from waflib.Tools.flex import decide_ext
|
||||||
|
|
||||||
@ -37,7 +38,7 @@ TaskGen.declare_chain(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
for y in (Build.BuildContext, Build.CleanContext, Build.InstallContext, Build.UninstallContext, Build.ListContext):
|
for y in (Build.BuildContext, Build.CleanContext, Build.InstallContext, Build.UninstallContext, Build.ListContext, clang_compilation_database.ClangDbContext):
|
||||||
class tmp(y):
|
class tmp(y):
|
||||||
variant = 'default'
|
variant = 'default'
|
||||||
|
|
||||||
|
@ -38,7 +38,7 @@ LIB_PATH="shared"
|
|||||||
|
|
||||||
os.environ['PYTHONUNBUFFERED'] = '1'
|
os.environ['PYTHONUNBUFFERED'] = '1'
|
||||||
|
|
||||||
if Context.HEXVERSION not in (0x2001200,):
|
if Context.HEXVERSION not in (0x2001400,):
|
||||||
Logs.error('''
|
Logs.error('''
|
||||||
Please use the version of waf that comes with Samba, not
|
Please use the version of waf that comes with Samba, not
|
||||||
a system installed version. See http://wiki.samba.org/index.php/Waf
|
a system installed version. See http://wiki.samba.org/index.php/Waf
|
||||||
|
25
third_party/waf/waflib/Configure.py
vendored
25
third_party/waf/waflib/Configure.py
vendored
@ -508,23 +508,27 @@ def find_binary(self, filenames, exts, paths):
|
|||||||
@conf
|
@conf
|
||||||
def run_build(self, *k, **kw):
|
def run_build(self, *k, **kw):
|
||||||
"""
|
"""
|
||||||
Create a temporary build context to execute a build. A reference to that build
|
Create a temporary build context to execute a build. A temporary reference to that build
|
||||||
context is kept on self.test_bld for debugging purposes, and you should not rely
|
context is kept on self.test_bld for debugging purposes.
|
||||||
on it too much (read the note on the cache below).
|
The arguments to this function are passed to a single task generator for that build.
|
||||||
The parameters given in the arguments to this function are passed as arguments for
|
Only three parameters are mandatory:
|
||||||
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
|
:param features: features to pass to a task generator created in the build
|
||||||
:type features: list of string
|
:type features: list of string
|
||||||
:param compile_filename: file to create for the compilation (default: *test.c*)
|
:param compile_filename: file to create for the compilation (default: *test.c*)
|
||||||
:type compile_filename: string
|
:type compile_filename: string
|
||||||
:param code: code to write in the filename to compile
|
:param code: input file contents
|
||||||
:type code: string
|
:type code: string
|
||||||
|
|
||||||
Though this function returns *0* by default, the build may set an attribute named *retval* on the
|
Though this function returns *0* by default, the build may bind 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.
|
build context object to return a particular value. See :py:func:`waflib.Tools.c_config.test_exec_fun` for example.
|
||||||
|
|
||||||
This function also features a cache which can be enabled by the following option::
|
The temporary builds creates a temporary folder; the name of that folder is calculated
|
||||||
|
by hashing input arguments to this function, with the exception of :py:class:`waflib.ConfigSet.ConfigSet`
|
||||||
|
objects which are used for both reading and writing values.
|
||||||
|
|
||||||
|
This function also features a cache which is disabled by default; that cache relies
|
||||||
|
on the hash value calculated as indicated above::
|
||||||
|
|
||||||
def options(opt):
|
def options(opt):
|
||||||
opt.add_option('--confcache', dest='confcache', default=0,
|
opt.add_option('--confcache', dest='confcache', default=0,
|
||||||
@ -538,7 +542,10 @@ def run_build(self, *k, **kw):
|
|||||||
buf = []
|
buf = []
|
||||||
for key in sorted(kw.keys()):
|
for key in sorted(kw.keys()):
|
||||||
v = kw[key]
|
v = kw[key]
|
||||||
if hasattr(v, '__call__'):
|
if isinstance(v, ConfigSet.ConfigSet):
|
||||||
|
# values are being written to, so they are excluded from contributing to the hash
|
||||||
|
continue
|
||||||
|
elif hasattr(v, '__call__'):
|
||||||
buf.append(Utils.h_fun(v))
|
buf.append(Utils.h_fun(v))
|
||||||
else:
|
else:
|
||||||
buf.append(str(v))
|
buf.append(str(v))
|
||||||
|
18
third_party/waf/waflib/Context.py
vendored
18
third_party/waf/waflib/Context.py
vendored
@ -6,20 +6,30 @@
|
|||||||
Classes and functions enabling the command system
|
Classes and functions enabling the command system
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import os, re, imp, sys
|
import os, re, sys
|
||||||
from waflib import Utils, Errors, Logs
|
from waflib import Utils, Errors, Logs
|
||||||
import waflib.Node
|
import waflib.Node
|
||||||
|
|
||||||
|
if sys.hexversion > 0x3040000:
|
||||||
|
import types
|
||||||
|
class imp(object):
|
||||||
|
new_module = lambda x: types.ModuleType(x)
|
||||||
|
else:
|
||||||
|
import imp
|
||||||
|
|
||||||
# the following 3 constants are updated on each new release (do not touch)
|
# the following 3 constants are updated on each new release (do not touch)
|
||||||
HEXVERSION=0x2001200
|
HEXVERSION=0x2001400
|
||||||
"""Constant updated on new releases"""
|
"""Constant updated on new releases"""
|
||||||
|
|
||||||
WAFVERSION="2.0.18"
|
WAFVERSION="2.0.20"
|
||||||
"""Constant updated on new releases"""
|
"""Constant updated on new releases"""
|
||||||
|
|
||||||
WAFREVISION="314689b8994259a84f0de0aaef74d7ce91f541ad"
|
WAFREVISION="668769470956da8c5b60817cb8884cd7d0f87cd4"
|
||||||
"""Git revision when the waf version is updated"""
|
"""Git revision when the waf version is updated"""
|
||||||
|
|
||||||
|
WAFNAME="waf"
|
||||||
|
"""Application name displayed on --help"""
|
||||||
|
|
||||||
ABI = 20
|
ABI = 20
|
||||||
"""Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)"""
|
"""Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)"""
|
||||||
|
|
||||||
|
31
third_party/waf/waflib/Options.py
vendored
31
third_party/waf/waflib/Options.py
vendored
@ -44,7 +44,7 @@ class opt_parser(optparse.OptionParser):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, ctx, allow_unknown=False):
|
def __init__(self, ctx, allow_unknown=False):
|
||||||
optparse.OptionParser.__init__(self, conflict_handler='resolve', add_help_option=False,
|
optparse.OptionParser.__init__(self, conflict_handler='resolve', add_help_option=False,
|
||||||
version='waf %s (%s)' % (Context.WAFVERSION, Context.WAFREVISION))
|
version='%s %s (%s)' % (Context.WAFNAME, Context.WAFVERSION, Context.WAFREVISION))
|
||||||
self.formatter.width = Logs.get_term_cols()
|
self.formatter.width = Logs.get_term_cols()
|
||||||
self.ctx = ctx
|
self.ctx = ctx
|
||||||
self.allow_unknown = allow_unknown
|
self.allow_unknown = allow_unknown
|
||||||
@ -62,6 +62,21 @@ class opt_parser(optparse.OptionParser):
|
|||||||
else:
|
else:
|
||||||
self.error(str(e))
|
self.error(str(e))
|
||||||
|
|
||||||
|
def _process_long_opt(self, rargs, values):
|
||||||
|
# --custom-option=-ftxyz is interpreted as -f -t... see #2280
|
||||||
|
if self.allow_unknown:
|
||||||
|
back = [] + rargs
|
||||||
|
try:
|
||||||
|
optparse.OptionParser._process_long_opt(self, rargs, values)
|
||||||
|
except optparse.BadOptionError:
|
||||||
|
while rargs:
|
||||||
|
rargs.pop()
|
||||||
|
rargs.extend(back)
|
||||||
|
rargs.pop(0)
|
||||||
|
raise
|
||||||
|
else:
|
||||||
|
optparse.OptionParser._process_long_opt(self, rargs, values)
|
||||||
|
|
||||||
def print_usage(self, file=None):
|
def print_usage(self, file=None):
|
||||||
return self.print_help(file)
|
return self.print_help(file)
|
||||||
|
|
||||||
@ -96,11 +111,11 @@ class opt_parser(optparse.OptionParser):
|
|||||||
lst.sort()
|
lst.sort()
|
||||||
ret = '\n'.join(lst)
|
ret = '\n'.join(lst)
|
||||||
|
|
||||||
return '''waf [commands] [options]
|
return '''%s [commands] [options]
|
||||||
|
|
||||||
Main commands (example: ./waf build -j4)
|
Main commands (example: ./%s build -j4)
|
||||||
%s
|
%s
|
||||||
''' % ret
|
''' % (Context.WAFNAME, Context.WAFNAME, ret)
|
||||||
|
|
||||||
|
|
||||||
class OptionsContext(Context.Context):
|
class OptionsContext(Context.Context):
|
||||||
@ -141,9 +156,9 @@ class OptionsContext(Context.Context):
|
|||||||
gr.add_option('-o', '--out', action='store', default='', help='build dir for the project', dest='out')
|
gr.add_option('-o', '--out', action='store', default='', help='build dir for the project', dest='out')
|
||||||
gr.add_option('-t', '--top', action='store', default='', help='src dir for the project', dest='top')
|
gr.add_option('-t', '--top', action='store', default='', help='src dir for the project', dest='top')
|
||||||
|
|
||||||
gr.add_option('--no-lock-in-run', action='store_true', default='', help=optparse.SUPPRESS_HELP, dest='no_lock_in_run')
|
gr.add_option('--no-lock-in-run', action='store_true', default=os.environ.get('NO_LOCK_IN_RUN', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_run')
|
||||||
gr.add_option('--no-lock-in-out', action='store_true', default='', help=optparse.SUPPRESS_HELP, dest='no_lock_in_out')
|
gr.add_option('--no-lock-in-out', action='store_true', default=os.environ.get('NO_LOCK_IN_OUT', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_out')
|
||||||
gr.add_option('--no-lock-in-top', action='store_true', default='', help=optparse.SUPPRESS_HELP, dest='no_lock_in_top')
|
gr.add_option('--no-lock-in-top', action='store_true', default=os.environ.get('NO_LOCK_IN_TOP', ''), help=optparse.SUPPRESS_HELP, dest='no_lock_in_top')
|
||||||
|
|
||||||
default_prefix = getattr(Context.g_module, 'default_prefix', os.environ.get('PREFIX'))
|
default_prefix = getattr(Context.g_module, 'default_prefix', os.environ.get('PREFIX'))
|
||||||
if not default_prefix:
|
if not default_prefix:
|
||||||
@ -282,6 +297,8 @@ class OptionsContext(Context.Context):
|
|||||||
elif arg != 'options':
|
elif arg != 'options':
|
||||||
commands.append(arg)
|
commands.append(arg)
|
||||||
|
|
||||||
|
if options.jobs < 1:
|
||||||
|
options.jobs = 1
|
||||||
for name in 'top out destdir prefix bindir libdir'.split():
|
for name in 'top out destdir prefix bindir libdir'.split():
|
||||||
# those paths are usually expanded from Context.launch_dir
|
# those paths are usually expanded from Context.launch_dir
|
||||||
if getattr(options, name, None):
|
if getattr(options, name, None):
|
||||||
|
6
third_party/waf/waflib/Scripting.py
vendored
6
third_party/waf/waflib/Scripting.py
vendored
@ -306,7 +306,7 @@ def distclean(ctx):
|
|||||||
|
|
||||||
# remove a build folder, if any
|
# remove a build folder, if any
|
||||||
cur = '.'
|
cur = '.'
|
||||||
if ctx.options.no_lock_in_top:
|
if os.environ.get('NO_LOCK_IN_TOP') or ctx.options.no_lock_in_top:
|
||||||
cur = ctx.options.out
|
cur = ctx.options.out
|
||||||
|
|
||||||
try:
|
try:
|
||||||
@ -333,9 +333,9 @@ def distclean(ctx):
|
|||||||
remove_and_log(env.out_dir, shutil.rmtree)
|
remove_and_log(env.out_dir, shutil.rmtree)
|
||||||
|
|
||||||
env_dirs = [env.out_dir]
|
env_dirs = [env.out_dir]
|
||||||
if not ctx.options.no_lock_in_top:
|
if not (os.environ.get('NO_LOCK_IN_TOP') or ctx.options.no_lock_in_top):
|
||||||
env_dirs.append(env.top_dir)
|
env_dirs.append(env.top_dir)
|
||||||
if not ctx.options.no_lock_in_run:
|
if not (os.environ.get('NO_LOCK_IN_RUN') or ctx.options.no_lock_in_run):
|
||||||
env_dirs.append(env.run_dir)
|
env_dirs.append(env.run_dir)
|
||||||
for k in env_dirs:
|
for k in env_dirs:
|
||||||
p = os.path.join(k, Options.lockfile)
|
p = os.path.join(k, Options.lockfile)
|
||||||
|
4
third_party/waf/waflib/Tools/c_aliases.py
vendored
4
third_party/waf/waflib/Tools/c_aliases.py
vendored
@ -38,7 +38,7 @@ def sniff_features(**kw):
|
|||||||
:return: the list of features for a task generator processing the source files
|
:return: the list of features for a task generator processing the source files
|
||||||
:rtype: list of string
|
:rtype: list of string
|
||||||
"""
|
"""
|
||||||
exts = get_extensions(kw['source'])
|
exts = get_extensions(kw.get('source', []))
|
||||||
typ = kw['typ']
|
typ = kw['typ']
|
||||||
feats = []
|
feats = []
|
||||||
|
|
||||||
@ -72,7 +72,7 @@ def sniff_features(**kw):
|
|||||||
feats.append(x + typ)
|
feats.append(x + typ)
|
||||||
will_link = True
|
will_link = True
|
||||||
if not will_link and not kw.get('features', []):
|
if not will_link and not kw.get('features', []):
|
||||||
raise Errors.WafError('Cannot link from %r, try passing eg: features="c cprogram"?' % kw)
|
raise Errors.WafError('Unable to determine how to link %r, try adding eg: features="c cshlib"?' % kw)
|
||||||
return feats
|
return feats
|
||||||
|
|
||||||
def set_features(kw, typ):
|
def set_features(kw, typ):
|
||||||
|
22
third_party/waf/waflib/Tools/c_config.py
vendored
22
third_party/waf/waflib/Tools/c_config.py
vendored
@ -86,6 +86,10 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
|
|||||||
:type uselib_store: string
|
:type uselib_store: string
|
||||||
:param env: config set or conf.env by default
|
:param env: config set or conf.env by default
|
||||||
:type env: :py:class:`waflib.ConfigSet.ConfigSet`
|
:type env: :py:class:`waflib.ConfigSet.ConfigSet`
|
||||||
|
:param force_static: force usage of static libraries
|
||||||
|
:type force_static: bool default False
|
||||||
|
:param posix: usage of POSIX mode for shlex lexical analiysis library
|
||||||
|
:type posix: bool default True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert(isinstance(line, str))
|
assert(isinstance(line, str))
|
||||||
@ -103,6 +107,8 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
|
|||||||
lex.commenters = ''
|
lex.commenters = ''
|
||||||
lst = list(lex)
|
lst = list(lex)
|
||||||
|
|
||||||
|
so_re = re.compile(r"\.so(?:\.[0-9]+)*$")
|
||||||
|
|
||||||
# append_unique is not always possible
|
# append_unique is not always possible
|
||||||
# for example, apple flags may require both -arch i386 and -arch ppc
|
# for example, apple flags may require both -arch i386 and -arch ppc
|
||||||
uselib = uselib_store
|
uselib = uselib_store
|
||||||
@ -144,7 +150,7 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
|
|||||||
elif x.startswith('-std='):
|
elif x.startswith('-std='):
|
||||||
prefix = 'CXXFLAGS' if '++' in x else 'CFLAGS'
|
prefix = 'CXXFLAGS' if '++' in x else 'CFLAGS'
|
||||||
app(prefix, x)
|
app(prefix, x)
|
||||||
elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie'):
|
elif x.startswith('+') or x in ('-pthread', '-fPIC', '-fpic', '-fPIE', '-fpie', '-flto', '-fno-lto'):
|
||||||
app('CFLAGS', x)
|
app('CFLAGS', x)
|
||||||
app('CXXFLAGS', x)
|
app('CXXFLAGS', x)
|
||||||
app('LINKFLAGS', x)
|
app('LINKFLAGS', x)
|
||||||
@ -180,7 +186,7 @@ def parse_flags(self, line, uselib_store, env=None, force_static=False, posix=No
|
|||||||
app('CFLAGS', tmp)
|
app('CFLAGS', tmp)
|
||||||
app('CXXFLAGS', tmp)
|
app('CXXFLAGS', tmp)
|
||||||
app('LINKFLAGS', tmp)
|
app('LINKFLAGS', tmp)
|
||||||
elif x.endswith(('.a', '.so', '.dylib', '.lib')):
|
elif x.endswith(('.a', '.dylib', '.lib')) or so_re.search(x):
|
||||||
appu('LINKFLAGS', x) # not cool, #762
|
appu('LINKFLAGS', x) # not cool, #762
|
||||||
else:
|
else:
|
||||||
self.to_log('Unhandled flag %r' % x)
|
self.to_log('Unhandled flag %r' % x)
|
||||||
@ -246,6 +252,8 @@ def exec_cfg(self, kw):
|
|||||||
* if modversion is given, then return the module version
|
* if modversion is given, then return the module version
|
||||||
* else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable
|
* else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable
|
||||||
|
|
||||||
|
:param path: the **-config program to use**
|
||||||
|
:type path: list of string
|
||||||
:param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests)
|
:param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests)
|
||||||
:type atleast_pkgconfig_version: string
|
:type atleast_pkgconfig_version: string
|
||||||
:param package: package name, for example *gtk+-2.0*
|
:param package: package name, for example *gtk+-2.0*
|
||||||
@ -260,6 +268,12 @@ def exec_cfg(self, kw):
|
|||||||
:type variables: list of string
|
:type variables: list of string
|
||||||
:param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES)
|
:param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES)
|
||||||
:type define_variable: dict(string: string)
|
:type define_variable: dict(string: string)
|
||||||
|
:param pkg_config_path: paths where pkg-config should search for .pc config files (overrides env.PKG_CONFIG_PATH if exists)
|
||||||
|
:type pkg_config_path: string, list of directories separated by colon
|
||||||
|
:param force_static: force usage of static libraries
|
||||||
|
:type force_static: bool default False
|
||||||
|
:param posix: usage of POSIX mode for shlex lexical analiysis library
|
||||||
|
:type posix: bool default True
|
||||||
"""
|
"""
|
||||||
|
|
||||||
path = Utils.to_list(kw['path'])
|
path = Utils.to_list(kw['path'])
|
||||||
@ -334,6 +348,7 @@ def check_cfg(self, *k, **kw):
|
|||||||
"""
|
"""
|
||||||
Checks for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc).
|
Checks for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc).
|
||||||
This wraps internal calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg`
|
This wraps internal calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg`
|
||||||
|
so check exec_cfg parameters descriptions for more details on kw passed
|
||||||
|
|
||||||
A few examples::
|
A few examples::
|
||||||
|
|
||||||
@ -1267,10 +1282,11 @@ def multicheck(self, *k, **kw):
|
|||||||
tasks = []
|
tasks = []
|
||||||
|
|
||||||
id_to_task = {}
|
id_to_task = {}
|
||||||
for dct in k:
|
for counter, dct in enumerate(k):
|
||||||
x = Task.classes['cfgtask'](bld=bld, env=None)
|
x = Task.classes['cfgtask'](bld=bld, env=None)
|
||||||
tasks.append(x)
|
tasks.append(x)
|
||||||
x.args = dct
|
x.args = dct
|
||||||
|
x.args['multicheck_counter'] = counter
|
||||||
x.bld = bld
|
x.bld = bld
|
||||||
x.conf = self
|
x.conf = self
|
||||||
x.args = dct
|
x.args = dct
|
||||||
|
15
third_party/waf/waflib/Tools/c_tests.py
vendored
15
third_party/waf/waflib/Tools/c_tests.py
vendored
@ -180,9 +180,15 @@ def check_large_file(self, **kw):
|
|||||||
########################################################################################
|
########################################################################################
|
||||||
|
|
||||||
ENDIAN_FRAGMENT = '''
|
ENDIAN_FRAGMENT = '''
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
#define testshlib_EXPORT __declspec(dllexport)
|
||||||
|
#else
|
||||||
|
#define testshlib_EXPORT
|
||||||
|
#endif
|
||||||
|
|
||||||
short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
|
short int ascii_mm[] = { 0x4249, 0x4765, 0x6E44, 0x6961, 0x6E53, 0x7953, 0 };
|
||||||
short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
|
short int ascii_ii[] = { 0x694C, 0x5454, 0x656C, 0x6E45, 0x6944, 0x6E61, 0 };
|
||||||
int use_ascii (int i) {
|
int testshlib_EXPORT use_ascii (int i) {
|
||||||
return ascii_mm[i] + ascii_ii[i];
|
return ascii_mm[i] + ascii_ii[i];
|
||||||
}
|
}
|
||||||
short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
|
short int ebcdic_ii[] = { 0x89D3, 0xE3E3, 0x8593, 0x95C5, 0x89C4, 0x9581, 0 };
|
||||||
@ -208,12 +214,12 @@ class grep_for_endianness(Task.Task):
|
|||||||
return -1
|
return -1
|
||||||
|
|
||||||
@feature('grep_for_endianness')
|
@feature('grep_for_endianness')
|
||||||
@after_method('process_source')
|
@after_method('apply_link')
|
||||||
def grep_for_endianness_fun(self):
|
def grep_for_endianness_fun(self):
|
||||||
"""
|
"""
|
||||||
Used by the endianness configuration test
|
Used by the endianness configuration test
|
||||||
"""
|
"""
|
||||||
self.create_task('grep_for_endianness', self.compiled_tasks[0].outputs[0])
|
self.create_task('grep_for_endianness', self.link_task.outputs[0])
|
||||||
|
|
||||||
@conf
|
@conf
|
||||||
def check_endianness(self):
|
def check_endianness(self):
|
||||||
@ -223,7 +229,8 @@ def check_endianness(self):
|
|||||||
tmp = []
|
tmp = []
|
||||||
def check_msg(self):
|
def check_msg(self):
|
||||||
return tmp[0]
|
return tmp[0]
|
||||||
self.check(fragment=ENDIAN_FRAGMENT, features='c grep_for_endianness',
|
|
||||||
|
self.check(fragment=ENDIAN_FRAGMENT, features='c cshlib grep_for_endianness',
|
||||||
msg='Checking for endianness', define='ENDIANNESS', tmp=tmp,
|
msg='Checking for endianness', define='ENDIANNESS', tmp=tmp,
|
||||||
okmsg=check_msg, confcache=None)
|
okmsg=check_msg, confcache=None)
|
||||||
return tmp[0]
|
return tmp[0]
|
||||||
|
2
third_party/waf/waflib/Tools/compiler_c.py
vendored
2
third_party/waf/waflib/Tools/compiler_c.py
vendored
@ -37,7 +37,7 @@ from waflib.Logs import debug
|
|||||||
|
|
||||||
c_compiler = {
|
c_compiler = {
|
||||||
'win32': ['msvc', 'gcc', 'clang'],
|
'win32': ['msvc', 'gcc', 'clang'],
|
||||||
'cygwin': ['gcc'],
|
'cygwin': ['gcc', 'clang'],
|
||||||
'darwin': ['clang', 'gcc'],
|
'darwin': ['clang', 'gcc'],
|
||||||
'aix': ['xlc', 'gcc', 'clang'],
|
'aix': ['xlc', 'gcc', 'clang'],
|
||||||
'linux': ['gcc', 'clang', 'icc'],
|
'linux': ['gcc', 'clang', 'icc'],
|
||||||
|
2
third_party/waf/waflib/Tools/compiler_cxx.py
vendored
2
third_party/waf/waflib/Tools/compiler_cxx.py
vendored
@ -38,7 +38,7 @@ from waflib.Logs import debug
|
|||||||
|
|
||||||
cxx_compiler = {
|
cxx_compiler = {
|
||||||
'win32': ['msvc', 'g++', 'clang++'],
|
'win32': ['msvc', 'g++', 'clang++'],
|
||||||
'cygwin': ['g++'],
|
'cygwin': ['g++', 'clang++'],
|
||||||
'darwin': ['clang++', 'g++'],
|
'darwin': ['clang++', 'g++'],
|
||||||
'aix': ['xlc++', 'g++', 'clang++'],
|
'aix': ['xlc++', 'g++', 'clang++'],
|
||||||
'linux': ['g++', 'clang++', 'icpc'],
|
'linux': ['g++', 'clang++', 'icpc'],
|
||||||
|
4
third_party/waf/waflib/Tools/fc.py
vendored
4
third_party/waf/waflib/Tools/fc.py
vendored
@ -13,8 +13,8 @@ from waflib.TaskGen import extension
|
|||||||
from waflib.Configure import conf
|
from waflib.Configure import conf
|
||||||
|
|
||||||
ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES', 'FCPPFLAGS'])
|
ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES', 'FCPPFLAGS'])
|
||||||
ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
|
ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
|
||||||
ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS'])
|
ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'LDFLAGS'])
|
||||||
ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
|
ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS'])
|
||||||
|
|
||||||
@extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
|
@extension('.f','.F','.f90','.F90','.for','.FOR','.f95','.F95','.f03','.F03','.f08','.F08')
|
||||||
|
14
third_party/waf/waflib/Tools/irixcc.py
vendored
14
third_party/waf/waflib/Tools/irixcc.py
vendored
@ -13,22 +13,11 @@ from waflib.Configure import conf
|
|||||||
@conf
|
@conf
|
||||||
def find_irixcc(conf):
|
def find_irixcc(conf):
|
||||||
v = conf.env
|
v = conf.env
|
||||||
cc = None
|
cc = conf.find_program('cc', var='CC')
|
||||||
if v.CC:
|
|
||||||
cc = v.CC
|
|
||||||
elif 'CC' in conf.environ:
|
|
||||||
cc = conf.environ['CC']
|
|
||||||
if not cc:
|
|
||||||
cc = conf.find_program('cc', var='CC')
|
|
||||||
if not cc:
|
|
||||||
conf.fatal('irixcc was not found')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
conf.cmd_and_log(cc + ['-version'])
|
conf.cmd_and_log(cc + ['-version'])
|
||||||
except Errors.WafError:
|
except Errors.WafError:
|
||||||
conf.fatal('%r -version could not be executed' % cc)
|
conf.fatal('%r -version could not be executed' % cc)
|
||||||
|
|
||||||
v.CC = cc
|
|
||||||
v.CC_NAME = 'irix'
|
v.CC_NAME = 'irix'
|
||||||
|
|
||||||
@conf
|
@conf
|
||||||
@ -57,7 +46,6 @@ def irixcc_common_flags(conf):
|
|||||||
|
|
||||||
def configure(conf):
|
def configure(conf):
|
||||||
conf.find_irixcc()
|
conf.find_irixcc()
|
||||||
conf.find_cpp()
|
|
||||||
conf.find_ar()
|
conf.find_ar()
|
||||||
conf.irixcc_common_flags()
|
conf.irixcc_common_flags()
|
||||||
conf.cc_load_tools()
|
conf.cc_load_tools()
|
||||||
|
2
third_party/waf/waflib/Tools/javaw.py
vendored
2
third_party/waf/waflib/Tools/javaw.py
vendored
@ -251,7 +251,7 @@ def use_javac_files(self):
|
|||||||
base_node = tg.path.get_bld()
|
base_node = tg.path.get_bld()
|
||||||
|
|
||||||
self.use_lst.append(base_node.abspath())
|
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)])
|
self.javac_task.dep_nodes.extend([dx for dx in base_node.ant_glob(JAR_RE, remove=False, quiet=True)])
|
||||||
|
|
||||||
for tsk in tg.tasks:
|
for tsk in tg.tasks:
|
||||||
self.javac_task.set_run_after(tsk)
|
self.javac_task.set_run_after(tsk)
|
||||||
|
2
third_party/waf/waflib/Tools/python.py
vendored
2
third_party/waf/waflib/Tools/python.py
vendored
@ -620,7 +620,7 @@ def configure(conf):
|
|||||||
v.PYO = getattr(Options.options, 'pyo', 1)
|
v.PYO = getattr(Options.options, 'pyo', 1)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
v.PYTAG = conf.cmd_and_log(conf.env.PYTHON + ['-c', "import imp;print(imp.get_tag())"]).strip()
|
v.PYTAG = conf.cmd_and_log(conf.env.PYTHON + ['-c', "import sys\ntry:\n print(sys.implementation.cache_tag)\nexcept AttributeError:\n import imp\n print(imp.get_tag())\n"]).strip()
|
||||||
except Errors.WafError:
|
except Errors.WafError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
6
third_party/waf/waflib/Tools/qt5.py
vendored
6
third_party/waf/waflib/Tools/qt5.py
vendored
@ -482,8 +482,8 @@ def configure(self):
|
|||||||
self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
|
self.fatal('No CXX compiler defined: did you forget to configure compiler_cxx first?')
|
||||||
|
|
||||||
# Qt5 may be compiled with '-reduce-relocations' which requires dependent programs to have -fPIE or -fPIC?
|
# Qt5 may be compiled with '-reduce-relocations' which requires dependent programs to have -fPIE or -fPIC?
|
||||||
frag = '#include <QApplication>\nint main(int argc, char **argv) {return 0;}\n'
|
frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
|
||||||
uses = 'QT5CORE QT5WIDGETS QT5GUI'
|
uses = 'QT5CORE'
|
||||||
for flag in [[], '-fPIE', '-fPIC', '-std=c++11' , ['-std=c++11', '-fPIE'], ['-std=c++11', '-fPIC']]:
|
for flag in [[], '-fPIE', '-fPIC', '-std=c++11' , ['-std=c++11', '-fPIE'], ['-std=c++11', '-fPIC']]:
|
||||||
msg = 'See if Qt files compile '
|
msg = 'See if Qt files compile '
|
||||||
if flag:
|
if flag:
|
||||||
@ -499,7 +499,7 @@ def configure(self):
|
|||||||
|
|
||||||
# FreeBSD does not add /usr/local/lib and the pkg-config files do not provide it either :-/
|
# FreeBSD does not add /usr/local/lib and the pkg-config files do not provide it either :-/
|
||||||
if Utils.unversioned_sys_platform() == 'freebsd':
|
if Utils.unversioned_sys_platform() == 'freebsd':
|
||||||
frag = '#include <QApplication>\nint main(int argc, char **argv) { QApplication app(argc, argv); return NULL != (void*) (&app);}\n'
|
frag = '#include <QMap>\nint main(int argc, char **argv) {QMap<int,int> m;return m.keys().size();}\n'
|
||||||
try:
|
try:
|
||||||
self.check(features='qt5 cxx cxxprogram', use=uses, fragment=frag, msg='Can we link Qt programs on FreeBSD directly?')
|
self.check(features='qt5 cxx cxxprogram', use=uses, fragment=frag, msg='Can we link Qt programs on FreeBSD directly?')
|
||||||
except self.errors.ConfigurationError:
|
except self.errors.ConfigurationError:
|
||||||
|
2
third_party/waf/waflib/Utils.py
vendored
2
third_party/waf/waflib/Utils.py
vendored
@ -891,7 +891,7 @@ def run_prefork_process(cmd, kwargs, cargs):
|
|||||||
"""
|
"""
|
||||||
Delegates process execution to a pre-forked process instance.
|
Delegates process execution to a pre-forked process instance.
|
||||||
"""
|
"""
|
||||||
if not 'env' in kwargs:
|
if not kwargs.get('env'):
|
||||||
kwargs['env'] = dict(os.environ)
|
kwargs['env'] = dict(os.environ)
|
||||||
try:
|
try:
|
||||||
obj = base64.b64encode(cPickle.dumps([cmd, kwargs, cargs]))
|
obj = base64.b64encode(cPickle.dumps([cmd, kwargs, cargs]))
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
# Christoph Koke, 2013
|
# Christoph Koke, 2013
|
||||||
|
# Alibek Omarov, 2019
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Writes the c and cpp compile commands into build/compile_commands.json
|
Writes the c and cpp compile commands into build/compile_commands.json
|
||||||
@ -8,14 +9,23 @@ see http://clang.llvm.org/docs/JSONCompilationDatabase.html
|
|||||||
|
|
||||||
Usage:
|
Usage:
|
||||||
|
|
||||||
def configure(conf):
|
Load this tool in `options` to be able to generate database
|
||||||
conf.load('compiler_cxx')
|
by request in command-line and before build:
|
||||||
...
|
|
||||||
conf.load('clang_compilation_database')
|
$ waf clangdb
|
||||||
|
|
||||||
|
def options(opt):
|
||||||
|
opt.load('clang_compilation_database')
|
||||||
|
|
||||||
|
Otherwise, load only in `configure` to generate it always before build.
|
||||||
|
|
||||||
|
def configure(conf):
|
||||||
|
conf.load('compiler_cxx')
|
||||||
|
...
|
||||||
|
conf.load('clang_compilation_database')
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import sys, os, json, shlex, pipes
|
from waflib import Logs, TaskGen, Task, Build, Scripting
|
||||||
from waflib import Logs, TaskGen, Task
|
|
||||||
|
|
||||||
Task.Task.keep_last_cmd = True
|
Task.Task.keep_last_cmd = True
|
||||||
|
|
||||||
@ -23,63 +33,103 @@ Task.Task.keep_last_cmd = True
|
|||||||
@TaskGen.after_method('process_use')
|
@TaskGen.after_method('process_use')
|
||||||
def collect_compilation_db_tasks(self):
|
def collect_compilation_db_tasks(self):
|
||||||
"Add a compilation database entry for compiled tasks"
|
"Add a compilation database entry for compiled tasks"
|
||||||
try:
|
if not isinstance(self.bld, ClangDbContext):
|
||||||
clang_db = self.bld.clang_compilation_database_tasks
|
return
|
||||||
except AttributeError:
|
|
||||||
clang_db = self.bld.clang_compilation_database_tasks = []
|
|
||||||
self.bld.add_post_fun(write_compilation_database)
|
|
||||||
|
|
||||||
tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
|
tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
|
||||||
for task in getattr(self, 'compiled_tasks', []):
|
for task in getattr(self, 'compiled_tasks', []):
|
||||||
if isinstance(task, tup):
|
if isinstance(task, tup):
|
||||||
clang_db.append(task)
|
self.bld.clang_compilation_database_tasks.append(task)
|
||||||
|
|
||||||
def write_compilation_database(ctx):
|
class ClangDbContext(Build.BuildContext):
|
||||||
"Write the clang compilation database as JSON"
|
'''generates compile_commands.json by request'''
|
||||||
database_file = ctx.bldnode.make_node('compile_commands.json')
|
cmd = 'clangdb'
|
||||||
Logs.info('Build commands will be stored in %s', database_file.path_from(ctx.path))
|
clang_compilation_database_tasks = []
|
||||||
try:
|
|
||||||
root = json.load(database_file)
|
def write_compilation_database(self):
|
||||||
except IOError:
|
"""
|
||||||
root = []
|
Write the clang compilation database as JSON
|
||||||
clang_db = dict((x['file'], x) for x in root)
|
"""
|
||||||
for task in getattr(ctx, 'clang_compilation_database_tasks', []):
|
database_file = self.bldnode.make_node('compile_commands.json')
|
||||||
|
Logs.info('Build commands will be stored in %s', database_file.path_from(self.path))
|
||||||
try:
|
try:
|
||||||
cmd = task.last_cmd
|
root = database_file.read_json()
|
||||||
except AttributeError:
|
except IOError:
|
||||||
continue
|
root = []
|
||||||
directory = getattr(task, 'cwd', ctx.variant_dir)
|
clang_db = dict((x['file'], x) for x in root)
|
||||||
f_node = task.inputs[0]
|
for task in self.clang_compilation_database_tasks:
|
||||||
filename = os.path.relpath(f_node.abspath(), directory)
|
try:
|
||||||
entry = {
|
cmd = task.last_cmd
|
||||||
"directory": directory,
|
except AttributeError:
|
||||||
"arguments": cmd,
|
continue
|
||||||
"file": filename,
|
f_node = task.inputs[0]
|
||||||
}
|
filename = f_node.path_from(task.get_cwd())
|
||||||
clang_db[filename] = entry
|
entry = {
|
||||||
root = list(clang_db.values())
|
"directory": task.get_cwd().abspath(),
|
||||||
database_file.write(json.dumps(root, indent=2))
|
"arguments": cmd,
|
||||||
|
"file": filename,
|
||||||
|
}
|
||||||
|
clang_db[filename] = entry
|
||||||
|
root = list(clang_db.values())
|
||||||
|
database_file.write_json(root)
|
||||||
|
|
||||||
# Override the runnable_status function to do a dummy/dry run when the file doesn't need to be compiled.
|
def execute(self):
|
||||||
# This will make sure compile_commands.json is always fully up to date.
|
"""
|
||||||
# Previously you could end up with a partial compile_commands.json if the build failed.
|
Build dry run
|
||||||
for x in ('c', 'cxx'):
|
"""
|
||||||
if x not in Task.classes:
|
self.restore()
|
||||||
continue
|
|
||||||
|
|
||||||
t = Task.classes[x]
|
if not self.all_envs:
|
||||||
|
self.load_envs()
|
||||||
|
|
||||||
def runnable_status(self):
|
self.recurse([self.run_dir])
|
||||||
def exec_command(cmd, **kw):
|
self.pre_build()
|
||||||
pass
|
|
||||||
|
|
||||||
run_status = self.old_runnable_status()
|
# we need only to generate last_cmd, so override
|
||||||
if run_status == Task.SKIP_ME:
|
# exec_command temporarily
|
||||||
setattr(self, 'old_exec_command', getattr(self, 'exec_command', None))
|
def exec_command(self, *k, **kw):
|
||||||
setattr(self, 'exec_command', exec_command)
|
return 0
|
||||||
self.run()
|
|
||||||
setattr(self, 'exec_command', getattr(self, 'old_exec_command', None))
|
|
||||||
return run_status
|
|
||||||
|
|
||||||
setattr(t, 'old_runnable_status', getattr(t, 'runnable_status', None))
|
for g in self.groups:
|
||||||
setattr(t, 'runnable_status', runnable_status)
|
for tg in g:
|
||||||
|
try:
|
||||||
|
f = tg.post
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
f()
|
||||||
|
|
||||||
|
if isinstance(tg, Task.Task):
|
||||||
|
lst = [tg]
|
||||||
|
else: lst = tg.tasks
|
||||||
|
for tsk in lst:
|
||||||
|
tup = tuple(y for y in [Task.classes.get(x) for x in ('c', 'cxx')] if y)
|
||||||
|
if isinstance(tsk, tup):
|
||||||
|
old_exec = tsk.exec_command
|
||||||
|
tsk.exec_command = exec_command
|
||||||
|
tsk.run()
|
||||||
|
tsk.exec_command = old_exec
|
||||||
|
|
||||||
|
self.write_compilation_database()
|
||||||
|
|
||||||
|
EXECUTE_PATCHED = False
|
||||||
|
def patch_execute():
|
||||||
|
global EXECUTE_PATCHED
|
||||||
|
|
||||||
|
if EXECUTE_PATCHED:
|
||||||
|
return
|
||||||
|
|
||||||
|
def new_execute_build(self):
|
||||||
|
"""
|
||||||
|
Invoke clangdb command before build
|
||||||
|
"""
|
||||||
|
if self.cmd.startswith('build'):
|
||||||
|
Scripting.run_command('clangdb')
|
||||||
|
|
||||||
|
old_execute_build(self)
|
||||||
|
|
||||||
|
old_execute_build = getattr(Build.BuildContext, 'execute_build', None)
|
||||||
|
setattr(Build.BuildContext, 'execute_build', new_execute_build)
|
||||||
|
EXECUTE_PATCHED = True
|
||||||
|
|
||||||
|
patch_execute()
|
||||||
|
1
third_party/waf/waflib/extras/doxygen.py
vendored
1
third_party/waf/waflib/extras/doxygen.py
vendored
@ -69,6 +69,7 @@ def parse_doxy(txt):
|
|||||||
class doxygen(Task.Task):
|
class doxygen(Task.Task):
|
||||||
vars = ['DOXYGEN', 'DOXYFLAGS']
|
vars = ['DOXYGEN', 'DOXYFLAGS']
|
||||||
color = 'BLUE'
|
color = 'BLUE'
|
||||||
|
ext_in = [ '.py', '.c', '.h', '.java', '.pb.cc' ]
|
||||||
|
|
||||||
def runnable_status(self):
|
def runnable_status(self):
|
||||||
'''
|
'''
|
||||||
|
15
third_party/waf/waflib/extras/gccdeps.py
vendored
15
third_party/waf/waflib/extras/gccdeps.py
vendored
@ -27,7 +27,7 @@ if not c_preproc.go_absolute:
|
|||||||
gccdeps_flags = ['-MMD']
|
gccdeps_flags = ['-MMD']
|
||||||
|
|
||||||
# Third-party tools are allowed to add extra names in here with append()
|
# Third-party tools are allowed to add extra names in here with append()
|
||||||
supported_compilers = ['gcc', 'icc', 'clang']
|
supported_compilers = ['gas', 'gcc', 'icc', 'clang']
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
|
if not self.__class__.__name__ in self.env.ENABLE_GCCDEPS:
|
||||||
@ -175,14 +175,14 @@ def wrap_compiled_task(classname):
|
|||||||
derived_class.scan = scan
|
derived_class.scan = scan
|
||||||
derived_class.sig_implicit_deps = sig_implicit_deps
|
derived_class.sig_implicit_deps = sig_implicit_deps
|
||||||
|
|
||||||
for k in ('c', 'cxx'):
|
for k in ('asm', 'c', 'cxx'):
|
||||||
if k in Task.classes:
|
if k in Task.classes:
|
||||||
wrap_compiled_task(k)
|
wrap_compiled_task(k)
|
||||||
|
|
||||||
@before_method('process_source')
|
@before_method('process_source')
|
||||||
@feature('force_gccdeps')
|
@feature('force_gccdeps')
|
||||||
def force_gccdeps(self):
|
def force_gccdeps(self):
|
||||||
self.env.ENABLE_GCCDEPS = ['c', 'cxx']
|
self.env.ENABLE_GCCDEPS = ['asm', 'c', 'cxx']
|
||||||
|
|
||||||
def configure(conf):
|
def configure(conf):
|
||||||
# in case someone provides a --enable-gccdeps command-line option
|
# in case someone provides a --enable-gccdeps command-line option
|
||||||
@ -191,6 +191,15 @@ def configure(conf):
|
|||||||
|
|
||||||
global gccdeps_flags
|
global gccdeps_flags
|
||||||
flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
|
flags = conf.env.GCCDEPS_FLAGS or gccdeps_flags
|
||||||
|
if conf.env.ASM_NAME in supported_compilers:
|
||||||
|
try:
|
||||||
|
conf.check(fragment='', features='asm force_gccdeps', asflags=flags, compile_filename='test.S', msg='Checking for asm flags %r' % ''.join(flags))
|
||||||
|
except Errors.ConfigurationError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
conf.env.append_value('ASFLAGS', flags)
|
||||||
|
conf.env.append_unique('ENABLE_GCCDEPS', 'asm')
|
||||||
|
|
||||||
if conf.env.CC_NAME in supported_compilers:
|
if conf.env.CC_NAME in supported_compilers:
|
||||||
try:
|
try:
|
||||||
conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
|
conf.check(fragment='int main() { return 0; }', features='c force_gccdeps', cflags=flags, msg='Checking for c flags %r' % ''.join(flags))
|
||||||
|
135
third_party/waf/waflib/extras/javatest.py
vendored
135
third_party/waf/waflib/extras/javatest.py
vendored
@ -1,6 +1,6 @@
|
|||||||
#! /usr/bin/env python
|
#! /usr/bin/env python
|
||||||
# encoding: utf-8
|
# encoding: utf-8
|
||||||
# Federico Pellegrin, 2017 (fedepell)
|
# Federico Pellegrin, 2019 (fedepell)
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Provides Java Unit test support using :py:class:`waflib.Tools.waf_unit_test.utest`
|
Provides Java Unit test support using :py:class:`waflib.Tools.waf_unit_test.utest`
|
||||||
@ -11,6 +11,10 @@ standard waf unit test environment. It has been tested with TestNG and JUnit
|
|||||||
but should be easily expandable to other frameworks given the flexibility of
|
but should be easily expandable to other frameworks given the flexibility of
|
||||||
ut_str provided by the standard waf unit test environment.
|
ut_str provided by the standard waf unit test environment.
|
||||||
|
|
||||||
|
The extra takes care also of managing non-java dependencies (ie. C/C++ libraries
|
||||||
|
using JNI or Python modules via JEP) and setting up the environment needed to run
|
||||||
|
them.
|
||||||
|
|
||||||
Example usage:
|
Example usage:
|
||||||
|
|
||||||
def options(opt):
|
def options(opt):
|
||||||
@ -20,15 +24,15 @@ def configure(conf):
|
|||||||
conf.load('java javatest')
|
conf.load('java javatest')
|
||||||
|
|
||||||
def build(bld):
|
def build(bld):
|
||||||
|
|
||||||
[ ... mainprog is built here ... ]
|
[ ... mainprog is built here ... ]
|
||||||
|
|
||||||
bld(features = 'javac javatest',
|
bld(features = 'javac javatest',
|
||||||
srcdir = 'test/',
|
srcdir = 'test/',
|
||||||
outdir = 'test',
|
outdir = 'test',
|
||||||
sourcepath = ['test'],
|
sourcepath = ['test'],
|
||||||
classpath = [ 'src' ],
|
classpath = [ 'src' ],
|
||||||
basedir = 'test',
|
basedir = 'test',
|
||||||
use = ['JAVATEST', 'mainprog'], # mainprog is the program being tested in src/
|
use = ['JAVATEST', 'mainprog'], # mainprog is the program being tested in src/
|
||||||
ut_str = 'java -cp ${CLASSPATH} ${JTRUNNER} ${SRC}',
|
ut_str = 'java -cp ${CLASSPATH} ${JTRUNNER} ${SRC}',
|
||||||
jtest_source = bld.path.ant_glob('test/*.xml'),
|
jtest_source = bld.path.ant_glob('test/*.xml'),
|
||||||
@ -53,10 +57,107 @@ The runner class presence on the system is checked for at configuration stage.
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from waflib import Task, TaskGen, Options
|
from waflib import Task, TaskGen, Options, Errors, Utils, Logs
|
||||||
|
from waflib.Tools import ccroot
|
||||||
|
|
||||||
|
JAR_RE = '**/*'
|
||||||
|
|
||||||
|
def _process_use_rec(self, name):
|
||||||
|
"""
|
||||||
|
Recursively process ``use`` for task generator with name ``name``..
|
||||||
|
Used by javatest_process_use.
|
||||||
|
"""
|
||||||
|
if name in self.javatest_use_not or name in self.javatest_use_seen:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
tg = self.bld.get_tgen_by_name(name)
|
||||||
|
except Errors.WafError:
|
||||||
|
self.javatest_use_not.add(name)
|
||||||
|
return
|
||||||
|
|
||||||
|
self.javatest_use_seen.append(name)
|
||||||
|
tg.post()
|
||||||
|
|
||||||
|
for n in self.to_list(getattr(tg, 'use', [])):
|
||||||
|
_process_use_rec(self, n)
|
||||||
|
|
||||||
@TaskGen.feature('javatest')
|
@TaskGen.feature('javatest')
|
||||||
@TaskGen.after_method('apply_java', 'use_javac_files', 'set_classpath')
|
@TaskGen.after_method('process_source', 'apply_link', 'use_javac_files')
|
||||||
|
def javatest_process_use(self):
|
||||||
|
"""
|
||||||
|
Process the ``use`` attribute which contains a list of task generator names and store
|
||||||
|
paths that later is used to populate the unit test runtime environment.
|
||||||
|
"""
|
||||||
|
self.javatest_use_not = set()
|
||||||
|
self.javatest_use_seen = []
|
||||||
|
self.javatest_libpaths = [] # strings or Nodes
|
||||||
|
self.javatest_pypaths = [] # strings or Nodes
|
||||||
|
self.javatest_dep_nodes = []
|
||||||
|
|
||||||
|
names = self.to_list(getattr(self, 'use', []))
|
||||||
|
for name in names:
|
||||||
|
_process_use_rec(self, name)
|
||||||
|
|
||||||
|
def extend_unique(lst, varlst):
|
||||||
|
ext = []
|
||||||
|
for x in varlst:
|
||||||
|
if x not in lst:
|
||||||
|
ext.append(x)
|
||||||
|
lst.extend(ext)
|
||||||
|
|
||||||
|
# Collect type specific info needed to construct a valid runtime environment
|
||||||
|
# for the test.
|
||||||
|
for name in self.javatest_use_seen:
|
||||||
|
tg = self.bld.get_tgen_by_name(name)
|
||||||
|
|
||||||
|
# Python-Java embedding crosstools such as JEP
|
||||||
|
if 'py' in tg.features:
|
||||||
|
# Python dependencies are added to PYTHONPATH
|
||||||
|
pypath = getattr(tg, 'install_from', tg.path)
|
||||||
|
|
||||||
|
if 'buildcopy' in tg.features:
|
||||||
|
# Since buildcopy is used we assume that PYTHONPATH in build should be used,
|
||||||
|
# not source
|
||||||
|
extend_unique(self.javatest_pypaths, [pypath.get_bld().abspath()])
|
||||||
|
|
||||||
|
# Add buildcopy output nodes to dependencies
|
||||||
|
extend_unique(self.javatest_dep_nodes, [o for task in getattr(tg, 'tasks', []) for o in getattr(task, 'outputs', [])])
|
||||||
|
else:
|
||||||
|
# If buildcopy is not used, depend on sources instead
|
||||||
|
extend_unique(self.javatest_dep_nodes, tg.source)
|
||||||
|
extend_unique(self.javatest_pypaths, [pypath.abspath()])
|
||||||
|
|
||||||
|
|
||||||
|
if getattr(tg, 'link_task', None):
|
||||||
|
# For tasks with a link_task (C, C++, D et.c.) include their library paths:
|
||||||
|
if not isinstance(tg.link_task, ccroot.stlink_task):
|
||||||
|
extend_unique(self.javatest_dep_nodes, tg.link_task.outputs)
|
||||||
|
extend_unique(self.javatest_libpaths, tg.link_task.env.LIBPATH)
|
||||||
|
|
||||||
|
if 'pyext' in tg.features:
|
||||||
|
# If the taskgen is extending Python we also want to add the interpreter libpath.
|
||||||
|
extend_unique(self.javatest_libpaths, tg.link_task.env.LIBPATH_PYEXT)
|
||||||
|
else:
|
||||||
|
# Only add to libpath if the link task is not a Python extension
|
||||||
|
extend_unique(self.javatest_libpaths, [tg.link_task.outputs[0].parent.abspath()])
|
||||||
|
|
||||||
|
if 'javac' in tg.features or 'jar' in tg.features:
|
||||||
|
if hasattr(tg, 'jar_task'):
|
||||||
|
# For Java JAR tasks depend on generated JAR
|
||||||
|
extend_unique(self.javatest_dep_nodes, tg.jar_task.outputs)
|
||||||
|
else:
|
||||||
|
# For Java non-JAR ones we need to glob generated files (Java output files are not predictable)
|
||||||
|
if hasattr(tg, 'outdir'):
|
||||||
|
base_node = tg.outdir
|
||||||
|
else:
|
||||||
|
base_node = tg.path.get_bld()
|
||||||
|
|
||||||
|
self.javatest_dep_nodes.extend([dx for dx in base_node.ant_glob(JAR_RE, remove=False, quiet=True)])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@TaskGen.feature('javatest')
|
||||||
|
@TaskGen.after_method('apply_java', 'use_javac_files', 'set_classpath', 'javatest_process_use')
|
||||||
def make_javatest(self):
|
def make_javatest(self):
|
||||||
"""
|
"""
|
||||||
Creates a ``utest`` task with a populated environment for Java Unit test execution
|
Creates a ``utest`` task with a populated environment for Java Unit test execution
|
||||||
@ -65,6 +166,9 @@ def make_javatest(self):
|
|||||||
tsk = self.create_task('utest')
|
tsk = self.create_task('utest')
|
||||||
tsk.set_run_after(self.javac_task)
|
tsk.set_run_after(self.javac_task)
|
||||||
|
|
||||||
|
# Dependencies from recursive use analysis
|
||||||
|
tsk.dep_nodes.extend(self.javatest_dep_nodes)
|
||||||
|
|
||||||
# Put test input files as waf_unit_test relies on that for some prints and log generation
|
# Put test input files as waf_unit_test relies on that for some prints and log generation
|
||||||
# If jtest_source is there, this is specially useful for passing XML for TestNG
|
# If jtest_source is there, this is specially useful for passing XML for TestNG
|
||||||
# that contain test specification, use that as inputs, otherwise test sources
|
# that contain test specification, use that as inputs, otherwise test sources
|
||||||
@ -97,6 +201,21 @@ def make_javatest(self):
|
|||||||
|
|
||||||
if not hasattr(self, 'ut_env'):
|
if not hasattr(self, 'ut_env'):
|
||||||
self.ut_env = dict(os.environ)
|
self.ut_env = dict(os.environ)
|
||||||
|
def add_paths(var, lst):
|
||||||
|
# Add list of paths to a variable, lst can contain strings or nodes
|
||||||
|
lst = [ str(n) for n in lst ]
|
||||||
|
Logs.debug("ut: %s: Adding paths %s=%s", self, var, lst)
|
||||||
|
self.ut_env[var] = os.pathsep.join(lst) + os.pathsep + self.ut_env.get(var, '')
|
||||||
|
|
||||||
|
add_paths('PYTHONPATH', self.javatest_pypaths)
|
||||||
|
|
||||||
|
if Utils.is_win32:
|
||||||
|
add_paths('PATH', self.javatest_libpaths)
|
||||||
|
elif Utils.unversioned_sys_platform() == 'darwin':
|
||||||
|
add_paths('DYLD_LIBRARY_PATH', self.javatest_libpaths)
|
||||||
|
add_paths('LD_LIBRARY_PATH', self.javatest_libpaths)
|
||||||
|
else:
|
||||||
|
add_paths('LD_LIBRARY_PATH', self.javatest_libpaths)
|
||||||
|
|
||||||
def configure(ctx):
|
def configure(ctx):
|
||||||
cp = ctx.env.CLASSPATH or '.'
|
cp = ctx.env.CLASSPATH or '.'
|
||||||
|
46
third_party/waf/waflib/extras/msvc_pdb.py
vendored
Normal file
46
third_party/waf/waflib/extras/msvc_pdb.py
vendored
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# Rafaël Kooi 2019
|
||||||
|
|
||||||
|
from waflib import TaskGen
|
||||||
|
|
||||||
|
@TaskGen.feature('c', 'cxx', 'fc')
|
||||||
|
@TaskGen.after_method('propagate_uselib_vars')
|
||||||
|
def add_pdb_per_object(self):
|
||||||
|
"""For msvc/fortran, specify a unique compile pdb per object, to work
|
||||||
|
around LNK4099. Flags are updated with a unique /Fd flag based on the
|
||||||
|
task output name. This is separate from the link pdb.
|
||||||
|
"""
|
||||||
|
if not hasattr(self, 'compiled_tasks'):
|
||||||
|
return
|
||||||
|
|
||||||
|
link_task = getattr(self, 'link_task', None)
|
||||||
|
|
||||||
|
for task in self.compiled_tasks:
|
||||||
|
if task.inputs and task.inputs[0].name.lower().endswith('.rc'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
add_pdb = False
|
||||||
|
for flagname in ('CFLAGS', 'CXXFLAGS', 'FCFLAGS'):
|
||||||
|
# several languages may be used at once
|
||||||
|
for flag in task.env[flagname]:
|
||||||
|
if flag[1:].lower() == 'zi':
|
||||||
|
add_pdb = True
|
||||||
|
break
|
||||||
|
|
||||||
|
if add_pdb:
|
||||||
|
node = task.outputs[0].change_ext('.pdb')
|
||||||
|
pdb_flag = '/Fd:' + node.abspath()
|
||||||
|
|
||||||
|
for flagname in ('CFLAGS', 'CXXFLAGS', 'FCFLAGS'):
|
||||||
|
buf = [pdb_flag]
|
||||||
|
for flag in task.env[flagname]:
|
||||||
|
if flag[1:3] == 'Fd' or flag[1:].lower() == 'fs' or flag[1:].lower() == 'mp':
|
||||||
|
continue
|
||||||
|
buf.append(flag)
|
||||||
|
task.env[flagname] = buf
|
||||||
|
|
||||||
|
if link_task and not node in link_task.dep_nodes:
|
||||||
|
link_task.dep_nodes.append(node)
|
||||||
|
if not node in task.outputs:
|
||||||
|
task.outputs.append(node)
|
17
third_party/waf/waflib/extras/pytest.py
vendored
17
third_party/waf/waflib/extras/pytest.py
vendored
@ -40,6 +40,8 @@ the following environment variables for the `pytest` test runner:
|
|||||||
|
|
||||||
- `pytest_libpath` attribute is used to manually specify additional linker paths.
|
- `pytest_libpath` attribute is used to manually specify additional linker paths.
|
||||||
|
|
||||||
|
3. Java class search path (CLASSPATH) of any Java/Javalike dependency
|
||||||
|
|
||||||
Note: `pytest` cannot automatically determine the correct `PYTHONPATH` for `pyext` taskgens
|
Note: `pytest` cannot automatically determine the correct `PYTHONPATH` for `pyext` taskgens
|
||||||
because the extension might be part of a Python package or used standalone:
|
because the extension might be part of a Python package or used standalone:
|
||||||
|
|
||||||
@ -119,6 +121,7 @@ def pytest_process_use(self):
|
|||||||
self.pytest_use_seen = []
|
self.pytest_use_seen = []
|
||||||
self.pytest_paths = [] # strings or Nodes
|
self.pytest_paths = [] # strings or Nodes
|
||||||
self.pytest_libpaths = [] # strings or Nodes
|
self.pytest_libpaths = [] # strings or Nodes
|
||||||
|
self.pytest_javapaths = [] # strings or Nodes
|
||||||
self.pytest_dep_nodes = []
|
self.pytest_dep_nodes = []
|
||||||
|
|
||||||
names = self.to_list(getattr(self, 'use', []))
|
names = self.to_list(getattr(self, 'use', []))
|
||||||
@ -157,6 +160,17 @@ def pytest_process_use(self):
|
|||||||
extend_unique(self.pytest_dep_nodes, tg.source)
|
extend_unique(self.pytest_dep_nodes, tg.source)
|
||||||
extend_unique(self.pytest_paths, [pypath.abspath()])
|
extend_unique(self.pytest_paths, [pypath.abspath()])
|
||||||
|
|
||||||
|
if 'javac' in tg.features:
|
||||||
|
# If a JAR is generated point to that, otherwise to directory
|
||||||
|
if getattr(tg, 'jar_task', None):
|
||||||
|
extend_unique(self.pytest_javapaths, [tg.jar_task.outputs[0].abspath()])
|
||||||
|
else:
|
||||||
|
extend_unique(self.pytest_javapaths, [tg.path.get_bld()])
|
||||||
|
|
||||||
|
# And add respective dependencies if present
|
||||||
|
if tg.use_lst:
|
||||||
|
extend_unique(self.pytest_javapaths, tg.use_lst)
|
||||||
|
|
||||||
if getattr(tg, 'link_task', None):
|
if getattr(tg, 'link_task', None):
|
||||||
# For tasks with a link_task (C, C++, D et.c.) include their library paths:
|
# For tasks with a link_task (C, C++, D et.c.) include their library paths:
|
||||||
if not isinstance(tg.link_task, ccroot.stlink_task):
|
if not isinstance(tg.link_task, ccroot.stlink_task):
|
||||||
@ -212,8 +226,9 @@ def make_pytest(self):
|
|||||||
Logs.debug("ut: %s: Adding paths %s=%s", self, var, lst)
|
Logs.debug("ut: %s: Adding paths %s=%s", self, var, lst)
|
||||||
self.ut_env[var] = os.pathsep.join(lst) + os.pathsep + self.ut_env.get(var, '')
|
self.ut_env[var] = os.pathsep.join(lst) + os.pathsep + self.ut_env.get(var, '')
|
||||||
|
|
||||||
# Prepend dependency paths to PYTHONPATH and LD_LIBRARY_PATH
|
# Prepend dependency paths to PYTHONPATH, CLASSPATH and LD_LIBRARY_PATH
|
||||||
add_paths('PYTHONPATH', self.pytest_paths)
|
add_paths('PYTHONPATH', self.pytest_paths)
|
||||||
|
add_paths('CLASSPATH', self.pytest_javapaths)
|
||||||
|
|
||||||
if Utils.is_win32:
|
if Utils.is_win32:
|
||||||
add_paths('PATH', self.pytest_libpaths)
|
add_paths('PATH', self.pytest_libpaths)
|
||||||
|
524
third_party/waf/waflib/extras/wafcache.py
vendored
Normal file
524
third_party/waf/waflib/extras/wafcache.py
vendored
Normal file
@ -0,0 +1,524 @@
|
|||||||
|
#! /usr/bin/env python
|
||||||
|
# encoding: utf-8
|
||||||
|
# Thomas Nagy, 2019 (ita)
|
||||||
|
|
||||||
|
"""
|
||||||
|
Filesystem-based cache system to share and re-use build artifacts
|
||||||
|
|
||||||
|
Cache access operations (copy to and from) are delegated to
|
||||||
|
independent pre-forked worker subprocesses.
|
||||||
|
|
||||||
|
The following environment variables may be set:
|
||||||
|
* WAFCACHE: several possibilities:
|
||||||
|
- File cache:
|
||||||
|
absolute path of the waf cache (~/.cache/wafcache_user,
|
||||||
|
where `user` represents the currently logged-in user)
|
||||||
|
- URL to a cache server, for example:
|
||||||
|
export WAFCACHE=http://localhost:8080/files/
|
||||||
|
in that case, GET/POST requests are made to urls of the form
|
||||||
|
http://localhost:8080/files/000000000/0 (cache management is then up to the server)
|
||||||
|
- GCS or S3 bucket
|
||||||
|
gs://my-bucket/
|
||||||
|
s3://my-bucket/
|
||||||
|
* WAFCACHE_NO_PUSH: if set, disables pushing to the cache
|
||||||
|
* WAFCACHE_VERBOSITY: if set, displays more detailed cache operations
|
||||||
|
|
||||||
|
File cache specific options:
|
||||||
|
Files are copied using hard links by default; if the cache is located
|
||||||
|
onto another partition, the system switches to file copies instead.
|
||||||
|
* WAFCACHE_TRIM_MAX_FOLDER: maximum amount of tasks to cache (1M)
|
||||||
|
* WAFCACHE_EVICT_MAX_BYTES: maximum amount of cache size in bytes (10GB)
|
||||||
|
* WAFCACHE_EVICT_INTERVAL_MINUTES: minimum time interval to try
|
||||||
|
and trim the cache (3 minutess)
|
||||||
|
Usage::
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
bld.load('wafcache')
|
||||||
|
...
|
||||||
|
|
||||||
|
To troubleshoot::
|
||||||
|
|
||||||
|
waf clean build --zones=wafcache
|
||||||
|
"""
|
||||||
|
|
||||||
|
import atexit, base64, errno, fcntl, getpass, os, shutil, sys, time, traceback, urllib3
|
||||||
|
try:
|
||||||
|
import subprocess32 as subprocess
|
||||||
|
except ImportError:
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
base_cache = os.path.expanduser('~/.cache/')
|
||||||
|
if not os.path.isdir(base_cache):
|
||||||
|
base_cache = '/tmp/'
|
||||||
|
default_wafcache_dir = os.path.join(base_cache, 'wafcache_' + getpass.getuser())
|
||||||
|
|
||||||
|
CACHE_DIR = os.environ.get('WAFCACHE', default_wafcache_dir)
|
||||||
|
TRIM_MAX_FOLDERS = int(os.environ.get('WAFCACHE_TRIM_MAX_FOLDER', 1000000))
|
||||||
|
EVICT_INTERVAL_MINUTES = int(os.environ.get('WAFCACHE_EVICT_INTERVAL_MINUTES', 3))
|
||||||
|
EVICT_MAX_BYTES = int(os.environ.get('WAFCACHE_EVICT_MAX_BYTES', 10**10))
|
||||||
|
WAFCACHE_NO_PUSH = 1 if os.environ.get('WAFCACHE_NO_PUSH') else 0
|
||||||
|
WAFCACHE_VERBOSITY = 1 if os.environ.get('WAFCACHE_VERBOSITY') else 0
|
||||||
|
OK = "ok"
|
||||||
|
|
||||||
|
try:
|
||||||
|
import cPickle
|
||||||
|
except ImportError:
|
||||||
|
import pickle as cPickle
|
||||||
|
|
||||||
|
if __name__ != '__main__':
|
||||||
|
from waflib import Task, Logs, Utils, Build
|
||||||
|
|
||||||
|
def can_retrieve_cache(self):
|
||||||
|
"""
|
||||||
|
New method for waf Task classes
|
||||||
|
"""
|
||||||
|
if not self.outputs:
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.cached = False
|
||||||
|
|
||||||
|
sig = self.signature()
|
||||||
|
ssig = Utils.to_hex(self.uid() + sig)
|
||||||
|
|
||||||
|
files_to = [node.abspath() for node in self.outputs]
|
||||||
|
err = cache_command(ssig, [], files_to)
|
||||||
|
if err.startswith(OK):
|
||||||
|
if WAFCACHE_VERBOSITY:
|
||||||
|
Logs.pprint('CYAN', ' Fetched %r from cache' % files_to)
|
||||||
|
else:
|
||||||
|
Logs.debug('wafcache: fetched %r from cache', files_to)
|
||||||
|
else:
|
||||||
|
if WAFCACHE_VERBOSITY:
|
||||||
|
Logs.pprint('YELLOW', ' No cache entry %s' % files_to)
|
||||||
|
else:
|
||||||
|
Logs.debug('wafcache: No cache entry %s: %s', files_to, err)
|
||||||
|
return False
|
||||||
|
|
||||||
|
self.cached = True
|
||||||
|
return True
|
||||||
|
|
||||||
|
def put_files_cache(self):
|
||||||
|
"""
|
||||||
|
New method for waf Task classes
|
||||||
|
"""
|
||||||
|
if WAFCACHE_NO_PUSH or getattr(self, 'cached', None) or not self.outputs:
|
||||||
|
return
|
||||||
|
|
||||||
|
bld = self.generator.bld
|
||||||
|
sig = self.signature()
|
||||||
|
ssig = Utils.to_hex(self.uid() + sig)
|
||||||
|
|
||||||
|
files_from = [node.abspath() for node in self.outputs]
|
||||||
|
err = cache_command(ssig, files_from, [])
|
||||||
|
|
||||||
|
if err.startswith(OK):
|
||||||
|
if WAFCACHE_VERBOSITY:
|
||||||
|
Logs.pprint('CYAN', ' Successfully uploaded %s to cache' % files_from)
|
||||||
|
else:
|
||||||
|
Logs.debug('wafcache: Successfully uploaded %r to cache', files_from)
|
||||||
|
else:
|
||||||
|
if WAFCACHE_VERBOSITY:
|
||||||
|
Logs.pprint('RED', ' Error caching step results %s: %s' % (files_from, err))
|
||||||
|
else:
|
||||||
|
Logs.debug('wafcache: Error caching results %s: %s', files_from, err)
|
||||||
|
|
||||||
|
bld.task_sigs[self.uid()] = self.cache_sig
|
||||||
|
|
||||||
|
def hash_env_vars(self, env, vars_lst):
|
||||||
|
"""
|
||||||
|
Reimplement BuildContext.hash_env_vars so that the resulting hash does not depend on local paths
|
||||||
|
"""
|
||||||
|
if not env.table:
|
||||||
|
env = env.parent
|
||||||
|
if not env:
|
||||||
|
return Utils.SIG_NIL
|
||||||
|
|
||||||
|
idx = str(id(env)) + str(vars_lst)
|
||||||
|
try:
|
||||||
|
cache = self.cache_env
|
||||||
|
except AttributeError:
|
||||||
|
cache = self.cache_env = {}
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
return self.cache_env[idx]
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
v = str([env[a] for a in vars_lst])
|
||||||
|
v = v.replace(self.srcnode.abspath().__repr__()[:-1], '')
|
||||||
|
m = Utils.md5()
|
||||||
|
m.update(v.encode())
|
||||||
|
ret = m.digest()
|
||||||
|
|
||||||
|
Logs.debug('envhash: %r %r', ret, v)
|
||||||
|
|
||||||
|
cache[idx] = ret
|
||||||
|
|
||||||
|
return ret
|
||||||
|
|
||||||
|
def uid(self):
|
||||||
|
"""
|
||||||
|
Reimplement Task.uid() so that the signature does not depend on local paths
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self.uid_
|
||||||
|
except AttributeError:
|
||||||
|
m = Utils.md5()
|
||||||
|
src = self.generator.bld.srcnode
|
||||||
|
up = m.update
|
||||||
|
up(self.__class__.__name__.encode())
|
||||||
|
for x in self.inputs + self.outputs:
|
||||||
|
up(x.path_from(src).encode())
|
||||||
|
self.uid_ = m.digest()
|
||||||
|
return self.uid_
|
||||||
|
|
||||||
|
|
||||||
|
def make_cached(cls):
|
||||||
|
"""
|
||||||
|
Enable the waf cache for a given task class
|
||||||
|
"""
|
||||||
|
if getattr(cls, 'nocache', None) or getattr(cls, 'has_cache', False):
|
||||||
|
return
|
||||||
|
|
||||||
|
m1 = getattr(cls, 'run', None)
|
||||||
|
def run(self):
|
||||||
|
if getattr(self, 'nocache', False):
|
||||||
|
return m1(self)
|
||||||
|
if self.can_retrieve_cache():
|
||||||
|
return 0
|
||||||
|
return m1(self)
|
||||||
|
cls.run = run
|
||||||
|
|
||||||
|
m2 = getattr(cls, 'post_run', None)
|
||||||
|
def post_run(self):
|
||||||
|
if getattr(self, 'nocache', False):
|
||||||
|
return m2(self)
|
||||||
|
ret = m2(self)
|
||||||
|
self.put_files_cache()
|
||||||
|
if hasattr(self, 'chmod'):
|
||||||
|
for node in self.outputs:
|
||||||
|
os.chmod(node.abspath(), self.chmod)
|
||||||
|
return ret
|
||||||
|
cls.post_run = post_run
|
||||||
|
cls.has_cache = True
|
||||||
|
|
||||||
|
process_pool = []
|
||||||
|
def get_process():
|
||||||
|
"""
|
||||||
|
Returns a worker process that can process waf cache commands
|
||||||
|
The worker process is assumed to be returned to the process pool when unused
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return process_pool.pop()
|
||||||
|
except IndexError:
|
||||||
|
filepath = os.path.dirname(os.path.abspath(__file__)) + os.sep + 'wafcache.py'
|
||||||
|
cmd = [sys.executable, '-c', Utils.readf(filepath)]
|
||||||
|
return subprocess.Popen(cmd, stdout=subprocess.PIPE, stdin=subprocess.PIPE, bufsize=0)
|
||||||
|
|
||||||
|
def atexit_pool():
|
||||||
|
for k in process_pool:
|
||||||
|
try:
|
||||||
|
os.kill(k.pid, 9)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
k.wait()
|
||||||
|
atexit.register(atexit_pool)
|
||||||
|
|
||||||
|
def build(bld):
|
||||||
|
"""
|
||||||
|
Called during the build process to enable file caching
|
||||||
|
"""
|
||||||
|
if process_pool:
|
||||||
|
# already called once
|
||||||
|
return
|
||||||
|
|
||||||
|
for x in range(bld.jobs):
|
||||||
|
process_pool.append(get_process())
|
||||||
|
|
||||||
|
Task.Task.can_retrieve_cache = can_retrieve_cache
|
||||||
|
Task.Task.put_files_cache = put_files_cache
|
||||||
|
Task.Task.uid = uid
|
||||||
|
Build.BuildContext.hash_env_vars = hash_env_vars
|
||||||
|
for x in reversed(list(Task.classes.values())):
|
||||||
|
make_cached(x)
|
||||||
|
|
||||||
|
def cache_command(sig, files_from, files_to):
|
||||||
|
"""
|
||||||
|
Create a command for cache worker processes, returns a pickled
|
||||||
|
base64-encoded tuple containing the task signature, a list of files to
|
||||||
|
cache and a list of files files to get from cache (one of the lists
|
||||||
|
is assumed to be empty)
|
||||||
|
"""
|
||||||
|
proc = get_process()
|
||||||
|
|
||||||
|
obj = base64.b64encode(cPickle.dumps([sig, files_from, files_to]))
|
||||||
|
proc.stdin.write(obj)
|
||||||
|
proc.stdin.write('\n'.encode())
|
||||||
|
proc.stdin.flush()
|
||||||
|
obj = proc.stdout.readline()
|
||||||
|
if not obj:
|
||||||
|
raise OSError('Preforked sub-process %r died' % proc.pid)
|
||||||
|
process_pool.append(proc)
|
||||||
|
return cPickle.loads(base64.b64decode(obj))
|
||||||
|
|
||||||
|
try:
|
||||||
|
copyfun = os.link
|
||||||
|
except NameError:
|
||||||
|
copyfun = shutil.copy2
|
||||||
|
|
||||||
|
def atomic_copy(orig, dest):
|
||||||
|
"""
|
||||||
|
Copy files to the cache, the operation is atomic for a given file
|
||||||
|
"""
|
||||||
|
global copyfun
|
||||||
|
tmp = dest + '.tmp'
|
||||||
|
up = os.path.dirname(dest)
|
||||||
|
try:
|
||||||
|
os.makedirs(up)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
|
try:
|
||||||
|
copyfun(orig, tmp)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno == errno.EXDEV:
|
||||||
|
copyfun = shutil.copy2
|
||||||
|
copyfun(orig, tmp)
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
os.rename(tmp, dest)
|
||||||
|
|
||||||
|
def lru_trim():
|
||||||
|
"""
|
||||||
|
the cache folders take the form:
|
||||||
|
`CACHE_DIR/0b/0b180f82246d726ece37c8ccd0fb1cde2650d7bfcf122ec1f169079a3bfc0ab9`
|
||||||
|
they are listed in order of last access, and then removed
|
||||||
|
until the amount of folders is within TRIM_MAX_FOLDERS and the total space
|
||||||
|
taken by files is less than EVICT_MAX_BYTES
|
||||||
|
"""
|
||||||
|
lst = []
|
||||||
|
for up in os.listdir(CACHE_DIR):
|
||||||
|
if len(up) == 2:
|
||||||
|
sub = os.path.join(CACHE_DIR, up)
|
||||||
|
for hval in os.listdir(sub):
|
||||||
|
path = os.path.join(sub, hval)
|
||||||
|
|
||||||
|
size = 0
|
||||||
|
for fname in os.listdir(path):
|
||||||
|
size += os.lstat(os.path.join(path, fname)).st_size
|
||||||
|
lst.append((os.stat(path).st_mtime, size, path))
|
||||||
|
|
||||||
|
lst.sort(key=lambda x: x[0])
|
||||||
|
lst.reverse()
|
||||||
|
|
||||||
|
tot = sum(x[1] for x in lst)
|
||||||
|
while tot > EVICT_MAX_BYTES or len(lst) > TRIM_MAX_FOLDERS:
|
||||||
|
_, tmp_size, path = lst.pop()
|
||||||
|
tot -= tmp_size
|
||||||
|
|
||||||
|
tmp = path + '.tmp'
|
||||||
|
try:
|
||||||
|
shutil.rmtree(tmp)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
try:
|
||||||
|
os.rename(path, tmp)
|
||||||
|
except OSError:
|
||||||
|
sys.stderr.write('Could not rename %r to %r' % (path, tmp))
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
shutil.rmtree(tmp)
|
||||||
|
except OSError:
|
||||||
|
sys.stderr.write('Could not remove %r' % tmp)
|
||||||
|
sys.stderr.write("Cache trimmed: %r bytes in %r folders left\n" % (tot, len(lst)))
|
||||||
|
|
||||||
|
|
||||||
|
def lru_evict():
|
||||||
|
"""
|
||||||
|
Reduce the cache size
|
||||||
|
"""
|
||||||
|
lockfile = os.path.join(CACHE_DIR, 'all.lock')
|
||||||
|
try:
|
||||||
|
st = os.stat(lockfile)
|
||||||
|
except EnvironmentError as e:
|
||||||
|
if e.errno == errno.ENOENT:
|
||||||
|
with open(lockfile, 'w') as f:
|
||||||
|
f.write('')
|
||||||
|
return
|
||||||
|
else:
|
||||||
|
raise
|
||||||
|
|
||||||
|
if st.st_mtime < time.time() - EVICT_INTERVAL_MINUTES * 60:
|
||||||
|
# check every EVICT_INTERVAL_MINUTES minutes if the cache is too big
|
||||||
|
# OCLOEXEC is unnecessary because no processes are spawned
|
||||||
|
fd = os.open(lockfile, os.O_RDWR | os.O_CREAT, 0o755)
|
||||||
|
try:
|
||||||
|
try:
|
||||||
|
fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
||||||
|
except EnvironmentError:
|
||||||
|
sys.stderr.write('another process is running!\n')
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# now dow the actual cleanup
|
||||||
|
lru_trim()
|
||||||
|
os.utime(lockfile, None)
|
||||||
|
finally:
|
||||||
|
os.close(fd)
|
||||||
|
|
||||||
|
class netcache(object):
|
||||||
|
def __init__(self):
|
||||||
|
self.http = urllib3.PoolManager()
|
||||||
|
|
||||||
|
def url_of(self, sig, i):
|
||||||
|
return "%s/%s/%s" % (CACHE_DIR, sig, i)
|
||||||
|
|
||||||
|
def upload(self, file_path, sig, i):
|
||||||
|
url = self.url_of(sig, i)
|
||||||
|
with open(file_path, 'rb') as f:
|
||||||
|
file_data = f.read()
|
||||||
|
r = self.http.request('POST', url, timeout=60,
|
||||||
|
fields={ 'file': ('%s/%s' % (sig, i), file_data), })
|
||||||
|
if r.status >= 400:
|
||||||
|
raise OSError("Invalid status %r %r" % (url, r.status))
|
||||||
|
|
||||||
|
def download(self, file_path, sig, i):
|
||||||
|
url = self.url_of(sig, i)
|
||||||
|
with self.http.request('GET', url, preload_content=False, timeout=60) as inf:
|
||||||
|
if inf.status >= 400:
|
||||||
|
raise OSError("Invalid status %r %r" % (url, inf.status))
|
||||||
|
with open(file_path, 'wb') as out:
|
||||||
|
shutil.copyfileobj(inf, out)
|
||||||
|
|
||||||
|
def copy_to_cache(self, sig, files_from, files_to):
|
||||||
|
try:
|
||||||
|
for i, x in enumerate(files_from):
|
||||||
|
if not os.path.islink(x):
|
||||||
|
self.upload(x, sig, i)
|
||||||
|
except Exception:
|
||||||
|
return traceback.format_exc()
|
||||||
|
return OK
|
||||||
|
|
||||||
|
def copy_from_cache(self, sig, files_from, files_to):
|
||||||
|
try:
|
||||||
|
for i, x in enumerate(files_to):
|
||||||
|
self.download(x, sig, i)
|
||||||
|
except Exception:
|
||||||
|
return traceback.format_exc()
|
||||||
|
return OK
|
||||||
|
|
||||||
|
class fcache(object):
|
||||||
|
def __init__(self):
|
||||||
|
if not os.path.exists(CACHE_DIR):
|
||||||
|
os.makedirs(CACHE_DIR)
|
||||||
|
if not os.path.exists(CACHE_DIR):
|
||||||
|
raise ValueError('Could not initialize the cache directory')
|
||||||
|
|
||||||
|
def copy_to_cache(self, sig, files_from, files_to):
|
||||||
|
"""
|
||||||
|
Copy files to the cache, existing files are overwritten,
|
||||||
|
and the copy is atomic only for a given file, not for all files
|
||||||
|
that belong to a given task object
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
for i, x in enumerate(files_from):
|
||||||
|
dest = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
|
||||||
|
atomic_copy(x, dest)
|
||||||
|
except Exception:
|
||||||
|
return traceback.format_exc()
|
||||||
|
else:
|
||||||
|
# attempt trimming if caching was successful:
|
||||||
|
# we may have things to trim!
|
||||||
|
lru_evict()
|
||||||
|
return OK
|
||||||
|
|
||||||
|
def copy_from_cache(self, sig, files_from, files_to):
|
||||||
|
"""
|
||||||
|
Copy files from the cache
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
for i, x in enumerate(files_to):
|
||||||
|
orig = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
|
||||||
|
atomic_copy(orig, x)
|
||||||
|
|
||||||
|
# success! update the cache time
|
||||||
|
os.utime(os.path.join(CACHE_DIR, sig[:2], sig), None)
|
||||||
|
except Exception:
|
||||||
|
return traceback.format_exc()
|
||||||
|
return OK
|
||||||
|
|
||||||
|
class bucket_cache(object):
|
||||||
|
def bucket_copy(self, source, target):
|
||||||
|
if CACHE_DIR.startswith('s3://'):
|
||||||
|
cmd = ['aws', 's3', 'cp', source, target]
|
||||||
|
else:
|
||||||
|
cmd = ['gsutil', 'cp', source, target]
|
||||||
|
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||||
|
out, err = proc.communicate()
|
||||||
|
if proc.returncode:
|
||||||
|
raise OSError('Error copy %r to %r using: %r (exit %r):\n out:%s\n err:%s' % (
|
||||||
|
source, target, cmd, proc.returncode, out.decode(), err.decode()))
|
||||||
|
|
||||||
|
def copy_to_cache(self, sig, files_from, files_to):
|
||||||
|
try:
|
||||||
|
for i, x in enumerate(files_from):
|
||||||
|
dest = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
|
||||||
|
self.bucket_copy(x, dest)
|
||||||
|
except Exception:
|
||||||
|
return traceback.format_exc()
|
||||||
|
return OK
|
||||||
|
|
||||||
|
def copy_from_cache(self, sig, files_from, files_to):
|
||||||
|
try:
|
||||||
|
for i, x in enumerate(files_to):
|
||||||
|
orig = os.path.join(CACHE_DIR, sig[:2], sig, str(i))
|
||||||
|
self.bucket_copy(orig, x)
|
||||||
|
except EnvironmentError:
|
||||||
|
return traceback.format_exc()
|
||||||
|
return OK
|
||||||
|
|
||||||
|
def loop(service):
|
||||||
|
"""
|
||||||
|
This function is run when this file is run as a standalone python script,
|
||||||
|
it assumes a parent process that will communicate the commands to it
|
||||||
|
as pickled-encoded tuples (one line per command)
|
||||||
|
|
||||||
|
The commands are to copy files to the cache or copy files from the
|
||||||
|
cache to a target destination
|
||||||
|
"""
|
||||||
|
# one operation is performed at a single time by a single process
|
||||||
|
# therefore stdin never has more than one line
|
||||||
|
txt = sys.stdin.readline().strip()
|
||||||
|
if not txt:
|
||||||
|
# parent process probably ended
|
||||||
|
sys.exit(1)
|
||||||
|
ret = OK
|
||||||
|
|
||||||
|
[sig, files_from, files_to] = cPickle.loads(base64.b64decode(txt))
|
||||||
|
if files_from:
|
||||||
|
# TODO return early when pushing files upstream
|
||||||
|
ret = service.copy_to_cache(sig, files_from, files_to)
|
||||||
|
elif files_to:
|
||||||
|
# the build process waits for workers to (possibly) obtain files from the cache
|
||||||
|
ret = service.copy_from_cache(sig, files_from, files_to)
|
||||||
|
else:
|
||||||
|
ret = "Invalid command"
|
||||||
|
|
||||||
|
obj = base64.b64encode(cPickle.dumps(ret))
|
||||||
|
sys.stdout.write(obj.decode())
|
||||||
|
sys.stdout.write('\n')
|
||||||
|
sys.stdout.flush()
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if CACHE_DIR.startswith('s3://') or CACHE_DIR.startswith('gs://'):
|
||||||
|
service = bucket_cache()
|
||||||
|
elif CACHE_DIR.startswith('http'):
|
||||||
|
service = netcache()
|
||||||
|
else:
|
||||||
|
service = fcache()
|
||||||
|
while 1:
|
||||||
|
try:
|
||||||
|
loop(service)
|
||||||
|
except KeyboardInterrupt:
|
||||||
|
break
|
||||||
|
|
Loading…
Reference in New Issue
Block a user