Merge branch 'master' into nwfq

This commit is contained in:
2021-06-25 09:59:41 +08:00
31 changed files with 3281 additions and 4 deletions
+533
View File
@@ -0,0 +1,533 @@
#!/usr/bin/env python
# encoding: utf-8
#
# partially based on boost.py written by Gernot Vormayr
# written by Ruediger Sonderfeld <ruediger@c-plusplus.de>, 2008
# modified by Bjoern Michaelsen, 2008
# modified by Luca Fossati, 2008
# rewritten for waf 1.5.1, Thomas Nagy, 2008
# rewritten for waf 1.6.2, Sylvain Rouquette, 2011
'''
This is an extra tool, not bundled with the default waf binary.
To add the boost tool to the waf file:
$ ./waf-light --tools=compat15,boost
or, if you have waf >= 1.6.2
$ ./waf update --files=boost
When using this tool, the wscript will look like:
def options(opt):
opt.load('compiler_cxx boost')
def configure(conf):
conf.load('compiler_cxx boost')
conf.check_boost(lib='system filesystem')
def build(bld):
bld(source='main.cpp', target='app', use='BOOST')
Options are generated, in order to specify the location of boost includes/libraries.
The `check_boost` configuration function allows to specify the used boost libraries.
It can also provide default arguments to the --boost-mt command-line arguments.
Everything will be packaged together in a BOOST component that you can use.
When using MSVC, a lot of compilation flags need to match your BOOST build configuration:
- you may have to add /EHsc to your CXXFLAGS or define boost::throw_exception if BOOST_NO_EXCEPTIONS is defined.
Errors: C4530
- boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC
So before calling `conf.check_boost` you might want to disabling by adding
conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
Errors:
- boost might also be compiled with /MT, which links the runtime statically.
If you have problems with redefined symbols,
self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc']
Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
'''
import sys
import re
from waflib import Utils, Logs, Errors
from waflib.Configure import conf
from waflib.TaskGen import feature, after_method
BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
BOOST_VERSION_FILE = 'boost/version.hpp'
BOOST_VERSION_CODE = '''
#include <iostream>
#include <boost/version.hpp>
int main() { std::cout << BOOST_LIB_VERSION << ":" << BOOST_VERSION << std::endl; }
'''
BOOST_ERROR_CODE = '''
#include <boost/system/error_code.hpp>
int main() { boost::system::error_code c; }
'''
PTHREAD_CODE = '''
#include <pthread.h>
static void* f(void*) { return 0; }
int main() {
pthread_t th;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&th, &attr, &f, 0);
pthread_join(th, 0);
pthread_cleanup_push(0, 0);
pthread_cleanup_pop(0);
pthread_attr_destroy(&attr);
}
'''
BOOST_THREAD_CODE = '''
#include <boost/thread.hpp>
int main() { boost::thread t; }
'''
BOOST_LOG_CODE = '''
#include <boost/log/trivial.hpp>
int main() { BOOST_LOG_TRIVIAL(info) << "boost_log is working"; }
'''
BOOST_LOG_SETUP_CODE = '''
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
int main() {
using namespace boost::log;
add_common_attributes();
add_console_log(std::clog, keywords::format = "%Message%");
BOOST_LOG_TRIVIAL(info) << "boost_log_setup is working";
}
'''
# toolsets from {boost_dir}/tools/build/v2/tools/common.jam
PLATFORM = Utils.unversioned_sys_platform()
detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
BOOST_TOOLSETS = {
'borland': 'bcb',
'clang': detect_clang,
'como': 'como',
'cw': 'cw',
'darwin': 'xgcc',
'edg': 'edg',
'g++': detect_mingw,
'gcc': detect_mingw,
'icpc': detect_intel,
'intel': detect_intel,
'kcc': 'kcc',
'kylix': 'bck',
'mipspro': 'mp',
'mingw': 'mgw',
'msvc': 'vc',
'qcc': 'qcc',
'sun': 'sw',
'sunc++': 'sw',
'tru64cxx': 'tru',
'vacpp': 'xlc'
}
def options(opt):
opt = opt.add_option_group('Boost Options')
opt.add_option('--boost-includes', type='string',
default='', dest='boost_includes',
help='''path to the directory where the boost includes are,
e.g., /path/to/boost_1_55_0/stage/include''')
opt.add_option('--boost-libs', type='string',
default='', dest='boost_libs',
help='''path to the directory where the boost libs are,
e.g., path/to/boost_1_55_0/stage/lib''')
opt.add_option('--boost-mt', action='store_true',
default=False, dest='boost_mt',
help='select multi-threaded libraries')
opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
help='''select libraries with tags (gd for debug, static is automatically added),
see doc Boost, Getting Started, chapter 6.1''')
opt.add_option('--boost-linkage_autodetect', action='store_true', dest='boost_linkage_autodetect',
help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
opt.add_option('--boost-toolset', type='string',
default='', dest='boost_toolset',
help='force a toolset e.g. msvc, vc90, \
gcc, mingw, mgw45 (default: auto)')
py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
opt.add_option('--boost-python', type='string',
default=py_version, dest='boost_python',
help='select the lib python with this version \
(default: %s)' % py_version)
@conf
def __boost_get_version_file(self, d):
if not d:
return None
dnode = self.root.find_dir(d)
if dnode:
return dnode.find_node(BOOST_VERSION_FILE)
return None
@conf
def boost_get_version(self, d):
"""silently retrieve the boost version number"""
node = self.__boost_get_version_file(d)
if node:
try:
txt = node.read()
except EnvironmentError:
Logs.error('Could not read the file %r' % node.abspath())
else:
re_but1 = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.+)"', re.M)
m1 = re_but1.search(txt)
re_but2 = re.compile('^#define\\s+BOOST_VERSION\\s+(\\d+)', re.M)
m2 = re_but2.search(txt)
if m1 and m2:
return (m1.group(1), m2.group(1))
return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True).split(':')
@conf
def boost_get_includes(self, *k, **kw):
includes = k and k[0] or kw.get('includes', None)
if includes and self.__boost_get_version_file(includes):
return includes
for d in self.environ.get('INCLUDE', '').split(';') + BOOST_INCLUDES:
if self.__boost_get_version_file(d):
return d
if includes:
self.end_msg('headers not found in %s' % includes, 'YELLOW')
self.fatal('The configuration failed')
else:
self.end_msg('headers not found, please provide a --boost-includes argument (see help)', 'YELLOW')
self.fatal('The configuration failed')
@conf
def boost_get_toolset(self, cc):
toolset = cc
if not cc:
build_platform = Utils.unversioned_sys_platform()
if build_platform in BOOST_TOOLSETS:
cc = build_platform
else:
cc = self.env.CXX_NAME
if cc in BOOST_TOOLSETS:
toolset = BOOST_TOOLSETS[cc]
return isinstance(toolset, str) and toolset or toolset(self.env)
@conf
def __boost_get_libs_path(self, *k, **kw):
''' return the lib path and all the files in it '''
if 'files' in kw:
return self.root.find_dir('.'), Utils.to_list(kw['files'])
libs = k and k[0] or kw.get('libs', None)
if libs:
path = self.root.find_dir(libs)
files = path.ant_glob('*boost_*')
if not libs or not files:
for d in self.environ.get('LIB', '').split(';') + BOOST_LIBS:
if not d:
continue
path = self.root.find_dir(d)
if path:
files = path.ant_glob('*boost_*')
if files:
break
path = self.root.find_dir(d + '64')
if path:
files = path.ant_glob('*boost_*')
if files:
break
if not path:
if libs:
self.end_msg('libs not found in %s' % libs, 'YELLOW')
self.fatal('The configuration failed')
else:
self.end_msg('libs not found, please provide a --boost-libs argument (see help)', 'YELLOW')
self.fatal('The configuration failed')
self.to_log('Found the boost path in %r with the libraries:' % path)
for x in files:
self.to_log(' %r' % x)
return path, files
@conf
def boost_get_libs(self, *k, **kw):
'''
return the lib path and the required libs
according to the parameters
'''
path, files = self.__boost_get_libs_path(**kw)
files = sorted(files, key=lambda f: (len(f.name), f.name), reverse=True)
toolset = self.boost_get_toolset(kw.get('toolset', ''))
toolset_pat = '(-%s[0-9]{0,3})' % toolset
version = '-%s' % self.env.BOOST_VERSION
def find_lib(re_lib, files):
for file in files:
if re_lib.search(file.name):
self.to_log('Found boost lib %s' % file)
return file
return None
def format_lib_name(name):
if name.startswith('lib') and self.env.CC_NAME != 'msvc':
name = name[3:]
return name[:name.rfind('.')]
def match_libs(lib_names, is_static):
libs = []
lib_names = Utils.to_list(lib_names)
if not lib_names:
return libs
t = []
if kw.get('mt', False):
t.append('-mt')
if kw.get('abi', None):
t.append('%s%s' % (is_static and '-s' or '-', kw['abi']))
elif is_static:
t.append('-s')
tags_pat = t and ''.join(t) or ''
ext = is_static and self.env.cxxstlib_PATTERN or self.env.cxxshlib_PATTERN
ext = ext.partition('%s')[2] # remove '%s' or 'lib%s' from PATTERN
for lib in lib_names:
if lib == 'python':
# for instance, with python='27',
# accepts '-py27', '-py2', '27' and '2'
# but will reject '-py3', '-py26', '26' and '3'
tags = '({0})?((-py{2})|(-py{1}(?=[^0-9]))|({2})|({1}(?=[^0-9]))|(?=[^0-9])(?!-py))'.format(tags_pat, kw['python'][0], kw['python'])
else:
tags = tags_pat
# Trying libraries, from most strict match to least one
for pattern in ['boost_%s%s%s%s%s$' % (lib, toolset_pat, tags, version, ext),
'boost_%s%s%s%s$' % (lib, tags, version, ext),
# Give up trying to find the right version
'boost_%s%s%s%s$' % (lib, toolset_pat, tags, ext),
'boost_%s%s%s$' % (lib, tags, ext),
'boost_%s%s$' % (lib, ext),
'boost_%s' % lib]:
self.to_log('Trying pattern %s' % pattern)
file = find_lib(re.compile(pattern), files)
if file:
libs.append(format_lib_name(file.name))
break
else:
self.end_msg('lib %s not found in %s' % (lib, path.abspath()), 'YELLOW')
self.fatal('The configuration failed')
return libs
return path.abspath(), match_libs(kw.get('lib', None), False), match_libs(kw.get('stlib', None), True)
@conf
def _check_pthread_flag(self, *k, **kw):
'''
Computes which flags should be added to CXXFLAGS and LINKFLAGS to compile in multi-threading mode
Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3,
boost/thread.hpp will trigger a #error if -pthread isn't used:
boost/config/requires_threads.hpp:47:5: #error "Compiler threading support
is not turned on. Please set the correct command line options for
threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)"
Based on _BOOST_PTHREAD_FLAG(): https://github.com/tsuna/boost.m4/blob/master/build-aux/boost.m4
'''
var = kw.get('uselib_store', 'BOOST')
self.start_msg('Checking the flags needed to use pthreads')
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# (none): in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -lpthreads: AIX (must check this before -lpthread)
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads)
# -pthreads: Solaris/GCC
# -mthreads: MinGW32/GCC, Lynx/GCC
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# -lpthread: GNU Linux, etc.
# --thread-safe: KAI C++
if Utils.unversioned_sys_platform() == 'sunos':
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
boost_pthread_flags = ['-pthreads', '-lpthread', '-mt', '-pthread']
else:
boost_pthread_flags = ['', '-lpthreads', '-Kthread', '-kthread', '-llthread', '-pthread',
'-pthreads', '-mthreads', '-lpthread', '--thread-safe', '-mt']
for boost_pthread_flag in boost_pthread_flags:
try:
self.env.stash()
self.env['CXXFLAGS_%s' % var] += [boost_pthread_flag]
self.env['LINKFLAGS_%s' % var] += [boost_pthread_flag]
self.check_cxx(code=PTHREAD_CODE, msg=None, use=var, execute=False, quiet=True)
self.end_msg(boost_pthread_flag)
return
except self.errors.ConfigurationError:
self.env.revert()
self.end_msg('none')
@conf
def check_boost(self, *k, **kw):
"""
Initialize boost libraries to be used.
Keywords: you can pass the same parameters as with the command line (without "--boost-").
Note that the command line has the priority, and should preferably be used.
"""
if not self.env['CXX']:
self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
params = {
'lib': k and k[0] or kw.get('lib', None),
'stlib': kw.get('stlib', None)
}
for key, value in self.options.__dict__.items():
if not key.startswith('boost_'):
continue
key = key[len('boost_'):]
params[key] = value and value or kw.get(key, '')
var = kw.get('uselib_store', 'BOOST')
if not self.env.DONE_FIND_BOOST_COMMON:
self.find_program('dpkg-architecture', var='DPKG_ARCHITECTURE', mandatory=False)
if self.env.DPKG_ARCHITECTURE:
deb_host_multiarch = self.cmd_and_log([self.env.DPKG_ARCHITECTURE[0], '-qDEB_HOST_MULTIARCH'])
BOOST_LIBS.insert(0, '/usr/lib/%s' % deb_host_multiarch.strip())
self.start_msg('Checking boost includes')
self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
versions = self.boost_get_version(inc)
self.env.BOOST_VERSION = versions[0]
self.env.BOOST_VERSION_NUMBER = int(versions[1])
self.end_msg('%d.%d.%d' % (int(versions[1]) / 100000,
int(versions[1]) / 100 % 1000,
int(versions[1]) % 100))
if Logs.verbose:
Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
self.env.DONE_FIND_BOOST_COMMON = True
if not params['lib'] and not params['stlib']:
return
if 'static' in kw or 'static' in params:
Logs.warn('boost: static parameter is deprecated, use stlib instead.')
self.start_msg('Checking boost libs')
path, libs, stlibs = self.boost_get_libs(**params)
self.env['LIBPATH_%s' % var] = [path]
self.env['STLIBPATH_%s' % var] = [path]
self.env['LIB_%s' % var] = libs
self.env['STLIB_%s' % var] = stlibs
self.end_msg(' '.join(libs + stlibs))
if Logs.verbose:
Logs.pprint('CYAN', ' path : %s' % path)
Logs.pprint('CYAN', ' shared libs : %s' % libs)
Logs.pprint('CYAN', ' static libs : %s' % stlibs)
def has_shlib(lib):
return params['lib'] and lib in params['lib']
def has_stlib(lib):
return params['stlib'] and lib in params['stlib']
def has_lib(lib):
return has_shlib(lib) or has_stlib(lib)
if has_lib('thread'):
# not inside try_link to make check visible in the output
self._check_pthread_flag(k, kw)
def try_link():
if has_lib('system'):
self.check_cxx(fragment=BOOST_ERROR_CODE, use=var, execute=False)
if has_lib('thread'):
self.check_cxx(fragment=BOOST_THREAD_CODE, use=var, execute=False)
if has_lib('log') or has_lib('log_setup'):
if not has_lib('thread'):
self.env['DEFINES_%s' % var] += ['BOOST_LOG_NO_THREADS']
if has_shlib('log') or has_shlib('log_setup'):
self.env['DEFINES_%s' % var] += ['BOOST_LOG_DYN_LINK']
if has_lib('log_setup'):
self.check_cxx(fragment=BOOST_LOG_SETUP_CODE, use=var, execute=False)
else:
self.check_cxx(fragment=BOOST_LOG_CODE, use=var, execute=False)
if params.get('linkage_autodetect', False):
self.start_msg('Attempting to detect boost linkage flags')
toolset = self.boost_get_toolset(kw.get('toolset', ''))
if toolset in ('vc',):
# disable auto-linking feature, causing error LNK1181
# because the code wants to be linked against
self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
# if no dlls are present, we guess the .lib files are not stubs
has_dlls = False
for x in Utils.listdir(path):
if x.endswith(self.env.cxxshlib_PATTERN % ''):
has_dlls = True
break
if not has_dlls:
self.env['STLIBPATH_%s' % var] = [path]
self.env['STLIB_%s' % var] = libs
del self.env['LIB_%s' % var]
del self.env['LIBPATH_%s' % var]
# we attempt to play with some known-to-work CXXFLAGS combinations
for cxxflags in (['/MD', '/EHsc'], []):
self.env.stash()
self.env['CXXFLAGS_%s' % var] += cxxflags
try:
try_link()
self.end_msg('ok: winning cxxflags combination: %s' % (self.env['CXXFLAGS_%s' % var]))
exc = None
break
except Errors.ConfigurationError as e:
self.env.revert()
exc = e
if exc is not None:
self.end_msg('Could not auto-detect boost linking flags combination, you may report it to boost.py author', ex=exc)
self.fatal('The configuration failed')
else:
self.end_msg('Boost linkage flags auto-detection not implemented (needed ?) for this toolchain')
self.fatal('The configuration failed')
else:
self.start_msg('Checking for boost linkage')
try:
try_link()
except Errors.ConfigurationError as e:
self.end_msg('Could not link against boost libraries using supplied options', 'YELLOW')
self.fatal('The configuration failed')
self.end_msg('ok')
@feature('cxx')
@after_method('apply_link')
def install_boost(self):
if install_boost.done or not Utils.is_win32 or not self.bld.cmd.startswith('install'):
return
install_boost.done = True
inst_to = getattr(self, 'install_path', '${BINDIR}')
for lib in self.env.LIB_BOOST:
try:
file = self.bld.find_file(self.env.cxxshlib_PATTERN % lib, self.env.LIBPATH_BOOST)
self.bld.install_files(inst_to, self.bld.root.find_node(file))
except:
continue
install_boost.done = False
@@ -0,0 +1,30 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
"""
Copyright (c) 2014-2016, Regents of the University of California,
Arizona Board of Regents,
Colorado State University,
University Pierre & Marie Curie, Sorbonne University,
Washington University in St. Louis,
Beijing Institute of Technology,
The University of Memphis.
This file is part of NFD (Named Data Networking Forwarding Daemon).
See AUTHORS.md for complete list of NFD authors and contributors.
NFD is free software: you can redistribute it and/or modify it under the terms
of the GNU General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
NFD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
NFD, e.g., in COPYING.md file. If not, see <http://www.gnu.org/licenses/>.
"""
from waflib.Configure import conf
def configure(conf):
pass
+22
View File
@@ -0,0 +1,22 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib import TaskGen
def options(opt):
opt.add_option('--with-coverage', action='store_true', default=False,
help='Add compiler flags to enable code coverage information')
def configure(conf):
if conf.options.with_coverage:
if not conf.options.debug:
conf.fatal('Code coverage flags require debug mode compilation (add --debug)')
conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'],
linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True)
@TaskGen.feature('cxx','cc')
@TaskGen.after('process_source')
def add_coverage(self):
if getattr(self, 'use', ''):
self.use += ' GCOV'
else:
self.use = 'GCOV'
@@ -0,0 +1,216 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib import Configure, Logs, Utils
def options(opt):
opt.add_option('--debug', '--with-debug', action='store_true', default=False,
help='Compile in debugging mode with minimal optimizations (-O0 or -Og)')
def configure(conf):
conf.start_msg('Checking C++ compiler version')
cxx = conf.env.CXX_NAME # generic name of the compiler
ccver = tuple(int(i) for i in conf.env.CC_VERSION)
ccverstr = '.'.join(conf.env.CC_VERSION)
errmsg = ''
warnmsg = ''
if cxx == 'gcc':
if ccver < (5, 3, 0):
errmsg = ('The version of gcc you are using is too old.\n'
'The minimum supported gcc version is 5.3.0.')
conf.flags = GccFlags()
elif cxx == 'clang':
if ccver < (3, 6, 0):
errmsg = ('The version of clang you are using is too old.\n'
'The minimum supported clang version is 3.6.0.')
conf.flags = ClangFlags()
else:
warnmsg = 'Note: %s compiler is unsupported' % cxx
conf.flags = CompilerFlags()
if errmsg:
conf.end_msg(ccverstr, color='RED')
conf.fatal(errmsg)
elif warnmsg:
conf.end_msg(ccverstr, color='YELLOW')
Logs.warn(warnmsg)
else:
conf.end_msg(ccverstr)
conf.areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0)
# General flags are always applied (e.g., selecting C++ language standard)
generalFlags = conf.flags.getGeneralFlags(conf)
conf.add_supported_cxxflags(generalFlags['CXXFLAGS'])
conf.add_supported_linkflags(generalFlags['LINKFLAGS'])
conf.env.DEFINES += generalFlags['DEFINES']
@Configure.conf
def check_compiler_flags(conf):
# Debug or optimized CXXFLAGS and LINKFLAGS are applied only if the
# corresponding environment variables are not set.
# DEFINES are always applied.
if conf.options.debug:
extraFlags = conf.flags.getDebugFlags(conf)
if conf.areCustomCxxflagsPresent:
missingFlags = [x for x in extraFlags['CXXFLAGS'] if x not in conf.env.CXXFLAGS]
if missingFlags:
Logs.warn('Selected debug mode, but CXXFLAGS is set to a custom value "%s"'
% ' '.join(conf.env.CXXFLAGS))
Logs.warn('Default flags "%s" will not be used' % ' '.join(missingFlags))
else:
extraFlags = conf.flags.getOptimizedFlags(conf)
if not conf.areCustomCxxflagsPresent:
conf.add_supported_cxxflags(extraFlags['CXXFLAGS'])
conf.add_supported_linkflags(extraFlags['LINKFLAGS'])
conf.env.DEFINES += extraFlags['DEFINES']
@Configure.conf
def add_supported_cxxflags(self, cxxflags):
"""
Check which cxxflags are supported by compiler and add them to env.CXXFLAGS variable
"""
if len(cxxflags) == 0:
return
self.start_msg('Checking supported CXXFLAGS')
supportedFlags = []
for flags in cxxflags:
flags = Utils.to_list(flags)
if self.check_cxx(cxxflags=['-Werror'] + flags, mandatory=False):
supportedFlags += flags
self.end_msg(' '.join(supportedFlags))
self.env.prepend_value('CXXFLAGS', supportedFlags)
@Configure.conf
def add_supported_linkflags(self, linkflags):
"""
Check which linkflags are supported by compiler and add them to env.LINKFLAGS variable
"""
if len(linkflags) == 0:
return
self.start_msg('Checking supported LINKFLAGS')
supportedFlags = []
for flags in linkflags:
flags = Utils.to_list(flags)
if self.check_cxx(linkflags=['-Werror'] + flags, mandatory=False):
supportedFlags += flags
self.end_msg(' '.join(supportedFlags))
self.env.prepend_value('LINKFLAGS', supportedFlags)
class CompilerFlags(object):
def getCompilerVersion(self, conf):
return tuple(int(i) for i in conf.env.CC_VERSION)
def getGeneralFlags(self, conf):
"""Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are always needed"""
return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': []}
def getDebugFlags(self, conf):
"""Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are needed only in debug mode"""
return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': ['_DEBUG']}
def getOptimizedFlags(self, conf):
"""Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are needed only in optimized mode"""
return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': ['NDEBUG']}
class GccBasicFlags(CompilerFlags):
"""
This class defines basic flags that work for both gcc and clang compilers
"""
def getGeneralFlags(self, conf):
flags = super(GccBasicFlags, self).getGeneralFlags(conf)
flags['CXXFLAGS'] += ['-std=c++14']
if Utils.unversioned_sys_platform() == 'linux':
flags['LINKFLAGS'] += ['-fuse-ld=gold']
elif Utils.unversioned_sys_platform() == 'freebsd':
flags['LINKFLAGS'] += ['-fuse-ld=lld']
return flags
def getDebugFlags(self, conf):
flags = super(GccBasicFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-O0',
'-Og', # gcc >= 4.8, clang >= 4.0
'-g3',
'-pedantic',
'-Wall',
'-Wextra',
'-Werror',
'-Wnon-virtual-dtor',
'-Wno-error=deprecated-declarations', # Bug #3795
'-Wno-error=maybe-uninitialized', # Bug #1615
'-Wno-unused-parameter',
]
flags['LINKFLAGS'] += ['-Wl,-O1']
return flags
def getOptimizedFlags(self, conf):
flags = super(GccBasicFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-O2',
'-g',
'-pedantic',
'-Wall',
'-Wextra',
'-Wnon-virtual-dtor',
'-Wno-unused-parameter',
]
flags['LINKFLAGS'] += ['-Wl,-O1']
return flags
class GccFlags(GccBasicFlags):
def getDebugFlags(self, conf):
flags = super(GccFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-fdiagnostics-color']
return flags
def getOptimizedFlags(self, conf):
flags = super(GccFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-fdiagnostics-color']
return flags
class ClangFlags(GccBasicFlags):
def getGeneralFlags(self, conf):
flags = super(ClangFlags, self).getGeneralFlags(conf)
if Utils.unversioned_sys_platform() == 'darwin' and self.getCompilerVersion(conf) >= (9, 0, 0):
# Bug #4296
flags['CXXFLAGS'] += [['-isystem', '/usr/local/include'], # for Homebrew
['-isystem', '/opt/local/include']] # for MacPorts
if Utils.unversioned_sys_platform() == 'freebsd':
flags['CXXFLAGS'] += [['-isystem', '/usr/local/include']] # Bug #4790
return flags
def getDebugFlags(self, conf):
flags = super(ClangFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-fcolor-diagnostics',
'-Wextra-semi',
'-Wundefined-func-template',
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
]
version = self.getCompilerVersion(conf)
if version < (3, 9, 0) or (Utils.unversioned_sys_platform() == 'darwin' and version < (8, 1, 0)):
flags['CXXFLAGS'] += ['-Wno-unknown-pragmas']
if version < (6, 0, 0):
flags['CXXFLAGS'] += ['-Wno-missing-braces'] # Bug #4721
return flags
def getOptimizedFlags(self, conf):
flags = super(ClangFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-fcolor-diagnostics',
'-Wextra-semi',
'-Wundefined-func-template',
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
]
version = self.getCompilerVersion(conf)
if version < (3, 9, 0) or (Utils.unversioned_sys_platform() == 'darwin' and version < (8, 1, 0)):
flags['CXXFLAGS'] += ['-Wno-unknown-pragmas']
if version < (6, 0, 0):
flags['CXXFLAGS'] += ['-Wno-missing-braces'] # Bug #4721
return flags
@@ -0,0 +1,28 @@
# encoding: utf-8
from waflib import Options, Logs
from waflib.Configure import conf
def addDependencyOptions(self, opt, name, extraHelp=''):
opt.add_option('--with-%s' % name, metavar='PATH',
help='Path to %s, e.g., /usr/local %s' % (name, extraHelp))
setattr(Options.OptionsContext, 'addDependencyOptions', addDependencyOptions)
@conf
def checkDependency(self, name, **kw):
root = kw.get('path', getattr(Options.options, 'with_%s' % name))
kw['msg'] = kw.get('msg', 'Checking for %s library' % name)
kw['uselib_store'] = kw.get('uselib_store', name.upper())
kw['define_name'] = kw.get('define_name', 'HAVE_%s' % kw['uselib_store'])
kw['mandatory'] = kw.get('mandatory', True)
if root:
isOk = self.check_cxx(includes='%s/include' % root,
libpath='%s/lib' % root,
**kw)
else:
isOk = self.check_cxx(**kw)
if isOk:
self.env[kw['define_name']] = True
+214
View File
@@ -0,0 +1,214 @@
#! /usr/bin/env python
# encoding: UTF-8
# Thomas Nagy 2008-2010 (ita)
"""
Doxygen support
Variables passed to bld():
* doxyfile -- the Doxyfile to use
When using this tool, the wscript will look like:
def options(opt):
opt.load('doxygen')
def configure(conf):
conf.load('doxygen')
# check conf.env.DOXYGEN, if it is mandatory
def build(bld):
if bld.env.DOXYGEN:
bld(features="doxygen", doxyfile='Doxyfile', ...)
def doxygen(bld):
if bld.env.DOXYGEN:
bld(features="doxygen", doxyfile='Doxyfile', ...)
"""
from fnmatch import fnmatchcase
import os, os.path, re, stat
from waflib import Task, Utils, Node, Logs, Errors, Build
from waflib.TaskGen import feature
DOXY_STR = '"${DOXYGEN}" - '
DOXY_FMTS = 'html latex man rft xml'.split()
DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
'''.split())
re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
re_nl = re.compile('\r*\n', re.M)
def parse_doxy(txt):
tbl = {}
txt = re_rl.sub('', txt)
lines = re_nl.split(txt)
for x in lines:
x = x.strip()
if not x or x.startswith('#') or x.find('=') < 0:
continue
if x.find('+=') >= 0:
tmp = x.split('+=')
key = tmp[0].strip()
if key in tbl:
tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
else:
tbl[key] = '+='.join(tmp[1:]).strip()
else:
tmp = x.split('=')
tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
return tbl
class doxygen(Task.Task):
vars = ['DOXYGEN', 'DOXYFLAGS']
color = 'BLUE'
def runnable_status(self):
'''
self.pars are populated in runnable_status - because this function is being
run *before* both self.pars "consumers" - scan() and run()
set output_dir (node) for the output
'''
for x in self.run_after:
if not x.hasrun:
return Task.ASK_LATER
if not getattr(self, 'pars', None):
txt = self.inputs[0].read()
self.pars = parse_doxy(txt)
if not self.pars.get('OUTPUT_DIRECTORY'):
self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath()
# Override with any parameters passed to the task generator
if getattr(self.generator, 'pars', None):
for k, v in self.generator.pars.items():
self.pars[k] = v
self.doxy_inputs = getattr(self, 'doxy_inputs', [])
if not self.pars.get('INPUT'):
self.doxy_inputs.append(self.inputs[0].parent)
else:
for i in self.pars.get('INPUT').split():
if os.path.isabs(i):
node = self.generator.bld.root.find_node(i)
else:
node = self.generator.path.find_node(i)
if not node:
self.generator.bld.fatal('Could not find the doxygen input %r' % i)
self.doxy_inputs.append(node)
if not getattr(self, 'output_dir', None):
bld = self.generator.bld
# First try to find an absolute path, then find or declare a relative path
self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
if not self.output_dir:
self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY'])
self.signature()
return Task.Task.runnable_status(self)
def scan(self):
exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
file_patterns = self.pars.get('FILE_PATTERNS','').split()
if not file_patterns:
file_patterns = DOXY_FILE_PATTERNS
if self.pars.get('RECURSIVE') == 'YES':
file_patterns = ["**/%s" % pattern for pattern in file_patterns]
nodes = []
names = []
for node in self.doxy_inputs:
if os.path.isdir(node.abspath()):
for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
nodes.append(m)
else:
nodes.append(node)
return (nodes, names)
def run(self):
dct = self.pars.copy()
dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs])
code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
code = code.encode() # for python 3
#fmt = DOXY_STR % (self.inputs[0].parent.abspath())
cmd = Utils.subst_vars(DOXY_STR, self.env)
env = self.env.env or None
proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath())
proc.communicate(code)
return proc.returncode
def post_run(self):
nodes = self.output_dir.ant_glob('**/*', quiet=True)
for x in nodes:
x.sig = Utils.h_file(x.abspath())
self.outputs += nodes
return Task.Task.post_run(self)
class tar(Task.Task):
"quick tar creation"
run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
color = 'RED'
after = ['doxygen']
def runnable_status(self):
for x in getattr(self, 'input_tasks', []):
if not x.hasrun:
return Task.ASK_LATER
if not getattr(self, 'tar_done_adding', None):
# execute this only once
self.tar_done_adding = True
for x in getattr(self, 'input_tasks', []):
self.set_inputs(x.outputs)
if not self.inputs:
return Task.SKIP_ME
return Task.Task.runnable_status(self)
def __str__(self):
tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs])
return '%s: %s\n' % (self.__class__.__name__, tgt_str)
@feature('doxygen')
def process_doxy(self):
if not getattr(self, 'doxyfile', None):
self.generator.bld.fatal('no doxyfile??')
node = self.doxyfile
if not isinstance(node, Node.Node):
node = self.path.find_resource(node)
if not node:
raise ValueError('doxygen file not found')
# the task instance
dsk = self.create_task('doxygen', node)
if getattr(self, 'doxy_tar', None):
tsk = self.create_task('tar')
tsk.input_tasks = [dsk]
tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
if self.doxy_tar.endswith('bz2'):
tsk.env['TAROPTS'] = ['cjf']
elif self.doxy_tar.endswith('gz'):
tsk.env['TAROPTS'] = ['czf']
else:
tsk.env['TAROPTS'] = ['cf']
def configure(conf):
'''
Check if doxygen and tar commands are present in the system
If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
variables will be set. Detection can be controlled by setting DOXYGEN and
TAR environmental variables.
'''
conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
conf.find_program('tar', var='TAR', mandatory=False)
# doxygen docs
from waflib.Build import BuildContext
class doxy(BuildContext):
cmd = "doxygen"
fun = "doxygen"
+148
View File
@@ -0,0 +1,148 @@
#! /usr/bin/env python
# encoding: utf-8
# Alexander Afanasyev (UCLA), 2014
"""
Enable precompiled C++ header support (currently only clang++ and g++ are supported)
To use this tool, wscript should look like:
def options(opt):
opt.load('pch')
# This will add `--with-pch` configure option.
# Unless --with-pch during configure stage specified, the precompiled header support is disabled
def configure(conf):
conf.load('pch')
# this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
# Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
def build(bld):
bld(features='cxx pch',
target='precompiled-headers',
name='precompiled-headers',
headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
# Other parameters to compile precompiled headers
# includes=...,
# export_includes=...,
# use=...,
# ...
# Exported parameters will be propagated even if precompiled headers are disabled
)
bld(
target='test',
features='cxx cxxprogram',
source='a.cpp b.cpp d.cpp main.cpp',
use='precompiled-headers',
)
# or
bld(
target='test',
features='pch cxx cxxprogram',
source='a.cpp b.cpp d.cpp main.cpp',
headers='a.h b.h c.h',
)
Note that precompiled header must have multiple inclusion guards. If the guards are missing, any benefit of precompiled header will be voided and compilation may fail in some cases.
"""
import os
from waflib import Task, TaskGen, Utils
from waflib.Tools import c_preproc, cxx
PCH_COMPILER_OPTIONS = {
'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
'g++': [['-include'], '.gch', ['-x', 'c++-header']],
}
def options(opt):
opt.add_option('--without-pch', action='store_false', default=True, dest='with_pch', help='''Try to use precompiled header to speed up compilation (only g++ and clang++)''')
def configure(conf):
if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
conf.env.WITH_PCH = True
flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
conf.env.CXXPCH_F = flags[0]
conf.env.CXXPCH_EXT = flags[1]
conf.env.CXXPCH_FLAGS = flags[2]
@TaskGen.feature('pch')
@TaskGen.before('process_source')
def apply_pch(self):
if not self.env.WITH_PCH:
return
if getattr(self.bld, 'pch_tasks', None) is None:
self.bld.pch_tasks = {}
if getattr(self, 'headers', None) is None:
return
self.headers = self.to_nodes(self.headers)
if getattr(self, 'name', None):
try:
task = self.bld.pch_tasks["%s.%s" % (self.name, self.idx)]
self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
except KeyError:
pass
out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
out = self.path.find_or_declare(out)
task = self.create_task('gchx', self.headers, out)
# target should be an absolute path of `out`, but without precompiled header extension
task.target = out.abspath()[:-len(out.suffix())]
self.pch_task = task
if getattr(self, 'name', None):
self.bld.pch_tasks["%s.%s" % (self.name, self.idx)] = task
@TaskGen.feature('cxx')
@TaskGen.after_method('process_source', 'propagate_uselib_vars')
def add_pch(self):
if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
return
pch = None
# find pch task, if any
if getattr(self, 'pch_task', None):
pch = self.pch_task
else:
for use in Utils.to_list(self.use):
try:
pch = self.bld.pch_tasks[use]
except KeyError:
pass
if pch:
for x in self.compiled_tasks:
x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
class gchx(Task.Task):
run_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${CXXPCH_FLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXXPCH_F:SRC} ${CXX_SRC_F}${SRC[0].abspath()} ${CXX_TGT_F}${TGT[0].abspath()} ${CPPFLAGS}'
scan = c_preproc.scan
color = 'BLUE'
ext_out=['.h']
def runnable_status(self):
try:
node_deps = self.generator.bld.node_deps[self.uid()]
except KeyError:
node_deps = []
ret = Task.Task.runnable_status(self)
if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
t = os.stat(self.outputs[0].abspath()).st_mtime
for n in self.inputs + node_deps:
if os.stat(n.abspath()).st_mtime > t:
return Task.RUN_ME
return ret
@@ -0,0 +1,22 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
def options(opt):
opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers',
help='Comma-separated list of compiler sanitizers to enable [default=none]')
def configure(conf):
for san in conf.options.sanitizers.split(','):
if not san:
continue
sanflag = '-fsanitize=%s' % san
conf.start_msg('Checking if compiler supports %s' % sanflag)
if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'],
linkflags=[sanflag], mandatory=False):
conf.end_msg('yes')
conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer'])
conf.env.append_unique('LINKFLAGS', [sanflag])
else:
conf.end_msg('no', color='RED')
conf.fatal('%s sanitizer is not supported by the current compiler' % san)
@@ -0,0 +1,79 @@
#!/usr/bin/env python
# encoding: utf-8
# inspired by code by Hans-Martin von Gaudecker, 2012
import os
from waflib import Node, Task, TaskGen, Errors, Logs, Build, Utils
class sphinx_build(Task.Task):
color = 'BLUE'
run_str = '${SPHINX_BUILD} -D ${VERSION} -D ${RELEASE} -q -b ${BUILDERNAME} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}'
def __str__(self):
env = self.env
src_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.inputs])
tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
if self.outputs: sep = ' -> '
else: sep = ''
return'%s [%s]: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),
self.env['BUILDERNAME'], src_str, sep, tgt_str)
@TaskGen.extension('.py', '.rst')
def sig_hook(self, node):
node.sig=Utils.h_file(node.abspath())
@TaskGen.feature("sphinx")
@TaskGen.before_method("process_source")
def apply_sphinx(self):
"""Set up the task generator with a Sphinx instance and create a task."""
inputs = []
for i in Utils.to_list(self.source):
if not isinstance(i, Node.Node):
node = self.path.find_node(node)
else:
node = i
if not node:
raise ValueError('[%s] file not found' % i)
inputs.append(node)
task = self.create_task('sphinx_build', inputs)
conf = self.path.find_node(self.config)
task.inputs.append(conf)
confdir = conf.parent.abspath()
buildername = getattr(self, 'builder', 'html')
srcdir = getattr(self, 'srcdir', confdir)
outdir = self.path.find_or_declare(getattr(self, 'outdir', buildername)).get_bld()
doctreedir = getattr(self, 'doctreedir', os.path.join(outdir.abspath(), '.doctrees'))
task.env['BUILDERNAME'] = buildername
task.env['SRCDIR'] = srcdir
task.env['DOCTREEDIR'] = doctreedir
task.env['OUTDIR'] = outdir.abspath()
task.env['VERSION'] = 'version=%s' % self.version
task.env['RELEASE'] = 'release=%s' % getattr(self, 'release', self.version)
import imp
confData = imp.load_source('sphinx_conf', conf.abspath())
if buildername == 'man':
for i in confData.man_pages:
target = outdir.find_or_declare('%s.%d' % (i[1], i[4]))
task.outputs.append(target)
if self.install_path:
self.bld.install_files('%s/man%d/' % (self.install_path, i[4]), target)
else:
task.outputs.append(outdir)
def configure(conf):
conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False)
# sphinx docs
from waflib.Build import BuildContext
class sphinx(BuildContext):
cmd = "sphinx"
fun = "sphinx"
@@ -0,0 +1,29 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
#
# Copyright (c) 2014-2019, Regents of the University of California
#
# GPL 3.0 license, see the COPYING.md file for more information
from waflib import Options
BOOST_ASIO_HAS_LOCAL_SOCKETS_CHECK = '''
#include <boost/asio/local/basic_endpoint.hpp>
#ifndef BOOST_ASIO_HAS_LOCAL_SOCKETS
#error "Unix sockets are not available on this platform"
#endif
'''
def addUnixOptions(self, opt):
opt.add_option('--force-unix-socket', action='store_true', default=False,
help='Forcefully enable Unix sockets support')
setattr(Options.OptionsContext, 'addUnixOptions', addUnixOptions)
def configure(conf):
def boost_asio_has_local_sockets():
return conf.check_cxx(msg='Checking if Unix sockets are supported',
fragment=BOOST_ASIO_HAS_LOCAL_SOCKETS_CHECK,
features='cxx', use='BOOST', mandatory=False)
conf.env.HAVE_UNIX_SOCKETS = conf.options.force_unix_socket or boost_asio_has_local_sockets()
conf.define_cond('HAVE_UNIX_SOCKETS', conf.env.HAVE_UNIX_SOCKETS)
@@ -0,0 +1,73 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib import Options, Logs, Errors, Configure
import re
def addWebsocketOptions(self, opt):
opt.add_option('--without-websocket', action='store_false', default=True,
dest='with_websocket', help='Disable WebSocket face support')
setattr(Options.OptionsContext, 'addWebsocketOptions', addWebsocketOptions)
@Configure.conf
def checkWebsocket(self, **kw):
if not self.options.with_websocket:
return
isMandatory = kw.get('mandatory', True)
self.start_msg('Checking for WebSocket++ includes')
try:
websocketDir = self.path.find_dir('websocketpp/websocketpp')
if not websocketDir:
raise Errors.WafError('Not found')
versionFile = websocketDir.find_node('version.hpp')
if not websocketDir:
raise Errors.WafError('WebSocket++ version file not found')
try:
txt = versionFile.read()
except (OSError, IOError):
raise Errors.WafError('Cannot read WebSocket++ version file')
version = [None, None, None]
# Looking for the following:
# static int const major_version = 0;
# static int const minor_version = 7;
# static int const patch_version = 0;
majorVersion = re.compile('^static int const major_version = (\\d+);$', re.M)
version[0] = majorVersion.search(txt)
minorVersion = re.compile('^static int const minor_version = (\\d+);$', re.M)
version[1] = minorVersion.search(txt)
patchVersion = re.compile('^static int const patch_version = (\\d+);$', re.M)
version[2] = patchVersion.search(txt)
if not version[0] or not version[1] or not version[2]:
raise Errors.WafError('Cannot detect WebSocket++ version')
self.env.WEBSOCKET_VERSION = [i.group(1) for i in version]
# todo: version checking, if necessary
self.end_msg('.'.join(self.env.WEBSOCKET_VERSION))
self.env.INCLUDES_WEBSOCKET = websocketDir.parent.abspath()
self.env.HAVE_WEBSOCKET = True
self.define('HAVE_WEBSOCKET', 1)
self.define('_WEBSOCKETPP_CPP11_STL_', 1)
except Errors.WafError as error:
if isMandatory:
self.end_msg(str(error), color='RED')
Logs.warn('If you are using NFD from the git repository, checkout the websocketpp submodule: ')
Logs.warn(' git submodule update --init')
Logs.warn('Otherwise, manually download and extract the websocketpp library:')
Logs.warn(' mkdir -p websocketpp')
Logs.warn(' curl -L https://github.com/cawka/websocketpp/archive/0.8.1-hotfix.tar.gz > websocketpp.tar.gz')
Logs.warn(' tar xf websocketpp.tar.gz -C websocketpp/ --strip 1')
Logs.warn('Alternatively, WebSocket support can be disabled with --without-websocket')
self.fatal('WebSocket++ not found')
else:
self.end_msg(str(error))
+1 -1
View File
@@ -38,7 +38,7 @@ def options(opt):
'coverage', 'pch', 'sanitizers', 'boost',
'dependency-checker', 'unix-socket', 'websocket',
'doxygen', 'sphinx_build'],
tooldir=['.waf-tools'])
tooldir=['waf-tools'])
nfdopt = opt.add_option_group('NFD Options')
+533
View File
@@ -0,0 +1,533 @@
#!/usr/bin/env python
# encoding: utf-8
#
# partially based on boost.py written by Gernot Vormayr
# written by Ruediger Sonderfeld <ruediger@c-plusplus.de>, 2008
# modified by Bjoern Michaelsen, 2008
# modified by Luca Fossati, 2008
# rewritten for waf 1.5.1, Thomas Nagy, 2008
# rewritten for waf 1.6.2, Sylvain Rouquette, 2011
'''
This is an extra tool, not bundled with the default waf binary.
To add the boost tool to the waf file:
$ ./waf-light --tools=compat15,boost
or, if you have waf >= 1.6.2
$ ./waf update --files=boost
When using this tool, the wscript will look like:
def options(opt):
opt.load('compiler_cxx boost')
def configure(conf):
conf.load('compiler_cxx boost')
conf.check_boost(lib='system filesystem')
def build(bld):
bld(source='main.cpp', target='app', use='BOOST')
Options are generated, in order to specify the location of boost includes/libraries.
The `check_boost` configuration function allows to specify the used boost libraries.
It can also provide default arguments to the --boost-mt command-line arguments.
Everything will be packaged together in a BOOST component that you can use.
When using MSVC, a lot of compilation flags need to match your BOOST build configuration:
- you may have to add /EHsc to your CXXFLAGS or define boost::throw_exception if BOOST_NO_EXCEPTIONS is defined.
Errors: C4530
- boost libraries will try to be smart and use the (pretty but often not useful) auto-linking feature of MSVC
So before calling `conf.check_boost` you might want to disabling by adding
conf.env.DEFINES_BOOST += ['BOOST_ALL_NO_LIB']
Errors:
- boost might also be compiled with /MT, which links the runtime statically.
If you have problems with redefined symbols,
self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
self.env['CXXFLAGS_%s' % var] += ['/MD', '/EHsc']
Passing `--boost-linkage_autodetect` might help ensuring having a correct linkage in some basic cases.
'''
import sys
import re
from waflib import Utils, Logs, Errors
from waflib.Configure import conf
from waflib.TaskGen import feature, after_method
BOOST_LIBS = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/sw/lib', '/lib']
BOOST_INCLUDES = ['/usr/include', '/usr/local/include', '/opt/local/include', '/sw/include']
BOOST_VERSION_FILE = 'boost/version.hpp'
BOOST_VERSION_CODE = '''
#include <iostream>
#include <boost/version.hpp>
int main() { std::cout << BOOST_LIB_VERSION << ":" << BOOST_VERSION << std::endl; }
'''
BOOST_ERROR_CODE = '''
#include <boost/system/error_code.hpp>
int main() { boost::system::error_code c; }
'''
PTHREAD_CODE = '''
#include <pthread.h>
static void* f(void*) { return 0; }
int main() {
pthread_t th;
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_create(&th, &attr, &f, 0);
pthread_join(th, 0);
pthread_cleanup_push(0, 0);
pthread_cleanup_pop(0);
pthread_attr_destroy(&attr);
}
'''
BOOST_THREAD_CODE = '''
#include <boost/thread.hpp>
int main() { boost::thread t; }
'''
BOOST_LOG_CODE = '''
#include <boost/log/trivial.hpp>
int main() { BOOST_LOG_TRIVIAL(info) << "boost_log is working"; }
'''
BOOST_LOG_SETUP_CODE = '''
#include <boost/log/trivial.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/common_attributes.hpp>
int main() {
using namespace boost::log;
add_common_attributes();
add_console_log(std::clog, keywords::format = "%Message%");
BOOST_LOG_TRIVIAL(info) << "boost_log_setup is working";
}
'''
# toolsets from {boost_dir}/tools/build/v2/tools/common.jam
PLATFORM = Utils.unversioned_sys_platform()
detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il'
detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang'
detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc'
BOOST_TOOLSETS = {
'borland': 'bcb',
'clang': detect_clang,
'como': 'como',
'cw': 'cw',
'darwin': 'xgcc',
'edg': 'edg',
'g++': detect_mingw,
'gcc': detect_mingw,
'icpc': detect_intel,
'intel': detect_intel,
'kcc': 'kcc',
'kylix': 'bck',
'mipspro': 'mp',
'mingw': 'mgw',
'msvc': 'vc',
'qcc': 'qcc',
'sun': 'sw',
'sunc++': 'sw',
'tru64cxx': 'tru',
'vacpp': 'xlc'
}
def options(opt):
opt = opt.add_option_group('Boost Options')
opt.add_option('--boost-includes', type='string',
default='', dest='boost_includes',
help='''path to the directory where the boost includes are,
e.g., /path/to/boost_1_55_0/stage/include''')
opt.add_option('--boost-libs', type='string',
default='', dest='boost_libs',
help='''path to the directory where the boost libs are,
e.g., path/to/boost_1_55_0/stage/lib''')
opt.add_option('--boost-mt', action='store_true',
default=False, dest='boost_mt',
help='select multi-threaded libraries')
opt.add_option('--boost-abi', type='string', default='', dest='boost_abi',
help='''select libraries with tags (gd for debug, static is automatically added),
see doc Boost, Getting Started, chapter 6.1''')
opt.add_option('--boost-linkage_autodetect', action='store_true', dest='boost_linkage_autodetect',
help="auto-detect boost linkage options (don't get used to it / might break other stuff)")
opt.add_option('--boost-toolset', type='string',
default='', dest='boost_toolset',
help='force a toolset e.g. msvc, vc90, \
gcc, mingw, mgw45 (default: auto)')
py_version = '%d%d' % (sys.version_info[0], sys.version_info[1])
opt.add_option('--boost-python', type='string',
default=py_version, dest='boost_python',
help='select the lib python with this version \
(default: %s)' % py_version)
@conf
def __boost_get_version_file(self, d):
if not d:
return None
dnode = self.root.find_dir(d)
if dnode:
return dnode.find_node(BOOST_VERSION_FILE)
return None
@conf
def boost_get_version(self, d):
"""silently retrieve the boost version number"""
node = self.__boost_get_version_file(d)
if node:
try:
txt = node.read()
except EnvironmentError:
Logs.error('Could not read the file %r' % node.abspath())
else:
re_but1 = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.+)"', re.M)
m1 = re_but1.search(txt)
re_but2 = re.compile('^#define\\s+BOOST_VERSION\\s+(\\d+)', re.M)
m2 = re_but2.search(txt)
if m1 and m2:
return (m1.group(1), m2.group(1))
return self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[d], execute=True, define_ret=True).split(':')
@conf
def boost_get_includes(self, *k, **kw):
includes = k and k[0] or kw.get('includes', None)
if includes and self.__boost_get_version_file(includes):
return includes
for d in self.environ.get('INCLUDE', '').split(';') + BOOST_INCLUDES:
if self.__boost_get_version_file(d):
return d
if includes:
self.end_msg('headers not found in %s' % includes, 'YELLOW')
self.fatal('The configuration failed')
else:
self.end_msg('headers not found, please provide a --boost-includes argument (see help)', 'YELLOW')
self.fatal('The configuration failed')
@conf
def boost_get_toolset(self, cc):
toolset = cc
if not cc:
build_platform = Utils.unversioned_sys_platform()
if build_platform in BOOST_TOOLSETS:
cc = build_platform
else:
cc = self.env.CXX_NAME
if cc in BOOST_TOOLSETS:
toolset = BOOST_TOOLSETS[cc]
return isinstance(toolset, str) and toolset or toolset(self.env)
@conf
def __boost_get_libs_path(self, *k, **kw):
''' return the lib path and all the files in it '''
if 'files' in kw:
return self.root.find_dir('.'), Utils.to_list(kw['files'])
libs = k and k[0] or kw.get('libs', None)
if libs:
path = self.root.find_dir(libs)
files = path.ant_glob('*boost_*')
if not libs or not files:
for d in self.environ.get('LIB', '').split(';') + BOOST_LIBS:
if not d:
continue
path = self.root.find_dir(d)
if path:
files = path.ant_glob('*boost_*')
if files:
break
path = self.root.find_dir(d + '64')
if path:
files = path.ant_glob('*boost_*')
if files:
break
if not path:
if libs:
self.end_msg('libs not found in %s' % libs, 'YELLOW')
self.fatal('The configuration failed')
else:
self.end_msg('libs not found, please provide a --boost-libs argument (see help)', 'YELLOW')
self.fatal('The configuration failed')
self.to_log('Found the boost path in %r with the libraries:' % path)
for x in files:
self.to_log(' %r' % x)
return path, files
@conf
def boost_get_libs(self, *k, **kw):
'''
return the lib path and the required libs
according to the parameters
'''
path, files = self.__boost_get_libs_path(**kw)
files = sorted(files, key=lambda f: (len(f.name), f.name), reverse=True)
toolset = self.boost_get_toolset(kw.get('toolset', ''))
toolset_pat = '(-%s[0-9]{0,3})' % toolset
version = '-%s' % self.env.BOOST_VERSION
def find_lib(re_lib, files):
for file in files:
if re_lib.search(file.name):
self.to_log('Found boost lib %s' % file)
return file
return None
def format_lib_name(name):
if name.startswith('lib') and self.env.CC_NAME != 'msvc':
name = name[3:]
return name[:name.rfind('.')]
def match_libs(lib_names, is_static):
libs = []
lib_names = Utils.to_list(lib_names)
if not lib_names:
return libs
t = []
if kw.get('mt', False):
t.append('-mt')
if kw.get('abi', None):
t.append('%s%s' % (is_static and '-s' or '-', kw['abi']))
elif is_static:
t.append('-s')
tags_pat = t and ''.join(t) or ''
ext = is_static and self.env.cxxstlib_PATTERN or self.env.cxxshlib_PATTERN
ext = ext.partition('%s')[2] # remove '%s' or 'lib%s' from PATTERN
for lib in lib_names:
if lib == 'python':
# for instance, with python='27',
# accepts '-py27', '-py2', '27' and '2'
# but will reject '-py3', '-py26', '26' and '3'
tags = '({0})?((-py{2})|(-py{1}(?=[^0-9]))|({2})|({1}(?=[^0-9]))|(?=[^0-9])(?!-py))'.format(tags_pat, kw['python'][0], kw['python'])
else:
tags = tags_pat
# Trying libraries, from most strict match to least one
for pattern in ['boost_%s%s%s%s%s$' % (lib, toolset_pat, tags, version, ext),
'boost_%s%s%s%s$' % (lib, tags, version, ext),
# Give up trying to find the right version
'boost_%s%s%s%s$' % (lib, toolset_pat, tags, ext),
'boost_%s%s%s$' % (lib, tags, ext),
'boost_%s%s$' % (lib, ext),
'boost_%s' % lib]:
self.to_log('Trying pattern %s' % pattern)
file = find_lib(re.compile(pattern), files)
if file:
libs.append(format_lib_name(file.name))
break
else:
self.end_msg('lib %s not found in %s' % (lib, path.abspath()), 'YELLOW')
self.fatal('The configuration failed')
return libs
return path.abspath(), match_libs(kw.get('lib', None), False), match_libs(kw.get('stlib', None), True)
@conf
def _check_pthread_flag(self, *k, **kw):
'''
Computes which flags should be added to CXXFLAGS and LINKFLAGS to compile in multi-threading mode
Yes, we *need* to put the -pthread thing in CPPFLAGS because with GCC3,
boost/thread.hpp will trigger a #error if -pthread isn't used:
boost/config/requires_threads.hpp:47:5: #error "Compiler threading support
is not turned on. Please set the correct command line options for
threading: -pthread (Linux), -pthreads (Solaris) or -mthreads (Mingw32)"
Based on _BOOST_PTHREAD_FLAG(): https://github.com/tsuna/boost.m4/blob/master/build-aux/boost.m4
'''
var = kw.get('uselib_store', 'BOOST')
self.start_msg('Checking the flags needed to use pthreads')
# The ordering *is* (sometimes) important. Some notes on the
# individual items follow:
# (none): in case threads are in libc; should be tried before -Kthread and
# other compiler flags to prevent continual compiler warnings
# -lpthreads: AIX (must check this before -lpthread)
# -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h)
# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able)
# -llthread: LinuxThreads port on FreeBSD (also preferred to -pthread)
# -pthread: GNU Linux/GCC (kernel threads), BSD/GCC (userland threads)
# -pthreads: Solaris/GCC
# -mthreads: MinGW32/GCC, Lynx/GCC
# -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it
# doesn't hurt to check since this sometimes defines pthreads too;
# also defines -D_REENTRANT)
# ... -mt is also the pthreads flag for HP/aCC
# -lpthread: GNU Linux, etc.
# --thread-safe: KAI C++
if Utils.unversioned_sys_platform() == 'sunos':
# On Solaris (at least, for some versions), libc contains stubbed
# (non-functional) versions of the pthreads routines, so link-based
# tests will erroneously succeed. (We need to link with -pthreads/-mt/
# -lpthread.) (The stubs are missing pthread_cleanup_push, or rather
# a function called by this macro, so we could check for that, but
# who knows whether they'll stub that too in a future libc.) So,
# we'll just look for -pthreads and -lpthread first:
boost_pthread_flags = ['-pthreads', '-lpthread', '-mt', '-pthread']
else:
boost_pthread_flags = ['', '-lpthreads', '-Kthread', '-kthread', '-llthread', '-pthread',
'-pthreads', '-mthreads', '-lpthread', '--thread-safe', '-mt']
for boost_pthread_flag in boost_pthread_flags:
try:
self.env.stash()
self.env['CXXFLAGS_%s' % var] += [boost_pthread_flag]
self.env['LINKFLAGS_%s' % var] += [boost_pthread_flag]
self.check_cxx(code=PTHREAD_CODE, msg=None, use=var, execute=False, quiet=True)
self.end_msg(boost_pthread_flag)
return
except self.errors.ConfigurationError:
self.env.revert()
self.end_msg('none')
@conf
def check_boost(self, *k, **kw):
"""
Initialize boost libraries to be used.
Keywords: you can pass the same parameters as with the command line (without "--boost-").
Note that the command line has the priority, and should preferably be used.
"""
if not self.env['CXX']:
self.fatal('load a c++ compiler first, conf.load("compiler_cxx")')
params = {
'lib': k and k[0] or kw.get('lib', None),
'stlib': kw.get('stlib', None)
}
for key, value in self.options.__dict__.items():
if not key.startswith('boost_'):
continue
key = key[len('boost_'):]
params[key] = value and value or kw.get(key, '')
var = kw.get('uselib_store', 'BOOST')
if not self.env.DONE_FIND_BOOST_COMMON:
self.find_program('dpkg-architecture', var='DPKG_ARCHITECTURE', mandatory=False)
if self.env.DPKG_ARCHITECTURE:
deb_host_multiarch = self.cmd_and_log([self.env.DPKG_ARCHITECTURE[0], '-qDEB_HOST_MULTIARCH'])
BOOST_LIBS.insert(0, '/usr/lib/%s' % deb_host_multiarch.strip())
self.start_msg('Checking boost includes')
self.env['INCLUDES_%s' % var] = inc = self.boost_get_includes(**params)
versions = self.boost_get_version(inc)
self.env.BOOST_VERSION = versions[0]
self.env.BOOST_VERSION_NUMBER = int(versions[1])
self.end_msg('%d.%d.%d' % (int(versions[1]) / 100000,
int(versions[1]) / 100 % 1000,
int(versions[1]) % 100))
if Logs.verbose:
Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var])
self.env.DONE_FIND_BOOST_COMMON = True
if not params['lib'] and not params['stlib']:
return
if 'static' in kw or 'static' in params:
Logs.warn('boost: static parameter is deprecated, use stlib instead.')
self.start_msg('Checking boost libs')
path, libs, stlibs = self.boost_get_libs(**params)
self.env['LIBPATH_%s' % var] = [path]
self.env['STLIBPATH_%s' % var] = [path]
self.env['LIB_%s' % var] = libs
self.env['STLIB_%s' % var] = stlibs
self.end_msg(' '.join(libs + stlibs))
if Logs.verbose:
Logs.pprint('CYAN', ' path : %s' % path)
Logs.pprint('CYAN', ' shared libs : %s' % libs)
Logs.pprint('CYAN', ' static libs : %s' % stlibs)
def has_shlib(lib):
return params['lib'] and lib in params['lib']
def has_stlib(lib):
return params['stlib'] and lib in params['stlib']
def has_lib(lib):
return has_shlib(lib) or has_stlib(lib)
if has_lib('thread'):
# not inside try_link to make check visible in the output
self._check_pthread_flag(k, kw)
def try_link():
if has_lib('system'):
self.check_cxx(fragment=BOOST_ERROR_CODE, use=var, execute=False)
if has_lib('thread'):
self.check_cxx(fragment=BOOST_THREAD_CODE, use=var, execute=False)
if has_lib('log') or has_lib('log_setup'):
if not has_lib('thread'):
self.env['DEFINES_%s' % var] += ['BOOST_LOG_NO_THREADS']
if has_shlib('log') or has_shlib('log_setup'):
self.env['DEFINES_%s' % var] += ['BOOST_LOG_DYN_LINK']
if has_lib('log_setup'):
self.check_cxx(fragment=BOOST_LOG_SETUP_CODE, use=var, execute=False)
else:
self.check_cxx(fragment=BOOST_LOG_CODE, use=var, execute=False)
if params.get('linkage_autodetect', False):
self.start_msg('Attempting to detect boost linkage flags')
toolset = self.boost_get_toolset(kw.get('toolset', ''))
if toolset in ('vc',):
# disable auto-linking feature, causing error LNK1181
# because the code wants to be linked against
self.env['DEFINES_%s' % var] += ['BOOST_ALL_NO_LIB']
# if no dlls are present, we guess the .lib files are not stubs
has_dlls = False
for x in Utils.listdir(path):
if x.endswith(self.env.cxxshlib_PATTERN % ''):
has_dlls = True
break
if not has_dlls:
self.env['STLIBPATH_%s' % var] = [path]
self.env['STLIB_%s' % var] = libs
del self.env['LIB_%s' % var]
del self.env['LIBPATH_%s' % var]
# we attempt to play with some known-to-work CXXFLAGS combinations
for cxxflags in (['/MD', '/EHsc'], []):
self.env.stash()
self.env['CXXFLAGS_%s' % var] += cxxflags
try:
try_link()
self.end_msg('ok: winning cxxflags combination: %s' % (self.env['CXXFLAGS_%s' % var]))
exc = None
break
except Errors.ConfigurationError as e:
self.env.revert()
exc = e
if exc is not None:
self.end_msg('Could not auto-detect boost linking flags combination, you may report it to boost.py author', ex=exc)
self.fatal('The configuration failed')
else:
self.end_msg('Boost linkage flags auto-detection not implemented (needed ?) for this toolchain')
self.fatal('The configuration failed')
else:
self.start_msg('Checking for boost linkage')
try:
try_link()
except Errors.ConfigurationError as e:
self.end_msg('Could not link against boost libraries using supplied options', 'YELLOW')
self.fatal('The configuration failed')
self.end_msg('ok')
@feature('cxx')
@after_method('apply_link')
def install_boost(self):
if install_boost.done or not Utils.is_win32 or not self.bld.cmd.startswith('install'):
return
install_boost.done = True
inst_to = getattr(self, 'install_path', '${BINDIR}')
for lib in self.env.LIB_BOOST:
try:
file = self.bld.find_file(self.env.cxxshlib_PATTERN % lib, self.env.LIBPATH_BOOST)
self.bld.install_files(inst_to, self.bld.root.find_node(file))
except:
continue
install_boost.done = False
@@ -0,0 +1,30 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib.Configure import conf
STD_TO_STRING = '''
#include <string>
int
main()
{
std::string s = std::to_string(0);
s = std::to_string(0l);
s = std::to_string(0ll);
s = std::to_string(0u);
s = std::to_string(0ul);
s = std::to_string(0ull);
s = std::to_string(0.0f);
s = std::to_string(0.0);
s = std::to_string(0.0l);
s.clear();
}
'''
@conf
def check_std_to_string(self):
if self.check_cxx(msg='Checking for std::to_string',
fragment=STD_TO_STRING, mandatory=False):
self.define('HAVE_STD_TO_STRING', 1)
def configure(conf):
conf.check_std_to_string()
@@ -0,0 +1,22 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib import TaskGen
def options(opt):
opt.add_option('--with-coverage', action='store_true', default=False,
help='Add compiler flags to enable code coverage information')
def configure(conf):
if conf.options.with_coverage:
if not conf.options.debug:
conf.fatal('Code coverage flags require debug mode compilation (add --debug)')
conf.check_cxx(cxxflags=['-fprofile-arcs', '-ftest-coverage', '-fPIC'],
linkflags=['-fprofile-arcs'], uselib_store='GCOV', mandatory=True)
@TaskGen.feature('cxx','cc')
@TaskGen.after('process_source')
def add_coverage(self):
if getattr(self, 'use', ''):
self.use += ' GCOV'
else:
self.use = 'GCOV'
@@ -0,0 +1,216 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib import Configure, Logs, Utils
def options(opt):
opt.add_option('--debug', '--with-debug', action='store_true', default=False,
help='Compile in debugging mode with minimal optimizations (-O0 or -Og)')
def configure(conf):
conf.start_msg('Checking C++ compiler version')
cxx = conf.env.CXX_NAME # generic name of the compiler
ccver = tuple(int(i) for i in conf.env.CC_VERSION)
ccverstr = '.'.join(conf.env.CC_VERSION)
errmsg = ''
warnmsg = ''
if cxx == 'gcc':
if ccver < (5, 3, 0):
errmsg = ('The version of gcc you are using is too old.\n'
'The minimum supported gcc version is 5.3.0.')
conf.flags = GccFlags()
elif cxx == 'clang':
if ccver < (3, 6, 0):
errmsg = ('The version of clang you are using is too old.\n'
'The minimum supported clang version is 3.6.0.')
conf.flags = ClangFlags()
else:
warnmsg = 'Note: %s compiler is unsupported' % cxx
conf.flags = CompilerFlags()
if errmsg:
conf.end_msg(ccverstr, color='RED')
conf.fatal(errmsg)
elif warnmsg:
conf.end_msg(ccverstr, color='YELLOW')
Logs.warn(warnmsg)
else:
conf.end_msg(ccverstr)
conf.areCustomCxxflagsPresent = (len(conf.env.CXXFLAGS) > 0)
# General flags are always applied (e.g., selecting C++ language standard)
generalFlags = conf.flags.getGeneralFlags(conf)
conf.add_supported_cxxflags(generalFlags['CXXFLAGS'])
conf.add_supported_linkflags(generalFlags['LINKFLAGS'])
conf.env.DEFINES += generalFlags['DEFINES']
@Configure.conf
def check_compiler_flags(conf):
# Debug or optimized CXXFLAGS and LINKFLAGS are applied only if the
# corresponding environment variables are not set.
# DEFINES are always applied.
if conf.options.debug:
extraFlags = conf.flags.getDebugFlags(conf)
if conf.areCustomCxxflagsPresent:
missingFlags = [x for x in extraFlags['CXXFLAGS'] if x not in conf.env.CXXFLAGS]
if missingFlags:
Logs.warn('Selected debug mode, but CXXFLAGS is set to a custom value "%s"'
% ' '.join(conf.env.CXXFLAGS))
Logs.warn('Default flags "%s" will not be used' % ' '.join(missingFlags))
else:
extraFlags = conf.flags.getOptimizedFlags(conf)
if not conf.areCustomCxxflagsPresent:
conf.add_supported_cxxflags(extraFlags['CXXFLAGS'])
conf.add_supported_linkflags(extraFlags['LINKFLAGS'])
conf.env.DEFINES += extraFlags['DEFINES']
@Configure.conf
def add_supported_cxxflags(self, cxxflags):
"""
Check which cxxflags are supported by compiler and add them to env.CXXFLAGS variable
"""
if len(cxxflags) == 0:
return
self.start_msg('Checking supported CXXFLAGS')
supportedFlags = []
for flags in cxxflags:
flags = Utils.to_list(flags)
if self.check_cxx(cxxflags=['-Werror'] + flags, mandatory=False):
supportedFlags += flags
self.end_msg(' '.join(supportedFlags))
self.env.prepend_value('CXXFLAGS', supportedFlags)
@Configure.conf
def add_supported_linkflags(self, linkflags):
"""
Check which linkflags are supported by compiler and add them to env.LINKFLAGS variable
"""
if len(linkflags) == 0:
return
self.start_msg('Checking supported LINKFLAGS')
supportedFlags = []
for flags in linkflags:
flags = Utils.to_list(flags)
if self.check_cxx(linkflags=['-Werror'] + flags, mandatory=False):
supportedFlags += flags
self.end_msg(' '.join(supportedFlags))
self.env.prepend_value('LINKFLAGS', supportedFlags)
class CompilerFlags(object):
def getCompilerVersion(self, conf):
return tuple(int(i) for i in conf.env.CC_VERSION)
def getGeneralFlags(self, conf):
"""Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are always needed"""
return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': []}
def getDebugFlags(self, conf):
"""Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are needed only in debug mode"""
return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': ['_DEBUG']}
def getOptimizedFlags(self, conf):
"""Get dict of CXXFLAGS, LINKFLAGS, and DEFINES that are needed only in optimized mode"""
return {'CXXFLAGS': [], 'LINKFLAGS': [], 'DEFINES': ['NDEBUG']}
class GccBasicFlags(CompilerFlags):
"""
This class defines basic flags that work for both gcc and clang compilers
"""
def getGeneralFlags(self, conf):
flags = super(GccBasicFlags, self).getGeneralFlags(conf)
flags['CXXFLAGS'] += ['-std=c++14']
if Utils.unversioned_sys_platform() == 'linux':
flags['LINKFLAGS'] += ['-fuse-ld=gold']
elif Utils.unversioned_sys_platform() == 'freebsd':
flags['LINKFLAGS'] += ['-fuse-ld=lld']
return flags
def getDebugFlags(self, conf):
flags = super(GccBasicFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-O0',
'-Og', # gcc >= 4.8, clang >= 4.0
'-g3',
'-pedantic',
'-Wall',
'-Wextra',
'-Werror',
'-Wnon-virtual-dtor',
'-Wno-error=deprecated-declarations', # Bug #3795
'-Wno-error=maybe-uninitialized', # Bug #1615
'-Wno-unused-parameter',
]
flags['LINKFLAGS'] += ['-Wl,-O1']
return flags
def getOptimizedFlags(self, conf):
flags = super(GccBasicFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-O2',
'-g',
'-pedantic',
'-Wall',
'-Wextra',
'-Wnon-virtual-dtor',
'-Wno-unused-parameter',
]
flags['LINKFLAGS'] += ['-Wl,-O1']
return flags
class GccFlags(GccBasicFlags):
def getDebugFlags(self, conf):
flags = super(GccFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-fdiagnostics-color']
return flags
def getOptimizedFlags(self, conf):
flags = super(GccFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-fdiagnostics-color']
return flags
class ClangFlags(GccBasicFlags):
def getGeneralFlags(self, conf):
flags = super(ClangFlags, self).getGeneralFlags(conf)
if Utils.unversioned_sys_platform() == 'darwin' and self.getCompilerVersion(conf) >= (9, 0, 0):
# Bug #4296
flags['CXXFLAGS'] += [['-isystem', '/usr/local/include'], # for Homebrew
['-isystem', '/opt/local/include']] # for MacPorts
if Utils.unversioned_sys_platform() == 'freebsd':
flags['CXXFLAGS'] += [['-isystem', '/usr/local/include']] # Bug #4790
return flags
def getDebugFlags(self, conf):
flags = super(ClangFlags, self).getDebugFlags(conf)
flags['CXXFLAGS'] += ['-fcolor-diagnostics',
'-Wextra-semi',
'-Wundefined-func-template',
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
]
version = self.getCompilerVersion(conf)
if version < (3, 9, 0) or (Utils.unversioned_sys_platform() == 'darwin' and version < (8, 1, 0)):
flags['CXXFLAGS'] += ['-Wno-unknown-pragmas']
if version < (6, 0, 0):
flags['CXXFLAGS'] += ['-Wno-missing-braces'] # Bug #4721
return flags
def getOptimizedFlags(self, conf):
flags = super(ClangFlags, self).getOptimizedFlags(conf)
flags['CXXFLAGS'] += ['-fcolor-diagnostics',
'-Wextra-semi',
'-Wundefined-func-template',
'-Wno-unused-local-typedef', # Bugs #2657 and #3209
]
version = self.getCompilerVersion(conf)
if version < (3, 9, 0) or (Utils.unversioned_sys_platform() == 'darwin' and version < (8, 1, 0)):
flags['CXXFLAGS'] += ['-Wno-unknown-pragmas']
if version < (6, 0, 0):
flags['CXXFLAGS'] += ['-Wno-missing-braces'] # Bug #4721
return flags
@@ -0,0 +1,214 @@
#! /usr/bin/env python
# encoding: UTF-8
# Thomas Nagy 2008-2010 (ita)
"""
Doxygen support
Variables passed to bld():
* doxyfile -- the Doxyfile to use
When using this tool, the wscript will look like:
def options(opt):
opt.load('doxygen')
def configure(conf):
conf.load('doxygen')
# check conf.env.DOXYGEN, if it is mandatory
def build(bld):
if bld.env.DOXYGEN:
bld(features="doxygen", doxyfile='Doxyfile', ...)
def doxygen(bld):
if bld.env.DOXYGEN:
bld(features="doxygen", doxyfile='Doxyfile', ...)
"""
from fnmatch import fnmatchcase
import os, os.path, re, stat
from waflib import Task, Utils, Node, Logs, Errors, Build
from waflib.TaskGen import feature
DOXY_STR = '"${DOXYGEN}" - '
DOXY_FMTS = 'html latex man rft xml'.split()
DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
'''.split())
re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
re_nl = re.compile('\r*\n', re.M)
def parse_doxy(txt):
tbl = {}
txt = re_rl.sub('', txt)
lines = re_nl.split(txt)
for x in lines:
x = x.strip()
if not x or x.startswith('#') or x.find('=') < 0:
continue
if x.find('+=') >= 0:
tmp = x.split('+=')
key = tmp[0].strip()
if key in tbl:
tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
else:
tbl[key] = '+='.join(tmp[1:]).strip()
else:
tmp = x.split('=')
tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
return tbl
class doxygen(Task.Task):
vars = ['DOXYGEN', 'DOXYFLAGS']
color = 'BLUE'
def runnable_status(self):
'''
self.pars are populated in runnable_status - because this function is being
run *before* both self.pars "consumers" - scan() and run()
set output_dir (node) for the output
'''
for x in self.run_after:
if not x.hasrun:
return Task.ASK_LATER
if not getattr(self, 'pars', None):
txt = self.inputs[0].read()
self.pars = parse_doxy(txt)
if not self.pars.get('OUTPUT_DIRECTORY'):
self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath()
# Override with any parameters passed to the task generator
if getattr(self.generator, 'pars', None):
for k, v in self.generator.pars.items():
self.pars[k] = v
self.doxy_inputs = getattr(self, 'doxy_inputs', [])
if not self.pars.get('INPUT'):
self.doxy_inputs.append(self.inputs[0].parent)
else:
for i in self.pars.get('INPUT').split():
if os.path.isabs(i):
node = self.generator.bld.root.find_node(i)
else:
node = self.generator.path.find_node(i)
if not node:
self.generator.bld.fatal('Could not find the doxygen input %r' % i)
self.doxy_inputs.append(node)
if not getattr(self, 'output_dir', None):
bld = self.generator.bld
# First try to find an absolute path, then find or declare a relative path
self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
if not self.output_dir:
self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY'])
self.signature()
return Task.Task.runnable_status(self)
def scan(self):
exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
file_patterns = self.pars.get('FILE_PATTERNS','').split()
if not file_patterns:
file_patterns = DOXY_FILE_PATTERNS
if self.pars.get('RECURSIVE') == 'YES':
file_patterns = ["**/%s" % pattern for pattern in file_patterns]
nodes = []
names = []
for node in self.doxy_inputs:
if os.path.isdir(node.abspath()):
for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
nodes.append(m)
else:
nodes.append(node)
return (nodes, names)
def run(self):
dct = self.pars.copy()
dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs])
code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
code = code.encode() # for python 3
#fmt = DOXY_STR % (self.inputs[0].parent.abspath())
cmd = Utils.subst_vars(DOXY_STR, self.env)
env = self.env.env or None
proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath())
proc.communicate(code)
return proc.returncode
def post_run(self):
nodes = self.output_dir.ant_glob('**/*', quiet=True)
for x in nodes:
x.sig = Utils.h_file(x.abspath())
self.outputs += nodes
return Task.Task.post_run(self)
class tar(Task.Task):
"quick tar creation"
run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
color = 'RED'
after = ['doxygen']
def runnable_status(self):
for x in getattr(self, 'input_tasks', []):
if not x.hasrun:
return Task.ASK_LATER
if not getattr(self, 'tar_done_adding', None):
# execute this only once
self.tar_done_adding = True
for x in getattr(self, 'input_tasks', []):
self.set_inputs(x.outputs)
if not self.inputs:
return Task.SKIP_ME
return Task.Task.runnable_status(self)
def __str__(self):
tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs])
return '%s: %s\n' % (self.__class__.__name__, tgt_str)
@feature('doxygen')
def process_doxy(self):
if not getattr(self, 'doxyfile', None):
self.generator.bld.fatal('no doxyfile??')
node = self.doxyfile
if not isinstance(node, Node.Node):
node = self.path.find_resource(node)
if not node:
raise ValueError('doxygen file not found')
# the task instance
dsk = self.create_task('doxygen', node)
if getattr(self, 'doxy_tar', None):
tsk = self.create_task('tar')
tsk.input_tasks = [dsk]
tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
if self.doxy_tar.endswith('bz2'):
tsk.env['TAROPTS'] = ['cjf']
elif self.doxy_tar.endswith('gz'):
tsk.env['TAROPTS'] = ['czf']
else:
tsk.env['TAROPTS'] = ['cf']
def configure(conf):
'''
Check if doxygen and tar commands are present in the system
If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
variables will be set. Detection can be controlled by setting DOXYGEN and
TAR environmental variables.
'''
conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
conf.find_program('tar', var='TAR', mandatory=False)
# doxygen docs
from waflib.Build import BuildContext
class doxy(BuildContext):
cmd = "doxygen"
fun = "doxygen"
@@ -0,0 +1,102 @@
#! /usr/bin/env python
# encoding: utf-8
# Yingdi Yu (UCLA) 2016
'''
When using this tool, the wscript will look like:
def options(opt):
opt.load('openssl')
def configure(conf):
conf.load('compiler_cxx openssl')
conf.check_openssl()
def build(bld):
bld(source='main.cpp', target='app', use='OPENSSL')
'''
import re
from waflib import Utils
from waflib.Configure import conf
OPENSSL_DIR_OSX = ['/usr/local', '/opt/local', '/usr/local/opt/openssl']
OPENSSL_DIR = ['/usr', '/usr/local', '/opt/local', '/sw']
def options(opt):
opt.add_option('--with-openssl', type='string', default=None, dest='openssl_dir',
help='directory where OpenSSL is installed, e.g., /usr/local')
@conf
def __openssl_get_version_file(self, dir):
try:
return self.root.find_dir(dir).find_node('include/openssl/opensslv.h')
except:
return None
@conf
def __openssl_find_root_and_version_file(self, *k, **kw):
root = k and k[0] or kw.get('path', self.options.openssl_dir)
file = self.__openssl_get_version_file(root)
if root and file:
return (root, file)
openssl_dir = []
if Utils.unversioned_sys_platform() == 'darwin':
openssl_dir = OPENSSL_DIR_OSX
else:
openssl_dir = OPENSSL_DIR
if not root:
for dir in openssl_dir:
file = self.__openssl_get_version_file(dir)
if file:
return (dir, file)
if root:
self.fatal('OpenSSL not found in %s' % root)
else:
self.fatal('OpenSSL not found, please provide a --with-openssl=PATH argument (see help)')
@conf
def check_openssl(self, *k, **kw):
self.start_msg('Checking for OpenSSL version')
(root, file) = self.__openssl_find_root_and_version_file(*k, **kw)
try:
txt = file.read()
re_version = re.compile('^#\\s*define\\s+OPENSSL_VERSION_NUMBER\\s+(.*)L', re.M)
version_number = re_version.search(txt)
re_version_text = re.compile('^#\\s*define\\s+OPENSSL_VERSION_TEXT\\s+(.*)', re.M)
version_text = re_version_text.search(txt)
if version_number and version_text:
version = version_number.group(1)
self.end_msg(version_text.group(1))
else:
self.fatal('OpenSSL version file is present, but is not recognizable')
except:
self.fatal('OpenSSL version file is not found or is not usable')
atleast_version = kw.get('atleast_version', 0)
if int(version, 16) < atleast_version:
self.fatal('The version of OpenSSL is too old\n'
'Please upgrade your distribution or manually install a newer version of OpenSSL')
if 'msg' not in kw:
kw['msg'] = 'Checking if OpenSSL library works'
if 'lib' not in kw:
kw['lib'] = ['ssl', 'crypto']
if 'uselib_store' not in kw:
kw['uselib_store'] = 'OPENSSL'
if 'define_name' not in kw:
kw['define_name'] = 'HAVE_%s' % kw['uselib_store']
if root:
kw['includes'] = '%s/include' % root
kw['libpath'] = '%s/lib' % root
self.check_cxx(**kw)
@@ -0,0 +1,42 @@
#! /usr/bin/env python
# encoding: utf-8
from waflib import Logs, Utils, TaskGen
from waflib.Configure import conf
@conf
def check_osx_framework_cxx(conf, fw_name, **kw):
conf.check_cxx(framework_name=fw_name,
uselib_store='OSX_' + fw_name.upper(),
fragment='#include <{0}/{0}.h>\nint main() {{}}'.format(fw_name),
**kw)
@conf
def check_osx_framework_mm(conf, fw_name, **kw):
conf.check_cxx(framework_name=fw_name,
uselib_store='OSX_' + fw_name.upper(),
fragment='#import <{0}/{0}.h>\nint main() {{}}'.format(fw_name),
compile_filename='test.mm',
**kw)
@conf
def check_osx_frameworks(conf, *k, **kw):
if Utils.unversioned_sys_platform() == 'darwin':
try:
conf.check_osx_framework_cxx('CoreFoundation', mandatory=True)
conf.check_osx_framework_cxx('Security', use='OSX_COREFOUNDATION', mandatory=True)
conf.check_osx_framework_cxx('SystemConfiguration', use='OSX_COREFOUNDATION', mandatory=True)
conf.check_osx_framework_mm('Foundation', mandatory=True)
conf.check_osx_framework_mm('CoreWLAN', use='OSX_FOUNDATION', mandatory=True)
conf.define('HAVE_OSX_FRAMEWORKS', 1)
conf.env['HAVE_OSX_FRAMEWORKS'] = True
except:
Logs.warn('Building on macOS, but one or more required frameworks are not functional.')
Logs.warn('Note that the frameworks are known to work only with the Apple clang compiler.')
@TaskGen.extension('.mm')
def m_hook(self, node):
'''Alias .mm files to be compiled the same as .cpp files, clang will do the right thing.'''
return self.create_compiled_task('cxx', node)
+148
View File
@@ -0,0 +1,148 @@
#! /usr/bin/env python
# encoding: utf-8
# Alexander Afanasyev (UCLA), 2014
"""
Enable precompiled C++ header support (currently only clang++ and g++ are supported)
To use this tool, wscript should look like:
def options(opt):
opt.load('pch')
# This will add `--with-pch` configure option.
# Unless --with-pch during configure stage specified, the precompiled header support is disabled
def configure(conf):
conf.load('pch')
# this will set conf.env.WITH_PCH if --with-pch is specified and the supported compiler is used
# Unless conf.env.WITH_PCH is set, the precompiled header support is disabled
def build(bld):
bld(features='cxx pch',
target='precompiled-headers',
name='precompiled-headers',
headers='a.h b.h c.h', # headers to pre-compile into `precompiled-headers`
# Other parameters to compile precompiled headers
# includes=...,
# export_includes=...,
# use=...,
# ...
# Exported parameters will be propagated even if precompiled headers are disabled
)
bld(
target='test',
features='cxx cxxprogram',
source='a.cpp b.cpp d.cpp main.cpp',
use='precompiled-headers',
)
# or
bld(
target='test',
features='pch cxx cxxprogram',
source='a.cpp b.cpp d.cpp main.cpp',
headers='a.h b.h c.h',
)
Note that precompiled header must have multiple inclusion guards. If the guards are missing, any benefit of precompiled header will be voided and compilation may fail in some cases.
"""
import os
from waflib import Task, TaskGen, Utils
from waflib.Tools import c_preproc, cxx
PCH_COMPILER_OPTIONS = {
'clang++': [['-include'], '.pch', ['-x', 'c++-header']],
'g++': [['-include'], '.gch', ['-x', 'c++-header']],
}
def options(opt):
opt.add_option('--without-pch', action='store_false', default=True, dest='with_pch', help='''Try to use precompiled header to speed up compilation (only g++ and clang++)''')
def configure(conf):
if (conf.options.with_pch and conf.env['COMPILER_CXX'] in PCH_COMPILER_OPTIONS.keys()):
conf.env.WITH_PCH = True
flags = PCH_COMPILER_OPTIONS[conf.env['COMPILER_CXX']]
conf.env.CXXPCH_F = flags[0]
conf.env.CXXPCH_EXT = flags[1]
conf.env.CXXPCH_FLAGS = flags[2]
@TaskGen.feature('pch')
@TaskGen.before('process_source')
def apply_pch(self):
if not self.env.WITH_PCH:
return
if getattr(self.bld, 'pch_tasks', None) is None:
self.bld.pch_tasks = {}
if getattr(self, 'headers', None) is None:
return
self.headers = self.to_nodes(self.headers)
if getattr(self, 'name', None):
try:
task = self.bld.pch_tasks["%s.%s" % (self.name, self.idx)]
self.bld.fatal("Duplicated 'pch' task with name %r" % "%s.%s" % (self.name, self.idx))
except KeyError:
pass
out = '%s.%d%s' % (self.target, self.idx, self.env['CXXPCH_EXT'])
out = self.path.find_or_declare(out)
task = self.create_task('gchx', self.headers, out)
# target should be an absolute path of `out`, but without precompiled header extension
task.target = out.abspath()[:-len(out.suffix())]
self.pch_task = task
if getattr(self, 'name', None):
self.bld.pch_tasks["%s.%s" % (self.name, self.idx)] = task
@TaskGen.feature('cxx')
@TaskGen.after_method('process_source', 'propagate_uselib_vars')
def add_pch(self):
if not (self.env['WITH_PCH'] and getattr(self, 'use', None) and getattr(self, 'compiled_tasks', None) and getattr(self.bld, 'pch_tasks', None)):
return
pch = None
# find pch task, if any
if getattr(self, 'pch_task', None):
pch = self.pch_task
else:
for use in Utils.to_list(self.use):
try:
pch = self.bld.pch_tasks[use]
except KeyError:
pass
if pch:
for x in self.compiled_tasks:
x.env.append_value('CXXFLAGS', self.env['CXXPCH_F'] + [pch.target])
class gchx(Task.Task):
run_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${CXXPCH_FLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXXPCH_F:SRC} ${CXX_SRC_F}${SRC[0].abspath()} ${CXX_TGT_F}${TGT[0].abspath()} ${CPPFLAGS}'
scan = c_preproc.scan
color = 'BLUE'
ext_out=['.h']
def runnable_status(self):
try:
node_deps = self.generator.bld.node_deps[self.uid()]
except KeyError:
node_deps = []
ret = Task.Task.runnable_status(self)
if ret == Task.SKIP_ME and self.env.CXX_NAME == 'clang':
t = os.stat(self.outputs[0].abspath()).st_mtime
for n in self.inputs + node_deps:
if os.stat(n.abspath()).st_mtime > t:
return Task.RUN_ME
return ret
@@ -0,0 +1,22 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
def options(opt):
opt.add_option('--with-sanitizer', action='store', default='', dest='sanitizers',
help='Comma-separated list of compiler sanitizers to enable [default=none]')
def configure(conf):
for san in conf.options.sanitizers.split(','):
if not san:
continue
sanflag = '-fsanitize=%s' % san
conf.start_msg('Checking if compiler supports %s' % sanflag)
if conf.check_cxx(cxxflags=['-Werror', sanflag, '-fno-omit-frame-pointer'],
linkflags=[sanflag], mandatory=False):
conf.end_msg('yes')
conf.env.append_unique('CXXFLAGS', [sanflag, '-fno-omit-frame-pointer'])
conf.env.append_unique('LINKFLAGS', [sanflag])
else:
conf.end_msg('no', color='RED')
conf.fatal('%s sanitizer is not supported by the current compiler' % san)
@@ -0,0 +1,79 @@
#!/usr/bin/env python
# encoding: utf-8
# inspired by code by Hans-Martin von Gaudecker, 2012
import os
from waflib import Node, Task, TaskGen, Errors, Logs, Build, Utils
class sphinx_build(Task.Task):
color = 'BLUE'
run_str = '${SPHINX_BUILD} -D ${VERSION} -D ${RELEASE} -q -b ${BUILDERNAME} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}'
def __str__(self):
env = self.env
src_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.inputs])
tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
if self.outputs: sep = ' -> '
else: sep = ''
return'%s [%s]: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),
self.env['BUILDERNAME'], src_str, sep, tgt_str)
@TaskGen.extension('.py', '.rst')
def sig_hook(self, node):
node.sig=Utils.h_file(node.abspath())
@TaskGen.feature("sphinx")
@TaskGen.before_method("process_source")
def apply_sphinx(self):
"""Set up the task generator with a Sphinx instance and create a task."""
inputs = []
for i in Utils.to_list(self.source):
if not isinstance(i, Node.Node):
node = self.path.find_node(node)
else:
node = i
if not node:
raise ValueError('[%s] file not found' % i)
inputs.append(node)
task = self.create_task('sphinx_build', inputs)
conf = self.path.find_node(self.config)
task.inputs.append(conf)
confdir = conf.parent.abspath()
buildername = getattr(self, 'builder', 'html')
srcdir = getattr(self, 'srcdir', confdir)
outdir = self.path.find_or_declare(getattr(self, 'outdir', buildername)).get_bld()
doctreedir = getattr(self, 'doctreedir', os.path.join(outdir.abspath(), '.doctrees'))
task.env['BUILDERNAME'] = buildername
task.env['SRCDIR'] = srcdir
task.env['DOCTREEDIR'] = doctreedir
task.env['OUTDIR'] = outdir.abspath()
task.env['VERSION'] = 'version=%s' % self.version
task.env['RELEASE'] = 'release=%s' % getattr(self, 'release', self.version)
import imp
confData = imp.load_source('sphinx_conf', conf.abspath())
if buildername == 'man':
for i in confData.man_pages:
target = outdir.find_or_declare('%s.%d' % (i[1], i[4]))
task.outputs.append(target)
if self.install_path:
self.bld.install_files('%s/man%d/' % (self.install_path, i[4]), target)
else:
task.outputs.append(outdir)
def configure(conf):
conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False)
# sphinx docs
from waflib.Build import BuildContext
class sphinx(BuildContext):
cmd = "sphinx"
fun = "sphinx"
@@ -0,0 +1,37 @@
#! /usr/bin/env python
# encoding: utf-8
from waflib.Configure import conf
def options(opt):
opt.add_option('--with-sqlite3', type='string', default=None, dest='sqlite3_dir',
help='directory where SQLite3 is installed, e.g., /usr/local')
@conf
def check_sqlite3(self, *k, **kw):
root = k and k[0] or kw.get('path', self.options.sqlite3_dir)
mandatory = kw.get('mandatory', True)
var = kw.get('uselib_store', 'SQLITE3')
if root:
self.check_cxx(lib='sqlite3',
msg='Checking for SQLite3 library',
define_name='HAVE_%s' % var,
uselib_store=var,
mandatory=mandatory,
includes='%s/include' % root,
libpath='%s/lib' % root)
else:
try:
self.check_cfg(package='sqlite3',
args=['--cflags', '--libs'],
global_define=True,
define_name='HAVE_%s' % var,
uselib_store='SQLITE3',
mandatory=True)
except:
self.check_cxx(lib='sqlite3',
msg='Checking for SQLite3 library',
define_name='HAVE_%s' % var,
uselib_store=var,
mandatory=mandatory)
+1 -1
View File
@@ -15,7 +15,7 @@ def options(opt):
'coverage', 'pch', 'sanitizers', 'osx-frameworks',
'boost', 'openssl', 'sqlite3',
'doxygen', 'sphinx_build'],
tooldir=['.waf-tools'])
tooldir=['waf-tools'])
opt = opt.add_option_group('ndn-cxx Options')
@@ -0,0 +1,27 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib.Configure import conf
OVERRIDE = '''
class Base
{
virtual void
f(int a);
};
class Derived : public Base
{
virtual void
f(int a) override;
};
'''
@conf
def check_override(self):
if self.check_cxx(msg='Checking for override specifier',
fragment=OVERRIDE,
features='cxx', mandatory=False):
self.define('HAVE_CXX_OVERRIDE', 1)
def configure(conf):
conf.check_override()
@@ -0,0 +1,28 @@
# encoding: utf-8
from waflib import Options, Logs
from waflib.Configure import conf
def addDependencyOptions(self, opt, name, extraHelp=''):
opt.add_option('--with-%s' % name, type='string', default=None,
dest='with_%s' % name,
help='Path to %s, e.g., /usr/local %s' % (name, extraHelp))
setattr(Options.OptionsContext, "addDependencyOptions", addDependencyOptions)
@conf
def checkDependency(self, name, **kw):
root = kw.get('path', getattr(Options.options, 'with_%s' % name))
kw['msg'] = kw.get('msg', 'Checking for %s library' % name)
kw['uselib_store'] = kw.get('uselib_store', name.upper())
kw['define_name'] = kw.get('define_name', 'HAVE_%s' % kw['uselib_store'])
kw['mandatory'] = kw.get('mandatory', True)
if root:
isOk = self.check_cxx(includes="%s/include" % root,
libpath="%s/lib" % root,
**kw)
else:
isOk = self.check_cxx(**kw)
if isOk:
self.env[kw['define_name']] = True
+214
View File
@@ -0,0 +1,214 @@
#! /usr/bin/env python
# encoding: UTF-8
# Thomas Nagy 2008-2010 (ita)
"""
Doxygen support
Variables passed to bld():
* doxyfile -- the Doxyfile to use
When using this tool, the wscript will look like:
def options(opt):
opt.load('doxygen')
def configure(conf):
conf.load('doxygen')
# check conf.env.DOXYGEN, if it is mandatory
def build(bld):
if bld.env.DOXYGEN:
bld(features="doxygen", doxyfile='Doxyfile', ...)
def doxygen(bld):
if bld.env.DOXYGEN:
bld(features="doxygen", doxyfile='Doxyfile', ...)
"""
from fnmatch import fnmatchcase
import os, os.path, re, stat
from waflib import Task, Utils, Node, Logs, Errors, Build
from waflib.TaskGen import feature
DOXY_STR = '"${DOXYGEN}" - '
DOXY_FMTS = 'html latex man rft xml'.split()
DOXY_FILE_PATTERNS = '*.' + ' *.'.join('''
c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3
inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx
'''.split())
re_rl = re.compile('\\\\\r*\n', re.MULTILINE)
re_nl = re.compile('\r*\n', re.M)
def parse_doxy(txt):
tbl = {}
txt = re_rl.sub('', txt)
lines = re_nl.split(txt)
for x in lines:
x = x.strip()
if not x or x.startswith('#') or x.find('=') < 0:
continue
if x.find('+=') >= 0:
tmp = x.split('+=')
key = tmp[0].strip()
if key in tbl:
tbl[key] += ' ' + '+='.join(tmp[1:]).strip()
else:
tbl[key] = '+='.join(tmp[1:]).strip()
else:
tmp = x.split('=')
tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip()
return tbl
class doxygen(Task.Task):
vars = ['DOXYGEN', 'DOXYFLAGS']
color = 'BLUE'
def runnable_status(self):
'''
self.pars are populated in runnable_status - because this function is being
run *before* both self.pars "consumers" - scan() and run()
set output_dir (node) for the output
'''
for x in self.run_after:
if not x.hasrun:
return Task.ASK_LATER
if not getattr(self, 'pars', None):
txt = self.inputs[0].read()
self.pars = parse_doxy(txt)
if not self.pars.get('OUTPUT_DIRECTORY'):
self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath()
# Override with any parameters passed to the task generator
if getattr(self.generator, 'pars', None):
for k, v in self.generator.pars.iteritems():
self.pars[k] = v
self.doxy_inputs = getattr(self, 'doxy_inputs', [])
if not self.pars.get('INPUT'):
self.doxy_inputs.append(self.inputs[0].parent)
else:
for i in self.pars.get('INPUT').split():
if os.path.isabs(i):
node = self.generator.bld.root.find_node(i)
else:
node = self.generator.path.find_node(i)
if not node:
self.generator.bld.fatal('Could not find the doxygen input %r' % i)
self.doxy_inputs.append(node)
if not getattr(self, 'output_dir', None):
bld = self.generator.bld
# First try to find an absolute path, then find or declare a relative path
self.output_dir = bld.root.find_dir(self.pars['OUTPUT_DIRECTORY'])
if not self.output_dir:
self.output_dir = bld.path.find_or_declare(self.pars['OUTPUT_DIRECTORY'])
self.signature()
return Task.Task.runnable_status(self)
def scan(self):
exclude_patterns = self.pars.get('EXCLUDE_PATTERNS','').split()
file_patterns = self.pars.get('FILE_PATTERNS','').split()
if not file_patterns:
file_patterns = DOXY_FILE_PATTERNS
if self.pars.get('RECURSIVE') == 'YES':
file_patterns = ["**/%s" % pattern for pattern in file_patterns]
nodes = []
names = []
for node in self.doxy_inputs:
if os.path.isdir(node.abspath()):
for m in node.ant_glob(incl=file_patterns, excl=exclude_patterns):
nodes.append(m)
else:
nodes.append(node)
return (nodes, names)
def run(self):
dct = self.pars.copy()
dct['INPUT'] = ' '.join(['"%s"' % x.abspath() for x in self.doxy_inputs])
code = '\n'.join(['%s = %s' % (x, dct[x]) for x in self.pars])
code = code.encode() # for python 3
#fmt = DOXY_STR % (self.inputs[0].parent.abspath())
cmd = Utils.subst_vars(DOXY_STR, self.env)
env = self.env.env or None
proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE, env=env, cwd=self.generator.bld.path.get_bld().abspath())
proc.communicate(code)
return proc.returncode
def post_run(self):
nodes = self.output_dir.ant_glob('**/*', quiet=True)
for x in nodes:
x.sig = Utils.h_file(x.abspath())
self.outputs += nodes
return Task.Task.post_run(self)
class tar(Task.Task):
"quick tar creation"
run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}'
color = 'RED'
after = ['doxygen']
def runnable_status(self):
for x in getattr(self, 'input_tasks', []):
if not x.hasrun:
return Task.ASK_LATER
if not getattr(self, 'tar_done_adding', None):
# execute this only once
self.tar_done_adding = True
for x in getattr(self, 'input_tasks', []):
self.set_inputs(x.outputs)
if not self.inputs:
return Task.SKIP_ME
return Task.Task.runnable_status(self)
def __str__(self):
tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs])
return '%s: %s\n' % (self.__class__.__name__, tgt_str)
@feature('doxygen')
def process_doxy(self):
if not getattr(self, 'doxyfile', None):
self.generator.bld.fatal('no doxyfile??')
node = self.doxyfile
if not isinstance(node, Node.Node):
node = self.path.find_resource(node)
if not node:
raise ValueError('doxygen file not found')
# the task instance
dsk = self.create_task('doxygen', node)
if getattr(self, 'doxy_tar', None):
tsk = self.create_task('tar')
tsk.input_tasks = [dsk]
tsk.set_outputs(self.path.find_or_declare(self.doxy_tar))
if self.doxy_tar.endswith('bz2'):
tsk.env['TAROPTS'] = ['cjf']
elif self.doxy_tar.endswith('gz'):
tsk.env['TAROPTS'] = ['czf']
else:
tsk.env['TAROPTS'] = ['cf']
def configure(conf):
'''
Check if doxygen and tar commands are present in the system
If the commands are present, then conf.env.DOXYGEN and conf.env.TAR
variables will be set. Detection can be controlled by setting DOXYGEN and
TAR environmental variables.
'''
conf.find_program('doxygen', var='DOXYGEN', mandatory=False)
conf.find_program('tar', var='TAR', mandatory=False)
# doxygen docs
from waflib.Build import BuildContext
class doxy(BuildContext):
cmd = "doxygen"
fun = "doxygen"
+79
View File
@@ -0,0 +1,79 @@
#!/usr/bin/env python
# encoding: utf-8
# inspired by code by Hans-Martin von Gaudecker, 2012
import os
from waflib import Node, Task, TaskGen, Errors, Logs, Build, Utils
class sphinx_build(Task.Task):
color = 'BLUE'
run_str = '${SPHINX_BUILD} -D ${VERSION} -D ${RELEASE} -q -b ${BUILDERNAME} -d ${DOCTREEDIR} ${SRCDIR} ${OUTDIR}'
def __str__(self):
env = self.env
src_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.inputs])
tgt_str = ' '.join([a.path_from(a.ctx.launch_node()) for a in self.outputs])
if self.outputs: sep = ' -> '
else: sep = ''
return'%s [%s]: %s%s%s\n'%(self.__class__.__name__.replace('_task',''),
self.env['BUILDERNAME'], src_str, sep, tgt_str)
@TaskGen.extension('.py', '.rst')
def sig_hook(self, node):
node.sig=Utils.h_file(node.abspath())
@TaskGen.feature("sphinx")
@TaskGen.before_method("process_source")
def apply_sphinx(self):
"""Set up the task generator with a Sphinx instance and create a task."""
inputs = []
for i in Utils.to_list(self.source):
if not isinstance(i, Node.Node):
node = self.path.find_node(node)
else:
node = i
if not node:
raise ValueError('[%s] file not found' % i)
inputs.append(node)
task = self.create_task('sphinx_build', inputs)
conf = self.path.find_node(self.config)
task.inputs.append(conf)
confdir = conf.parent.abspath()
buildername = getattr(self, "builder", "html")
srcdir = getattr(self, "srcdir", confdir)
outdir = self.path.find_or_declare(getattr(self, "outdir", buildername)).get_bld()
doctreedir = getattr(self, "doctreedir", os.path.join(outdir.abspath(), ".doctrees"))
task.env['BUILDERNAME'] = buildername
task.env['SRCDIR'] = srcdir
task.env['DOCTREEDIR'] = doctreedir
task.env['OUTDIR'] = outdir.abspath()
task.env['VERSION'] = "version=%s" % self.VERSION
task.env['RELEASE'] = "release=%s" % self.VERSION
import imp
confData = imp.load_source('sphinx_conf', conf.abspath())
if buildername == "man":
for i in confData.man_pages:
target = outdir.find_or_declare('%s.%d' % (i[1], i[4]))
task.outputs.append(target)
if self.install_path:
self.bld.install_files("%s/man%d/" % (self.install_path, i[4]), target)
else:
task.outputs.append(outdir)
def configure(conf):
conf.find_program('sphinx-build', var='SPHINX_BUILD', mandatory=False)
# sphinx docs
from waflib.Build import BuildContext
class sphinx(BuildContext):
cmd = "sphinx"
fun = "sphinx"
+30
View File
@@ -0,0 +1,30 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
#
# Copyright (c) 2014, Regents of the University of California
#
# GPL 3.0 license, see the COPYING.md file for more information
from waflib import Configure
IS_DEFAULT_CONSTRUCTIBLE_CHECK = '''
#include <type_traits>
static_assert(std::is_default_constructible<int>::value, "");
'''
IS_MOVE_CONSTRUCTIBLE_CHECK = '''
#include <type_traits>
static_assert(std::is_move_constructible<int>::value, "");
'''
def configure(conf):
if conf.check_cxx(msg='Checking for std::is_default_constructible',
fragment=IS_DEFAULT_CONSTRUCTIBLE_CHECK,
features='cxx', mandatory=False):
conf.define('HAVE_IS_DEFAULT_CONSTRUCTIBLE', 1)
conf.env['HAVE_IS_DEFAULT_CONSTRUCTIBLE'] = True
if conf.check_cxx(msg='Checking for std::is_move_constructible',
fragment=IS_MOVE_CONSTRUCTIBLE_CHECK,
features='cxx', mandatory=False):
conf.define('HAVE_IS_MOVE_CONSTRUCTIBLE', 1)
conf.env['HAVE_IS_MOVE_CONSTRUCTIBLE'] = True
+60
View File
@@ -0,0 +1,60 @@
# -*- Mode: python; py-indent-offset: 4; indent-tabs-mode: nil; coding: utf-8; -*-
from waflib.Configure import conf
from waflib import Utils
import os
def splitVersion(version):
base = version.split('-')[0]
split = [v for v in base.split('.')]
return base, version, split
@conf
def getVersion(conf, submodule, **kw):
tagPrefix = kw.get('tag', '%s-' % submodule)
baseVersion = kw.get('base_version', '0.0.0')
submodule = conf.path.find_node(submodule)
gitVersion = baseVersion
didGetVersion = False
try:
cmd = ['git', 'describe', '--always', '--match', '%s*' % tagPrefix]
p = Utils.subprocess.Popen(cmd, stdout=Utils.subprocess.PIPE,
cwd=submodule.abspath(),
stderr=None, stdin=None)
out = str(p.communicate()[0].strip())
didGetVersion = (p.returncode == 0 and out != "")
if didGetVersion:
if out.startswith(tagPrefix):
gitVersion = out[len(tagPrefix):]
else:
gitVersion = "%s-commit-%s" % (baseVersion, out)
except OSError:
pass
versionFile = submodule.find_node('VERSION')
if not didGetVersion and versionFile is not None:
try:
return splitVersion(versionFile.read())
except (OSError, IOError):
pass
# version was obtained from git, update VERSION file if necessary
if versionFile is not None:
try:
version = versionFile.read()
versionFile = None # no need to update
except (OSError, IOError):
Logs.warn("VERSION file exists, but not readable")
else:
versionFile = submodule.make_node('VERSION')
if versionFile:
try:
versionFile.write(gitVersion)
except (OSError, IOError):
Logs.warn("VERSION file is not writeable")
return splitVersion(gitVersion)
+2 -2
View File
@@ -16,9 +16,9 @@ def required_boost_libs(conf):
conf.env.REQUIRED_BOOST_LIBS += REQUIRED_BOOST_LIBS
def options(opt):
opt.load(['version'], tooldir=['%s/.waf-tools' % opt.path.abspath()])
opt.load(['version'], tooldir=['%s/waf-tools' % opt.path.abspath()])
opt.load(['doxygen', 'sphinx_build', 'compiler-features', 'sqlite3', 'openssl'],
tooldir=['%s/ndn-cxx/.waf-tools' % opt.path.abspath()])
tooldir=['%s/ndn-cxx/waf-tools' % opt.path.abspath()])
def configure(conf):
conf.load(['doxygen', 'sphinx_build', 'compiler-features', 'version', 'sqlite3', 'openssl'])