2095 lines
75 KiB
Python
2095 lines
75 KiB
Python
#SConstruct
|
|
|
|
# needed imports
|
|
import binascii
|
|
import errno
|
|
import subprocess
|
|
import sys
|
|
import os
|
|
import SCons.Util
|
|
|
|
def message(program,msg):
|
|
print "%s: %s" % (program.program_message_prefix, msg)
|
|
|
|
# endianess-checker
|
|
def checkEndian():
|
|
if ARGUMENTS.has_key('endian'):
|
|
r = ARGUMENTS['endian']
|
|
if r == "little" or r == "big":
|
|
return r
|
|
raise SCons.Errors.UserError("Unknown endian value: %s" % r)
|
|
import struct
|
|
array = struct.pack('cccc', '\x01', '\x02', '\x03', '\x04')
|
|
i = struct.unpack('i', array)
|
|
if i == struct.unpack('<i', array):
|
|
return "little"
|
|
elif i == struct.unpack('>i', array):
|
|
return "big"
|
|
return "unknown"
|
|
|
|
def get_Werror_string(l):
|
|
if l and '-Werror' in l:
|
|
return '-W'
|
|
return '-Werror='
|
|
|
|
class ConfigureTests:
|
|
class Collector:
|
|
def __init__(self):
|
|
self.tests = []
|
|
def __call__(self,f):
|
|
self.tests.append(f.__name__)
|
|
return f
|
|
class PreservedEnvironment:
|
|
def __init__(self,env,keys):
|
|
self.flags = {k: env.get(k, [])[:] for k in keys}
|
|
def restore(self,env):
|
|
env.Replace(**self.flags)
|
|
def __getitem__(self,key):
|
|
return self.flags.__getitem__(key)
|
|
class ForceVerboseLog:
|
|
def __init__(self,env):
|
|
# Force verbose output to sconf.log
|
|
self.cc_env_strings = {}
|
|
for k in ['CXXCOMSTR']:
|
|
try:
|
|
self.cc_env_strings[k] = env[k]
|
|
del env[k]
|
|
except KeyError:
|
|
pass
|
|
def restore(self,env):
|
|
# Restore potential quiet build options
|
|
env.Replace(**self.cc_env_strings)
|
|
# Force test to report failure
|
|
sconf_force_failure = 'force-failure'
|
|
# Force test to report success, and modify flags like it
|
|
# succeeded
|
|
sconf_force_success = 'force-success'
|
|
# Force test to report success, do not modify flags
|
|
sconf_assume_success = 'assume-success'
|
|
_implicit_test = Collector()
|
|
_custom_test = Collector()
|
|
implicit_tests = _implicit_test.tests
|
|
custom_tests = _custom_test.tests
|
|
comment_not_supported = '/* not supported */'
|
|
__flags_Werror = {k:['-Werror'] for k in ['CXXFLAGS']}
|
|
_cxx_conformance_cxx11 = 11
|
|
_cxx_conformance_cxx14 = 14
|
|
def __init__(self,msgprefix,user_settings):
|
|
self.msgprefix = msgprefix
|
|
self.user_settings = user_settings
|
|
self.successful_flags = {}
|
|
self.__cxx_conformance = None
|
|
self.__automatic_compiler_tests = {
|
|
'.cpp': self.check_cxx_works,
|
|
}
|
|
@classmethod
|
|
def describe(cls,name):
|
|
f = getattr(cls, name)
|
|
if f.__doc__:
|
|
lines = f.__doc__.rstrip().split('\n')
|
|
if lines[-1].startswith("help:"):
|
|
return lines[-1][5:]
|
|
return None
|
|
@staticmethod
|
|
def _quote_macro_value(v):
|
|
return v.strip().replace('\n', ' \\\n')
|
|
def _check_forced(self,context,name):
|
|
return getattr(self.user_settings, 'sconf_%s' % name)
|
|
def _check_macro(self,context,macro_name,macro_value,test,**kwargs):
|
|
macro_value = self._quote_macro_value(macro_value)
|
|
r = self.Compile(context, text="""
|
|
#define {macro_name} {macro_value}
|
|
{test}
|
|
""".format(macro_name=macro_name, macro_value=macro_value, test=test), **kwargs)
|
|
if r:
|
|
context.sconf.Define(macro_name, macro_value)
|
|
else:
|
|
context.sconf.Define(macro_name, self.comment_not_supported)
|
|
def __compiler_test_already_done(self,context):
|
|
pass
|
|
def _check_compiler_works(self,context,ext):
|
|
self.__automatic_compiler_tests.pop(ext, self.__compiler_test_already_done)(context)
|
|
def _extend_successflags(self,k,v):
|
|
self.successful_flags.setdefault(k, []).extend(v)
|
|
def Compile(self,context,**kwargs):
|
|
if self.user_settings.lto:
|
|
self.Compile = self.Link
|
|
return self.Link(context, **kwargs)
|
|
return self._Test(context,action=context.TryCompile, **kwargs)
|
|
def Link(self,context,**kwargs):
|
|
return self._Test(context,action=context.TryLink, **kwargs)
|
|
def _Test(self,context,text,msg,action,main='',ext='.cpp',testflags={},successflags={},skipped=None,successmsg=None,failuremsg=None,expect_failure=False):
|
|
self._check_compiler_works(context,ext)
|
|
context.Message('%s: checking %s...' % (self.msgprefix, msg))
|
|
if skipped is not None:
|
|
context.Result('(skipped){skipped}'.format(skipped=skipped))
|
|
return
|
|
env_flags = self.PreservedEnvironment(context.env, successflags.keys() + testflags.keys() + self.__flags_Werror.keys() + ['CPPDEFINES'])
|
|
context.env.Append(**successflags)
|
|
frame = None
|
|
forced = None
|
|
try:
|
|
1//0
|
|
except ZeroDivisionError:
|
|
frame = sys.exc_info()[2].tb_frame.f_back
|
|
while frame is not None:
|
|
co_name = frame.f_code.co_name
|
|
if co_name[0:6] == 'check_':
|
|
forced = self._check_forced(context, co_name[6:])
|
|
break
|
|
frame = frame.f_back
|
|
caller_modified_env_flags = self.PreservedEnvironment(context.env, self.__flags_Werror.keys() + testflags.keys())
|
|
# Always pass -Werror
|
|
context.env.Append(**self.__flags_Werror)
|
|
context.env.Append(**testflags)
|
|
if forced is None:
|
|
cc_env_strings = self.ForceVerboseLog(context.env)
|
|
undef_SDL_main = '#undef main /* avoid -Dmain=SDL_main from libSDL */\n'
|
|
r = action(undef_SDL_main + text + '\nint main(int argc,char**argv){(void)argc;(void)argv;' + main + ';}\n', ext)
|
|
if expect_failure:
|
|
r = not r
|
|
cc_env_strings.restore(context.env)
|
|
context.Result((successmsg if r else failuremsg) or r)
|
|
else:
|
|
choices = (self.sconf_force_failure, self.sconf_force_success, self.sconf_assume_success)
|
|
if forced not in choices:
|
|
try:
|
|
forced = choices[int(forced)]
|
|
except ValueError:
|
|
raise SCons.Errors.UserError("Unknown force value for sconf_%s: %s" % (co_name[6:], forced))
|
|
except IndexError:
|
|
raise SCons.Errors.UserError("Out of range force value for sconf_%s: %s" % (co_name[6:], forced))
|
|
if forced == self.sconf_force_failure:
|
|
r = False
|
|
elif forced == self.sconf_force_success or forced == self.sconf_assume_success:
|
|
r = True
|
|
else:
|
|
raise SCons.Errors.UserError("Unknown force value for sconf_%s: %s" % (co_name[6:], forced))
|
|
if expect_failure:
|
|
r = not r
|
|
context.Result('(forced){inverted}{forced}'.format(forced=forced, inverted='(inverted)' if expect_failure else ''))
|
|
# On success, revert to base flags + successflags
|
|
# On failure, revert to base flags
|
|
if r and forced != self.sconf_assume_success:
|
|
caller_modified_env_flags.restore(context.env)
|
|
context.env.Replace(CPPDEFINES=env_flags['CPPDEFINES'])
|
|
for d in successflags.pop('CPPDEFINES', []):
|
|
if isinstance(d, str):
|
|
d = (d,None)
|
|
context.sconf.Define(d[0], d[1])
|
|
for (k,v) in successflags.items():
|
|
self._extend_successflags(k, v)
|
|
else:
|
|
env_flags.restore(context.env)
|
|
return r
|
|
def _check_system_library(self,context,header,main,lib,successflags={}):
|
|
include = '\n'.join(['#include <%s>' % h for h in header])
|
|
# Test library. On success, good. On failure, test header to
|
|
# give the user more help.
|
|
if self.Link(context, text=include, main=main, msg='for usable library ' + lib, successflags=successflags):
|
|
return
|
|
if self.Compile(context, text=include, main=main, msg='for usable header ' + header[-1]):
|
|
raise SCons.Errors.StopError("Header %s is usable, but library %s is not usable." % (header[-1], lib))
|
|
if self.Compile(context, text=include, main=main, msg='for parseable header ' + header[-1]):
|
|
raise SCons.Errors.StopError("Header %s is parseable, but cannot compile the test program." % (header[-1]))
|
|
raise SCons.Errors.StopError("Header %s is missing or unusable." % (header[-1]))
|
|
@_custom_test
|
|
def check_libphysfs(self,context):
|
|
self._check_system_library(context,header=['physfs.h'],main='''
|
|
PHYSFS_File *f;
|
|
char b[1] = {0};
|
|
PHYSFS_init("");
|
|
f = PHYSFS_openWrite("a");
|
|
PHYSFS_sint64 w = PHYSFS_write(f, b, 1, 1);
|
|
(void)w;
|
|
PHYSFS_close(f);
|
|
f = PHYSFS_openRead("a");
|
|
PHYSFS_sint64 r = PHYSFS_read(f, b, 1, 1);
|
|
(void)r;
|
|
PHYSFS_close(f);
|
|
''',
|
|
lib='physfs',
|
|
successflags={'LIBS' : ('physfs',)}
|
|
)
|
|
@_custom_test
|
|
def check_SDL_mixer(self,context):
|
|
msg = 'whether to use SDL_mixer'
|
|
context.Display('%s: checking %s...' % (self.msgprefix, msg))
|
|
# SDL_mixer support?
|
|
context.Result(self.user_settings.sdlmixer)
|
|
if not self.user_settings.sdlmixer:
|
|
return
|
|
self._extend_successflags('CPPDEFINES', ['USE_SDLMIXER'])
|
|
successflags = {}
|
|
if self.user_settings.host_platform == 'darwin':
|
|
successflags['FRAMEWORKS'] = ['SDL_mixer']
|
|
successflags['CPPPATH'] = [os.path.join(os.getenv("HOME"), 'Library/Frameworks/SDL_mixer.framework/Headers'), '/Library/Frameworks/SDL_mixer.framework/Headers']
|
|
self._check_system_library(context,header=['SDL_mixer.h'],main='''
|
|
int i = Mix_Init(MIX_INIT_FLAC | MIX_INIT_OGG);
|
|
(void)i;
|
|
Mix_Pause(0);
|
|
Mix_ResumeMusic();
|
|
Mix_Quit();
|
|
''',
|
|
lib='SDL_mixer', successflags=successflags)
|
|
@_implicit_test
|
|
def check_cxx_works(self,context):
|
|
"""
|
|
help:assume C++ compiler works
|
|
"""
|
|
if self.Link(context, text='', msg='whether C++ compiler and linker work'):
|
|
return
|
|
if self.Compile(context, text='', msg='whether C++ compiler works'):
|
|
raise SCons.Errors.StopError("C++ compiler works, but C++ linker does not work.")
|
|
raise SCons.Errors.StopError("C++ compiler does not work.")
|
|
@_custom_test
|
|
def check_compiler_redundant_decl_warning(self,context):
|
|
f = {'CXXFLAGS' : [get_Werror_string(context.env['CXXFLAGS']) + 'redundant-decls']}
|
|
# Test for http://gcc.gnu.org/bugzilla/show_bug.cgi?id=15867
|
|
text = '''
|
|
template <typename>
|
|
struct A {
|
|
int a(){return 0;}
|
|
};
|
|
template <>
|
|
int A<int>::a(){return 1;}
|
|
'''
|
|
if self.Compile(context, text=text, msg='whether C++ compiler treats specializations as distinct', successflags=f):
|
|
return
|
|
if self.Compile(context, text='int a();int a();', msg='whether C++ compiler implements -Wredundant-decls', testflags=f, expect_failure=True):
|
|
return
|
|
self.Compile(context, text='int a();', msg='whether C++ compiler accepts -Wredundant-decls', testflags=f)
|
|
@_custom_test
|
|
def check_attribute_error(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((error))
|
|
"""
|
|
f = '''
|
|
void a()__attribute__((__error__("a called")));
|
|
'''
|
|
if self.Compile(context, text=f, main='if("0"[0]==\'1\')a();', msg='whether compiler optimizes function __attribute__((__error__))'):
|
|
context.sconf.Define('DXX_HAVE_ATTRIBUTE_ERROR')
|
|
context.sconf.Define('__attribute_error(M)', '__attribute__((__error__(M)))')
|
|
else:
|
|
self.Compile(context, text=f, main='a();', msg='whether compiler understands function __attribute__((__error__))', expect_failure=True) or \
|
|
self.Compile(context, text=f, msg='whether compiler accepts function __attribute__((__error__))')
|
|
context.sconf.Define('__attribute_error(M)', self.comment_not_supported)
|
|
@_custom_test
|
|
def check_builtin_bswap(self,context):
|
|
b = '(void)__builtin_bswap{bits}(static_cast<uint{bits}_t>(argc));'
|
|
include = '''
|
|
#include <cstdint>
|
|
'''
|
|
main = '''
|
|
{b64}
|
|
{b32}
|
|
#ifdef DXX_HAVE_BUILTIN_BSWAP16
|
|
{b16}
|
|
#endif
|
|
'''.format(
|
|
b64 = b.format(bits=64),
|
|
b32 = b.format(bits=32),
|
|
b16 = b.format(bits=16),
|
|
)
|
|
if self.Cxx11Compile(context, text=include, main=main, msg='whether compiler implements __builtin_bswap{16,32,64} functions', successflags={'CPPDEFINES' : ['DXX_HAVE_BUILTIN_BSWAP', 'DXX_HAVE_BUILTIN_BSWAP16']}):
|
|
return
|
|
if self.Cxx11Compile(context, text=include, main=main, msg='whether compiler implements __builtin_bswap{32,64} functions', successflags={'CPPDEFINES' : ['DXX_HAVE_BUILTIN_BSWAP']}):
|
|
return
|
|
@_custom_test
|
|
def check_builtin_constant_p(self,context):
|
|
"""
|
|
help:assume compiler supports compile-time __builtin_constant_p
|
|
"""
|
|
f = '''
|
|
int c(int);
|
|
static int a(int b){
|
|
return __builtin_constant_p(b) ? 1 : %s;
|
|
}
|
|
'''
|
|
main = 'return a(1) + a(2)'
|
|
if self.Link(context, text=f % 'c(b)', main=main, msg='whether compiler optimizes __builtin_constant_p'):
|
|
context.sconf.Define('DXX_HAVE_BUILTIN_CONSTANT_P')
|
|
context.sconf.Define('dxx_builtin_constant_p(A)', '__builtin_constant_p(A)')
|
|
else:
|
|
self.Compile(context, text=f % '2', main=main, msg='whether compiler accepts __builtin_constant_p')
|
|
context.sconf.Define('dxx_builtin_constant_p(A)', '((void)(A),0)')
|
|
@_custom_test
|
|
def check_builtin_object_size(self,context):
|
|
"""
|
|
help:assume compiler supports __builtin_object_size
|
|
"""
|
|
f = '''
|
|
int a();
|
|
static inline int a(char *c){
|
|
return __builtin_object_size(c,0) == 4 ? 1 : %s;
|
|
}
|
|
'''
|
|
main = '''
|
|
char c[4];
|
|
return a(c);
|
|
'''
|
|
if self.Link(context, text=f % 'a()', main=main, msg='whether compiler optimizes __builtin_object_size'):
|
|
context.sconf.Define('DXX_HAVE_BUILTIN_OBJECT_SIZE')
|
|
else:
|
|
self.Compile(context, text=f % '2', main=main, msg='whether compiler accepts __builtin_object_size')
|
|
@_custom_test
|
|
def check_embedded_compound_statement(self,context):
|
|
f = '''
|
|
return ({ 1 + 2; });
|
|
'''
|
|
if self.Compile(context, text='', main=f, msg='whether compiler understands embedded compound statements'):
|
|
context.sconf.Define('DXX_BEGIN_COMPOUND_STATEMENT', '')
|
|
context.sconf.Define('DXX_END_COMPOUND_STATEMENT', '')
|
|
else:
|
|
context.sconf.Define('DXX_BEGIN_COMPOUND_STATEMENT', '[&]')
|
|
context.sconf.Define('DXX_END_COMPOUND_STATEMENT', '()')
|
|
context.sconf.Define('DXX_ALWAYS_ERROR_FUNCTION(F,S)', r'( DXX_BEGIN_COMPOUND_STATEMENT { \
|
|
void F() __attribute_error(S); \
|
|
F(); \
|
|
} DXX_END_COMPOUND_STATEMENT )')
|
|
@_custom_test
|
|
def check_attribute_alloc_size(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((alloc_size))
|
|
"""
|
|
macro_name = '__attribute_alloc_size(A,...)'
|
|
macro_value = '__attribute__((alloc_size(A, ## __VA_ARGS__)))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
char*a(int)__attribute_alloc_size(1);
|
|
char*b(int,int)__attribute_alloc_size(1,2);
|
|
""", msg='for function __attribute__((alloc_size))')
|
|
@_custom_test
|
|
def check_attribute_cold(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((cold))
|
|
"""
|
|
macro_name = '__attribute_cold'
|
|
macro_value = '__attribute__((cold))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
__attribute_cold char*a(int);
|
|
""", msg='for function __attribute__((cold))')
|
|
@_custom_test
|
|
def check_attribute_format_arg(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((format_arg))
|
|
"""
|
|
macro_name = '__attribute_format_arg(A)'
|
|
macro_value = '__attribute__((format_arg(A)))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
char*a(char*)__attribute_format_arg(1);
|
|
""", msg='for function __attribute__((format_arg))')
|
|
@_custom_test
|
|
def check_attribute_format_printf(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((format(printf)))
|
|
"""
|
|
macro_name = '__attribute_format_printf(A,B)'
|
|
macro_value = '__attribute__((format(printf,A,B)))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
int a(char*,...)__attribute_format_printf(1,2);
|
|
int b(char*)__attribute_format_printf(1,0);
|
|
""", msg='for function __attribute__((format(printf)))')
|
|
@_custom_test
|
|
def check_attribute_malloc(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((malloc))
|
|
"""
|
|
macro_name = '__attribute_malloc()'
|
|
macro_value = '__attribute__((malloc))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
int *a()__attribute_malloc();
|
|
""", msg='for function __attribute__((malloc))')
|
|
@_custom_test
|
|
def check_attribute_nonnull(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((nonnull))
|
|
"""
|
|
macro_name = '__attribute_nonnull(...)'
|
|
macro_value = '__attribute__((nonnull __VA_ARGS__))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
int a(int*)__attribute_nonnull();
|
|
int b(int*)__attribute_nonnull((1));
|
|
""", msg='for function __attribute__((nonnull))')
|
|
@_custom_test
|
|
def check_attribute_used(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((used))
|
|
"""
|
|
macro_name = '__attribute_used'
|
|
macro_value = '__attribute__((used))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
static void a()__attribute_used;
|
|
static void a(){}
|
|
""", msg='for function __attribute__((used))')
|
|
@_custom_test
|
|
def check_attribute_warn_unused_result(self,context):
|
|
"""
|
|
help:assume compiler supports __attribute__((warn_unused_result))
|
|
"""
|
|
macro_name = '__attribute_warn_unused_result'
|
|
macro_value = '__attribute__((warn_unused_result))'
|
|
self._check_macro(context,macro_name=macro_name,macro_value=macro_value,test="""
|
|
int a()__attribute_warn_unused_result;
|
|
int a(){return 0;}
|
|
""", msg='for function __attribute__((warn_unused_result))')
|
|
@_implicit_test
|
|
def check_cxx11(self,context):
|
|
"""
|
|
help:assume C++ compiler supports C++11
|
|
"""
|
|
return self._check_cxx_std_flag(context, ('-std=gnu++0x', '-std=c++0x'), self._cxx_conformance_cxx11)
|
|
@_implicit_test
|
|
def check_cxx14(self,context):
|
|
"""
|
|
help:assume C++ compiler supports C++14
|
|
"""
|
|
return self._check_cxx_std_flag(context, ('-std=gnu++14', '-std=c++14'), self._cxx_conformance_cxx14)
|
|
def _check_cxx_std_flag(self,context,flags,level):
|
|
for f in flags:
|
|
r = self.Compile(context, text='', msg='whether C++ compiler accepts {f}'.format(f=f), successflags={'CXXFLAGS': [f]})
|
|
if r:
|
|
return level
|
|
return 0
|
|
def Cxx11Compile(self,context,*args,**kwargs):
|
|
kwargs.setdefault('skipped', self.__skip_missing_cxx_std(context, self._cxx_conformance_cxx11, 'no C++11 support'))
|
|
return self.Compile(context,*args,**kwargs)
|
|
def Cxx14Compile(self,context,*args,**kwargs):
|
|
kwargs.setdefault('skipped', self.__skip_missing_cxx_std(context, self._cxx_conformance_cxx14, 'no C++14 support'))
|
|
return self.Compile(context,*args,**kwargs)
|
|
def __skip_missing_cxx_std(self,context,level,text):
|
|
if self.__cxx_conformance is None:
|
|
self.__cxx_conformance = self.check_cxx14(context) or self.check_cxx11(context)
|
|
if self.__cxx_conformance < level:
|
|
return text
|
|
@_implicit_test
|
|
def check_boost_array(self,context,**kwargs):
|
|
"""
|
|
help:assume Boost.Array works
|
|
"""
|
|
return self.Compile(context, msg='for Boost.Array', successflags={'CPPDEFINES' : ['DXX_HAVE_BOOST_ARRAY']}, **kwargs)
|
|
@_implicit_test
|
|
def check_cxx_array(self,context,**kwargs):
|
|
"""
|
|
help:assume <array> works
|
|
"""
|
|
return self.Cxx11Compile(context, msg='for <array>', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX_ARRAY']}, **kwargs)
|
|
@_implicit_test
|
|
def check_cxx_tr1_array(self,context,**kwargs):
|
|
"""
|
|
help:assume <tr1/array> works
|
|
"""
|
|
return self.Compile(context, msg='for <tr1/array>', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX_TR1_ARRAY']}, **kwargs)
|
|
@_custom_test
|
|
def _check_cxx_array(self,context):
|
|
include = '''
|
|
#include "compiler-array.h"
|
|
'''
|
|
main = '''
|
|
array<int,2>b;
|
|
b[0]=1;
|
|
'''
|
|
how = self.check_cxx_array(context, text=include, main=main) or self.check_boost_array(context, text=include, main=main) or self.check_cxx_tr1_array(context, text=include, main=main)
|
|
if not how:
|
|
raise SCons.Errors.StopError("C++ compiler does not support <array> or Boost.Array or <tr1/array>.")
|
|
@_custom_test
|
|
def check_cxx11_function_auto(self,context):
|
|
"""
|
|
help:assume compiler supports C++11 function declarator syntax
|
|
"""
|
|
f = '''
|
|
auto f()->int;
|
|
'''
|
|
if not self.Cxx11Compile(context, text=f, msg='for C++11 function declarator syntax'):
|
|
raise SCons.Errors.StopError("C++ compiler does not support C++11 function declarator syntax.")
|
|
def _check_static_assert_method(self,context,msg,f,testflags={},_Compile=Compile,**kwargs):
|
|
return _Compile(self, context, text=f % 'true', msg=msg % 'true', testflags=testflags, **kwargs) and \
|
|
_Compile(self, context, text=f % 'false', msg=msg % 'false', expect_failure=True, successflags=testflags, **kwargs)
|
|
@_implicit_test
|
|
def check_boost_static_assert(self,context,f):
|
|
"""
|
|
help:assume Boost.StaticAssert works
|
|
"""
|
|
return self._check_static_assert_method(context, 'for Boost.StaticAssert when %s', f, testflags={'CPPDEFINES' : ['DXX_HAVE_BOOST_STATIC_ASSERT']})
|
|
@_implicit_test
|
|
def check_c_typedef_static_assert(self,context,f):
|
|
"""
|
|
help:assume C typedef-based static assertion works
|
|
"""
|
|
return self._check_static_assert_method(context, 'for C typedef static assertion when %s', f, testflags={'CPPDEFINES' : ['DXX_HAVE_C_TYPEDEF_STATIC_ASSERT']})
|
|
@_implicit_test
|
|
def check_cxx11_static_assert(self,context,f):
|
|
"""
|
|
help:assume compiler supports C++ intrinsic static_assert
|
|
"""
|
|
return self._check_static_assert_method(context, 'for C++11 intrinsic static_assert when %s', f, testflags={'CPPDEFINES' : ['DXX_HAVE_CXX11_STATIC_ASSERT']}, _Compile=ConfigureTests.Cxx11Compile)
|
|
@_custom_test
|
|
def _check_static_assert(self,context):
|
|
f = '''
|
|
#include "compiler-static_assert.h"
|
|
static_assert(%s, "");
|
|
'''
|
|
how = self.check_cxx11_static_assert(context,f) or self.check_boost_static_assert(context,f) or self.check_c_typedef_static_assert(context,f)
|
|
if not how:
|
|
raise SCons.Errors.StopError("C++ compiler does not support static_assert or Boost.StaticAssert or typedef-based static assertion.")
|
|
@_implicit_test
|
|
def check_boost_type_traits(self,context,f):
|
|
"""
|
|
help:assume Boost.TypeTraits works
|
|
"""
|
|
return self.Compile(context, text=f, msg='for Boost.TypeTraits', ext='.cpp', successflags={'CPPDEFINES' : ['DXX_HAVE_BOOST_TYPE_TRAITS']})
|
|
@_implicit_test
|
|
def check_cxx11_type_traits(self,context,f):
|
|
"""
|
|
help:assume <type_traits> works
|
|
"""
|
|
return self.Cxx11Compile(context, text=f, msg='for <type_traits>', ext='.cpp', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX11_TYPE_TRAITS']})
|
|
@_custom_test
|
|
def _check_type_traits(self,context):
|
|
f = '''
|
|
#include "compiler-type_traits.h"
|
|
typedef tt::conditional<true,int,long>::type a;
|
|
typedef tt::conditional<false,int,long>::type b;
|
|
'''
|
|
if self.check_cxx11_type_traits(context, f) or self.check_boost_type_traits(context, f):
|
|
context.sconf.Define('DXX_HAVE_TYPE_TRAITS')
|
|
@_implicit_test
|
|
def check_boost_foreach(self,context,**kwargs):
|
|
"""
|
|
help:assume Boost.Foreach works
|
|
"""
|
|
return self.Compile(context, msg='for Boost.Foreach', successflags={'CPPDEFINES' : ['DXX_HAVE_BOOST_FOREACH']}, **kwargs)
|
|
@_implicit_test
|
|
def check_cxx11_range_for(self,context,**kwargs):
|
|
return self.Cxx11Compile(context, msg='for C++11 range-based for', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX11_RANGE_FOR']}, **kwargs)
|
|
@_custom_test
|
|
def _check_range_based_for(self,context):
|
|
include = '''
|
|
#include "compiler-range_for.h"
|
|
'''
|
|
main = '''
|
|
int b[2];
|
|
range_for(int&c,b)c=0;
|
|
'''
|
|
if not self.check_cxx11_range_for(context, text=include, main=main) and not self.check_boost_foreach(context, text=include, main=main):
|
|
raise SCons.Errors.StopError("C++ compiler does not support range-based for or Boost.Foreach.")
|
|
@_custom_test
|
|
def check_cxx11_constexpr(self,context):
|
|
f = '''
|
|
struct A {};
|
|
constexpr A a(){return {};}
|
|
'''
|
|
if not self.Cxx11Compile(context, text=f, msg='for C++11 constexpr', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX11_CONSTEXPR']}):
|
|
context.sconf.Define('constexpr', self.comment_not_supported)
|
|
@_implicit_test
|
|
def check_pch(self,context):
|
|
for how in [{'CXXFLAGS' : ['-x', 'c++-header']}]:
|
|
result = self.Compile(context, text='', msg='whether compiler supports pre-compiled headers', testflags=how)
|
|
if result:
|
|
self.pch_flags = how
|
|
return result
|
|
@_custom_test
|
|
def _check_pch(self,context):
|
|
self.pch_flags = None
|
|
msg = 'when to pre-compile headers'
|
|
context.Display('%s: checking %s...' % (self.msgprefix, msg))
|
|
if self.user_settings.pch:
|
|
count = int(self.user_settings.pch)
|
|
else:
|
|
count = 0
|
|
if count <= 0:
|
|
context.Result('never')
|
|
return
|
|
context.Display('if used at least %u time%s\n' % (count, 's' if count > 1 else ''))
|
|
if not self.check_pch(context):
|
|
raise SCons.Errors.StopError("C++ compiler does not support pre-compiled headers.")
|
|
@_custom_test
|
|
def check_cxx11_explicit_bool(self,context):
|
|
"""
|
|
help:assume compiler supports explicit operator bool
|
|
"""
|
|
f = '''
|
|
struct A{explicit operator bool();};
|
|
'''
|
|
r = self.Cxx11Compile(context, text=f, msg='for explicit operator bool')
|
|
macro_name = 'dxx_explicit_operator_bool'
|
|
if r:
|
|
context.sconf.Define(macro_name, 'explicit')
|
|
context.sconf.Define('DXX_HAVE_EXPLICIT_OPERATOR_BOOL')
|
|
else:
|
|
context.sconf.Define(macro_name, self.comment_not_supported)
|
|
@_custom_test
|
|
def check_cxx11_explicit_delete(self,context):
|
|
"""
|
|
help:assume compiler supports explicitly deleted functions
|
|
"""
|
|
f = '''
|
|
int a()=delete;
|
|
'''
|
|
r = self.Cxx11Compile(context, text=f, msg='for explicitly deleted functions')
|
|
macro_name = 'DXX_CXX11_EXPLICIT_DELETE'
|
|
if r:
|
|
context.sconf.Define(macro_name, '=delete')
|
|
context.sconf.Define('DXX_HAVE_CXX11_EXPLICIT_DELETE')
|
|
else:
|
|
context.sconf.Define(macro_name, self.comment_not_supported)
|
|
@_implicit_test
|
|
def check_cxx11_free_begin(self,context,**kwargs):
|
|
return self.Cxx11Compile(context, msg='for C++11 functions begin(), end()', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX11_BEGIN']}, **kwargs)
|
|
@_implicit_test
|
|
def check_boost_free_begin(self,context,**kwargs):
|
|
return self.Compile(context, msg='for Boost.Range functions begin(), end()', successflags={'CPPDEFINES' : ['DXX_HAVE_BOOST_BEGIN']}, **kwargs)
|
|
@_custom_test
|
|
def _check_free_begin_function(self,context):
|
|
f = '''
|
|
#include "compiler-begin.h"
|
|
struct A {
|
|
typedef int *iterator;
|
|
typedef const int *const_iterator;
|
|
iterator begin(){return 0;}
|
|
iterator end(){return 0;}
|
|
const_iterator begin() const{return 0;}
|
|
const_iterator end() const{return 0;}
|
|
};
|
|
#define F(C){\
|
|
C int a[1]{0};\
|
|
C A b{};\
|
|
if(begin(a)||end(a)||begin(b)||end(b))return 1;\
|
|
}
|
|
'''
|
|
main = 'F()F(const)'
|
|
if not self.check_cxx11_free_begin(context, text=f, main=main) and not self.check_boost_free_begin(context, text=f, main=main):
|
|
raise SCons.Errors.StopError("C++ compiler does not support free functions begin() and end().")
|
|
@_implicit_test
|
|
def check_cxx11_addressof(self,context,**kwargs):
|
|
return self.Cxx11Compile(context, msg='for C++11 function addressof()', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX11_ADDRESSOF']}, **kwargs)
|
|
@_implicit_test
|
|
def check_boost_addressof(self,context,**kwargs):
|
|
return self.Compile(context, msg='for Boost.Utility function addressof()', successflags={'CPPDEFINES' : ['DXX_HAVE_BOOST_ADDRESSOF']}, **kwargs)
|
|
@_custom_test
|
|
def _check_free_addressof_function(self,context):
|
|
f = '''
|
|
#include "compiler-addressof.h"
|
|
struct A {
|
|
void operator&();
|
|
};
|
|
'''
|
|
main = '''
|
|
A b;
|
|
return addressof(b) != 0;
|
|
'''
|
|
if not self.check_cxx11_addressof(context, text=f, main=main) and not self.check_boost_addressof(context, text=f, main=main):
|
|
raise SCons.Errors.StopError("C++ compiler does not support free function addressof().")
|
|
@_custom_test
|
|
def check_cxx14_exchange(self,context):
|
|
f = '''
|
|
#include "compiler-exchange.h"
|
|
'''
|
|
self.Cxx14Compile(context, text=f, main='return exchange(argc, 5)', msg='for C++14 exchange', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX14_EXCHANGE']})
|
|
@_custom_test
|
|
def check_cxx14_integer_sequence(self,context):
|
|
f = '''
|
|
#include <utility>
|
|
using std::integer_sequence;
|
|
using std::index_sequence;
|
|
'''
|
|
self.Cxx14Compile(context, text=f, msg='for C++14 integer_sequence', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX14_INTEGER_SEQUENCE']})
|
|
@_custom_test
|
|
def check_cxx14_make_unique(self,context):
|
|
f = '''
|
|
#include "compiler-make_unique.h"
|
|
'''
|
|
main = '''
|
|
make_unique<int>(0);
|
|
make_unique<int[]>(1);
|
|
'''
|
|
self.Cxx14Compile(context, text=f, main=main, msg='for C++14 make_unique', successflags={'CPPDEFINES' : ['DXX_HAVE_CXX14_MAKE_UNIQUE']})
|
|
@_implicit_test
|
|
def check_cxx11_inherit_constructor(self,context,text,fmtargs,**kwargs):
|
|
"""
|
|
help:assume compiler supports inheriting constructors
|
|
"""
|
|
macro_value = self._quote_macro_value('''
|
|
typedef B,##__VA_ARGS__ _dxx_constructor_base_type;
|
|
using _dxx_constructor_base_type::_dxx_constructor_base_type;''')
|
|
if self.Cxx11Compile(context, text=text.format(macro_value=macro_value, **fmtargs), msg='for C++11 inherited constructors', **kwargs):
|
|
return macro_value
|
|
return None
|
|
@_implicit_test
|
|
def check_cxx11_variadic_forward_constructor(self,context,text,fmtargs,**kwargs):
|
|
"""
|
|
help:assume compiler supports variadic template-based constructor forwarding
|
|
"""
|
|
macro_value = self._quote_macro_value('''
|
|
template <typename... Args>
|
|
D(Args&&... args) :
|
|
B,##__VA_ARGS__{std::forward<Args>(args)...} {}
|
|
''')
|
|
if self.Cxx11Compile(context, text='#include <algorithm>\n' + text.format(macro_value=macro_value, **fmtargs), msg='for C++11 variadic templates on constructors', **kwargs):
|
|
return macro_value
|
|
return None
|
|
@_custom_test
|
|
def _check_forward_constructor(self,context):
|
|
text = '''
|
|
#define {macro_name}{macro_parameters} {macro_value}
|
|
struct A {{
|
|
A(int){{}}
|
|
}};
|
|
struct B:A {{
|
|
{macro_name}(B,A);
|
|
}};
|
|
'''
|
|
macro_name = 'DXX_INHERIT_CONSTRUCTORS'
|
|
macro_parameters = '(D,B,...)'
|
|
# C++03 support is possible with enumerated out template
|
|
# variations. If someone finds a worthwhile compiler without
|
|
# variadic templates, enumerated templates can be added.
|
|
for f in (self.check_cxx11_inherit_constructor, self.check_cxx11_variadic_forward_constructor):
|
|
macro_value = f(context, text=text, main='B(0)', fmtargs={'macro_name':macro_name, 'macro_parameters':macro_parameters})
|
|
if macro_value:
|
|
break
|
|
if not macro_value:
|
|
raise SCons.Errors.StopError("C++ compiler does not support constructor forwarding.")
|
|
context.sconf.Define(macro_name + macro_parameters, macro_value)
|
|
@_custom_test
|
|
def check_cxx11_template_alias(self,context):
|
|
text = '''
|
|
template <typename>
|
|
struct A;
|
|
template <typename T>
|
|
using B = A<T>;
|
|
'''
|
|
main = '''
|
|
A<int> *a = 0;
|
|
B<int> *b = a;
|
|
(void)b;
|
|
'''
|
|
if self.Cxx11Compile(context, text=text, main=main, msg='for C++11 template aliases'):
|
|
context.sconf.Define('DXX_HAVE_CXX11_TEMPLATE_ALIAS')
|
|
@_custom_test
|
|
def check_cxx11_ref_qualifier(self,context):
|
|
text = '''
|
|
struct A {
|
|
int a()&{return 1;}
|
|
int a()&&{return 2;}
|
|
};
|
|
'''
|
|
main = '''
|
|
A a;
|
|
return a.a() != A().a();
|
|
'''
|
|
if self.Cxx11Compile(context, text=text, main=main, msg='for C++11 reference qualified methods'):
|
|
context.sconf.Define('DXX_HAVE_CXX11_REF_QUALIFIER')
|
|
@_custom_test
|
|
def check_deep_tuple(self,context):
|
|
text = '''
|
|
#include <tuple>
|
|
static inline std::tuple<{type}> make() {{
|
|
return std::make_tuple({value});
|
|
}}
|
|
static void a(){{
|
|
std::tuple<{type}> t = make();
|
|
(void)t;
|
|
}}
|
|
'''
|
|
count = 20
|
|
if self.Compile(context, text=text.format(type=','.join(('int',)*count), value=','.join(('0',)*count)), main='a()', msg='whether compiler handles 20-element tuples'):
|
|
return
|
|
count = 2
|
|
if self.Compile(context, text=text.format(type=','.join(('int',)*count), value=','.join(('0',)*count)), main='a()', msg='whether compiler handles 2-element tuples'):
|
|
raise SCons.Errors.StopError("Compiler cannot handle tuples of 20 elements. Raise the template instantiation depth.")
|
|
raise SCons.Errors.StopError("Compiler cannot handle tuples of 2 elements.")
|
|
@_implicit_test
|
|
def check_poison_valgrind(self,context):
|
|
context.Display('%s: checking %s...' % (self.msgprefix, 'whether to use Valgrind poisoning'))
|
|
r = self.user_settings.poison == 'valgrind'
|
|
context.Result(r)
|
|
if not r:
|
|
return
|
|
text = '''
|
|
#include "poison.h"
|
|
'''
|
|
main = '''
|
|
DXX_MAKE_MEM_UNDEFINED(&argc, sizeof(argc));
|
|
'''
|
|
if self.Compile(context, text=text, main=main, msg='whether Valgrind memcheck header works', successflags={'CPPDEFINES' : ['DXX_HAVE_POISON_VALGRIND']}):
|
|
return True
|
|
raise SCons.Errors.StopError("Valgrind poison requested, but <valgrind/memcheck.h> does not work.")
|
|
@_custom_test
|
|
def _check_poison_method(self,context):
|
|
if self.check_poison_valgrind(context):
|
|
context.sconf.Define('DXX_HAVE_POISON')
|
|
|
|
class LazyObjectConstructor:
|
|
def __get_lazy_object(self,srcname,transform_target):
|
|
env = self.env
|
|
o = env.StaticObject(target='%s%s%s' % (self.user_settings.builddir, transform_target(self, srcname), env["OBJSUFFIX"]), source=srcname)
|
|
if env._dxx_pch_node:
|
|
env.Depends(o, self.env._dxx_pch_node)
|
|
return o
|
|
def __lazy_objects(self,name,source):
|
|
try:
|
|
return self.__lazy_object_cache[name]
|
|
except KeyError as e:
|
|
def __strip_extension(self,name):
|
|
return os.path.splitext(name)[0]
|
|
value = []
|
|
for s in source:
|
|
if isinstance(s, str):
|
|
s = {'source': [s]}
|
|
transform_target = s.get('transform_target', __strip_extension)
|
|
value.extend([self.__get_lazy_object(srcname, transform_target) for srcname in s['source']])
|
|
self.__lazy_object_cache[name] = value
|
|
return value
|
|
|
|
@staticmethod
|
|
def create_lazy_object_getter(sources):
|
|
name = repr(sources)
|
|
return lambda s: s.__lazy_objects(name, sources)
|
|
|
|
@classmethod
|
|
def create_lazy_object_property(cls,sources):
|
|
return property(cls.create_lazy_object_getter(sources))
|
|
|
|
def __init__(self):
|
|
self.__lazy_object_cache = {}
|
|
|
|
class FilterHelpText:
|
|
def __init__(self):
|
|
self.visible_arguments = []
|
|
self._sconf_align = None
|
|
def FormatVariableHelpText(self, env, opt, help, default, actual, aliases):
|
|
if not opt in self.visible_arguments:
|
|
return ''
|
|
if not self._sconf_align:
|
|
self._sconf_align = len(max((s for s in self.visible_arguments if s[:6] == 'sconf_'), key=len))
|
|
l = []
|
|
if default is not None:
|
|
if isinstance(default, str) and not default.isalnum():
|
|
default = '"%s"' % default
|
|
l.append("default: {default}".format(default=default))
|
|
actual = getattr(self, opt, None)
|
|
if actual is not None:
|
|
if isinstance(actual, str) and not actual.isalnum():
|
|
actual = '"%s"' % actual
|
|
l.append("current: {current}".format(current=actual))
|
|
return (" {opt:%u} {help}" % (self._sconf_align if opt[:6] == 'sconf_' else 15)).format(opt=opt, help=help) + (" [" + "; ".join(l) + "]" if l else '') + '\n'
|
|
|
|
class DXXCommon(LazyObjectConstructor):
|
|
__shared_program_instance = [0]
|
|
__shared_header_file_list = []
|
|
__endian = checkEndian()
|
|
@property
|
|
def program_message_prefix(self):
|
|
return '%s.%d' % (self.PROGRAM_NAME, self.program_instance)
|
|
# Settings which affect how the files are compiled
|
|
class UserBuildSettings:
|
|
# Paths for the Videocore libs/includes on the Raspberry Pi
|
|
RPI_DEFAULT_VC_PATH='/opt/vc'
|
|
default_OGLES_LIB = 'GLES_CM'
|
|
_default_prefix = '/usr/local'
|
|
def default_builddir(self):
|
|
builddir_prefix = self.builddir_prefix
|
|
builddir_suffix = self.builddir_suffix
|
|
default_builddir = builddir_prefix or ''
|
|
if builddir_prefix is not None or builddir_suffix is not None:
|
|
fields = [
|
|
self.host_platform,
|
|
os.path.basename(self.CXX) if self.CXX else None,
|
|
]
|
|
compiler_flags = '\n'.join((getattr(self, attr) or '').strip() for attr in ['CPPFLAGS', 'CXXFLAGS'])
|
|
if compiler_flags:
|
|
# Mix in CRC of CXXFLAGS to get reasonable uniqueness
|
|
# when flags are changed. A full hash is
|
|
# unnecessary here.
|
|
crc = binascii.crc32(compiler_flags)
|
|
if crc < 0:
|
|
crc = crc + 0x100000000
|
|
fields.append('{:08x}'.format(crc))
|
|
if self.pch:
|
|
fields.append('p%s' % self.pch)
|
|
fields.append(''.join(a[1] if getattr(self, a[0]) else (a[2] if len(a) > 2 else '')
|
|
for a in (
|
|
('debug', 'dbg'),
|
|
('lto', 'lto'),
|
|
('profiler', 'prf'),
|
|
('editor', 'ed'),
|
|
('opengl', 'ogl', 'sdl'),
|
|
('opengles', 'es'),
|
|
('raspberrypi', 'rpi'),
|
|
)))
|
|
default_builddir += '-'.join([f for f in fields if f])
|
|
if builddir_suffix is not None:
|
|
default_builddir += builddir_prefix
|
|
return default_builddir
|
|
def default_memdebug(self):
|
|
return self.debug
|
|
# automatic setup for raspberrypi
|
|
def default_opengles(self):
|
|
if self.raspberrypi:
|
|
return True
|
|
return False
|
|
def selected_OGLES_LIB(self):
|
|
if self.raspberrypi:
|
|
return 'GLESv2'
|
|
return self.default_OGLES_LIB
|
|
def __default_DATA_DIR(self):
|
|
return self.prefix + '/share/games/' + self._program.target
|
|
@staticmethod
|
|
def _generic_variable(key,help,default):
|
|
return (key, help, default)
|
|
@staticmethod
|
|
def _enum_variable(key,help,default,allowed_values):
|
|
return EnumVariable(key, help, default, allowed_values)
|
|
def _options(self):
|
|
return (
|
|
{
|
|
'variable': self._enum_variable,
|
|
'arguments': [
|
|
('sconf_%s' % name[6:], None, ConfigureTests.describe(name) or ('assume result of %s' % name), {'allowed_values' : ['0', '1', '2', ConfigureTests.sconf_force_failure, ConfigureTests.sconf_force_success, ConfigureTests.sconf_assume_success]}) for name in ConfigureTests.implicit_tests + ConfigureTests.custom_tests if name[0] != '_'
|
|
],
|
|
},
|
|
{
|
|
'variable': BoolVariable,
|
|
'arguments': (
|
|
('raspberrypi', False, 'build for Raspberry Pi (automatically sets opengles and opengles_lib)'),
|
|
('git_describe_version', os.path.exists(os.environ.get('GIT_DIR', '.git')), 'include git --describe in extra_version'),
|
|
('git_status', True, 'include git status'),
|
|
('versid_depend_all', False, 'rebuild vers_id.cpp if any object file changes'),
|
|
),
|
|
},
|
|
{
|
|
'variable': self._generic_variable,
|
|
'arguments': (
|
|
('rpi_vc_path', self.RPI_DEFAULT_VC_PATH, 'directory for RPi VideoCore libraries'),
|
|
('opengles_lib', self.selected_OGLES_LIB, 'name of the OpenGL ES library to link against'),
|
|
('prefix', self._default_prefix, 'installation prefix directory (Linux only)'),
|
|
('sharepath', self.__default_DATA_DIR, 'directory for shared game data (Linux only)'),
|
|
('pch', None, 'pre-compile headers used this many times'),
|
|
),
|
|
},
|
|
{
|
|
'variable': BoolVariable,
|
|
'arguments': (
|
|
('check_header_includes', False, 'compile test each header (developer option)'),
|
|
('debug', False, 'build DEBUG binary which includes asserts, debugging output, cheats and more output'),
|
|
('memdebug', self.default_memdebug, 'build with malloc tracking'),
|
|
('lto', False, 'enable gcc link time optimization'),
|
|
('profiler', False, 'profiler build'),
|
|
('opengl', True, 'build with OpenGL support'),
|
|
('opengles', self.default_opengles, 'build with OpenGL ES support'),
|
|
('editor', False, 'include editor into build (!EXPERIMENTAL!)'),
|
|
('sdlmixer', True, 'build with SDL_Mixer support for sound and music (includes external music support)'),
|
|
('ipv6', False, 'enable IPv6 compability'),
|
|
('use_udp', True, 'enable UDP support'),
|
|
('use_tracker', True, 'enable Tracker support (requires UDP)'),
|
|
('verbosebuild', False, 'print out all compiler/linker messages during building'),
|
|
),
|
|
},
|
|
{
|
|
'variable': self._generic_variable,
|
|
'arguments': (
|
|
('CHOST', os.environ.get('CHOST'), 'CHOST of output'),
|
|
('CXX', os.environ.get('CXX'), 'C++ compiler command'),
|
|
('PKG_CONFIG', os.environ.get('PKG_CONFIG'), 'PKG_CONFIG to run (Linux only)'),
|
|
('RC', os.environ.get('RC'), 'Windows resource compiler command'),
|
|
('extra_version', None, 'text to append to version, such as VCS identity'),
|
|
),
|
|
},
|
|
{
|
|
'variable': self._generic_variable,
|
|
'stack': ' ',
|
|
'arguments': (
|
|
('CPPFLAGS', os.environ.get('CPPFLAGS'), 'C preprocessor flags'),
|
|
('CXXFLAGS', os.environ.get('CXXFLAGS'), 'C++ compiler flags'),
|
|
('LDFLAGS', os.environ.get('LDFLAGS'), 'Linker flags'),
|
|
('LIBS', os.environ.get('LIBS'), 'Libraries to link'),
|
|
),
|
|
},
|
|
{
|
|
'variable': self._enum_variable,
|
|
'arguments': (
|
|
('host_platform', 'linux' if sys.platform == 'linux2' else sys.platform, 'cross-compile to specified platform', {'allowed_values' : ['win32', 'darwin', 'linux']}),
|
|
('poison', 'none', 'method for poisoning free memory', {'allowed_values' : ('none', 'valgrind')}),
|
|
),
|
|
},
|
|
{
|
|
'variable': self._generic_variable,
|
|
'arguments': (
|
|
('builddir_prefix', None, 'prefix to generated build directory'),
|
|
('builddir_suffix', None, 'suffix to generated build directory'),
|
|
# This must be last so that default_builddir will
|
|
# have access to other properties.
|
|
('builddir', self.default_builddir, 'build in specified directory'),
|
|
),
|
|
},
|
|
)
|
|
@staticmethod
|
|
def _names(name,prefix):
|
|
return ['%s%s%s' % (p, '_' if p else '', name) for p in prefix]
|
|
def __init__(self,program=None):
|
|
self._program = program
|
|
def register_variables(self,prefix,variables):
|
|
self.known_variables = []
|
|
for grp in self._options():
|
|
variable = grp['variable']
|
|
stack = grp.get('stack', None)
|
|
for opt in grp['arguments']:
|
|
(name,value,help) = opt[0:3]
|
|
kwargs = opt[3] if len(opt) > 3 else {}
|
|
if name not in variables.keys():
|
|
filtered_help.visible_arguments.append(name)
|
|
variables.Add(variable(key=name, help=help, default=None if callable(value) else value, **kwargs))
|
|
names = self._names(name, prefix)
|
|
for n in names:
|
|
if n not in variables.keys():
|
|
variables.Add(variable(key=n, help=help, default=None, **kwargs))
|
|
if not name in names:
|
|
names.append(name)
|
|
self.known_variables.append((names, name, value, stack))
|
|
if stack:
|
|
for n in names:
|
|
variables.Add(self._generic_variable(key='%s_stop' % n, help=None, default=None))
|
|
def read_variables(self,variables,d):
|
|
for (namelist,cname,dvalue,stack) in self.known_variables:
|
|
value = None
|
|
found_value = False
|
|
for n in namelist:
|
|
try:
|
|
v = d[n]
|
|
found_value = True
|
|
if stack:
|
|
if callable(v):
|
|
value = v(dvalue=dvalue, value=value, stack=stack)
|
|
else:
|
|
if value:
|
|
value = stack.join([value, v])
|
|
else:
|
|
value = v
|
|
if d.get(n + '_stop', None):
|
|
break
|
|
continue
|
|
value = v
|
|
break
|
|
except KeyError as e:
|
|
pass
|
|
if not found_value:
|
|
value = dvalue
|
|
if callable(value):
|
|
value = value()
|
|
setattr(self, cname, value)
|
|
if self.builddir != '' and self.builddir[-1:] != '/':
|
|
self.builddir += '/'
|
|
def clone(self):
|
|
clone = DXXCommon.UserBuildSettings(None)
|
|
for grp in clone._options():
|
|
for o in grp['arguments']:
|
|
name = o[0]
|
|
value = getattr(self, name)
|
|
setattr(clone, name, value)
|
|
return clone
|
|
class UserInstallSettings:
|
|
def _options(self):
|
|
return (
|
|
{
|
|
'variable': self._generic_variable,
|
|
'arguments': (
|
|
('DESTDIR', None, 'installation stage directory'),
|
|
('program_name', None, 'name of built program'),
|
|
),
|
|
},
|
|
{
|
|
'variable': BoolVariable,
|
|
'arguments': (
|
|
('register_install_target', True, 'report install target to SCons core'),
|
|
),
|
|
},
|
|
)
|
|
class UserSettings(UserBuildSettings,UserInstallSettings):
|
|
def _options(self):
|
|
return DXXCommon.UserBuildSettings._options(self) + DXXCommon.UserInstallSettings._options(self)
|
|
# Base class for platform-specific settings processing
|
|
class _PlatformSettings:
|
|
tools = None
|
|
ogllibs = ''
|
|
osasmdef = None
|
|
platform_objects = []
|
|
__pkg_config_path = None
|
|
__pkg_config_cache = {}
|
|
def __init__(self,program,user_settings):
|
|
self.__program = program
|
|
self.user_settings = user_settings
|
|
@property
|
|
def env(self):
|
|
return self.__program.env
|
|
@staticmethod
|
|
def get_pkg_config_name(user_settings):
|
|
if user_settings.PKG_CONFIG:
|
|
return user_settings.PKG_CONFIG
|
|
else:
|
|
if user_settings.CHOST:
|
|
return '%s-pkg-config' % user_settings.CHOST
|
|
else:
|
|
return 'pkg-config'
|
|
@classmethod
|
|
def get_pkg_config_path(cls,program):
|
|
if cls.__pkg_config_path:
|
|
return cls.__pkg_config_path[0]
|
|
pkgconfig = cls.get_pkg_config_name(program.user_settings)
|
|
if pkgconfig[0] != '/':
|
|
# Only valid on non-Windows
|
|
for p in os.environ.get('PATH', '').split(':'):
|
|
try:
|
|
fp = os.path.join(p, pkgconfig)
|
|
os.close(os.open(fp, os.O_RDONLY))
|
|
pkgconfig = fp
|
|
break
|
|
except OSError as e:
|
|
if e.errno == errno.ENOENT or e.errno == errno.EACCES:
|
|
continue
|
|
raise
|
|
if pkgconfig[0] != '/':
|
|
message(program, "no usable pkg-config \"%s\" found in $PATH" % pkgconfig)
|
|
pkgconfig = None
|
|
else:
|
|
message(program, "using pkg-config at %s" % pkgconfig)
|
|
cls.__pkg_config_path = (pkgconfig,)
|
|
return pkgconfig
|
|
@classmethod
|
|
def _find_pkg_config(cls,program,env,pkg,name):
|
|
pkgconfig = cls.get_pkg_config_path(program)
|
|
if not pkgconfig:
|
|
message(program, "skipping %s pkg-config settings" % name)
|
|
return {}
|
|
cmd = '%s --cflags --libs %s' % (pkgconfig,pkg)
|
|
cache = cls.__pkg_config_cache
|
|
try:
|
|
return cache[cmd]
|
|
except KeyError as e:
|
|
if (program.user_settings.verbosebuild != 0):
|
|
message(program, "reading %s settings from `%s`" % (name, cmd))
|
|
try:
|
|
flags = env.ParseFlags('!' + cmd)
|
|
except OSError as o:
|
|
message(program, "pkg-config failed; user must add required flags via environment for `%s`" % cmd)
|
|
flags = {}
|
|
cache[cmd] = flags
|
|
return flags
|
|
@staticmethod
|
|
def _merge_pkg_config(env,flags):
|
|
env.MergeFlags({k:flags[k] for k in flags.keys() if k[0] == 'C' or k[0] == 'L'})
|
|
def merge_SDL_mixer_config(self,program,env):
|
|
self._merge_pkg_config(env, self._find_pkg_config(program, env, 'SDL_mixer', 'SDL_mixer'))
|
|
def merge_sdl_config(self,program,env):
|
|
self._merge_pkg_config(env, self._find_pkg_config(program, env, 'sdl', 'SDL'))
|
|
# Settings to apply to mingw32 builds
|
|
class Win32PlatformSettings(_PlatformSettings):
|
|
tools = ['mingw']
|
|
osasmdef = 'win32'
|
|
def adjust_environment(self,program,env):
|
|
env.Append(CPPDEFINES = ['_WIN32', 'HAVE_STRUCT_TIMEVAL', 'WIN32_LEAN_AND_MEAN'])
|
|
class DarwinPlatformSettings(_PlatformSettings):
|
|
def __init__(self,program,user_settings):
|
|
DXXCommon._PlatformSettings.__init__(self,program,user_settings)
|
|
def adjust_environment(self,program,env):
|
|
env.Append(CPPDEFINES = ['HAVE_STRUCT_TIMESPEC', 'HAVE_STRUCT_TIMEVAL', '__unix__'])
|
|
env.Append(CPPPATH = [os.path.join(os.getenv("HOME"), 'Library/Frameworks/SDL.framework/Headers'), '/Library/Frameworks/SDL.framework/Headers'])
|
|
env.Append(FRAMEWORKS = ['ApplicationServices', 'Cocoa', 'SDL'])
|
|
if (self.user_settings.opengl == 1) or (self.user_settings.opengles == 1):
|
|
env.Append(FRAMEWORKS = ['OpenGL'])
|
|
env.Append(FRAMEWORKPATH = [os.path.join(os.getenv("HOME"), 'Library/Frameworks'), '/System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks'])
|
|
# Settings to apply to Linux builds
|
|
class LinuxPlatformSettings(_PlatformSettings):
|
|
osasmdef = 'elf'
|
|
__opengl_libs = ['GL', 'GLU']
|
|
__pkg_config_sdl = {}
|
|
def __init__(self,program,user_settings):
|
|
DXXCommon._PlatformSettings.__init__(self,program,user_settings)
|
|
if (user_settings.opengles == 1):
|
|
self.ogllibs = [ user_settings.opengles_lib, 'EGL']
|
|
else:
|
|
self.ogllibs = self.__opengl_libs
|
|
def adjust_environment(self,program,env):
|
|
env.Append(CPPDEFINES = ['HAVE_STRUCT_TIMESPEC', 'HAVE_STRUCT_TIMEVAL'])
|
|
env.Append(CCFLAGS = ['-pthread'])
|
|
|
|
def __init__(self):
|
|
LazyObjectConstructor.__init__(self)
|
|
self.sources = []
|
|
self.__shared_program_instance[0] += 1
|
|
self.program_instance = self.__shared_program_instance[0]
|
|
|
|
@staticmethod
|
|
def _collect_pch_candidates(target,source,env):
|
|
for t in target:
|
|
scanner = t.get_source_scanner(source[0])
|
|
deps = scanner(source[0], env, scanner.path(env))
|
|
for d in deps:
|
|
ds = str(d)
|
|
env.__dxx_pch_candidates[ds] = env.__dxx_pch_candidates.get(ds, 0) + 1
|
|
return (target, source)
|
|
|
|
@staticmethod
|
|
def write_pch_inclusion_file(target, source, env):
|
|
with open(str(target[0]), 'wt') as f:
|
|
f.write('/* BEGIN PCH GENERATED FILE\n * Threshold=%u\n */\n' % env.__dxx_pch_inclusion_count)
|
|
for (name,count) in env.__dxx_pch_candidates.items():
|
|
if count >= env.__dxx_pch_inclusion_count:
|
|
f.write('#include "%s"\t/* %u */\n' % (name, count))
|
|
env.Depends(target, name)
|
|
f.write('/* END PCH GENERATED FILE */\n')
|
|
|
|
def create_header_targets(self):
|
|
fs = SCons.Node.FS.get_default_fs()
|
|
builddir = self.user_settings.builddir
|
|
check_header_includes = os.path.join(builddir, 'check_header_includes.cpp')
|
|
if not self.__shared_header_file_list:
|
|
open(check_header_includes, 'wt')
|
|
git = subprocess.Popen(['git', 'ls-files', '-z', '--', '*.h'], executable='/usr/bin/git', stdout=subprocess.PIPE, close_fds=True)
|
|
headers = git.communicate(None)[0]
|
|
excluded_directories = (
|
|
'common/arch/cocoa/',
|
|
'common/arch/carbon/',
|
|
)
|
|
self.__shared_header_file_list.extend([h for h in headers.split('\0') if h and not h.startswith(excluded_directories)])
|
|
dirname = os.path.join(builddir, self.srcdir)
|
|
kwargs = {
|
|
'CXXFLAGS' : self.env['CXXFLAGS'][:],
|
|
'source' : check_header_includes
|
|
}
|
|
Depends = self.env.Depends
|
|
StaticObject = self.env.StaticObject
|
|
OBJSUFFIX = self.env['OBJSUFFIX']
|
|
for name in self.__shared_header_file_list:
|
|
if not name:
|
|
continue
|
|
if self.srcdir == 'common' and not name.startswith('common/'):
|
|
# Skip game-specific headers when testing common
|
|
continue
|
|
if self.srcdir[0] == 'd' and name[0] == 'd' and not name.startswith(self.srcdir):
|
|
# Skip d1 in d2 and d2 in d1
|
|
continue
|
|
CPPFLAGS = self.env['CPPFLAGS'][:]
|
|
if name[:24] == 'common/include/compiler-':
|
|
CPPFLAGS.extend(['-include', 'dxxsconf.h'])
|
|
CPPFLAGS.extend(['-include', name])
|
|
if self.user_settings.verbosebuild:
|
|
kwargs['CXXCOMSTR'] = "Checking %s %s %s" % (self.target, builddir, name)
|
|
Depends(StaticObject(target=os.path.join('%s/chi/%s%s' % (dirname, name, OBJSUFFIX)), CPPFLAGS=CPPFLAGS, **kwargs), fs.File(name))
|
|
|
|
def create_pch_node(self,dirname,configure_pch_flags):
|
|
if self.user_settings.check_header_includes:
|
|
self.create_header_targets()
|
|
if not configure_pch_flags:
|
|
self.env._dxx_pch_node = None
|
|
return
|
|
dirname = os.path.join(self.user_settings.builddir, dirname)
|
|
target = os.path.join(dirname, 'pch.h.gch')
|
|
source = os.path.join(dirname, 'pch.cpp')
|
|
self.env._dxx_pch_node = self.env.StaticObject(target=target, source=source, CXXFLAGS=self.env['CXXFLAGS'] + configure_pch_flags['CXXFLAGS'])
|
|
self.env.Append(CXXFLAGS = ['-include', str(self.env._dxx_pch_node[0])[:-4], '-Winvalid-pch'])
|
|
self.env.__dxx_pch_candidates = {}
|
|
self.env.__dxx_pch_inclusion_count = int(self.user_settings.pch)
|
|
self.env['BUILDERS']['StaticObject'].add_emitter('.cpp', self._collect_pch_candidates)
|
|
self.env.Command(source, None, self.write_pch_inclusion_file)
|
|
|
|
def _quote_cppdefine(self,s):
|
|
r = ''
|
|
prior = False
|
|
for c in str(s):
|
|
# No xdigit support in str
|
|
if c in ' ()*+,-./:=[]_' or (c.isalnum() and not (prior and (c.isdigit() or c in 'abcdefABCDEF'))):
|
|
r += c
|
|
elif c == '\n':
|
|
r += r'\n'
|
|
else:
|
|
r += '\\\\x' + binascii.b2a_hex(c)
|
|
prior = True
|
|
continue
|
|
prior = False
|
|
return '\\"' + r + '\\"'
|
|
|
|
def prepare_environment(self):
|
|
# Prettier build messages......
|
|
target_string = ' -o $TARGET'
|
|
cxxcom = self.env['CXXCOM']
|
|
if target_string + ' ' in cxxcom:
|
|
self.env['CXXCOM'] = cxxcom.replace(target_string, '') + target_string
|
|
if (self.user_settings.verbosebuild == 0):
|
|
builddir = self.user_settings.builddir if self.user_settings.builddir != '' else '.'
|
|
self.env["CXXCOMSTR"] = "Compiling %s %s $SOURCE" % (self.target, builddir)
|
|
self.env["LINKCOMSTR"] = "Linking %s $TARGET" % self.target
|
|
self.env["ARCOMSTR"] = "Archiving $TARGET ..."
|
|
self.env["RANLIBCOMSTR"] = "Indexing $TARGET ..."
|
|
|
|
# Use -Wundef to catch when a shared source file includes a
|
|
# shared header that misuses conditional compilation. Use
|
|
# -Werror=undef to make this fatal. Both are needed, since
|
|
# gcc 4.5 silently ignores -Werror=undef. On gcc 4.5, misuse
|
|
# produces a warning. On gcc 4.7, misuse produces an error.
|
|
Werror = get_Werror_string(self.user_settings.CXXFLAGS)
|
|
self.env.Append(CCFLAGS = ['-Wall', Werror + 'missing-declarations', Werror + 'pointer-arith', Werror + 'undef', Werror + 'type-limits', '-funsigned-char', Werror + 'format-security'])
|
|
self.env.Append(CPPPATH = ['common/include', 'common/main', '.', self.user_settings.builddir])
|
|
self.env.Append(CPPFLAGS = SCons.Util.CLVar('-Wno-sign-compare'))
|
|
if (self.user_settings.editor == 1):
|
|
self.env.Append(CPPPATH = ['common/include/editor'])
|
|
# Get traditional compiler environment variables
|
|
for cc in ['CXX', 'RC']:
|
|
value = getattr(self.user_settings, cc)
|
|
if value is not None:
|
|
self.env[cc] = value
|
|
for flags in ['CPPFLAGS', 'CXXFLAGS', 'LIBS']:
|
|
value = getattr(self.user_settings, flags)
|
|
if value is not None:
|
|
self.env.Append(**{flags : SCons.Util.CLVar(value)})
|
|
if self.user_settings.LDFLAGS:
|
|
self.env.Append(LINKFLAGS = SCons.Util.CLVar(self.user_settings.LDFLAGS))
|
|
if self.user_settings.lto:
|
|
f = ['-flto', '-fno-fat-lto-objects']
|
|
self.env.Append(CXXFLAGS = f, LINKFLAGS = f)
|
|
|
|
def check_endian(self):
|
|
# set endianess
|
|
if (self.__endian == "big"):
|
|
message(self, "BigEndian machine detected")
|
|
self.env.Append(CPPDEFINES = ['WORDS_BIGENDIAN'])
|
|
elif (self.__endian == "little"):
|
|
message(self, "LittleEndian machine detected")
|
|
|
|
def check_platform(self):
|
|
# windows or *nix?
|
|
platform_name = self.user_settings.host_platform
|
|
if self._argument_prefix_list:
|
|
prefix = ' with prefix list %s' % list(self._argument_prefix_list)
|
|
else:
|
|
prefix = ''
|
|
message(self, "compiling on %s for %s into %s%s" % (sys.platform, platform_name, self.user_settings.builddir or '.', prefix))
|
|
if platform_name == 'win32':
|
|
platform = self.Win32PlatformSettings
|
|
elif platform_name == 'darwin':
|
|
platform = self.DarwinPlatformSettings
|
|
else:
|
|
platform = self.LinuxPlatformSettings
|
|
self.platform_settings = platform(self, self.user_settings)
|
|
# Acquire environment object...
|
|
self.env = Environment(ENV = os.environ, tools = platform.tools)
|
|
# On Linux hosts, always run this. It should work even when
|
|
# cross-compiling a Rebirth to run elsewhere.
|
|
if sys.platform == 'linux2' or sys.platform == 'darwin':
|
|
self.platform_settings.merge_sdl_config(self, self.env)
|
|
if self.user_settings.sdlmixer:
|
|
self.platform_settings.merge_SDL_mixer_config(self, self.env)
|
|
self.platform_settings.adjust_environment(self, self.env)
|
|
|
|
def process_user_settings(self):
|
|
env = self.env
|
|
# opengl or software renderer?
|
|
if (self.user_settings.opengl == 1) or (self.user_settings.opengles == 1):
|
|
if (self.user_settings.opengles == 1):
|
|
message(self, "building with OpenGL ES")
|
|
env.Append(CPPDEFINES = ['OGLES'])
|
|
else:
|
|
message(self, "building with OpenGL")
|
|
env.Append(CPPDEFINES = ['OGL'])
|
|
|
|
# debug?
|
|
if (self.user_settings.debug == 1):
|
|
message(self, "including: DEBUG")
|
|
env.Prepend(CXXFLAGS = ['-g'])
|
|
else:
|
|
env.Append(CPPDEFINES = ['NDEBUG', 'RELEASE'])
|
|
env.Prepend(CXXFLAGS = ['-O2'])
|
|
if self.user_settings.memdebug:
|
|
message(self, "including: MEMDEBUG")
|
|
env.Append(CPPDEFINES = ['DEBUG_MEMORY_ALLOCATIONS'])
|
|
|
|
# profiler?
|
|
if (self.user_settings.profiler == 1):
|
|
env.Append(CPPFLAGS = ['-pg'])
|
|
|
|
#editor build?
|
|
if (self.user_settings.editor == 1):
|
|
env.Append(CPPDEFINES = ['EDITOR'])
|
|
|
|
# IPv6 compability?
|
|
if (self.user_settings.ipv6 == 1):
|
|
env.Append(CPPDEFINES = ['IPv6'])
|
|
|
|
# UDP support?
|
|
if (self.user_settings.use_udp == 1):
|
|
env.Append(CPPDEFINES = ['USE_UDP'])
|
|
# Tracker support? (Relies on UDP)
|
|
if( self.user_settings.use_tracker == 1 ):
|
|
env.Append( CPPDEFINES = [ 'USE_TRACKER' ] )
|
|
|
|
# Raspberry Pi?
|
|
if (self.user_settings.raspberrypi == 1):
|
|
print "using Raspberry Pi vendor libs in %s" % self.user_settings.rpi_vc_path
|
|
env.Append(CPPDEFINES = ['RPI', 'WORDS_NEED_ALIGNMENT'])
|
|
env.Append(CPPPATH = [
|
|
self.user_settings.rpi_vc_path+'/include',
|
|
self.user_settings.rpi_vc_path+'/include/interface/vcos/pthreads',
|
|
self.user_settings.rpi_vc_path+'/include/interface/vmcs_host/linux'])
|
|
env.Append(LIBPATH = self.user_settings.rpi_vc_path + '/lib')
|
|
env.Append(LIBS = ['bcm_host'])
|
|
|
|
class DXXArchive(DXXCommon):
|
|
srcdir = 'common'
|
|
target = 'dxx-common'
|
|
__objects_common = DXXCommon.create_lazy_object_property([os.path.join(srcdir, f) for f in [
|
|
'2d/2dsline.cpp',
|
|
'2d/bitblt.cpp',
|
|
'2d/bitmap.cpp',
|
|
'2d/box.cpp',
|
|
'2d/canvas.cpp',
|
|
'2d/circle.cpp',
|
|
'2d/disc.cpp',
|
|
'2d/gpixel.cpp',
|
|
'2d/line.cpp',
|
|
'2d/pixel.cpp',
|
|
'2d/rect.cpp',
|
|
'2d/rle.cpp',
|
|
'2d/scalec.cpp',
|
|
'3d/draw.cpp',
|
|
'3d/globvars.cpp',
|
|
'3d/instance.cpp',
|
|
'3d/matrix.cpp',
|
|
'3d/points.cpp',
|
|
'3d/rod.cpp',
|
|
'3d/setup.cpp',
|
|
'arch/sdl/joy.cpp',
|
|
'arch/sdl/rbaudio.cpp',
|
|
'arch/sdl/window.cpp',
|
|
'maths/fixc.cpp',
|
|
'maths/rand.cpp',
|
|
'maths/tables.cpp',
|
|
'maths/vecmat.cpp',
|
|
'misc/error.cpp',
|
|
'misc/hmp.cpp',
|
|
'misc/ignorecase.cpp',
|
|
'misc/strutil.cpp',
|
|
'texmap/ntmap.cpp',
|
|
'texmap/scanline.cpp'
|
|
]
|
|
])
|
|
objects_editor = DXXCommon.create_lazy_object_property([os.path.join(srcdir, f) for f in [
|
|
'editor/func.cpp',
|
|
'ui/button.cpp',
|
|
'ui/checkbox.cpp',
|
|
'ui/dialog.cpp',
|
|
'ui/file.cpp',
|
|
'ui/gadget.cpp',
|
|
'ui/icon.cpp',
|
|
'ui/inputbox.cpp',
|
|
'ui/keypad.cpp',
|
|
'ui/keypress.cpp',
|
|
'ui/listbox.cpp',
|
|
'ui/menu.cpp',
|
|
'ui/menubar.cpp',
|
|
'ui/message.cpp',
|
|
'ui/radio.cpp',
|
|
'ui/scroll.cpp',
|
|
'ui/ui.cpp',
|
|
'ui/uidraw.cpp',
|
|
'ui/userbox.cpp'
|
|
]
|
|
])
|
|
# for non-ogl
|
|
objects_arch_sdl = DXXCommon.create_lazy_object_property([os.path.join(srcdir, f) for f in [
|
|
'3d/clipper.cpp',
|
|
'texmap/tmapflat.cpp'
|
|
]
|
|
])
|
|
objects_arch_sdlmixer = DXXCommon.create_lazy_object_property([os.path.join(srcdir, f) for f in [
|
|
'arch/sdl/digi_mixer_music.cpp',
|
|
]
|
|
])
|
|
class Win32PlatformSettings(LazyObjectConstructor, DXXCommon.Win32PlatformSettings):
|
|
platform_objects = LazyObjectConstructor.create_lazy_object_property([
|
|
'common/arch/win32/messagebox.cpp'
|
|
])
|
|
def __init__(self,program,user_settings):
|
|
LazyObjectConstructor.__init__(self)
|
|
DXXCommon.Win32PlatformSettings.__init__(self, program, user_settings)
|
|
self.user_settings = user_settings
|
|
class DarwinPlatformSettings(LazyObjectConstructor, DXXCommon.DarwinPlatformSettings):
|
|
platform_objects = LazyObjectConstructor.create_lazy_object_property([
|
|
'common/arch/cocoa/messagebox.mm',
|
|
'common/arch/cocoa/SDLMain.m'
|
|
])
|
|
|
|
def __init__(self, program, user_settings):
|
|
LazyObjectConstructor.__init__(self)
|
|
DXXCommon.DarwinPlatformSettings.__init__(self, program, user_settings)
|
|
self.user_settings = user_settings
|
|
@property
|
|
def objects_common(self):
|
|
objects_common = self.__objects_common
|
|
return objects_common + self.platform_settings.platform_objects
|
|
def __init__(self,user_settings):
|
|
self.PROGRAM_NAME = 'DXX-Archive'
|
|
self._argument_prefix_list = None
|
|
DXXCommon.__init__(self)
|
|
self.user_settings = user_settings.clone()
|
|
self.check_platform()
|
|
self.prepare_environment()
|
|
self.check_endian()
|
|
self.process_user_settings()
|
|
self.configure_environment()
|
|
self.create_pch_node(self.srcdir, self.configure_pch_flags)
|
|
|
|
def configure_environment(self):
|
|
fs = SCons.Node.FS.get_default_fs()
|
|
builddir = fs.Dir(self.user_settings.builddir or '.')
|
|
tests = ConfigureTests(self.program_message_prefix, self.user_settings)
|
|
log_file=fs.File('sconf.log', builddir)
|
|
conf = self.env.Configure(custom_tests = {
|
|
k:getattr(tests, k) for k in tests.custom_tests
|
|
},
|
|
conf_dir=fs.Dir('.sconf_temp', builddir),
|
|
log_file=log_file,
|
|
config_h=fs.File('dxxsconf.h', builddir),
|
|
clean=False,
|
|
help=False
|
|
)
|
|
self.configure_added_environment_flags = tests.successful_flags
|
|
self.configure_pch_flags = None
|
|
if not conf.env:
|
|
return
|
|
try:
|
|
for k in tests.custom_tests:
|
|
getattr(conf, k)()
|
|
except SCons.Errors.StopError as e:
|
|
raise SCons.Errors.StopError(e.args[0] + ' See {log_file} for details.'.format(log_file=log_file), *e.args[1:])
|
|
self.env = conf.Finish()
|
|
self.configure_pch_flags = tests.pch_flags
|
|
|
|
class DXXProgram(DXXCommon):
|
|
# version number
|
|
VERSION_MAJOR = 0
|
|
VERSION_MINOR = 58
|
|
VERSION_MICRO = 1
|
|
static_archive_construction = {}
|
|
# None when unset. Tuple of one once cached.
|
|
_computed_extra_version = None
|
|
def _apply_target_name(self,name):
|
|
return os.path.join(os.path.dirname(name), '.%s.%s' % (self.target, os.path.splitext(os.path.basename(name))[0]))
|
|
objects_similar_arch_ogl = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join('similar', f) for f in [
|
|
'arch/ogl/gr.cpp',
|
|
'arch/ogl/ogl.cpp',
|
|
]
|
|
],
|
|
'transform_target':_apply_target_name,
|
|
}])
|
|
objects_similar_arch_sdl = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join('similar', f) for f in [
|
|
'arch/sdl/gr.cpp',
|
|
]
|
|
],
|
|
'transform_target':_apply_target_name,
|
|
}])
|
|
objects_similar_arch_sdlmixer = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join('similar', f) for f in [
|
|
'arch/sdl/digi_mixer.cpp',
|
|
'arch/sdl/jukebox.cpp'
|
|
]
|
|
],
|
|
'transform_target':_apply_target_name,
|
|
}])
|
|
__objects_common = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join('similar', f) for f in [
|
|
'2d/font.cpp',
|
|
'2d/palette.cpp',
|
|
'2d/pcx.cpp',
|
|
'3d/interp.cpp',
|
|
'arch/sdl/digi.cpp',
|
|
'arch/sdl/digi_audio.cpp',
|
|
'arch/sdl/event.cpp',
|
|
'arch/sdl/init.cpp',
|
|
'arch/sdl/key.cpp',
|
|
'arch/sdl/mouse.cpp',
|
|
'arch/sdl/timer.cpp',
|
|
'main/ai.cpp',
|
|
'main/aipath.cpp',
|
|
'main/automap.cpp',
|
|
'main/bm.cpp',
|
|
'main/cntrlcen.cpp',
|
|
'main/collide.cpp',
|
|
'main/config.cpp',
|
|
'main/console.cpp',
|
|
'main/controls.cpp',
|
|
'main/credits.cpp',
|
|
'main/digiobj.cpp',
|
|
'main/effects.cpp',
|
|
'main/endlevel.cpp',
|
|
'main/fireball.cpp',
|
|
'main/fuelcen.cpp',
|
|
'main/fvi.cpp',
|
|
'main/game.cpp',
|
|
'main/gamecntl.cpp',
|
|
'main/gamefont.cpp',
|
|
'main/gamemine.cpp',
|
|
'main/gamerend.cpp',
|
|
'main/gamesave.cpp',
|
|
'main/gameseg.cpp',
|
|
'main/gameseq.cpp',
|
|
'main/gauges.cpp',
|
|
'main/hostage.cpp',
|
|
'main/hud.cpp',
|
|
'main/iff.cpp',
|
|
'main/inferno.cpp',
|
|
'main/kconfig.cpp',
|
|
'main/kmatrix.cpp',
|
|
'main/laser.cpp',
|
|
'main/lighting.cpp',
|
|
'main/menu.cpp',
|
|
'main/mglobal.cpp',
|
|
'main/mission.cpp',
|
|
'main/morph.cpp',
|
|
'main/multi.cpp',
|
|
'main/multibot.cpp',
|
|
'main/newdemo.cpp',
|
|
'main/newmenu.cpp',
|
|
'main/object.cpp',
|
|
'main/paging.cpp',
|
|
'main/physics.cpp',
|
|
'main/piggy.cpp',
|
|
'main/player.cpp',
|
|
'main/playsave.cpp',
|
|
'main/polyobj.cpp',
|
|
'main/powerup.cpp',
|
|
'main/render.cpp',
|
|
'main/robot.cpp',
|
|
'main/scores.cpp',
|
|
'main/segment.cpp',
|
|
'main/slew.cpp',
|
|
'main/songs.cpp',
|
|
'main/state.cpp',
|
|
'main/switch.cpp',
|
|
'main/terrain.cpp',
|
|
'main/texmerge.cpp',
|
|
'main/text.cpp',
|
|
'main/titles.cpp',
|
|
'main/vclip.cpp',
|
|
'main/wall.cpp',
|
|
'main/weapon.cpp',
|
|
'mem/mem.cpp',
|
|
'misc/args.cpp',
|
|
'misc/hash.cpp',
|
|
'misc/physfsx.cpp',
|
|
]
|
|
],
|
|
'transform_target':_apply_target_name,
|
|
}])
|
|
objects_editor = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join('similar', f) for f in [
|
|
'editor/autosave.cpp',
|
|
'editor/centers.cpp',
|
|
'editor/curves.cpp',
|
|
'main/dumpmine.cpp',
|
|
'editor/eglobal.cpp',
|
|
'editor/elight.cpp',
|
|
'editor/eobject.cpp',
|
|
'editor/eswitch.cpp',
|
|
'editor/group.cpp',
|
|
'editor/info.cpp',
|
|
'editor/kbuild.cpp',
|
|
'editor/kcurve.cpp',
|
|
'editor/kfuncs.cpp',
|
|
'editor/kgame.cpp',
|
|
'editor/khelp.cpp',
|
|
'editor/kmine.cpp',
|
|
'editor/ksegmove.cpp',
|
|
'editor/ksegsel.cpp',
|
|
'editor/ksegsize.cpp',
|
|
'editor/ktmap.cpp',
|
|
'editor/kview.cpp',
|
|
'editor/med.cpp',
|
|
'editor/meddraw.cpp',
|
|
'editor/medmisc.cpp',
|
|
'editor/medrobot.cpp',
|
|
'editor/medsel.cpp',
|
|
'editor/medwall.cpp',
|
|
'editor/mine.cpp',
|
|
'editor/objpage.cpp',
|
|
'editor/segment.cpp',
|
|
'editor/seguvs.cpp',
|
|
'editor/texpage.cpp',
|
|
'editor/texture.cpp',
|
|
]
|
|
],
|
|
'transform_target':_apply_target_name,
|
|
}])
|
|
|
|
objects_use_udp = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join('similar', f) for f in [
|
|
'main/net_udp.cpp',
|
|
]
|
|
],
|
|
'transform_target':_apply_target_name,
|
|
}])
|
|
class UserSettings(DXXCommon.UserSettings):
|
|
@property
|
|
def BIN_DIR(self):
|
|
# installation path
|
|
return self.prefix + '/bin'
|
|
# Settings to apply to mingw32 builds
|
|
class Win32PlatformSettings(DXXCommon.Win32PlatformSettings):
|
|
def __init__(self,program,user_settings):
|
|
DXXCommon.Win32PlatformSettings.__init__(self,program,user_settings)
|
|
user_settings.sharepath = ''
|
|
self.platform_objects = self.platform_objects[:]
|
|
def adjust_environment(self,program,env):
|
|
DXXCommon.Win32PlatformSettings.adjust_environment(self, program, env)
|
|
rcbasename = os.path.join(program.srcdir, 'arch/win32/%s' % program.target)
|
|
self.platform_objects.append(env.RES(target='%s%s%s' % (program.user_settings.builddir, rcbasename, env["OBJSUFFIX"]), source='%s.rc' % rcbasename))
|
|
env.Append(CPPPATH = [os.path.join(program.srcdir, 'arch/win32/include')])
|
|
env.Append(LINKFLAGS = '-mwindows')
|
|
env.Append(LIBS = ['glu32', 'wsock32', 'ws2_32', 'winmm', 'mingw32', 'SDLmain', 'SDL'])
|
|
# Settings to apply to Apple builds
|
|
class DarwinPlatformSettings(DXXCommon.DarwinPlatformSettings):
|
|
def __init__(self,program,user_settings):
|
|
DXXCommon.DarwinPlatformSettings.__init__(self,program,user_settings)
|
|
user_settings.sharepath = ''
|
|
self.platform_objects = self.platform_objects[:]
|
|
def adjust_environment(self,program,env):
|
|
DXXCommon.DarwinPlatformSettings.adjust_environment(self, program, env)
|
|
VERSION = str(program.VERSION_MAJOR) + '.' + str(program.VERSION_MINOR)
|
|
if (program.VERSION_MICRO):
|
|
VERSION += '.' + str(program.VERSION_MICRO)
|
|
env['VERSION_NUM'] = VERSION
|
|
env['VERSION_NAME'] = program.PROGRAM_NAME + ' v' + VERSION
|
|
# Settings to apply to Linux builds
|
|
class LinuxPlatformSettings(DXXCommon.LinuxPlatformSettings):
|
|
def __init__(self,program,user_settings):
|
|
DXXCommon.LinuxPlatformSettings.__init__(self,program,user_settings)
|
|
if user_settings.sharepath and user_settings.sharepath[-1] != '/':
|
|
user_settings.sharepath += '/'
|
|
|
|
@property
|
|
def objects_common(self):
|
|
objects_common = self.__objects_common
|
|
if (self.user_settings.use_udp == 1):
|
|
objects_common = objects_common + self.objects_use_udp
|
|
return objects_common + self.platform_settings.platform_objects
|
|
def __init__(self,prefix,variables):
|
|
self.variables = variables
|
|
self._argument_prefix_list = prefix
|
|
DXXCommon.__init__(self)
|
|
self.banner()
|
|
self.user_settings = self.UserSettings(program=self)
|
|
self.user_settings.register_variables(prefix=prefix, variables=self.variables)
|
|
|
|
def init(self,substenv):
|
|
self.user_settings.read_variables(self.variables, substenv)
|
|
if not DXXProgram.static_archive_construction.has_key(self.user_settings.builddir):
|
|
DXXProgram.static_archive_construction[self.user_settings.builddir] = DXXArchive(self.user_settings)
|
|
self.check_platform()
|
|
self.prepare_environment()
|
|
self.check_endian()
|
|
self.process_user_settings()
|
|
self.register_program()
|
|
|
|
def prepare_environment(self):
|
|
DXXCommon.prepare_environment(self)
|
|
archive = DXXProgram.static_archive_construction[self.user_settings.builddir]
|
|
self.env.Append(**archive.configure_added_environment_flags)
|
|
self.create_pch_node(self.srcdir, archive.configure_pch_flags)
|
|
self.env.Append(CPPDEFINES = [('DXX_VERSION_SEQ', ','.join([str(self.VERSION_MAJOR), str(self.VERSION_MINOR), str(self.VERSION_MICRO)]))])
|
|
# For PRIi64
|
|
self.env.Append(CPPDEFINES = [('__STDC_FORMAT_MACROS',)])
|
|
self.env.Append(CPPPATH = [os.path.join(self.srcdir, f) for f in ['include', 'main', 'arch/include']])
|
|
|
|
def banner(self):
|
|
VERSION_STRING = ' v' + str(self.VERSION_MAJOR) + '.' + str(self.VERSION_MINOR) + '.' + str(self.VERSION_MICRO)
|
|
print '\n===== ' + self.PROGRAM_NAME + VERSION_STRING + ' =====\n'
|
|
|
|
def check_platform(self):
|
|
DXXCommon.check_platform(self)
|
|
env = self.env
|
|
# windows or *nix?
|
|
if sys.platform == 'darwin':
|
|
VERSION = str(self.VERSION_MAJOR) + '.' + str(self.VERSION_MINOR)
|
|
if (self.VERSION_MICRO):
|
|
VERSION += '.' + str(self.VERSION_MICRO)
|
|
env['VERSION_NUM'] = VERSION
|
|
env['VERSION_NAME'] = self.PROGRAM_NAME + ' v' + VERSION
|
|
env.Append(LIBS = ['m'])
|
|
|
|
def process_user_settings(self):
|
|
DXXCommon.process_user_settings(self)
|
|
env = self.env
|
|
# opengl or software renderer?
|
|
|
|
# profiler?
|
|
if (self.user_settings.profiler == 1):
|
|
env.Append(LINKFLAGS = '-pg')
|
|
|
|
#editor build?
|
|
if (self.user_settings.editor == 1):
|
|
env.Append(CPPPATH = [os.path.join(self.srcdir, 'include/editor')])
|
|
|
|
env.Append(CPPDEFINES = [('SHAREPATH', '\\"' + str(self.user_settings.sharepath) + '\\"')])
|
|
|
|
def register_program(self):
|
|
self._register_program(self.shortname)
|
|
|
|
@classmethod
|
|
def compute_extra_version(cls):
|
|
c = cls._computed_extra_version
|
|
if c is None:
|
|
git = (os.environ.get('GIT', 'git')).split()
|
|
v = s = None
|
|
if git:
|
|
v = cls._compute_extra_version(git)
|
|
if v:
|
|
g = subprocess.Popen(git + ['status', '--short', '--branch'], executable=git[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
(go, ge) = g.communicate(None)
|
|
if not g.wait():
|
|
s = go
|
|
cls._computed_extra_version = c = (v or '', s)
|
|
return c
|
|
|
|
@classmethod
|
|
def _compute_extra_version(cls, git):
|
|
try:
|
|
g = subprocess.Popen(git + ['describe', '--tags', '--abbrev=8'], executable=git[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
except OSError as e:
|
|
if e.errno == errno.ENOENT:
|
|
return None
|
|
raise
|
|
(go, ge) = g.communicate(None)
|
|
if g.wait():
|
|
return None
|
|
g = subprocess.Popen(git + ['diff', '--quiet', '--cached'], executable=git[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
c = g.wait()
|
|
g = subprocess.Popen(git + ['diff', '--quiet'], executable=git[0], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
d = g.wait()
|
|
return go.split('\n')[0] + ('+' if c else '') + ('*' if d else '')
|
|
|
|
def _register_program(self,dxxstr,program_specific_objects=[]):
|
|
env = self.env
|
|
exe_target = os.path.join(self.srcdir, self.target)
|
|
static_archive_construction = self.static_archive_construction[self.user_settings.builddir]
|
|
objects = static_archive_construction.objects_common[:]
|
|
objects.extend(self.objects_common)
|
|
objects.extend(program_specific_objects)
|
|
if (self.user_settings.sdlmixer == 1):
|
|
objects.extend(static_archive_construction.objects_arch_sdlmixer)
|
|
objects.extend(self.objects_similar_arch_sdlmixer)
|
|
if (self.user_settings.opengl == 1) or (self.user_settings.opengles == 1):
|
|
env.Append(LIBS = self.platform_settings.ogllibs)
|
|
objects.extend(self.objects_similar_arch_ogl)
|
|
else:
|
|
message(self, "building with Software Renderer")
|
|
objects.extend(static_archive_construction.objects_arch_sdl)
|
|
objects.extend(self.objects_similar_arch_sdl)
|
|
if (self.user_settings.editor == 1):
|
|
objects.extend(self.objects_editor)
|
|
objects.extend(static_archive_construction.objects_editor)
|
|
exe_target += '-editor'
|
|
if self.user_settings.program_name:
|
|
exe_target = self.user_settings.program_name
|
|
versid_build_environ = ['CXX', 'CPPFLAGS', 'CXXFLAGS', 'LINKFLAGS']
|
|
versid_cppdefines = env['CPPDEFINES'][:]
|
|
versid_cppdefines.extend([('DESCENT_%s' % k, self._quote_cppdefine(env.get(k, ''))) for k in versid_build_environ])
|
|
v = subprocess.Popen(env['CXX'].split(' ') + ['--version'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
(so,se) = v.communicate(None)
|
|
if not v.returncode and (so or se):
|
|
v = (so or se).split('\n')[0]
|
|
versid_cppdefines.append(('DESCENT_%s' % 'CXX_version', self._quote_cppdefine(v)))
|
|
versid_build_environ.append('CXX_version')
|
|
extra_version = self.user_settings.extra_version
|
|
if extra_version is None:
|
|
extra_version = 'v%u.%u' % (self.VERSION_MAJOR, self.VERSION_MINOR)
|
|
if self.VERSION_MICRO:
|
|
extra_version += '.%u' % self.VERSION_MICRO
|
|
git_describe_version = (self.compute_extra_version() if self.user_settings.git_describe_version else ('', ''))
|
|
if git_describe_version[0] and not (extra_version and (extra_version == git_describe_version[0] or (extra_version[0] == 'v' and extra_version[1:] == git_describe_version[0]))):
|
|
# Suppress duplicate output
|
|
if extra_version:
|
|
extra_version += ' '
|
|
extra_version += git_describe_version[0]
|
|
if extra_version:
|
|
versid_cppdefines.append(('DESCENT_VERSION_EXTRA', self._quote_cppdefine(extra_version)))
|
|
versid_cppdefines.append(('DESCENT_git_status', self._quote_cppdefine(git_describe_version[1])))
|
|
versid_build_environ.append('git_status')
|
|
versid_cppdefines.append(('DXX_RBE"(A)"', "'" + ''.join(['A(%s)' % k for k in versid_build_environ]) + "'"))
|
|
versid_objlist = [self.env.StaticObject(target='%s%s%s' % (self.user_settings.builddir, self._apply_target_name(s), self.env["OBJSUFFIX"]), source=s, CPPDEFINES=versid_cppdefines) for s in ['similar/main/vers_id.cpp']]
|
|
if self.user_settings.versid_depend_all:
|
|
env.Depends(versid_objlist[0], objects)
|
|
if env._dxx_pch_node:
|
|
env.Depends(versid_objlist[0], env._dxx_pch_node)
|
|
objects.extend(versid_objlist)
|
|
# finally building program...
|
|
exe_node = env.Program(target=os.path.join(self.user_settings.builddir, str(exe_target)), source = self.sources + objects)
|
|
if self.user_settings.host_platform != 'darwin':
|
|
if self.user_settings.register_install_target:
|
|
install_dir = (self.user_settings.DESTDIR or '') + self.user_settings.BIN_DIR
|
|
env.Install(install_dir, exe_node)
|
|
env.Alias('install', install_dir)
|
|
else:
|
|
syspath = sys.path[:]
|
|
cocoa = 'common/arch/cocoa'
|
|
sys.path += [cocoa]
|
|
import tool_bundle
|
|
sys.path = syspath
|
|
tool_bundle.TOOL_BUNDLE(env)
|
|
env.MakeBundle(os.path.join(self.user_settings.builddir, self.PROGRAM_NAME + '.app'), exe_node,
|
|
'free.%s-rebirth' % dxxstr, os.path.join(cocoa, 'Info.plist'),
|
|
typecode='APPL', creator='DCNT',
|
|
icon_file=os.path.join(cocoa, '%s-rebirth.icns' % dxxstr),
|
|
resources=[[os.path.join(self.srcdir, s), s] for s in ['English.lproj/InfoPlist.strings']])
|
|
|
|
def GenerateHelpText(self):
|
|
return self.variables.GenerateHelpText(self.env)
|
|
|
|
class D1XProgram(DXXProgram):
|
|
PROGRAM_NAME = 'D1X-Rebirth'
|
|
target = 'd1x-rebirth'
|
|
srcdir = 'd1x-rebirth'
|
|
shortname = 'd1x'
|
|
def prepare_environment(self):
|
|
DXXProgram.prepare_environment(self)
|
|
# Flags and stuff for all platforms...
|
|
self.env.Append(CPPDEFINES = ['DXX_BUILD_DESCENT_I'])
|
|
|
|
# general source files
|
|
__objects_common = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join(srcdir, f) for f in [
|
|
'main/bmread.cpp',
|
|
'main/custom.cpp',
|
|
'main/snddecom.cpp',
|
|
#'tracker/client/tracker_client.c'
|
|
]
|
|
],
|
|
}])
|
|
@property
|
|
def objects_common(self):
|
|
return self.__objects_common + DXXProgram.objects_common.fget(self)
|
|
|
|
# for editor
|
|
__objects_editor = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join(srcdir, f) for f in [
|
|
'main/hostage.cpp',
|
|
'editor/ehostage.cpp',
|
|
]
|
|
],
|
|
}])
|
|
@property
|
|
def objects_editor(self):
|
|
return self.__objects_editor + DXXProgram.objects_editor.fget(self)
|
|
|
|
class D2XProgram(DXXProgram):
|
|
PROGRAM_NAME = 'D2X-Rebirth'
|
|
target = 'd2x-rebirth'
|
|
srcdir = 'd2x-rebirth'
|
|
shortname = 'd2x'
|
|
def prepare_environment(self):
|
|
DXXProgram.prepare_environment(self)
|
|
# Flags and stuff for all platforms...
|
|
self.env.Append(CPPDEFINES = ['DXX_BUILD_DESCENT_II'])
|
|
|
|
# general source files
|
|
__objects_common = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join(srcdir, f) for f in [
|
|
'libmve/decoder8.cpp',
|
|
'libmve/decoder16.cpp',
|
|
'libmve/mve_audio.cpp',
|
|
'libmve/mvelib.cpp',
|
|
'libmve/mveplay.cpp',
|
|
'main/escort.cpp',
|
|
'main/gamepal.cpp',
|
|
'main/movie.cpp',
|
|
'misc/physfsrwops.cpp',
|
|
]
|
|
],
|
|
}])
|
|
@property
|
|
def objects_common(self):
|
|
return self.__objects_common + DXXProgram.objects_common.fget(self)
|
|
|
|
# for editor
|
|
__objects_editor = DXXCommon.create_lazy_object_property([{
|
|
'source':[os.path.join(srcdir, f) for f in [
|
|
'main/bmread.cpp',
|
|
]
|
|
],
|
|
}])
|
|
@property
|
|
def objects_editor(self):
|
|
return self.__objects_editor + DXXProgram.objects_editor.fget(self)
|
|
|
|
variables = Variables(['site-local.py'], ARGUMENTS)
|
|
filtered_help = FilterHelpText()
|
|
variables.FormatVariableHelpText = filtered_help.FormatVariableHelpText
|
|
def _filter_duplicate_prefix_elements(e,s):
|
|
r = e not in s
|
|
s.add(e)
|
|
return r
|
|
def register_program(program):
|
|
s = program.shortname
|
|
import itertools
|
|
l = [v for (k,v) in ARGLIST if k == s or k == 'dxx'] or [1]
|
|
# Fallback case: build the regular configuration.
|
|
if len(l) == 1:
|
|
try:
|
|
if int(l[0]):
|
|
return [program((s,''), variables)]
|
|
return []
|
|
except ValueError:
|
|
# If not an integer, treat this as a configuration profile.
|
|
pass
|
|
r = []
|
|
seen = set()
|
|
for e in l:
|
|
for prefix in itertools.product(*[v.split('+') for v in e.split(',')]):
|
|
duplicates = set()
|
|
prefix = tuple(p for p in prefix if _filter_duplicate_prefix_elements(p, duplicates))
|
|
if prefix in seen:
|
|
continue
|
|
seen.add(prefix)
|
|
prefix = ['%s%s%s' % (s, '_' if p else '', p) for p in prefix] + list(prefix)
|
|
r.append(program(prefix, variables))
|
|
return r
|
|
d1x = register_program(D1XProgram)
|
|
d2x = register_program(D2XProgram)
|
|
|
|
# show some help when running scons -h
|
|
h = 'DXX-Rebirth, SConstruct file help:' + """
|
|
|
|
Type 'scons' to build the binary.
|
|
Type 'scons install' to build (if it hasn't been done) and install.
|
|
Type 'scons -c' to clean up.
|
|
|
|
Extra options (add them to command line, like 'scons extraoption=value'):
|
|
d1x=[0/1] Disable/enable D1X-Rebirth
|
|
d1x=prefix-list Enable D1X-Rebirth with prefix-list modifiers
|
|
d2x=[0/1] Disable/enable D2X-Rebirth
|
|
d2x=prefix-list Enable D2X-Rebirth with prefix-list modifiers
|
|
dxx=VALUE Equivalent to d1x=VALUE d2x=VALUE
|
|
"""
|
|
substenv = SCons.Environment.SubstitutionEnvironment()
|
|
variables.Update(substenv)
|
|
for d in d1x + d2x:
|
|
d.init(substenv)
|
|
h += d.PROGRAM_NAME + ('.%d:\n' % d.program_instance) + d.GenerateHelpText()
|
|
Help(h)
|
|
unknown = variables.UnknownVariables()
|
|
# Delete known unregistered variables
|
|
unknown.pop('d1x', None)
|
|
unknown.pop('d2x', None)
|
|
unknown.pop('dxx', None)
|
|
ignore_unknown_variables = unknown.pop('ignore_unknown_variables', '0')
|
|
if unknown:
|
|
try:
|
|
ignore_unknown_variables = int(ignore_unknown_variables)
|
|
except ValueError:
|
|
ignore_unknown_variables = False
|
|
if not ignore_unknown_variables:
|
|
raise SCons.Errors.StopError('Unknown values specified on command line.' +
|
|
''.join(['\n\t%s' % k for k in unknown.keys()]) +
|
|
'\nRemove unknown values or set ignore_unknown_variables=1 to continue.')
|
|
|
|
#EOF
|