You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
987 lines
38 KiB
Python
987 lines
38 KiB
Python
# -*- mode: python -*-
|
|
|
|
# idx map:
|
|
# 500xx libshit
|
|
# 510xx boost
|
|
# 513xx lua53
|
|
# 514xx libc++
|
|
# 515xx tracy
|
|
# 516xx capnproto (optional)
|
|
# 517xx yaml-cpp (optional)
|
|
|
|
import subprocess
|
|
try:
|
|
VERSION = subprocess.check_output(
|
|
['git', 'describe', '--tags', '--always'],
|
|
stderr = subprocess.PIPE,
|
|
universal_newlines = True).strip('\n').lstrip('v')
|
|
except:
|
|
with open('VERSION', 'r') as f:
|
|
VERSION = f.readline().strip('\n')
|
|
|
|
from waflib.TaskGen import before_method, after_method, feature, extension
|
|
def fixup_rc():
|
|
# ignore .rc files when not on windows/no resource compiler
|
|
@extension('.rc')
|
|
def rc_override(self, node):
|
|
if self.env.WINRC:
|
|
from waflib.Tools.winres import rc_file
|
|
rc_file(self, node)
|
|
|
|
from waflib import Context
|
|
try:
|
|
with open('wscript_user.py', 'r') as f:
|
|
exec(compile(f.read(), 'wscript_user.py', 'exec'), Context.g_module.__dict__)
|
|
except IOError:
|
|
pass
|
|
|
|
if not Context.g_module:
|
|
APPNAME = 'libshit'
|
|
try: did_I_mention_that_python_is_a_horrible_language()
|
|
except:
|
|
class AttributeDict(dict):
|
|
def __init__(self, *args, **kwargs):
|
|
super(AttributeDict, self).__init__(*args, **kwargs)
|
|
self.__dict__ = self
|
|
import sys
|
|
Context.g_module = AttributeDict(sys.exc_info()[2].tb_frame.f_globals)
|
|
|
|
from waflib.Tools import c_config, c_preproc
|
|
c_config.MACRO_TO_DESTOS['__vita__'] = 'vita'
|
|
c_preproc.go_absolute = True # TODO?
|
|
|
|
app = Context.g_module.APPNAME.upper()
|
|
libshit_cross = getattr(Context.g_module, 'LIBSHIT_CROSS', False)
|
|
libshit_translate = getattr(Context.g_module, 'LIBSHIT_TRANSLATE', app == 'LIBSHIT')
|
|
libshit_translate_yaml = getattr(Context.g_module, 'LIBSHIT_TRANSLATE_YAML', False)
|
|
libshit_translate_generate = getattr(Context.g_module, 'LIBSHIT_TRANSLATE_GENERATE', False)
|
|
|
|
import os
|
|
translate_yaml_hack = os.getenv('LIBSHIT_HAS_YAML_CPP_IN_MISC')
|
|
if translate_yaml_hack:
|
|
Context.g_module.LIBSHIT_CROSS = True
|
|
# I have no freakin idea how can a module get a reference itself when a
|
|
# module is not loaded normally but evaled, the normal sys.modules trick
|
|
# doesn't work that's for sure.
|
|
LIBSHIT_CROSS = True
|
|
libshit_cross = True
|
|
libshit_translate_yaml = True
|
|
libshit_translate_generate = True
|
|
|
|
def options(opt):
|
|
opt.load('with_selector', tooldir=opt.path.abspath())
|
|
opt.load('compiler_c compiler_cxx clang_compilation_database md5_tstamp')
|
|
grp = opt.get_option_group('configure options')
|
|
|
|
grp.add_option('--optimize', action='store_true', default=False,
|
|
help='Enable some default optimizations')
|
|
grp.add_option('--optimize-ext', action='store_true', default=False,
|
|
help='Optimize ext libs even if %s is in debug mode' % app.title())
|
|
grp.add_option('--release', action='store_true', default=False,
|
|
help='Release mode (NDEBUG + optimize)')
|
|
grp.add_option('--with-tests', action='store_true', default=False,
|
|
help='Enable tests')
|
|
grp.add_option('--with-valgrind', action='store_true', default=False,
|
|
help='Enable valgrind instrumentation')
|
|
|
|
grp.add_option('--min-windows-version', action='store',
|
|
help='Minimal windows version to support. 5.1 for XP, 6.1 for win7, etc')
|
|
grp.add_option('--no-pie', action='store_true', default=False,
|
|
help='Do not use -fPIE even when supported (use if you static link libraries built without -fPIC/-fPIE or you just want marginally faster code)')
|
|
|
|
opt.recurse('ext', name='options', once=False)
|
|
if translate_yaml_hack:
|
|
opt.recurse('misc', name='options', wscript='wscript_yaml_cpp', once=False)
|
|
|
|
def configure(cfg):
|
|
from waflib import Logs
|
|
if cfg.options.release:
|
|
cfg.options.optimize = True
|
|
|
|
cfg.load('with_selector', tooldir=cfg.path.abspath())
|
|
|
|
variant = cfg.variant
|
|
environ = cfg.environ
|
|
|
|
# sanity check
|
|
if variant != '' and cfg.bldnode.name != variant:
|
|
cfg.fatal('cfg.bldnode %r doesn not end in %r' % (cfg.bldnode, variant))
|
|
|
|
# setup host compiler
|
|
cfg.setenv(variant + '_host')
|
|
Logs.pprint('NORMAL', 'Configuring host compiler '+variant)
|
|
|
|
cross = False
|
|
# replace xxx with HOST_xxx env vars
|
|
cfg.environ = environ.copy()
|
|
for k in list(cfg.environ):
|
|
if k[0:5] == 'HOST_':
|
|
cross = True
|
|
cfg.environ[k[5:]] = cfg.environ[k]
|
|
del cfg.environ[k]
|
|
|
|
if cross:
|
|
configure_variant(cfg)
|
|
|
|
# host build executables don't need memcheck
|
|
cfg.env.append_value('DEFINES_'+app, ['LIBSHIT_WITH_MEMCHECK=0'])
|
|
|
|
if cfg.options.optimize or cfg.options.optimize_ext:
|
|
# host executables on cross compile, so -march=native is ok
|
|
cfg.filter_flags(['CFLAGS', 'CXXFLAGS'], ['-O1', '-march=native'])
|
|
|
|
# ----------------------------------------------------------------------
|
|
# setup target
|
|
cfg.setenv(variant)
|
|
Logs.pprint('NORMAL', 'Configuring target compiler '+variant)
|
|
cfg.env.CROSS = cross
|
|
cfg.environ = environ
|
|
|
|
configure_variant(cfg)
|
|
|
|
if cfg.env.DEST_OS == 'win32':
|
|
cfg.env.append_value('CFLAGS', '-gcodeview')
|
|
cfg.env.append_value('CXXFLAGS', '-gcodeview')
|
|
cfg.env.append_value('LINKFLAGS', '-g')
|
|
else:
|
|
cfg.filter_flags(['CFLAGS', 'CXXFLAGS', 'LINKFLAGS'], ['-ggdb3'])
|
|
|
|
if cfg.options.optimize:
|
|
cfg.filter_flags(['CFLAGS', 'CXXFLAGS', 'LINKFLAGS'], [
|
|
'-O3', '-flto', '-fno-fat-lto-objects',
|
|
'-fomit-frame-pointer'])
|
|
|
|
if cfg.env.DEST_OS == 'win32':
|
|
cfg.env.append_value('LINKFLAGS', '-Wl,-opt:icf,-opt:lldtailmerge')
|
|
else:
|
|
cfg.env.append_value('LINKFLAGS', '-Wl,-O1')
|
|
elif cfg.options.optimize_ext:
|
|
cfg.filter_flags(['CFLAGS_EXT', 'CXXFLAGS_EXT'], ['-O3'])
|
|
|
|
if cfg.options.release:
|
|
cfg.define('NDEBUG', 1)
|
|
cfg.env.WITH_TESTS = cfg.options.with_tests
|
|
|
|
if cfg.options.with_valgrind:
|
|
cfg.check_cxx(header_name='valgrind/memcheck.h', define_name='')
|
|
cfg.env.append_value(
|
|
'DEFINES_'+app, ['LIBSHIT_WITH_MEMCHECK=%d' % cfg.options.with_valgrind])
|
|
|
|
Logs.pprint('NORMAL', 'Configuring ext '+variant)
|
|
cfg.recurse('ext', name='configure', once=False)
|
|
if translate_yaml_hack:
|
|
cfg.recurse('misc', name='configure', wscript='wscript_yaml_cpp', once=False)
|
|
|
|
def configure_variant(ctx):
|
|
ctx.add_os_flags('ASFLAGS', dup=False)
|
|
# override flags specific to app/bundled libraries
|
|
for v in [app, 'EXT']:
|
|
ctx.add_os_flags('ASFLAGS_'+v, dup=False)
|
|
ctx.add_os_flags('CPPFLAGS_'+v, dup=False)
|
|
ctx.add_os_flags('CFLAGS_'+v, dup=False)
|
|
ctx.add_os_flags('CXXFLAGS_'+v, dup=False)
|
|
ctx.add_os_flags('LINKFLAGS_'+v, dup=False)
|
|
ctx.add_os_flags('LDFLAGS_'+v, dup=False)
|
|
|
|
ctx.load('compiler_c compiler_cxx gccdeps')
|
|
|
|
ctx.filter_flags(['CFLAGS', 'CXXFLAGS'], [
|
|
# error on unknown arguments, including unknown options that turns
|
|
# unknown argument warnings into error. どうして?
|
|
'-Werror=unknown-warning-option',
|
|
'-Werror=ignored-optimization-argument',
|
|
'-Werror=unknown-argument',
|
|
], seq=False)
|
|
ctx.filter_flags(['CFLAGS', 'CXXFLAGS'], [
|
|
'-fdiagnostics-color', '-fdiagnostics-show-option',
|
|
'-fdata-sections', '-ffunction-sections', '-fexceptions',
|
|
])
|
|
if ctx.env.DEST_OS == 'win32':
|
|
ctx.env.append_value('LINKFLAGS', ['-Wl,-opt:ref'])
|
|
elif ctx.check_cc(linkflags='-Wl,--gc-sections', mandatory=False):
|
|
ctx.env.append_value('LINKFLAGS', ['-Wl,--gc-sections'])
|
|
|
|
if ctx.env.DEST_OS != 'win32' and \
|
|
ctx.check_cc(linkflags='-Wl,--wrap=foo', mandatory=False):
|
|
ctx.env.WITH_LINKER_WRAP = True
|
|
ctx.env.append_value('LINKFLAGS_LIBSHIT', ['-Wl,--wrap=abort'])
|
|
|
|
ctx.filter_flags(['CFLAGS_'+app, 'CXXFLAGS_'+app], [
|
|
'-Wall', '-Wextra', '-pedantic', '-Wdouble-promotion',
|
|
'-Wno-parentheses', '-Wno-assume', '-Wno-attributes',
|
|
'-Wimplicit-fallthrough', '-Wno-dangling-else', '-Wno-unused-parameter',
|
|
# I don't even know what this warning supposed to mean, gcc. how can you
|
|
# not set a parameter?
|
|
'-Wno-unused-but-set-parameter',
|
|
# __try in lua exception handler
|
|
'-Wno-language-extension-token',
|
|
# parameter passing changed in an ancient gcc verison warning
|
|
'-Wno-psabi',
|
|
])
|
|
# c++ only warnings, shut up gcc
|
|
ctx.filter_flags(['CXXFLAGS_'+app], [
|
|
'-Wold-style-cast', '-Woverloaded-virtual',
|
|
'-Wno-undefined-var-template', # TYPE_NAME usage
|
|
# -Wsubobject-linkage: warning message is criminally bad, basically it
|
|
# complains about defining/using something from anonymous namespace in a
|
|
# header which normally makes sense, except in "*.binding.hpp"s.
|
|
# unfortunately pragma disabling this warning in the binding files does
|
|
# not work. see also https://gcc.gnu.org/bugzilla/show_bug.cgi?id=51440
|
|
'-Wno-subobject-linkage', '-Wno-sign-compare',
|
|
'-Wno-extra-semi', # warns on valid ; in structs
|
|
])
|
|
|
|
# gcc is a piece of crap: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=53431
|
|
# missing-prototypes/missing-declarations: of course, clang and gcc flags
|
|
# mean different things, and gcc yells when -Wmissing-prototypes is used in
|
|
# c++
|
|
if ctx.env.COMPILER_CXX == 'clang++':
|
|
ctx.filter_flags(['CFLAGS_'+app, 'CXXFLAGS_'+app], [
|
|
'-Werror=undef', '-Wmissing-prototypes',
|
|
])
|
|
elif ctx.env.COMPILER_CXX == 'g++':
|
|
ctx.filter_flags(['CFLAGS_'+app, 'CXXFLAGS_'+app], [
|
|
'-Wno-pedantic', # empty semicolons outside functions are valid since 2011...
|
|
'-Wno-unknown-pragmas' # do not choke on #pragma clang
|
|
])
|
|
ctx.filter_flags(['CXXFLAGS_'+app], ['-Wmissing-declarations'])
|
|
|
|
ctx.filter_flags(['CFLAGS_EXT', 'CXXFLAGS_EXT'], [
|
|
'-Wno-parentheses-equality', # boost fs, windows build
|
|
'-Wno-assume', '-Wno-inconsistent-missing-override', # boost fs
|
|
'-Wno-string-plus-int', # lua53
|
|
'-Wno-deprecated-declarations', '-Wno-ignored-qualifiers', # capnp
|
|
])
|
|
|
|
if ctx.check_cxx(cxxflags=['-isystem', '.'],
|
|
features='cxx', mandatory=False,
|
|
msg='Checking for compiler flag -isystem'):
|
|
ctx.env.CPPSYSPATH_ST = ['-isystem']
|
|
else:
|
|
ctx.env.CPPSYSPATH_ST = ctx.env.CPPPATH_ST
|
|
|
|
ctx.check_cc(cflags='-std=c11')
|
|
ctx.check_cxx(cxxflags='-std=c++17')
|
|
ctx.env.append_value('CFLAGS', ['-std=c11'])
|
|
ctx.env.append_value('CXXFLAGS', ['-std=c++17'])
|
|
|
|
if ctx.options.release:
|
|
# only hide symbols in release builds, this creates better stacktraces
|
|
# in tools that doesn't support DWARF debug info
|
|
ctx.filter_flags(['CFLAGS', 'CXXFLAGS'], ['-fvisibility=hidden'])
|
|
|
|
if not ctx.options.no_pie and \
|
|
ctx.check_cc(cflags='-fPIE', linkflags='-pie', mandatory=False):
|
|
ctx.env.append_value('CFLAGS', ['-fPIE'])
|
|
ctx.env.append_value('CXXFLAGS', ['-fPIE'])
|
|
ctx.env.append_value('LINKFLAGS', ['-fPIE', '-pie'])
|
|
ctx.filter_flags(['CFLAGS', 'CXXFLAGS'], ['-fstack-protector'])
|
|
if ctx.check_cc(linkflags='-Wl,-z,relro,-z,now', mandatory=False):
|
|
ctx.env.append_value('LINKFLAGS', ['-Wl,-z,relro,-z,now'])
|
|
# TODO: glibc is a clusterfuck, as it produces shittons of warnings when you
|
|
# define _FORTIFY_SOURCE without optimization because this shit doesn't work
|
|
# without optimizations, and they think this is a feature. So I'd have to
|
|
# somehow figure out whether the user specified any flag that turns on
|
|
# optimizations and define this shit only then. Or just ignore it, I'll use
|
|
# your fucking security feature when you make it not fucking impossible to
|
|
# use, you fucking buttlicker security idiots.
|
|
# https://sourceware.org/bugzilla/show_bug.cgi?id=13979
|
|
# https://archive.md/eYs12
|
|
|
|
# defs = ctx.get_defs('glibc', '#include <stdio.h>')
|
|
# glibc = '__GLIBC__' in defs
|
|
# ctx.end_msg(glibc)
|
|
# if glibc:
|
|
# ctx.env.append_value('DEFINES', '_FORTIFY_SOURCE=2')
|
|
|
|
if ctx.env.DEST_OS == 'win32':
|
|
# fixup: waf expects mingw, not clang in half-msvc-emulation-mode
|
|
while '-Wl,--enable-auto-import' in ctx.env.LINKFLAGS:
|
|
ctx.env.LINKFLAGS.remove('-Wl,--enable-auto-import')
|
|
ctx.env.IMPLIB_ST = '-Wl,-implib:%s'
|
|
ctx.env.implib_PATTERN = '%s.lib'
|
|
ctx.env.cstlib_PATTERN = ctx.env.cxxstlib_PATTERN = '%s.lib'
|
|
ctx.env.def_PATTERN = '-Wl,-def:%s'
|
|
ctx.env.STLIB_MARKER = ''
|
|
ctx.env.SHLIB_MARKER = ''
|
|
|
|
if ctx.options.min_windows_version:
|
|
str_ver = ctx.options.min_windows_version
|
|
ver_ary = list(map(int, str_ver.split('.')))
|
|
if len(ver_ary) != 2:
|
|
ctx.fatal('Invalid windows version %r' % str_ver)
|
|
|
|
ctx.env.LINKFLAGS_WINDOWS_CLI = [
|
|
'-Xlinker', '/subsystem:console,'+str_ver]
|
|
ctx.env.LINKFLAGS_WINDOWS_GUI = [
|
|
'-Xlinker', '/subsystem:windows,'+str_ver]
|
|
ctx.env.WINVER = '0x%02x%02x' % tuple(ver_ary)
|
|
ctx.env.append_value(
|
|
'DEFINES', ['LIBSHIT_WINVER_MIN=%s' % ctx.env.WINVER])
|
|
else:
|
|
# use whatever the toolchain defaults to...
|
|
ctx.env.LINKFLAGS_WINDOWS_CLI = ['-Xlinker', '/subsystem:console']
|
|
ctx.env.LINKFLAGS_WINDOWS_GUI = ['-Xlinker', '/subsystem:windows']
|
|
|
|
for x in ['CXXFLAGS', 'CFLAGS']:
|
|
ctx.env.append_value(x, [
|
|
'-mno-incremental-linker-compatible', # no timestamp in obj files
|
|
# clang tries to be a smartass and warns about an unused
|
|
# argument when we're doing LTO, since in this case the output
|
|
# is not a COFF abomination, but an llvm bitcode file, so this
|
|
# option indeed has no effect, but it's fucking gay and it looks
|
|
# like there are no way to disable this titlicker case other
|
|
# than getting rid of the whole warning
|
|
# (and no, -Wno-.... -mno-... -Wunu... doesn't work)
|
|
'-Wno-unused-command-line-argument',
|
|
])
|
|
ctx.env.append_value('LINKFLAGS', [
|
|
'-nostdlib', '-Wl,-defaultlib:msvcrt', # -MD cont
|
|
'-Wl,-defaultlib:oldnames', # lua isatty
|
|
'-Wl,-Brepro', # removes timestamp to make output reproducible
|
|
'-Wl,-largeaddressaware',
|
|
])
|
|
ctx.define('_MT', 1)
|
|
ctx.define('_DLL', 1)
|
|
|
|
ctx.load('winres')
|
|
ctx.add_os_flags('WINRCFLAGS')
|
|
ctx.define('_CRT_SECURE_NO_WARNINGS', 1)
|
|
ctx.define('UNICODE', 1)
|
|
ctx.define('_UNICODE', 1)
|
|
|
|
ctx.check_cxx(lib='advapi32')
|
|
ctx.check_cxx(lib='dbgeng')
|
|
ctx.check_cxx(lib='kernel32')
|
|
ctx.check_cxx(lib='ole32')
|
|
ctx.check_cxx(lib='shell32')
|
|
ctx.check_cxx(lib='user32')
|
|
|
|
m = ctx.get_defs('msvc lib version', '#include <yvals.h>', cxx=True)
|
|
cpp_ver = m['_CPPLIB_VER']
|
|
if cpp_ver == '610':
|
|
ctx.end_msg('610, activating include patching', color='YELLOW')
|
|
inc = [ctx.path.find_node('msvc_include').abspath()]
|
|
ctx.env.prepend_value('SYSTEM_INCLUDES', inc)
|
|
ctx.env.append_value('DEFINES_BOOST', ['BOOST_NO_CXX14_CONSTEXPR'])
|
|
else:
|
|
ctx.end_msg(cpp_ver)
|
|
elif ctx.env.DEST_OS == 'vita':
|
|
inc = [ ctx.path.find_node('vita_include').abspath() ]
|
|
# type-limits: char is unsigned, thank you very much
|
|
ctx.env.prepend_value('CXXFLAGS', ['-Wno-type-limits'])
|
|
ctx.env.prepend_value('SYSTEM_INCLUDES', inc)
|
|
ctx.env.append_value('DEFINES_BOOST', ['BOOST_HAS_STDINT_H'])
|
|
ldflags = [
|
|
# muldefs: override of vita libc's abort with lto
|
|
'-Wl,-q,-z,nocopyreloc,-z,muldefs','-nostdlib', '-lsupc++',
|
|
'-Wl,--start-group', '-lgcc', '-lSceLibc_stub', '-lpthread',
|
|
# linked automatically by vitasdk gcc when not using -nostdlib
|
|
# we don't need some of them, but unused libraries don't end
|
|
# up in the final executable, so it doesn't hurt
|
|
'-lSceRtc_stub', '-lSceSysmem_stub', '-lSceKernelThreadMgr_stub',
|
|
'-lSceKernelModulemgr_stub', '-lSceIofilemgr_stub',
|
|
'-lSceProcessmgr_stub', '-lSceLibKernel_stub', '-lSceNet_stub',
|
|
'-Wl,--end-group'
|
|
]
|
|
ctx.env.append_value('LDFLAGS', ldflags)
|
|
else:
|
|
# mutexes (used by logger) need this on some platforms
|
|
ctx.check_cc(lib='pthread', mandatory=False)
|
|
|
|
ctx.env.append_value('LINKFLAGS', '-rdynamic')
|
|
# needed by debug stacktrace
|
|
ctx.check_cxx(lib='dl')
|
|
|
|
def build(bld):
|
|
fixup_rc()
|
|
fixup_fail_cxx()
|
|
bld.recurse('ext', name='build', once=False)
|
|
if translate_yaml_hack:
|
|
bld.recurse('misc', name='build', wscript='wscript_yaml_cpp', once=False)
|
|
|
|
build_libshit(bld, '')
|
|
if libshit_cross: bld.only_host_env(build_libshit)
|
|
|
|
def build_libshit(ctx, pref):
|
|
ctx.objects(idx = 50004 + (len(pref)>0),
|
|
source = ['src/libshit/except.cpp'],
|
|
uselib = ['LIBBACKTRACE', app],
|
|
use = ['BACKTRACE', 'BOOST', 'DBGENG', 'DL', 'DOCTEST',
|
|
'LUA', 'OLE32', 'USER32',
|
|
'LIBSHIT', pref+'lua'],
|
|
includes = 'src',
|
|
export_includes = 'src',
|
|
defines = ['LIBSHIT_WITH_ABORT_WRAP=%d' % (not not ctx.env.WITH_LINKER_WRAP) ],
|
|
target = pref+'libshit-except')
|
|
|
|
src = [
|
|
'src/libshit/container/polymorphic_stack.cpp',
|
|
'src/libshit/logger.cpp',
|
|
'src/libshit/low_io.cpp',
|
|
'src/libshit/maybe_owning_string.cpp',
|
|
'src/libshit/number_format.cpp',
|
|
'src/libshit/options.cpp',
|
|
'src/libshit/random.cpp',
|
|
'src/libshit/string_utils.cpp',
|
|
'src/libshit/wtf8.cpp',
|
|
]
|
|
if ctx.env.DEST_OS == 'vita':
|
|
src += ['src/libshit/vita_fixup.c']
|
|
else:
|
|
src += ['src/libshit/dynamic_lib.cpp']
|
|
if libshit_translate:
|
|
src += ['src/libshit/translate/plural.cpp']
|
|
if ctx.env.WITH_LUA != 'none':
|
|
ctx(idx = 50006 + (len(pref)>0),
|
|
source = [
|
|
'src/libshit/logger.lua',
|
|
'src/libshit/lua/base_funcs.lua',
|
|
'src/libshit/lua/lua53_polyfill.lua',
|
|
],
|
|
target = pref+'libshit-lua-gen-hdr')
|
|
src += [
|
|
'src/libshit/lua/base.cpp',
|
|
'src/libshit/lua/user_type.cpp',
|
|
'src/libshit/lua/userdata.cpp',
|
|
]
|
|
if ctx.env.WITH_TRACY != 'none':
|
|
src += [ 'src/libshit/tracy_alloc.cpp' ]
|
|
|
|
if ctx.env.WITH_TESTS:
|
|
src += [ 'test/test_helper.cpp' ]
|
|
|
|
ctx.objects(idx = 50000 + (len(pref)>0),
|
|
source = src,
|
|
tg_after = pref+'libshit-lua-gen-hdr',
|
|
uselib = app,
|
|
use = ['TRACY', 'PTHREAD',
|
|
'ADVAPI32', # windows random
|
|
pref+'tracy', pref+'libshit-except'],
|
|
target = pref+'libshit-base-notest')
|
|
|
|
if ctx.env.WITH_TESTS:
|
|
src = [
|
|
'test/abomination.cpp',
|
|
'test/bitfield.cpp',
|
|
'test/container/ordered_map.cpp',
|
|
'test/container/parent_list.cpp',
|
|
'test/container/simple_vector.cpp',
|
|
'test/flexible_struct.cpp',
|
|
'test/nonowning_string.cpp',
|
|
'test/shared_ptr.cpp',
|
|
'test/utf_low_level.cpp',
|
|
]
|
|
if ctx.env.WITH_LUA != 'none':
|
|
src += [
|
|
'test/lua/function_call.cpp',
|
|
'test/lua/function_ref.cpp',
|
|
'test/lua/type_traits.cpp',
|
|
'test/lua/user_type.cpp',
|
|
]
|
|
|
|
ctx.objects(idx = 50014 + (len(pref)>0),
|
|
source = src,
|
|
uselib = app,
|
|
use = [pref+'libshit-base-notest'],
|
|
target = pref+'libshit-base')
|
|
else:
|
|
ctx.objects(idx = 50014 + (len(pref)>0),
|
|
use = [pref+'libshit-base-notest'],
|
|
target = pref+'libshit-base')
|
|
|
|
# This doesn't map cleanly to our cross compilation abstractionism. We need
|
|
# to build translate twice regardless whether we're cross compiling or not
|
|
# (we don't want the dump thing to end up in the normal target binaries, but
|
|
# host binaries are only used for build time tools, so it doesn't make any
|
|
# sense to compile them twice too). This means Libshit::Translate will be
|
|
# unavailable in cross compiled tools though.
|
|
if pref == '' and libshit_translate:
|
|
src = [
|
|
'src/libshit/translate/context.cpp',
|
|
'src/libshit/translate/format.cpp',
|
|
'src/libshit/translate/format_opcode_argument.cpp',
|
|
'src/libshit/translate/format_opcode_gender.cpp',
|
|
'src/libshit/translate/format_opcode_plural.cpp',
|
|
'src/libshit/translate/format_parser.cpp',
|
|
'src/libshit/translate/node.cpp',
|
|
]
|
|
def build_host(ctx, pref):
|
|
ctx.program(idx = 50010,
|
|
features = 'no_doctest',
|
|
source = src + ['src/libshit/translate/generate.cpp'],
|
|
defines = 'LIBSHIT_TRANSLATE_DUMP=1 LIBSHIT_TRANSLATE_YAML=1',
|
|
uselib = ['LIBSHIT', 'YAML-CPP', app],
|
|
use = [pref+'libshit-base-notest', pref+'yaml-cpp'],
|
|
target = 'libshit-translate-generate')
|
|
if libshit_translate_generate: ctx.in_host_env(build_host)
|
|
|
|
if libshit_translate_generate and ctx.env.WITH_TESTS:
|
|
ctx(idx = 50011,
|
|
name = 'translate-gen-libshit-test-builtin',
|
|
features = 'translate_gen',
|
|
source = 'test/translate/builtin.yml',
|
|
target = ['test/translate/builtin_ctx.cpp',
|
|
'test/translate/builtin_ctx.hpp'],
|
|
namespace = 'Libshit::Translate::Test',
|
|
id = 'Builtin')
|
|
src += [
|
|
'test/translate/builtin.cpp',
|
|
'test/translate/builtin_ctx.cpp',
|
|
]
|
|
if libshit_translate_yaml and ctx.env.WITH_TESTS:
|
|
src += ['test/translate/translate.cpp']
|
|
|
|
defs = [
|
|
'LIBSHIT_TRANSLATE_DUMP=0',
|
|
'LIBSHIT_TRANSLATE_YAML=%d' % libshit_translate_yaml,
|
|
]
|
|
use = [pref+'libshit-base']
|
|
if libshit_translate_yaml:
|
|
use += ['YAML-CPP', pref+'yaml-cpp']
|
|
ctx.objects(idx = 50012 + (len(pref)>0),
|
|
source = src,
|
|
tg_after = 'translate-gen-libshit-test-builtin',
|
|
includes = 'test',
|
|
defines = defs,
|
|
export_defines = defs,
|
|
uselib = app,
|
|
use = use,
|
|
target = pref+'libshit')
|
|
else:
|
|
ctx.objects(idx = 50012 + (len(pref)>0),
|
|
use = pref+'libshit-base',
|
|
target = pref+'libshit')
|
|
|
|
if ctx.env.WITH_TESTS and app == 'LIBSHIT':
|
|
ctx.program(idx = 50002 + (len(pref)>0),
|
|
source = 'test/libshit_standalone.cpp',
|
|
uselib = 'WINDOWS_CLI',
|
|
use = pref+'libshit',
|
|
target = pref+'libshit-tests')
|
|
|
|
################################################################################
|
|
# random utilities
|
|
|
|
def fixup_fail_cxx():
|
|
# feature fail_cxx: expect compilation failure
|
|
import sys
|
|
from waflib import Logs
|
|
from waflib.Task import Task
|
|
from waflib.Tools import c_preproc
|
|
@extension('.cpp')
|
|
def fail_cxx_ext(self, node):
|
|
if 'fail_cxx' in self.features:
|
|
return self.create_compiled_task('fail_cxx', node)
|
|
else:
|
|
return self.create_compiled_task('cxx', node)
|
|
|
|
class fail_cxx(Task):
|
|
#run_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT[0].abspath()}'
|
|
def run(tsk):
|
|
env = tsk.env
|
|
gen = tsk.generator
|
|
bld = gen.bld
|
|
cwdx = getattr(bld, 'cwdx', bld.bldnode) # TODO single cwd value in waf 1.9
|
|
wd = getattr(tsk, 'cwd', None)
|
|
def to_list(xx):
|
|
if isinstance(xx, str): return [xx]
|
|
return xx
|
|
tsk.last_cmd = lst = []
|
|
lst.extend(to_list(env['CXX']))
|
|
lst.extend(tsk.colon('ARCH_ST', 'ARCH'))
|
|
lst.extend(to_list(env['CXXFLAGS']))
|
|
lst.extend(to_list(env['CPPFLAGS']))
|
|
lst.extend(tsk.colon('FRAMEWORKPATH_ST', 'FRAMEWORKPATH'))
|
|
lst.extend(tsk.colon('CPPPATH_ST', 'INCPATHS'))
|
|
lst.extend(tsk.colon('DEFINES_ST', 'DEFINES'))
|
|
lst.extend(to_list(env['CXX_SRC_F']))
|
|
lst.extend([a.path_from(cwdx) for a in tsk.inputs])
|
|
lst.extend(to_list(env['CXX_TGT_F']))
|
|
lst.append(tsk.outputs[0].abspath())
|
|
lst = [x for x in lst if x]
|
|
try:
|
|
(out,err) = bld.cmd_and_log(
|
|
lst, cwd=bld.variant_dir, env=env.env or None,
|
|
quiet=0, output=0)
|
|
Logs.error("%s compiled successfully, but it shouldn't" % tsk.inputs[0])
|
|
Logs.info(out, extra={'stream':sys.stdout, 'c1': ''})
|
|
Logs.info(err, extra={'stream':sys.stderr, 'c1': ''})
|
|
return -1
|
|
except Exception as e:
|
|
# create output to silence missing file errors
|
|
open(tsk.outputs[0].abspath(), 'w').close()
|
|
return 0
|
|
|
|
vars = ['CXXDEPS'] # unused variable to depend on, just in case
|
|
ext_in = ['.h'] # set the build order easily by using ext_out=['.h']
|
|
scan = c_preproc.scan
|
|
|
|
|
|
from waflib.Configure import conf
|
|
def filter_single_flag(ctx, vars, flag, feat, ret, **kw):
|
|
testflag=flag
|
|
if flag[0:5] == '-Wno-':
|
|
testflag = '-W'+flag[5:]
|
|
ctx.check_cxx(cflags=[testflag], cxxflags=[testflag], features=feat,
|
|
msg='Checking for compiler flag '+flag, **kw)
|
|
|
|
ret.append(flag)
|
|
|
|
def generic_filter_flags(ctx, vars, flags, feat, seq=False):
|
|
ret = []
|
|
|
|
if len(flags) == 1:
|
|
filter_single_flag(ctx, vars, flags[0], feat, ret, mandatory=False)
|
|
elif seq:
|
|
for flag in flags:
|
|
filter_single_flag(ctx, vars, flag, feat, ret, mandatory=False)
|
|
elif len(flags) > 1:
|
|
args = []
|
|
for flag in flags:
|
|
def x(ctx, flag=flag):
|
|
ctx.in_msg = True
|
|
return filter_single_flag(ctx, vars, flag, feat, ret)
|
|
args.append({'func': x, 'msg': 'Checking for compiler flag '+flag,
|
|
'mandatory': False})
|
|
ctx.multicheck(*args)
|
|
|
|
for flag in flags:
|
|
if flag in ret:
|
|
for var in vars:
|
|
ctx.env.append_value(var, flag)
|
|
return ret
|
|
|
|
@conf
|
|
def filter_flags(ctx, vars, flags, **kw):
|
|
return generic_filter_flags(ctx, vars, flags, 'cxx', **kw)
|
|
|
|
@conf
|
|
def filter_flags_c(ctx, vars, flags, **kw):
|
|
return generic_filter_flags(ctx, vars, flags, 'c', **kw)
|
|
|
|
def change_var(ctx, to):
|
|
if getattr(ctx, 'setenv', False):
|
|
ctx.setenv(to)
|
|
else:
|
|
ctx.variant = to
|
|
ctx.all_envs[to].derive()
|
|
|
|
@conf
|
|
def only_host_env(ctx, f):
|
|
if not ctx.env.CROSS: return
|
|
variant = ctx.variant
|
|
change_var(ctx, variant + '_host')
|
|
f(ctx, 'host/')
|
|
change_var(ctx, variant)
|
|
|
|
@conf
|
|
def in_host_env(ctx, f):
|
|
if ctx.env.CROSS:
|
|
variant = ctx.variant
|
|
change_var(ctx, variant + '_host')
|
|
f(ctx, 'host/')
|
|
change_var(ctx, variant)
|
|
else:
|
|
f(ctx, '')
|
|
|
|
@conf
|
|
def both_host_env(ctx, f):
|
|
f(ctx, '')
|
|
if ctx.env.CROSS:
|
|
variant = ctx.variant
|
|
change_var(ctx, variant + '_host')
|
|
f(ctx, 'host/')
|
|
change_var(ctx, variant)
|
|
|
|
# waf 1.7 style logging: c/cxx instead of vague shit like 'processing',
|
|
# 'compiling', etc.
|
|
from waflib.Task import Task
|
|
def getname(self):
|
|
return self.__class__.__name__ + ':'
|
|
Task.keyword = getname
|
|
|
|
|
|
@feature('c', 'cxx', 'includes')
|
|
@after_method('propagate_uselib_vars', 'process_source', 'apply_incpaths')
|
|
def apply_sysincpaths(self):
|
|
lst = self.to_incnodes(self.to_list(getattr(self, 'system_includes', [])) +
|
|
self.env['SYSTEM_INCLUDES'])
|
|
self.includes_nodes += lst
|
|
self.env['SYSINCPATHS'] = [x.abspath() for x in lst]
|
|
|
|
from waflib import Errors
|
|
@feature('c', 'cxx')
|
|
@after_method('process_source')
|
|
def add_tg_after(self):
|
|
after = []
|
|
for x in self.to_list(getattr(self, 'tg_after', [])):
|
|
try:
|
|
after.append(self.bld.get_tgen_by_name(x).tasks)
|
|
except Errors.WafError:
|
|
pass
|
|
|
|
if not after: return
|
|
|
|
node_deps = self.bld.node_deps
|
|
for tsk in self.compiled_tasks:
|
|
try:
|
|
node_deps[tsk.uid()]
|
|
except KeyError:
|
|
for a in after:
|
|
for a2 in a:
|
|
tsk.set_run_after(a2)
|
|
|
|
from waflib.Tools import ccroot
|
|
@feature('c', 'cxx')
|
|
@after_method('apply_link')
|
|
def add_msvc_pdb(self):
|
|
link = getattr(self, 'link_task', None)
|
|
if not link or self.env.DEST_OS != 'win32' or \
|
|
isinstance(link, ccroot.stlink_task) or '-g' not in self.env.LINKFLAGS:
|
|
return
|
|
link.outputs.append(link.outputs[0].change_ext('.pdb'))
|
|
|
|
from waflib.Tools import c, cxx
|
|
from waflib.Task import compile_fun
|
|
from waflib import Utils
|
|
from waflib.Tools.ccroot import USELIB_VARS
|
|
for cls in [c.c, cxx.cxx]:
|
|
run_str = cls.orig_run_str.replace('INCPATHS', 'INCPATHS} ${CPPSYSPATH_ST:SYSINCPATHS')
|
|
(f, dvars) = compile_fun(run_str, cls.shell)
|
|
cls.hcode = Utils.h_cmd(run_str)
|
|
cls.run = f
|
|
cls.vars = list(set(cls.vars + dvars))
|
|
cls.vars.sort()
|
|
USELIB_VARS['c'].add('SYSTEM_INCLUDES')
|
|
USELIB_VARS['cxx'].add('SYSTEM_INCLUDES')
|
|
USELIB_VARS['includes'].add('SYSTEM_INCLUDES')
|
|
|
|
# No tests, even if libshit has. Only use it for tools that are only needed
|
|
# during the build, because libshit will be still linked with tests, you'll just
|
|
# save the tests in this target. Also depend on libshit-base-notest instead of
|
|
# libshit(-base)? unless you need translation.
|
|
@feature('no_doctest')
|
|
@after_method('propagate_uselib_vars')
|
|
def fixup_test_define(self):
|
|
defs = self.env['DEFINES']
|
|
baddef = 'LIBSHIT_WITH_TESTS=1'
|
|
if baddef in defs:
|
|
defs.remove(baddef)
|
|
defs.append('LIBSHIT_WITH_TESTS=0')
|
|
|
|
|
|
# random config helpers
|
|
# reusable rule-like tasks
|
|
@conf
|
|
def rule_like(ctx, name):
|
|
def x(self):
|
|
self.meths.remove('process_source')
|
|
tsk = self.create_task(name)
|
|
self.task = tsk
|
|
tsk.inputs = self.to_nodes(self.source)
|
|
|
|
# from TaskGen.process_rule
|
|
if isinstance(self.target, str):
|
|
self.target = self.target.split()
|
|
if not isinstance(self.target, list):
|
|
self.target = [self.target]
|
|
for x in self.target:
|
|
if isinstance(x, str):
|
|
tsk.outputs.append(self.path.find_or_declare(x))
|
|
else:
|
|
x.parent.mkdir() # if a node was given, create the required folders
|
|
tsk.outputs.append(x)
|
|
|
|
x.__name__ = 'rule_like_%s' % name
|
|
feature(name)(x)
|
|
before_method('process_source')(x)
|
|
|
|
# get preprocessor defs
|
|
import re
|
|
defs_re = re.compile('^#define ([^ ]+) (.*)$')
|
|
@conf
|
|
def get_defs(ctx, msg, file, cxx=False):
|
|
ctx.start_msg('Checking for '+msg)
|
|
cmd = [] # python, why u no have Array#flatten?
|
|
cmd += cxx and ctx.env.CXX or ctx.env.CC
|
|
cmd += ctx.env.CPPFLAGS
|
|
cmd += cxx and ctx.env.CXXFLAGS or ctx.env.CFLAGS
|
|
if isinstance(file, str):
|
|
node = ctx.bldnode.make_node(cxx and 'test.cpp' or 'test.c')
|
|
node.write(file)
|
|
file = node
|
|
|
|
# -MD/MMD with -E causes clang to write a test.d to the working dir!
|
|
# yes, this is how Array#delete looks like in python abomination...
|
|
try: cmd.remove('-MD')
|
|
except ValueError: pass
|
|
try: cmd.remove('-MMD')
|
|
except ValueError: pass
|
|
cmd += ['-E', file.abspath(), '-dM' ]
|
|
try:
|
|
out = ctx.cmd_and_log(cmd)
|
|
except Exception:
|
|
ctx.end_msg(False)
|
|
raise
|
|
|
|
return dict(map(lambda l: defs_re.match(l).groups(), out.splitlines()))
|
|
|
|
@conf
|
|
def gen_version_hpp(bld, name):
|
|
import re
|
|
lst = list(filter(lambda x: re.match('^[0-9]+$', x), re.split('[\.-]', VERSION)))
|
|
# prevent parsing a git shortrev as a number
|
|
if len(lst) < 2: lst = []
|
|
rc_ver = ','.join((lst + ['0']*4)[0:4])
|
|
bld(features = 'subst',
|
|
source = name + '.in',
|
|
target = name,
|
|
before = ['c','cxx'],
|
|
VERSION = VERSION,
|
|
RC_VERSION = rc_ver)
|
|
|
|
# force the body into a temp environment, then write a header, because you can't
|
|
# use write_config_header without brutally polluting the global env
|
|
from waflib import Build
|
|
import os
|
|
class CfgHeaderBlock:
|
|
def __init__(self, ctx, name):
|
|
self.ctx = ctx
|
|
self.name = name
|
|
|
|
def __enter__(self):
|
|
self.ctx.env.stash()
|
|
self.ctx.env.define_key = []
|
|
|
|
def __exit__(self, type, value, traceback):
|
|
# because the checks are run parallel, the order of the defines is
|
|
# pretty random, so sort them to prevent rebuilding the whole shit after
|
|
# almost every reconfigure
|
|
if not type:
|
|
self.ctx.env.define_key.sort()
|
|
self.ctx.write_config_header(self.name, top=True)
|
|
# revert env, except CFG_FILES because that's needed for `./waf clean`
|
|
# to not clean the generated header files
|
|
cfg_files = self.ctx.env[Build.CFG_FILES]
|
|
self.ctx.env.revert()
|
|
self.ctx.env[Build.CFG_FILES] = cfg_files
|
|
|
|
@conf
|
|
def cfg_header_block(ctx, name):
|
|
return CfgHeaderBlock(ctx, name)
|
|
|
|
# Generate a fragment to check for a function exported by a header.
|
|
@conf
|
|
def check_func_fragment(ctx, name, inc):
|
|
# braindead clang implicitly declares some built-in functions, like strlcpy
|
|
# (and produces a warning to include <string.h>, which is inlcluded in the
|
|
# previous line), so do something like cmake to force a reference to the
|
|
# function, so it will bail out with a link error, even if the compiler
|
|
# wants to outsmart us.
|
|
return '\n'.join(map(lambda x: '#include <%s>' % x, Utils.to_list(inc))) + \
|
|
'\nint main() {\n#ifndef %s\nreturn *((int*)(&%s));\n#endif\n }' % (name,name)
|
|
|
|
def multi_check_common(ctx, name, kw):
|
|
if 'compiler' not in kw:
|
|
kw['compiler'] = 'c'
|
|
if 'mandatory' not in kw:
|
|
kw['mandatory'] = False
|
|
|
|
if 'define_name' not in kw:
|
|
kw['define_name'] = ctx.have_define(name)
|
|
|
|
@conf
|
|
def multi_check_hdr(ctx, name, **kw):
|
|
multi_check_common(ctx, name, kw)
|
|
if 'msg' not in kw:
|
|
kw['msg'] = 'Checking for header %s' % name
|
|
kw['header_name'] = name
|
|
return kw
|
|
|
|
@conf
|
|
def multi_check_func(ctx, name, inc, **kw):
|
|
multi_check_common(ctx, name, kw)
|
|
if 'msg' not in kw:
|
|
kw['msg'] = 'Checking for function %s' % name
|
|
kw['fragment'] = ctx.check_func_fragment(name, inc)
|
|
return kw
|
|
|
|
@conf
|
|
def multi_check_lib(ctx, name, **kw):
|
|
multi_check_common(ctx, name, kw)
|
|
if 'msg' not in kw:
|
|
kw['msg'] = 'Checking for library %s' % name
|
|
kw['lib'] = name
|
|
return kw
|
|
|
|
def recurse_subdirs(ctx, name, dirs):
|
|
for d in dirs:
|
|
ctx.recurse(d, name=name, once=False)
|
|
Context.Context.recurse_subdirs = recurse_subdirs
|
|
|
|
# translate generator task
|
|
class translate_gen(Task):
|
|
run_str = '${tsk.executable} ${SRC} ${tsk.namespace} ${tsk.id} ${TGT}'
|
|
color = 'BLUE'
|
|
rule_like(None, 'translate_gen')
|
|
|
|
@feature('translate_gen')
|
|
@after_method('rule_like_translate_gen')
|
|
def set_executable_translate_gen(self):
|
|
nd = self.bld.get_tgen_by_name('libshit-translate-generate').link_task.outputs[0]
|
|
|
|
self.task.id = self.id
|
|
self.task.namespace = self.namespace
|
|
self.task.executable = nd.abspath()
|
|
self.task.dep_nodes.append(nd)
|
|
|
|
from waflib import Logs
|
|
class CleandeadContext(Build.BuildContext):
|
|
'''removes not generated files from build dir'''
|
|
cmd = 'cleandead'
|
|
|
|
def execute_build(self):
|
|
# based on BuildContext, ListContext and CleanContext
|
|
self.recurse([self.run_dir])
|
|
self.pre_build()
|
|
self.timer = Utils.Timer()
|
|
|
|
for g in self.groups:
|
|
for tg in g:
|
|
try:
|
|
f = tg.post
|
|
except AttributeError:
|
|
pass
|
|
else:
|
|
f()
|
|
|
|
try:
|
|
# force the cache initialization
|
|
self.get_tgen_by_name('')
|
|
except Errors.WafError:
|
|
pass
|
|
|
|
outputs = set()
|
|
for env in self.all_envs.values():
|
|
outputs.update(self.root.find_or_declare(f) for f in env[Build.CFG_FILES])
|
|
for tg in self.task_gen_cache_names.values():
|
|
for tsk in tg.tasks:
|
|
outputs.update(tsk.outputs)
|
|
# gccdeps doesn't add .d files as outputs...
|
|
outputs.update(map(lambda x: x.change_ext('.d'), tsk.outputs))
|
|
|
|
excluded_dirs = 'compile_commands.json .lock* .wafpickle* *conf_check_*/** config.log %s' % Build.CACHE_DIR
|
|
for n in reversed(self.bldnode.ant_glob('**/*', excl=excluded_dirs, quiet=True, dir=True)):
|
|
if n in outputs: continue
|
|
try:
|
|
if os.path.isdir(n.abspath()):
|
|
os.rmdir(n.abspath())
|
|
Logs.warn('Removed stale directory %s' % n)
|
|
else:
|
|
os.remove(n.abspath())
|
|
Logs.warn('Removed stale file %s' % n)
|
|
except OSError:
|
|
pass
|
|
n.evict()
|