2006-12-19 02:25:50 +00:00
#SConstruct
# needed imports
2015-09-13 20:23:05 +00:00
from collections import ( defaultdict , Counter as collections_counter )
2013-11-16 22:59:17 +00:00
import binascii
2014-07-26 17:40:12 +00:00
import errno
2015-09-13 20:23:05 +00:00
import itertools
2013-11-16 22:59:17 +00:00
import subprocess
2006-12-19 02:25:50 +00:00
import sys
import os
2007-05-22 01:20:06 +00:00
import SCons . Util
2006-12-19 02:25:50 +00:00
2015-09-13 20:23:05 +00:00
# Disable injecting tools into default namespace
SCons . Defaults . DefaultEnvironment ( tools = [ ] )
2013-06-16 19:28:38 +00:00
def message ( program , msg ) :
print " %s : %s " % ( program . program_message_prefix , msg )
2014-09-17 02:09:53 +00:00
def get_Werror_string ( l ) :
2014-09-27 00:32:22 +00:00
if l and ' -Werror ' in l :
2014-09-17 02:09:53 +00:00
return ' -W '
return ' -Werror= '
2015-04-22 02:44:30 +00:00
class StaticSubprocess :
2016-09-11 18:49:16 +00:00
# This class contains utility functions for calling subprocesses
# that are expected to return the same output for every call. The
# output is cached after the first call, so that callers can request
# the output again later without causing the program to be run again.
#
# Suitable programs are not required to depend solely on the
# parameters, but are assumed to depend only on state that is
# unlikely to change in the brief time that SConstruct runs. For
# example, a tool might be upgraded by the system administrator,
# changing its version string. However, we assume nobody upgrades
# their tools in the middle of an SConstruct run. Results are not
# cached to persistent storage, so an upgrade performed after one
# SConstruct run, but before the next, will not cause any
# inconsistencies.
2015-09-26 21:17:13 +00:00
from shlex import split as shlex_split
2015-04-22 02:44:30 +00:00
class CachedCall :
def __init__ ( self , out , err , returncode ) :
self . out = out
self . err = err
self . returncode = returncode
2015-09-26 21:17:13 +00:00
# @staticmethod delayed so that default arguments pick up the
# undecorated form.
def pcall ( args , stderr = None , _call_cache = { } , _CachedCall = CachedCall ) :
# Use repr since callers may construct the same argument
# list independently.
## >>> a = ['git', '--version']
## >>> b = ['git', '--version']
## >>> a is b
## False
## >>> id(a) == id(b)
## False
## >>> a == b
## True
## >>> repr(a) == repr(b)
## True
2015-04-22 02:44:30 +00:00
a = repr ( args )
try :
2015-09-26 21:17:13 +00:00
return _call_cache [ a ]
2015-04-22 02:44:30 +00:00
except KeyError :
pass
2016-01-09 16:38:15 +00:00
p = subprocess . Popen ( args , stdout = subprocess . PIPE , stderr = stderr )
2015-04-22 02:44:30 +00:00
( o , e ) = p . communicate ( )
2015-09-26 21:17:13 +00:00
_call_cache [ a ] = c = _CachedCall ( o , e , p . wait ( ) )
2015-04-22 02:44:30 +00:00
return c
2015-09-26 21:17:13 +00:00
def qcall ( args , stderr = None , _pcall = pcall , _shlex_split = shlex_split ) :
return _pcall ( _shlex_split ( args ) , stderr )
@staticmethod
def get_version_head ( cmd , _qcall = qcall ) :
v = _qcall ( ' %s %s ' % ( cmd , ' --version ' ) , stderr = subprocess . PIPE )
try :
return v . __version_head
except AttributeError :
v . __version_head = r = ( v . out or v . err ) . splitlines ( ) [ 0 ] if not v . returncode and ( v . out or v . err ) else None
return r
pcall = staticmethod ( pcall )
qcall = staticmethod ( qcall )
shlex_split = staticmethod ( shlex_split )
2015-04-22 02:44:30 +00:00
2016-07-21 01:43:22 +00:00
class ToolchainInformation ( StaticSubprocess ) :
@staticmethod
def get_tool_path ( env , tool , _qcall = StaticSubprocess . qcall ) :
# Include $LINKFLAGS since -fuse-ld=gold influences the path
# printed for the linker.
tool = env . subst ( ' $CXX $CXXFLAGS $LINKFLAGS -print-prog-name= %s ' % tool )
return tool , _qcall ( tool ) . out . strip ( )
2016-10-02 00:34:50 +00:00
@staticmethod
def show_partial_environ ( env , f ) :
for v in (
' CXX ' ,
' CPPDEFINES ' ,
' CPPPATH ' ,
' CPPFLAGS ' ,
' CXXFLAGS ' ,
' LIBS ' ,
' LINKFLAGS ' ,
) :
f ( " %s : %r " % ( v , env . get ( v , None ) ) )
penv = env [ ' ENV ' ]
for v in (
' CCACHE_PREFIX ' ,
' DISTCC_HOSTS ' ,
) :
f ( " $ %s : %r " % ( v , penv . get ( v , None ) ) )
2016-07-21 01:43:22 +00:00
2015-04-22 02:44:30 +00:00
class Git ( StaticSubprocess ) :
2016-10-21 02:16:47 +00:00
class ComputedExtraVersion ( object ) :
__slots__ = ( ' describe ' , ' status ' , ' diffstat_HEAD ' , ' revparse_HEAD ' )
def __init__ ( self , describe , status , diffstat_HEAD , revparse_HEAD ) :
self . describe = describe
self . status = status
self . diffstat_HEAD = diffstat_HEAD
self . revparse_HEAD = revparse_HEAD
2017-01-08 22:32:01 +00:00
def __repr__ ( self ) :
return ' ComputedExtraVersion( %r , %r , %r , %r ) ' % ( self . describe , self . status , self . diffstat_HEAD , self . revparse_HEAD )
2016-10-21 02:16:47 +00:00
UnknownExtraVersion = ComputedExtraVersion ( ' ' , None , None , None )
# None when unset. Instance of ComputedExtraVersion once cached.
2016-09-11 18:49:16 +00:00
__computed_extra_version = None
2015-04-22 02:44:30 +00:00
__path_git = None
2016-12-10 17:51:07 +00:00
# `__pcall_missing_git`, `__pcall_found_git`, and `pcall` must have
# compatible signatures. In any given run, there will be at most
# one call to `pcall`, which then patches itself out of the call
# chain. A run will call either __pcall_missing_git or
# __pcall_found_git (depending on whether $GIT is blank), but never
# both in the same run.
2015-04-22 02:44:30 +00:00
@classmethod
2015-09-29 02:41:22 +00:00
def __pcall_missing_git ( cls , args , stderr = None , _missing_git = StaticSubprocess . CachedCall ( None , None , 1 ) ) :
return _missing_git
@classmethod
def __pcall_found_git ( cls , args , stderr = None , _pcall = StaticSubprocess . pcall ) :
return _pcall ( cls . __path_git + args , stderr = stderr )
2016-09-24 18:06:10 +00:00
@classmethod
2015-09-29 02:41:22 +00:00
def pcall ( cls , args , stderr = None ) :
2015-04-22 02:44:30 +00:00
git = cls . __path_git
if git is None :
2015-09-26 21:17:13 +00:00
cls . __path_git = git = cls . shlex_split ( os . environ . get ( ' GIT ' , ' git ' ) )
2015-09-29 02:41:22 +00:00
cls . pcall = f = cls . __pcall_found_git if git else cls . __pcall_missing_git
return f ( args , stderr )
2016-09-24 18:06:10 +00:00
def spcall ( cls , args , stderr = None ) :
g = cls . pcall ( args , stderr )
2015-04-22 02:44:30 +00:00
if g . returncode :
return None
return g . out
2016-09-11 18:49:16 +00:00
@classmethod
def compute_extra_version ( cls , _spcall = spcall ) :
c = cls . __computed_extra_version
if c is None :
v = cls . __compute_extra_version ( )
2016-10-21 02:16:47 +00:00
cls . __computed_extra_version = c = cls . ComputedExtraVersion (
2016-09-11 18:49:16 +00:00
v ,
_spcall ( cls , [ ' status ' , ' --short ' , ' --branch ' ] ) ,
_spcall ( cls , [ ' diff ' , ' --stat ' , ' HEAD ' ] ) ,
2016-10-21 02:16:47 +00:00
_spcall ( cls , [ ' rev-parse ' , ' HEAD ' ] ) . rstrip ( ) ,
) if v is not None else cls . UnknownExtraVersion
2016-09-11 18:49:16 +00:00
return c
# Run `git describe --tags --abbrev=12`.
# On failure, return None.
2016-12-10 17:51:07 +00:00
# On success, return a string containing:
2016-09-11 18:49:16 +00:00
# first line of output of describe
# '*' if there are unstaged changes else ''
# '+' if there are staged changes else ''
@classmethod
2016-09-24 18:06:10 +00:00
def __compute_extra_version ( cls ) :
2016-09-11 18:49:16 +00:00
try :
2016-09-24 18:06:10 +00:00
g = cls . pcall ( [ ' describe ' , ' --tags ' , ' --abbrev=12 ' ] , stderr = subprocess . PIPE )
2016-09-11 18:49:16 +00:00
except OSError as e :
if e . errno == errno . ENOENT :
return None
raise
2016-09-24 18:06:10 +00:00
if g . returncode :
return None
_pcall = cls . pcall
return ( g . out . split ( ' \n ' ) [ 0 ] + \
( ' * ' if _pcall ( [ ' diff ' , ' --quiet ' ] ) . returncode else ' ' ) + \
( ' + ' if _pcall ( [ ' diff ' , ' --quiet ' , ' --cached ' ] ) . returncode else ' ' )
)
2015-04-22 02:44:30 +00:00
2013-06-27 02:35:22 +00:00
class ConfigureTests :
class Collector :
2016-08-12 04:08:57 +00:00
class RecordedTest ( object ) :
__slots__ = ( ' desc ' , ' name ' )
2015-09-13 21:02:19 +00:00
def __init__ ( self , name , desc ) :
self . name = name
self . desc = desc
2013-06-27 02:35:22 +00:00
def __init__ ( self ) :
self . tests = [ ]
2015-08-22 20:43:03 +00:00
self . record = self . tests . append
2013-06-27 02:35:22 +00:00
def __call__ ( self , f ) :
2015-09-13 21:02:19 +00:00
desc = None
doc = getattr ( f , ' __doc__ ' , None )
if doc is not None :
doc = doc . rstrip ( ) . splitlines ( )
if doc and doc [ - 1 ] . startswith ( " help: " ) :
desc = doc [ - 1 ] [ 5 : ]
self . record ( self . RecordedTest ( f . __name__ , desc ) )
2013-06-27 02:35:22 +00:00
return f
2015-09-13 21:02:19 +00:00
2016-08-12 04:08:57 +00:00
class Cxx11RequiredFeature ( object ) :
__slots__ = ( ' main ' , ' name ' , ' text ' )
2015-08-03 03:11:25 +00:00
def __init__ ( self , name , text , main = ' ' ) :
self . name = name
name = { ' N ' : ' test_ ' + name . replace ( ' ' , ' _ ' ) }
self . text = text % name
self . main = ( ' { ' + ( main % name ) + ' } \n ' ) if main else ' '
2016-08-12 04:08:57 +00:00
class CxxRequiredFeatures ( object ) :
__slots__ = ( ' features ' , ' main ' , ' text ' )
2016-08-12 04:08:57 +00:00
def __init__ ( self , features ) :
self . features = features
self . main = ' ' . join ( [ f . main for f in features ] )
self . text = ' ' . join ( [ f . text for f in features ] )
2015-10-03 17:17:49 +00:00
class PCHAction :
def __init__ ( self , context ) :
self . _context = context
def __call__ ( self , text , ext ) :
# Ignore caller-supplied text, since _Test always includes a
# definition of main(). Using the caller-supplied text
# would provide one main() in the PCH and another in the
# test which includes the PCH.
env = self . _context . env
s = env [ ' OBJSUFFIX ' ]
env [ ' OBJSUFFIX ' ] = ' .h.gch '
result = self . _context . TryCompile ( '''
/ * Define this here . Use it in the file which includes the PCH . If the
* compiler skips including the PCH , and does not fail for that reason
* alone , then it will fail when the symbol is used in the later test ,
* since the only definition comes from the PCH .
* /
#define dxx_compiler_supports_pch
''' , ext)
env [ ' OBJSUFFIX ' ] = s
return result
2013-12-05 21:55:29 +00:00
class PreservedEnvironment :
2015-09-26 21:17:12 +00:00
# One empty list for all the defaults. The comprehension
# creates copies, so it is safe for the default value to be
# shared.
def __init__ ( self , env , keys , _l = [ ] ) :
self . flags = { k : env . get ( k , _l ) [ : ] for k in keys }
2015-12-03 03:26:49 +00:00
self . __getitem__ = self . flags . __getitem__
2013-12-05 21:55:29 +00:00
def restore ( self , env ) :
env . Replace ( * * self . flags )
2016-10-15 17:51:48 +00:00
class ForceVerboseLog ( PreservedEnvironment ) :
2013-12-05 21:55:29 +00:00
def __init__ ( self , env ) :
# Force verbose output to sconf.log
2016-10-15 17:51:48 +00:00
self . flags = { }
2015-09-13 21:02:19 +00:00
for k in (
' CXXCOMSTR ' ,
' LINKCOMSTR ' ,
) :
2013-12-05 21:55:29 +00:00
try :
2015-09-26 21:17:12 +00:00
# env is like a dict, but does not have .pop(), so
# emulate it with a lookup + delete.
2016-10-15 17:51:48 +00:00
self . flags [ k ] = env [ k ]
2013-12-05 21:55:29 +00:00
del env [ k ]
except KeyError :
pass
2015-04-02 02:36:52 +00:00
class pkgconfig :
2016-01-12 03:28:45 +00:00
def _get_pkg_config_exec_path ( context , msgprefix , pkgconfig ) :
Display = context . Display
2015-04-02 02:36:52 +00:00
if not pkgconfig :
2016-01-12 03:28:45 +00:00
Display ( " %s : pkg-config disabled by user settings \n " % msgprefix )
2015-04-02 02:36:52 +00:00
return pkgconfig
if os . sep in pkgconfig :
2016-01-12 03:28:45 +00:00
Display ( " %s : using pkg-config at user specified path %s \n " % ( msgprefix , pkgconfig ) )
2015-04-02 02:36:52 +00:00
return pkgconfig
2015-09-29 02:41:22 +00:00
join = os . path . join
2015-04-02 02:36:52 +00:00
# No path specified, search in $PATH
for p in os . environ . get ( ' PATH ' , ' ' ) . split ( os . pathsep ) :
2015-09-29 02:41:22 +00:00
fp = join ( p , pkgconfig )
2015-04-02 02:36:52 +00:00
try :
os . close ( os . open ( fp , os . O_RDONLY ) )
except OSError as e :
# Ignore on permission errors. If pkg-config is
# runnable but not readable, the user must
# specify its path.
if e . errno == errno . ENOENT or e . errno == errno . EACCES :
continue
raise
2016-01-12 03:28:45 +00:00
Display ( " %s : using pkg-config at discovered path %s \n " % ( msgprefix , fp ) )
2015-04-02 02:36:52 +00:00
return fp
2016-01-12 03:28:45 +00:00
Display ( " %s : no usable pkg-config %r found in $PATH \n " % ( msgprefix , pkgconfig ) )
2015-09-29 02:41:22 +00:00
def __get_pkg_config_path ( context , message , user_settings , display_name ,
_get_pkg_config_exec_path = _get_pkg_config_exec_path ,
_cache = { } ) :
pkgconfig = user_settings . PKG_CONFIG
if pkgconfig is None :
CHOST = user_settings . CHOST
pkgconfig = ( ' %s -pkg-config ' % CHOST ) if CHOST else ' pkg-config '
2016-01-13 04:33:44 +00:00
if sys . platform == ' win32 ' :
pkgconfig + = ' .exe '
2015-04-02 02:36:52 +00:00
try :
2015-09-26 21:17:12 +00:00
return _cache [ pkgconfig ]
2015-04-02 02:36:52 +00:00
except KeyError :
2015-09-29 02:41:22 +00:00
_cache [ pkgconfig ] = path = _get_pkg_config_exec_path ( context , message , pkgconfig )
2015-04-02 02:36:52 +00:00
return path
2015-09-29 02:41:22 +00:00
@staticmethod
def merge ( context , message , user_settings , pkgconfig_name , display_name ,
2016-01-13 04:33:44 +00:00
guess_flags ,
2015-09-29 02:41:22 +00:00
__get_pkg_config_path = __get_pkg_config_path ,
_cache = { } ) :
2016-01-12 03:28:45 +00:00
Display = context . Display
Display ( " %s : checking %s pkg-config %s \n " % ( message , display_name , pkgconfig_name ) )
2015-09-29 02:41:22 +00:00
pkgconfig = __get_pkg_config_path ( context , message , user_settings , display_name )
2015-04-02 02:36:52 +00:00
if not pkgconfig :
2016-01-13 04:33:44 +00:00
Display ( " %s : skipping %s pkg-config; using default flags %r \n " % ( message , display_name , guess_flags ) )
return guess_flags
2015-04-02 02:36:52 +00:00
cmd = ' %s --cflags --libs %s ' % ( pkgconfig , pkgconfig_name )
try :
2015-09-26 21:17:12 +00:00
flags = _cache [ cmd ]
2016-01-12 03:28:45 +00:00
Display ( " %s : reusing %s settings from ` %s `: %r \n " % ( message , display_name , cmd , flags ) )
2015-04-02 02:36:52 +00:00
return flags
except KeyError as e :
2016-01-12 03:28:45 +00:00
Display ( " %s : reading %s settings from ` %s ` \n " % ( message , display_name , cmd ) )
2015-04-02 02:36:52 +00:00
try :
2015-09-29 02:41:22 +00:00
flags = {
k : v for k , v in context . env . ParseFlags ( ' ! ' + cmd ) . items ( )
if v and ( k [ 0 ] == ' C ' or k [ 0 ] == ' L ' )
}
2016-01-12 03:28:45 +00:00
Display ( " %s : %s settings: %r \n " % ( message , display_name , flags ) )
2016-09-25 04:52:48 +00:00
v = ' %s --modversion %s ' % ( pkgconfig , pkgconfig_name )
Display ( " %s : reading %s version from ` %s ` \n " % ( message , pkgconfig_name , v ) )
v = StaticSubprocess . qcall ( v )
if v . out :
Display ( " %s : %s version: %r \n " % ( message , display_name , v . out . split ( ' \n ' ) [ 0 ] ) )
2015-04-02 02:36:52 +00:00
except OSError as o :
2016-01-13 04:33:44 +00:00
Display ( " %s : %s pkg-config failed; using default flags for ` %s `: %r \n " % ( message , display_name , cmd , guess_flags ) )
flags = guess_flags
2015-09-26 21:17:12 +00:00
_cache [ cmd ] = flags
2015-04-02 02:36:52 +00:00
return flags
2014-07-19 22:25:27 +00:00
# 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 '
2015-08-22 20:43:04 +00:00
expect_sconf_success = ' success '
expect_sconf_failure = ' failure '
2013-06-27 02:35:22 +00:00
_implicit_test = Collector ( )
_custom_test = Collector ( )
implicit_tests = _implicit_test . tests
custom_tests = _custom_test . tests
comment_not_supported = ' /* not supported */ '
2016-10-09 17:35:03 +00:00
__python_import_struct = None
__flags_Werror = { ' CXXFLAGS ' : [ ' -Werror ' ] }
2014-06-21 17:54:02 +00:00
_cxx_conformance_cxx11 = 11
2014-06-01 20:49:07 +00:00
_cxx_conformance_cxx14 = 14
2015-11-14 18:17:22 +00:00
__cxx_conformance = None
2016-08-12 04:08:57 +00:00
__cxx11_required_features = CxxRequiredFeatures ( [
2015-08-03 03:11:25 +00:00
Cxx11RequiredFeature ( ' constexpr ' , '''
struct % ( N ) s { } ;
2015-09-26 21:17:12 +00:00
static constexpr % ( N ) s get_ % ( N ) s ( ) { return { } ; }
''' , '''
get_ % ( N ) s ( ) ;
2016-08-14 23:47:26 +00:00
''' ),
Cxx11RequiredFeature ( ' extern constexpr ' , '''
/ * < gcc - 4.9 rejects this * /
extern const int % ( N ) s ;
constexpr int % ( N ) s = 0 ;
2015-08-03 03:11:25 +00:00
''' ),
Cxx11RequiredFeature ( ' nullptr ' , '''
#include <cstddef>
std : : nullptr_t % ( N ) s1 = nullptr ;
int * % ( N ) s2 = nullptr ;
2015-09-26 21:17:12 +00:00
''' , '''
% ( N ) s2 = % ( N ) s1 ;
2015-08-03 03:11:25 +00:00
''' ),
Cxx11RequiredFeature ( ' explicit operator bool ' , '''
struct % ( N ) s {
explicit operator bool ( ) ;
} ;
''' ),
Cxx11RequiredFeature ( ' template aliases ' , '''
using % ( N ) s_typedef = int ;
template < typename >
struct % ( N ) s_struct ;
template < typename T >
using % ( N ) s_alias = % ( N ) s_struct < T > ;
''' , '''
% ( N ) s_struct < int > * a = nullptr ;
% ( N ) s_alias < int > * b = a ;
2015-09-26 21:17:12 +00:00
% ( N ) s_typedef * c = nullptr ;
2015-08-03 03:11:25 +00:00
( void ) b ;
2015-09-26 21:17:12 +00:00
( void ) c ;
2015-08-03 03:11:25 +00:00
''' ),
Cxx11RequiredFeature ( ' trailing function return type ' , '''
auto % ( N ) s ( ) - > int ;
''' ),
Cxx11RequiredFeature ( ' class scope static constexpr assignment ' , '''
struct % ( N ) s_instance {
} ;
struct % ( N ) s_container {
static constexpr % ( N ) s_instance a = { } ;
} ;
''' ),
Cxx11RequiredFeature ( ' braced base class initialization ' , '''
struct % ( N ) s_base {
int a ;
} ;
struct % ( N ) s_derived : % ( N ) s_base {
% ( N ) s_derived ( int e ) : % ( N ) s_base { e } { }
} ;
''' ),
2016-07-31 22:25:49 +00:00
Cxx11RequiredFeature ( ' std::array ' , '''
#include "compiler-array.h"
''' , '''
array < int , 2 > b ;
b [ 0 ] = 1 ;
'''
2016-08-08 00:07:19 +00:00
) ,
Cxx11RequiredFeature ( ' range-based for ' , '''
#include "compiler-range_for.h"
''' , '''
int b [ 2 ] ;
range_for ( int & c , b ) c = 0 ;
'''
2016-08-08 00:07:20 +00:00
) ,
Cxx11RequiredFeature ( ' <type_traits> ' , '''
#include <type_traits>
''' , '''
typename std : : conditional < sizeof ( argc ) == sizeof ( int ) , int , long > : : type a = 0 ;
typename std : : conditional < sizeof ( argc ) != sizeof ( int ) , int , long > : : type b = 0 ;
( void ) a ;
( void ) b ;
'''
2016-07-31 22:25:49 +00:00
) ,
2015-08-03 03:11:25 +00:00
Cxx11RequiredFeature ( ' std::unordered_map::emplace ' , '''
#include <unordered_map>
''' , '''
std : : unordered_map < int , int > m ;
m . emplace ( 0 , 0 ) ;
'''
2016-12-10 17:51:07 +00:00
) ,
Cxx11RequiredFeature ( ' reference qualified methods ' , '''
struct % ( N ) s {
int a ( ) const & { return 1 ; }
int a ( ) const & & { return 2 ; }
} ;
''' , '''
% ( N ) s a ;
auto b = a . a ( ) != % ( N ) s ( ) . a ( ) ;
( void ) b ;
'''
2015-08-03 03:11:25 +00:00
) ,
2016-08-12 04:08:57 +00:00
] )
2015-04-02 02:36:52 +00:00
def __init__ ( self , msgprefix , user_settings , platform_settings ) :
2013-06-27 02:35:22 +00:00
self . msgprefix = msgprefix
self . user_settings = user_settings
2015-04-02 02:36:52 +00:00
self . platform_settings = platform_settings
2015-09-13 20:23:05 +00:00
self . successful_flags = defaultdict ( list )
2015-08-22 20:43:04 +00:00
self . _sconf_results = [ ]
2015-10-24 03:13:11 +00:00
self . __tool_versions = [ ]
2016-07-15 03:43:03 +00:00
self . __defined_macros = ' '
2016-08-13 22:20:13 +00:00
# Some tests check the functionality of the compiler's
# optimizer.
#
# When LTO is used, the optimizer is deferred to link time.
# Force all tests to be Link tests when LTO is enabled.
self . Compile = self . Link if user_settings . lto else self . _Compile
2014-07-19 17:34:02 +00:00
def _quote_macro_value ( v ) :
return v . strip ( ) . replace ( ' \n ' , ' \\ \n ' )
2015-08-22 20:43:03 +00:00
def _check_sconf_forced ( self , calling_function ) :
2015-08-22 20:43:04 +00:00
return self . _check_forced ( calling_function ) , self . _check_expected ( calling_function )
@staticmethod
def _find_calling_sconf_function ( ) :
2015-08-22 20:43:03 +00:00
try :
1 / / 0
except ZeroDivisionError :
frame = sys . exc_info ( ) [ 2 ] . tb_frame . f_back . f_back
while frame is not None :
co_name = frame . f_code . co_name
if co_name [ : 6 ] == ' check_ ' :
2015-08-22 20:43:04 +00:00
return co_name [ 6 : ]
2015-08-22 20:43:03 +00:00
frame = frame . f_back
2015-09-19 23:04:36 +00:00
# This assertion is hit if a test is asked to deduce its caller
# (calling_function=None), but no function in the call stack appears to
# be a checking function.
assert False , " SConf caller not specified and no acceptable caller in stack. "
2015-08-22 20:43:03 +00:00
def _check_forced ( self , name ) :
2015-09-19 23:04:36 +00:00
# This getattr will raise AttributeError if called for a function which
# is not a registered test. Tests must be registered as an implicit
# test (in implicit_tests, usually by applying the @_implicit_test
# decorator) or a custom test (in custom_tests, usually by applying the
# @_custom_test decorator).
#
# Unregistered tests are never documented and cannot be overridden by
# the user.
2013-06-27 02:35:22 +00:00
return getattr ( self . user_settings , ' sconf_ %s ' % name )
2015-08-22 20:43:04 +00:00
def _check_expected ( self , name ) :
2015-09-19 23:04:36 +00:00
# The remarks for _check_forced apply here too.
2015-08-22 20:43:04 +00:00
r = getattr ( self . user_settings , ' expect_sconf_ %s ' % name )
if r is not None :
if r == self . expect_sconf_success :
return 1
if r == self . expect_sconf_failure :
return 0
return r
2015-12-03 03:26:49 +00:00
def _check_macro ( self , context , macro_name , macro_value , test , _comment_not_supported = comment_not_supported , * * kwargs ) :
2013-06-27 02:35:22 +00:00
r = self . Compile ( context , text = """
#define {macro_name} {macro_value}
{ test }
""" .format(macro_name=macro_name, macro_value=macro_value, test=test), **kwargs)
2016-07-15 03:43:03 +00:00
if not r :
macro_value = _comment_not_supported
2016-10-08 23:24:21 +00:00
self . _define_macro ( context , macro_name , macro_value )
def _define_macro ( self , context , macro_name , macro_value ) :
2016-07-15 03:43:03 +00:00
context . sconf . Define ( macro_name , macro_value )
self . __defined_macros + = ' #define %s %s \n ' % ( macro_name , macro_value )
2015-09-19 23:04:36 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_ccache_distcc_ld_works ' , " assume ccache, distcc, C++ compiler, and C++ linker work " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_ccache_ld_works ' , " assume ccache, C++ compiler, and C++ linker work " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_distcc_ld_works ' , " assume distcc, C++ compiler, and C++ linker work " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_ld_works ' , " assume C++ compiler and linker work " ) )
2016-08-17 04:44:22 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_ld_blank_libs_works ' , " assume C++ compiler and linker work with empty $LIBS " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_ld_blank_libs_ldflags_works ' , " assume C++ compiler and linker work with empty $LIBS and empty $LDFLAGS " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_cxx_blank_cxxflags_works ' , " assume C++ compiler works with empty $CXXFLAGS " ) )
2015-09-19 23:04:36 +00:00
# This must be the first custom test. This test verifies the compiler
# works and disables any use of ccache/distcc for the duration of the
# configure run.
#
# SCons caches configuration results and tests are usually very small, so
# ccache will provide limited benefit.
#
# Some tests are expected to raise a compiler error. If distcc is used
# and DISTCC_FALLBACK prevents local retries, then distcc interprets a
# compiler error as an indication that the volunteer which served that
# compile is broken and should be blacklisted. Suppress use of distcc for
# all tests to avoid spurious blacklist entries.
#
# During the main build, compiling remotely can allow more jobs to run in
# parallel. Tests are serialized by SCons, so distcc is helpful during
# testing only if compiling remotely is faster than compiling locally.
# This may be true for embedded systems that distcc to desktops, but will
# not be true for desktops or laptops that distcc to similar sized
# machines.
@_custom_test
def check_cxx_works ( self , context ) :
"""
help : assume C + + compiler works
"""
2016-09-11 18:49:16 +00:00
# Use %r to print the tuple in an unambiguous form.
context . Log ( ' scons: dxx: version: %r \n ' % ( Git . compute_extra_version ( ) , ) )
2015-09-19 23:04:36 +00:00
cenv = context . env
penv = cenv [ ' ENV ' ]
self . __cxx_com_prefix = cenv [ ' CXXCOM ' ]
# Require ccache to run the next stage, but allow it to write the
# result to cache. This lets the test validate that ccache fails for
# an unusable CCACHE_DIR and also validate that the next stage handles
# the input correctly. Without this, a cached result may hide that
# the next stage compiler (or wrapper) worked when a prior run
# performed the test, but is now broken.
CCACHE_RECACHE = penv . get ( ' CCACHE_RECACHE ' , None )
penv [ ' CCACHE_RECACHE ' ] = ' 1 '
most_recent_error = self . _check_cxx_works ( context )
if most_recent_error is not None :
raise SCons . Errors . StopError ( most_recent_error )
if CCACHE_RECACHE is None :
del penv [ ' CCACHE_RECACHE ' ]
else :
penv [ ' CCACHE_RECACHE ' ] = CCACHE_RECACHE
# If ccache/distcc are in use, disable them during testing.
# This assignment is also done in _check_cxx_works, but only on an
# error path. Repeat it here so that it is effective on the success
# path. It cannot be moved above the call to _check_cxx_works because
# some tests in _check_cxx_works rely on its original value.
cenv [ ' CXXCOM ' ] = cenv . _dxx_cxxcom_no_prefix
2015-09-19 23:04:36 +00:00
self . _check_cxx_conformance_level ( context )
2015-10-24 03:13:11 +00:00
def _show_tool_version ( self , context , tool , desc , save_tool_version = True ) :
2015-09-26 21:17:13 +00:00
# These version results are not used for anything, but are
# collected here so that users who post only a build log will
# still supply at least some useful information.
#
# This is split into two lines so that the first line is printed
# before the function call required to format the string for the
# second line.
Display = context . Display
Display ( ' %s : checking version of %s %r ... ' % ( self . msgprefix , desc , tool ) )
2015-12-11 03:39:40 +00:00
try :
v = StaticSubprocess . get_version_head ( tool )
except OSError as e :
if e . errno == errno . ENOENT or e . errno == errno . EACCES :
Display ( ' error: %s \n ' % e . strerror )
raise SCons . Errors . StopError ( ' Failed to run %s . ' % tool )
raise
2015-10-24 03:13:11 +00:00
if save_tool_version :
self . __tool_versions . append ( ( tool , v ) )
Display ( ' %r \n ' % v )
2015-09-26 21:17:13 +00:00
def _show_indirect_tool_version ( self , context , CXX , tool , desc ) :
Display = context . Display
Display ( ' %s : checking path to %s ... ' % ( self . msgprefix , desc ) )
2016-07-21 01:43:22 +00:00
tool , name = ToolchainInformation . get_tool_path ( context . env , tool )
2015-10-24 03:13:11 +00:00
self . __tool_versions . append ( ( tool , name ) )
2015-09-26 21:17:13 +00:00
if not name :
# Strange, but not fatal for this to fail.
Display ( ' ! %r \n ' % name )
return
Display ( ' %r \n ' % name )
self . _show_tool_version ( context , name , desc )
2015-10-24 03:13:11 +00:00
def _check_cxx_works ( self , context , _crc32 = binascii . crc32 ) :
2015-09-19 23:04:36 +00:00
# Test whether the compiler+linker+optional wrapper(s) work. If
# anything fails, a StopError is guaranteed on return. However, to
# help the user, this function pushes through all the combinations and
# reports the StopError for the least complicated issue. If both the
# compiler and the linker fail, the compiler will be reported, since
# the linker might work once the compiler is fixed.
#
# If a test fails, then the pending StopError allows this function to
# safely modify the construction environment and process environment
# without reverting its changes.
most_recent_error = None
Link = self . Link
cenv = context . env
use_distcc = self . user_settings . distcc
2015-09-26 21:17:13 +00:00
use_ccache = self . user_settings . ccache
if self . user_settings . show_tool_version :
CXX = cenv [ ' CXX ' ]
self . _show_tool_version ( context , CXX , ' C++ compiler ' )
self . _show_indirect_tool_version ( context , CXX , ' as ' , ' assembler ' )
self . _show_indirect_tool_version ( context , CXX , ' ld ' , ' linker ' )
if use_distcc :
2015-10-24 03:13:11 +00:00
self . _show_tool_version ( context , use_distcc , ' distcc ' , False )
2015-09-26 21:17:13 +00:00
if use_ccache :
2015-10-24 03:13:11 +00:00
self . _show_tool_version ( context , use_ccache , ' ccache ' , False )
# Use C++ single line comment so that it is guaranteed to extend
# to the end of the line. repr ensures that embedded newlines
# will be escaped and that the final character will not be a
# backslash.
self . __commented_tool_versions = s = ' ' . join ( ' // %r => %r \n ' % ( v [ 0 ] , v [ 1 ] ) for v in self . __tool_versions )
self . __tool_versions = '''
/ * This test is always false . Use a non - trivial condition to
* discourage external text scanners from recognizing that the block
* is never compiled .
* /
#if 1 < -1
% .8 x
% s
#endif
''' % (_crc32(s), s)
2016-10-02 00:34:50 +00:00
ToolchainInformation . show_partial_environ ( cenv , lambda s , _Display = context . Display , _msgprefix = self . msgprefix : _Display ( " %s : \t %s \n " % ( _msgprefix , s ) ) )
2015-09-26 21:17:13 +00:00
if use_ccache :
2015-09-19 23:04:36 +00:00
if use_distcc :
if Link ( context , text = ' ' , msg = ' whether ccache, distcc, C++ compiler, and linker work ' , calling_function = ' ccache_distcc_ld_works ' ) :
return
most_recent_error = ' ccache and C++ linker work, but distcc does not work. '
# Disable distcc so that the next call to self.Link tests only
# ccache+linker.
del cenv [ ' ENV ' ] [ ' CCACHE_PREFIX ' ]
if Link ( context , text = ' ' , msg = ' whether ccache, C++ compiler, and linker work ' , calling_function = ' ccache_ld_works ' ) :
return most_recent_error
most_recent_error = ' C++ linker works, but ccache does not work. '
elif use_distcc :
if Link ( context , text = ' ' , msg = ' whether distcc, C++ compiler, and linker work ' , calling_function = ' distcc_ld_works ' ) :
return
most_recent_error = ' C++ linker works, but distcc does not work. '
else :
# This assertion fails if the environment's $CXXCOM was modified
# to use a prefix, but both user_settings.ccache and
# user_settings.distcc evaluate to false.
assert cenv . _dxx_cxxcom_no_prefix is cenv [ ' CXXCOM ' ] , " Unexpected prefix in $CXXCOM. "
# If ccache/distcc are in use, then testing with one or both of them
# failed. Disable them so that the next test can check whether the
# local linker works.
#
# If they are not in use, this assignment is a no-op.
cenv [ ' CXXCOM ' ] = cenv . _dxx_cxxcom_no_prefix
if Link ( context , text = ' ' , msg = ' whether C++ compiler and linker work ' , calling_function = ' ld_works ' ) :
2016-08-17 04:44:22 +00:00
# If ccache or distcc are in use, this block is only reached
# when one or both of them failed. `most_recent_error` will
# be a description of the failure. If neither are in use,
# `most_recent_error` will be None.
2015-09-19 23:04:36 +00:00
return most_recent_error
# Force only compile, even if LTO is enabled.
elif self . _Compile ( context , text = ' ' , msg = ' whether C++ compiler works ' , calling_function = ' cxx_works ' ) :
2016-08-17 04:44:22 +00:00
specified_LIBS = ' or ' if cenv . get ( ' LIBS ' ) else ' '
if specified_LIBS :
cenv [ ' LIBS ' ] = [ ]
if Link ( context , text = ' ' , msg = ' whether C++ compiler and linker work with blank $LIBS ' , calling_function = ' ld_blank_libs_works ' ) :
# Using $LIBS="" allowed the test to succeed. $LIBS
# specifies one or more unusable libraries. Usually
# this is because it specifies a library which does
# not exist or is an incompatible architecture.
return ' C++ compiler works. C++ linker works with blank $LIBS. C++ linker does not work with specified $LIBS. '
if cenv [ ' LINKFLAGS ' ] :
cenv [ ' LINKFLAGS ' ] = [ ]
if Link ( context , text = ' ' , msg = ' whether C++ compiler and linker work with blank $LIBS and blank $LDFLAGS ' , calling_function = ' ld_blank_libs_ldflags_works ' ) :
# Using LINKFLAGS="" allowed the test to succeed.
# To avoid bloat, there is no further test to see
# whether the link will work with user-specified
# LIBS after LINKFLAGS is cleared. The user must
# fix at least one problem anyway. If the user is
# unlucky, fixing LINKFLAGS will result in a
# different error on the next run. If the user is
# lucky, fixing LINKFLAGS will allow the build to
# run.
return ' C++ compiler works. C++ linker works with blank $LIBS and blank $LDFLAGS. C++ linker does not work with blank $LIBS and specified $LDFLAGS. '
2016-08-22 00:31:15 +00:00
return ' C++ compiler works. C++ linker does not work with specified %(LIBS)s blank $LIBS and specified $LINKFLAGS. C++ linker does not work with blank $LIBS and blank $LINKFLAGS. ' % {
2016-08-17 04:44:22 +00:00
' LIBS ' : specified_LIBS ,
}
2015-09-19 23:04:36 +00:00
else :
2016-08-17 04:44:22 +00:00
if cenv [ ' CXXFLAGS ' ] :
cenv [ ' CXXFLAGS ' ] = [ ]
if self . _Compile ( context , text = ' ' , msg = ' whether C++ compiler works with blank $CXXFLAGS ' , calling_function = ' cxx_blank_cxxflags_works ' ) :
return ' C++ compiler works with blank $CXXFLAGS. C++ compiler does not work with specified $CXXFLAGS. '
2015-09-19 23:04:36 +00:00
return ' C++ compiler does not work. '
2015-09-19 23:04:36 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_cxx11 ' , " assume C++ compiler supports C++11 " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_cxx14 ' , " assume C++ compiler supports C++14 " ) )
2015-12-03 03:26:49 +00:00
__cxx_conformance_CXXFLAGS = [ None ]
def _check_cxx_conformance_level ( self , context , _levels = (
# List standards in descending order of preference
_cxx_conformance_cxx14 ,
# C++11 is required, so list it last. Omit the comma as a
# reminder not to append elements to the list.
_cxx_conformance_cxx11
) , _CXXFLAGS = __cxx_conformance_CXXFLAGS ,
_successflags = { ' CXXFLAGS ' : __cxx_conformance_CXXFLAGS }
) :
2015-09-19 23:04:36 +00:00
# Testing the compiler option parser only needs Compile, even when LTO
# is enabled.
Compile = self . _Compile
# GCC started with -std=gnu++0x for C++0x (later C++11). In gcc-4.7,
# GCC began accepting -std=gnu++11. Since gcc-4.6 does not accept
# some constructs used in the code, use the newer name here.
#
# Accepted options by version:
#
# gcc-4.6 -std=gnu++0x
#
# gcc-4.7 -std=gnu++0x
# gcc-4.7 -std=gnu++11
#
# gcc-4.8 -std=gnu++0x
# gcc-4.8 -std=gnu++11
# gcc-4.8 -std=gnu++1y
#
# gcc-4.9 -std=gnu++0x
# gcc-4.9 -std=gnu++11
# gcc-4.9 -std=gnu++1y
# gcc-4.9 -std=gnu++14
#
# gcc-5 -std=gnu++0x
# gcc-5 -std=gnu++11
# gcc-5 -std=gnu++1y
# gcc-5 -std=gnu++14
# gcc-5 -std=gnu++1z
# gcc-5 -std=gnu++17
#
# In all supported cases except gcc-4.8, gcc accepts the number-only
# form if it accepts the approximated form. The only C++14 feature of
# interest in gcc-4.8 is return type deduction, which cannot be used
# until gcc-4.7 is retired. Therefore, it is acceptable for this
# check not to detect C++14 support in gcc-4.8.
2015-12-03 03:26:49 +00:00
for level in _levels :
2015-09-19 23:04:36 +00:00
opt = ' -std=gnu++ %u ' % level
2015-12-03 03:26:49 +00:00
_CXXFLAGS [ 0 ] = opt
if Compile ( context , text = ' ' , msg = ' whether C++ compiler accepts {opt} ' . format ( opt = opt ) , successflags = _successflags , calling_function = ' cxx %s ' % level ) :
2015-09-19 23:04:36 +00:00
self . __cxx_conformance = level
return
raise SCons . Errors . StopError ( ' C++ compiler does not accept any supported C++ -std option. ' )
2015-08-22 20:43:03 +00:00
def _Test ( self , context , text , msg , action , main = ' ' , ext = ' .cpp ' , testflags = { } , successflags = { } , skipped = None , successmsg = None , failuremsg = None , expect_failure = False , calling_function = None ) :
2015-08-22 20:43:04 +00:00
if calling_function is None :
calling_function = self . _find_calling_sconf_function ( )
2013-06-27 02:35:22 +00:00
context . Message ( ' %s : checking %s ... ' % ( self . msgprefix , msg ) )
if skipped is not None :
context . Result ( ' (skipped) {skipped} ' . format ( skipped = skipped ) )
2015-08-22 20:43:04 +00:00
if self . user_settings . record_sconf_results :
self . _sconf_results . append ( ( calling_function , ' skipped ' ) )
2013-06-27 02:35:22 +00:00
return
2014-09-17 02:11:43 +00:00
env_flags = self . PreservedEnvironment ( context . env , successflags . keys ( ) + testflags . keys ( ) + self . __flags_Werror . keys ( ) + [ ' CPPDEFINES ' ] )
2015-04-02 02:36:52 +00:00
context . env . MergeFlags ( successflags )
2015-08-22 20:43:04 +00:00
forced , expected = self . _check_sconf_forced ( calling_function )
2013-12-05 21:55:29 +00:00
caller_modified_env_flags = self . PreservedEnvironment ( context . env , self . __flags_Werror . keys ( ) + testflags . keys ( ) )
2013-06-27 02:35:22 +00:00
# Always pass -Werror
context . env . Append ( * * self . __flags_Werror )
2013-11-26 05:03:00 +00:00
context . env . Append ( * * testflags )
2015-09-26 21:17:12 +00:00
# If forced is None, run the test. Otherwise, skip the test and
# take an action determined by the value of forced.
2014-06-30 03:26:49 +00:00
if forced is None :
2015-09-19 23:04:35 +00:00
r = action ( '''
% s
2015-10-24 03:13:11 +00:00
% s
2016-07-15 03:43:03 +00:00
% s
2015-09-19 23:04:35 +00:00
#undef main /* avoid -Dmain=SDL_main from libSDL */
int main ( int argc , char * * argv ) { ( void ) argc ; ( void ) argv ;
% s
; }
2016-07-15 03:43:03 +00:00
''' % (self.__tool_versions, self.__defined_macros, text, main), ext)
2015-09-26 21:17:12 +00:00
# Some tests check that the compiler rejects an input.
# SConf considers the result a failure when the compiler
# rejects the input. For tests that consider a rejection to
# be the good result, this conditional flips the sense of
# the result so that a compiler rejection is reported as
# success.
2014-06-30 03:26:49 +00:00
if expect_failure :
r = not r
context . Result ( ( successmsg if r else failuremsg ) or r )
2015-08-22 20:43:04 +00:00
if expected is not None and r != expected :
2015-09-19 23:04:35 +00:00
raise SCons . Errors . StopError ( ' Expected and actual results differ. Test should %s , but it did not. ' % ( ' succeed ' if expected else ' fail ' ) )
2014-06-30 03:26:49 +00:00
else :
2014-07-19 22:25:27 +00:00
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 :
2015-09-26 21:17:12 +00:00
# Pretend the test returned a failure result
2014-07-19 22:25:27 +00:00
r = False
elif forced == self . sconf_force_success or forced == self . sconf_assume_success :
2015-09-26 21:17:12 +00:00
# Pretend the test succeeded. Forced success modifies
# the environment as if the test had run and succeeded.
# Assumed success modifies the environment as if the
# test had run and failed.
#
# The latter is used when the user arranges for the
# environment to be correct. For example, if the
# compiler understands C++14, but uses a non-standard
# name for the option, the user would set assume-success
# and add the appropriate option to CXXFLAGS.
2014-07-19 22:25:27 +00:00
r = True
else :
raise SCons . Errors . UserError ( " Unknown force value for sconf_ %s : %s " % ( co_name [ 6 : ] , forced ) )
2015-09-26 21:17:12 +00:00
# Flip the sense of the forced value, so that users can
# treat "force-failure" as force-bad-result regardless of
# whether the bad result is that the compiler rejected good
# input or that the compiler accepted bad input.
2014-06-30 03:26:49 +00:00
if expect_failure :
2014-07-19 22:25:27 +00:00
r = not r
context . Result ( ' (forced) {inverted} {forced} ' . format ( forced = forced , inverted = ' (inverted) ' if expect_failure else ' ' ) )
2013-06-27 02:35:22 +00:00
# On success, revert to base flags + successflags
# On failure, revert to base flags
2014-07-19 22:25:27 +00:00
if r and forced != self . sconf_assume_success :
2013-12-05 21:55:29 +00:00
caller_modified_env_flags . restore ( context . env )
2013-12-04 21:47:42 +00:00
context . env . Replace ( CPPDEFINES = env_flags [ ' CPPDEFINES ' ] )
2015-12-03 03:26:49 +00:00
f = self . successful_flags
2015-09-26 21:17:12 +00:00
# Move most CPPDEFINES to the generated header, so that
# command lines are shorter.
2015-12-03 03:26:49 +00:00
for k , v in successflags . iteritems ( ) :
if k == ' CPPDEFINES ' :
2015-03-22 18:49:21 +00:00
continue
2015-12-03 03:26:49 +00:00
f [ k ] . extend ( v )
d = successflags . get ( ' CPPDEFINES ' , None )
if d :
append_CPPDEFINE = f [ ' CPPDEFINES ' ] . append
2016-08-13 22:20:13 +00:00
Define = context . sconf . Define
2015-12-03 03:26:49 +00:00
for v in d :
# v is 'NAME' for -DNAME
# v is ('NAME', 'VALUE') for -DNAME=VALUE
d = ( v , None ) if isinstance ( v , str ) else v
if d [ 0 ] in ( ' _REENTRANT ' , ) :
# Blacklist defines that must not be moved to the
# configuration header.
append_CPPDEFINE ( v )
continue
2016-08-13 22:20:13 +00:00
Define ( d [ 0 ] , d [ 1 ] )
2013-06-27 02:35:22 +00:00
else :
2013-12-05 21:55:29 +00:00
env_flags . restore ( context . env )
2015-08-22 20:43:04 +00:00
self . _sconf_results . append ( ( calling_function , r ) )
2013-06-27 02:35:22 +00:00
return r
2015-12-03 03:26:49 +00:00
def _Compile ( self , context , _Test = _Test , * * kwargs ) :
return _Test ( self , context , action = context . TryCompile , * * kwargs )
def Link ( self , context , _Test = _Test , * * kwargs ) :
return _Test ( self , context , action = context . TryLink , * * kwargs )
2015-09-26 21:17:12 +00:00
# Compile and link a program that uses a system library. On
# success, return None. On failure, return a tuple indicating which
# stage failed and providing a text error message to show to the
# user. Some callers handle failure by retrying with other options.
# Others abort the SConf run.
2015-04-09 02:29:58 +00:00
def _soft_check_system_library ( self , context , header , main , lib , successflags = { } ) :
2013-12-05 22:47:43 +00:00
include = ' \n ' . join ( [ ' #include < %s > ' % h for h in header ] )
2015-12-03 03:26:49 +00:00
header = header [ - 1 ]
2013-12-05 22:47:43 +00:00
# Test library. On success, good. On failure, test header to
# give the user more help.
2015-09-19 23:04:35 +00:00
if self . Link ( context , text = include , main = main , msg = ' for usable library %s ' % lib , successflags = successflags ) :
2013-12-05 22:47:43 +00:00
return
2016-08-13 22:20:13 +00:00
Compile = self . Compile
if Compile ( context , text = include , main = main , msg = ' for usable header %s ' % header , testflags = successflags ) :
2015-12-03 03:26:49 +00:00
return ( 0 , " Header %s is usable, but library %s is not usable. " % ( header , lib ) )
2016-08-13 22:20:13 +00:00
if Compile ( context , text = include , main = ' ' , msg = ' for parseable header %s ' % header , testflags = successflags ) :
2015-12-03 03:26:49 +00:00
return ( 1 , " Header %s is parseable, but cannot compile the test program. " % header )
2016-12-22 05:21:17 +00:00
successflags . setdefault ( ' CXXFLAGS ' , [ ] ) . append ( ' -E ' )
if Compile ( context , text = include , main = ' ' , msg = ' for parseable header %s ' % header , testflags = successflags ) :
return ( 2 , " Header %s exists, but cannot compile an empty program. " % header )
return ( 3 , " Header %s is missing or unusable. " % header )
2015-09-26 21:17:12 +00:00
# Compile and link a program that uses a system library. On
# success, return None. On failure, abort the SConf run.
2015-04-09 02:29:58 +00:00
def _check_system_library ( self , * args , * * kwargs ) :
e = self . _soft_check_system_library ( * args , * * kwargs )
if e :
raise SCons . Errors . StopError ( e [ 1 ] )
2016-01-26 03:45:07 +00:00
# User settings tests are hidden because they do not respect sconf_*
# overrides, so the user should not be offered an override.
2013-12-05 22:47:43 +00:00
@_custom_test
2016-10-09 17:35:03 +00:00
def _check_user_settings_endian ( self , context , __endian_names = ( ' little ' , ' big ' ) ) :
cls = self . __class__
endian = self . user_settings . host_endian
if endian is None :
struct = cls . __python_import_struct
if struct is None :
import struct
cls . __python_import_struct = struct
a = struct . pack ( ' cccc ' , ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' )
unpack = struct . unpack
i = unpack ( ' i ' , a )
if i == unpack ( ' <i ' , a ) :
endian = 0
elif i == unpack ( ' >i ' , a ) :
endian = 1
else :
raise SCons . Errors . UserError ( " Unknown host endian: unpack( ' i ' , %r ) == %r ; set host_endian= ' big ' or host_endian= ' little ' as appropriate. " % ( a , i ) )
else :
if endian == __endian_names [ 0 ] :
endian = 0
else :
endian = 1
context . Result ( ' %s : checking endian to use... %s ' % ( self . msgprefix , __endian_names [ endian ] ) )
self . _define_macro ( context , ' DXX_WORDS_BIGENDIAN ' , endian )
@_custom_test
2016-10-09 17:35:03 +00:00
def _check_user_settings_words_need_alignment ( self , context ) :
self . _result_check_user_setting ( context , self . user_settings . words_need_alignment , ' DXX_WORDS_NEED_ALIGNMENT ' , ' word alignment fixups ' )
@_custom_test
2016-09-24 18:06:11 +00:00
def _check_user_settings_opengl ( self , context ) :
2016-01-12 03:28:45 +00:00
user_settings = self . user_settings
Result = context . Result
2016-09-24 18:06:11 +00:00
Define = context . sconf . Define
2016-09-24 18:06:11 +00:00
Define ( ' DXX_USE_OGL ' , int ( user_settings . opengl ) )
2016-09-24 18:06:11 +00:00
Define ( ' DXX_USE_OGLES ' , int ( user_settings . opengles ) )
2016-01-12 03:28:45 +00:00
if user_settings . opengles :
2016-09-24 18:06:11 +00:00
s = ' OpenGL ES '
2016-01-12 03:28:45 +00:00
elif user_settings . opengl :
2016-09-24 18:06:11 +00:00
s = ' OpenGL '
2016-01-12 03:28:45 +00:00
else :
2016-09-24 18:06:11 +00:00
s = ' software renderer '
Result ( ' %s : building with %s ' % ( self . msgprefix , s ) )
2016-09-11 18:49:15 +00:00
def _result_check_user_setting ( self , context , condition , CPPDEFINES , label , int = int , str = str ) :
if isinstance ( CPPDEFINES , str ) :
2016-11-12 20:58:16 +00:00
self . _define_macro ( context , CPPDEFINES , int ( condition ) )
2016-09-11 18:49:15 +00:00
elif condition :
2016-01-26 03:45:07 +00:00
self . successful_flags [ ' CPPDEFINES ' ] . extend ( CPPDEFINES )
context . Result ( ' %s : checking whether to enable %s ... %s ' % ( self . msgprefix , label , ' yes ' if condition else ' no ' ) )
@_custom_test
def _check_user_settings_debug ( self , context , _CPPDEFINES = ( ( ' NDEBUG ' , ) , ( ' RELEASE ' , ) ) ) :
self . _result_check_user_setting ( context , not self . user_settings . debug , _CPPDEFINES , ' release options ' )
2016-01-12 03:28:45 +00:00
@_custom_test
2016-01-26 03:45:07 +00:00
def _check_user_settings_memdebug ( self , context , _CPPDEFINES = ( ( ' DEBUG_MEMORY_ALLOCATIONS ' , ) , ) ) :
self . _result_check_user_setting ( context , self . user_settings . memdebug , _CPPDEFINES , ' memory allocation tracking ' )
@_custom_test
2016-09-11 18:49:16 +00:00
def _check_user_settings_editor ( self , context , _CPPDEFINES = ' DXX_USE_EDITOR ' ) :
2016-01-26 03:45:07 +00:00
self . _result_check_user_setting ( context , self . user_settings . editor , _CPPDEFINES , ' level editor ' )
@_custom_test
2016-09-11 18:49:15 +00:00
def _check_user_settings_ipv6 ( self , context , _CPPDEFINES = ' DXX_USE_IPv6 ' ) :
2016-01-26 03:45:07 +00:00
self . _result_check_user_setting ( context , self . user_settings . ipv6 , _CPPDEFINES , ' IPv6 support ' )
@_custom_test
2016-09-11 18:49:15 +00:00
def _check_user_settings_udp ( self , context , _CPPDEFINES = ' DXX_USE_UDP ' ) :
2016-01-26 03:45:08 +00:00
self . _result_check_user_setting ( context , self . user_settings . use_udp , _CPPDEFINES , ' multiplayer over UDP ' )
@_custom_test
2016-09-11 18:49:15 +00:00
def _check_user_settings_tracker ( self , context , _CPPDEFINES = ' DXX_USE_TRACKER ' ) :
2016-09-11 18:49:16 +00:00
use_tracker = self . user_settings . use_tracker
self . _result_check_user_setting ( context , use_tracker , _CPPDEFINES , ' UDP game tracker ' )
# The legacy UDP tracker does not need either curl or jsoncpp.
# The new HTTP tracker requires both. Force `use_tracker` to
# False for now. Remove this comment and this assignment when
# the HTTP tracker is made active.
use_tracker = False
if use_tracker :
self . check_curl ( context )
self . check_jsoncpp ( context )
@_implicit_test
def check_curl ( self , context ,
_header = ( ' curl/curl.h ' , ) ,
_guess_flags = { ' LIBS ' : [ ' curl ' ] } ,
_main = '''
CURL * c = curl_easy_init ( ) ;
curl_easy_setopt ( c , CURLOPT_WRITEFUNCTION , nullptr ) ;
curl_easy_cleanup ( c ) ;
''' ):
successflags = self . pkgconfig . merge ( context , self . msgprefix , self . user_settings , ' libcurl ' , ' curl ' , _guess_flags ) . copy ( )
successflags [ ' CPPDEFINES ' ] = successflags . get ( ' CPPDEFINES ' , [ ] ) + [ ' DXX_HAVE_LIBCURL ' ]
self . _check_system_library ( context , header = _header , main = _main , lib = ' curl ' , successflags = successflags )
@_implicit_test
def check_jsoncpp ( self , context ,
_header = (
' memory ' ,
' json/json.h ' ,
) ,
_guess_flags = { ' LIBS ' : [ ' jsoncpp ' ] , ' CPPPATH ' : [ ' /usr/include/jsoncpp ' ] } ,
_main = '''
Json : : Value v ;
v [ " a " ] = " a " ;
v [ " b " ] = 1 ;
/ / This code is silly , but it uses many of the required
/ / symbols , so if it builds , jsoncpp is probably installed correctly .
const std : : string & & s = Json : : writeString ( Json : : StreamWriterBuilder ( ) , v ) ;
std : : string errs ;
std : : unique_ptr < Json : : CharReader > (
Json : : CharReaderBuilder ( ) . newCharReader ( )
) - > parse ( s . data ( ) , & s . data ( ) [ s . size ( ) ] , & v , & errs ) ;
''' ):
successflags = self . pkgconfig . merge ( context , self . msgprefix , self . user_settings , ' jsoncpp ' , ' jsoncpp ' , _guess_flags )
self . _check_system_library ( context , header = _header , main = _main , lib = ' jsoncpp ' , successflags = successflags )
2016-01-26 03:45:08 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_libphysfs ( self , context , _header = ( ' physfs.h ' , ) ) :
2015-04-09 02:29:58 +00:00
main = '''
2013-12-05 22:47:43 +00:00
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 ) ;
2015-04-09 02:29:58 +00:00
'''
l = [ ' physfs ' ]
successflags = { ' LIBS ' : l }
2015-12-03 03:26:49 +00:00
e = self . _soft_check_system_library ( context , header = _header , main = main , lib = ' physfs ' , successflags = successflags )
2015-04-09 02:29:58 +00:00
if not e :
return
if e [ 0 ] == 0 :
2016-01-12 03:28:45 +00:00
context . Display ( " %s : physfs header usable; adding zlib and retesting library \n " % self . msgprefix )
2015-04-09 02:29:58 +00:00
l . append ( ' z ' )
2015-12-03 03:26:49 +00:00
e = self . _soft_check_system_library ( context , header = _header , main = main , lib = ' physfs ' , successflags = successflags )
2015-04-09 02:29:58 +00:00
if e :
raise SCons . Errors . StopError ( e [ 1 ] )
2013-12-05 23:39:50 +00:00
@_custom_test
2015-07-14 02:42:12 +00:00
def _check_SDL ( self , context ) :
if self . user_settings . sdl2 :
2016-02-23 04:18:13 +00:00
check_libSDL = self . check_libSDL2
check_SDL_mixer = self . check_SDL2_mixer
2015-07-14 02:42:12 +00:00
else :
2016-02-23 04:18:13 +00:00
check_libSDL = self . check_libSDL
check_SDL_mixer = self . check_SDL_mixer
check_libSDL ( context )
check_SDL_mixer ( context )
2015-07-14 02:42:12 +00:00
@_implicit_test
2016-01-13 04:33:44 +00:00
def check_libSDL ( self , context , _guess_flags = {
2016-02-23 04:18:13 +00:00
' LIBS ' : [ ' SDL ' ] if sys . platform != ' darwin ' else [ ] ,
2016-01-13 04:33:44 +00:00
} ) :
self . _check_libSDL ( context , ' ' , _guess_flags )
2015-07-14 02:42:12 +00:00
@_implicit_test
2016-01-13 04:33:44 +00:00
def check_libSDL2 ( self , context , _guess_flags = {
2016-02-23 04:18:13 +00:00
' LIBS ' : [ ' SDL2 ' ] if sys . platform != ' darwin ' else [ ] ,
2016-01-13 04:33:44 +00:00
} ) :
self . _check_libSDL ( context , ' 2 ' , _guess_flags )
def _check_libSDL ( self , context , sdl2 , guess_flags ) :
2015-11-01 21:15:40 +00:00
user_settings = self . user_settings
2016-01-13 04:33:44 +00:00
successflags = self . pkgconfig . merge ( context , self . msgprefix , user_settings , ' sdl %s ' % sdl2 , ' SDL %s ' % sdl2 , guess_flags ) . copy ( )
2015-11-01 21:15:40 +00:00
if user_settings . max_joysticks :
# If joysticks are enabled, but all possible inputs are
# disabled, then disable joystick support.
if not ( user_settings . max_axes_per_joystick or user_settings . max_buttons_per_joystick or user_settings . max_hats_per_joystick ) :
user_settings . max_joysticks = 0
else :
# If joysticks are disabled, then disable all possible
# inputs.
user_settings . max_axes_per_joystick = user_settings . max_buttons_per_joystick = user_settings . max_hats_per_joystick = 0
2015-12-04 03:36:32 +00:00
successflags [ ' CPPDEFINES ' ] = CPPDEFINES = successflags . get ( ' CPPDEFINES ' , [ ] ) [ : ]
CPPDEFINES . extend ( (
2016-09-25 04:52:48 +00:00
( ' DXX_MAX_JOYSTICKS ' , user_settings . max_joysticks ) ,
2016-09-25 04:52:49 +00:00
( ' DXX_MAX_AXES_PER_JOYSTICK ' , user_settings . max_axes_per_joystick ) ,
2016-09-25 04:52:49 +00:00
( ' DXX_MAX_BUTTONS_PER_JOYSTICK ' , user_settings . max_buttons_per_joystick ) ,
2016-09-25 04:52:49 +00:00
( ' DXX_MAX_HATS_PER_JOYSTICK ' , user_settings . max_hats_per_joystick ) ,
2015-11-01 21:15:40 +00:00
) )
context . Display ( ' %s : checking whether to enable joystick support... %s \n ' % ( self . msgprefix , ' yes ' if user_settings . max_joysticks else ' no ' ) )
2015-07-14 02:42:12 +00:00
# SDL2 removed CD-rom support.
init_cdrom = ' 0 ' if sdl2 else ' SDL_INIT_CDROM '
2015-01-03 23:44:32 +00:00
self . _check_system_library ( context , header = [ ' SDL.h ' ] , main = '''
SDL_RWops * ops = reinterpret_cast < SDL_RWops * > ( argv ) ;
2016-09-25 04:52:48 +00:00
#if DXX_MAX_JOYSTICKS
2015-11-01 21:15:40 +00:00
#define DXX_SDL_INIT_JOYSTICK SDL_INIT_JOYSTICK |
#else
#define DXX_SDL_INIT_JOYSTICK
#endif
SDL_Init ( DXX_SDL_INIT_JOYSTICK % s | SDL_INIT_VIDEO | SDL_INIT_AUDIO ) ;
2016-09-25 04:52:48 +00:00
#if DXX_MAX_JOYSTICKS
2015-09-19 23:04:36 +00:00
auto n = SDL_NumJoysticks ( ) ;
( void ) n ;
2015-11-01 21:15:40 +00:00
#endif
2015-07-14 02:42:12 +00:00
SDL_QuitSubSystem ( SDL_INIT_VIDEO ) ;
2015-01-03 23:44:32 +00:00
SDL_FreeRW ( ops ) ;
SDL_Quit ( ) ;
2015-09-19 23:04:35 +00:00
''' % i nit_cdrom,
2015-04-02 02:36:52 +00:00
lib = ' SDL ' , successflags = successflags
2015-01-03 23:44:32 +00:00
)
2016-01-13 04:33:44 +00:00
# SDL_mixer/SDL2_mixer use the same -I line as SDL/SDL2
2015-07-14 02:42:12 +00:00
@_implicit_test
2016-01-13 04:33:44 +00:00
def check_SDL_mixer ( self , context , _guess_flags = {
2016-02-23 04:18:13 +00:00
' LIBS ' : [ ' SDL_mixer ' ] if sys . platform != ' darwin ' else [ ] ,
2016-01-13 04:33:44 +00:00
} ) :
self . _check_SDL_mixer ( context , ' ' , _guess_flags )
2015-07-14 02:42:12 +00:00
@_implicit_test
2016-01-13 04:33:44 +00:00
def check_SDL2_mixer ( self , context , _guess_flags = {
2016-02-23 04:18:13 +00:00
' LIBS ' : [ ' SDL2_mixer ' ] if sys . platform != ' darwin ' else [ ] ,
2016-01-13 04:33:44 +00:00
} ) :
self . _check_SDL_mixer ( context , ' 2 ' , _guess_flags )
def _check_SDL_mixer ( self , context , sdl2 , guess_flags ) :
2015-09-19 23:04:35 +00:00
mixer = ' SDL %s _mixer ' % sdl2
2016-01-12 03:28:45 +00:00
user_settings = self . user_settings
context . Display ( ' %s : checking whether to use %s ... %s \n ' % ( self . msgprefix , mixer , ' yes ' if user_settings . sdlmixer else ' no ' ) )
2013-12-05 23:39:50 +00:00
# SDL_mixer support?
2016-09-11 18:49:15 +00:00
use_sdlmixer = user_settings . sdlmixer
2016-10-08 23:24:21 +00:00
self . _define_macro ( context , ' DXX_USE_SDLMIXER ' , int ( use_sdlmixer ) )
2016-09-11 18:49:15 +00:00
if not use_sdlmixer :
2013-12-05 23:39:50 +00:00
return
2016-01-13 04:33:44 +00:00
successflags = self . pkgconfig . merge ( context , self . msgprefix , user_settings , mixer , mixer , guess_flags )
2016-01-12 03:28:45 +00:00
if user_settings . host_platform == ' darwin ' :
2015-09-29 02:41:22 +00:00
successflags = successflags . copy ( )
2015-07-14 02:42:12 +00:00
successflags [ ' FRAMEWORKS ' ] = [ mixer ]
2015-09-19 23:04:35 +00:00
relative_headers = ' Library/Frameworks/ %s .framework/Headers ' % mixer
successflags [ ' CPPPATH ' ] = [ os . path . join ( os . getenv ( " HOME " ) , relative_headers ) , ' / %s ' % relative_headers ]
2014-12-03 23:12:59 +00:00
self . _check_system_library ( context , header = [ ' SDL_mixer.h ' ] , main = '''
2013-12-05 23:39:50 +00:00
int i = Mix_Init ( MIX_INIT_FLAC | MIX_INIT_OGG ) ;
( void ) i ;
Mix_Pause ( 0 ) ;
Mix_ResumeMusic ( ) ;
Mix_Quit ( ) ;
''' ,
2015-07-14 02:42:12 +00:00
lib = mixer , successflags = successflags )
2013-06-27 02:35:22 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_compiler_missing_field_initializers ( self , context ,
_testflags_warn = { ' CXXFLAGS ' : [ ' -Wmissing-field-initializers ' ] } ,
_successflags_nowarn = { ' CXXFLAGS ' : [ ' -Wno-missing-field-initializers ' ] }
) :
2015-09-26 21:17:12 +00:00
"""
Test whether the compiler warns for a statement of the form
variable = { } ;
gcc - 4. x warns for this form , but - Wno - missing - field - initializers silences it .
gcc - 5 does not warn .
This form is used extensively in the code as a shorthand for resetting
variables to their default - constructed value .
"""
2015-01-15 04:30:03 +00:00
text = ' struct A { int a;}; '
main = ' A a {} ;(void)a; '
2016-08-13 22:20:13 +00:00
Compile = self . Compile
if Compile ( context , text = text , main = main , msg = ' whether C++ compiler accepts {} initialization ' , testflags = _testflags_warn ) or \
Compile ( context , text = text , main = main , msg = ' whether C++ compiler understands -Wno-missing-field-initializers ' , successflags = _successflags_nowarn ) or \
not Compile ( context , text = text , main = main , msg = ' whether C++ compiler always errors for {} initialization ' , expect_failure = True ) :
2015-01-15 04:30:03 +00:00
return
2015-03-27 02:50:23 +00:00
raise SCons . Errors . StopError ( " C++ compiler errors on {} initialization, even with -Wno-missing-field-initializers. " )
2015-01-15 04:30:03 +00:00
@_custom_test
2016-10-29 23:16:19 +00:00
def check_compiler_extended_identifiers ( self , context , main = r '''
#define DXX_RENAME_IDENTIFIER2(I,N) I##$##N
#define DXX_RENAME_IDENTIFIER(I,N) DXX_RENAME_IDENTIFIER2(I,N)
#define DXX_extended a\u012b\u012f\u013d
#define var DXX_RENAME_IDENTIFIER(var, DXX_extended)
int var ;
( void ) var ;
#undef var
#undef DXX_extended
#undef DXX_RENAME_IDENTIFIER
#undef DXX_RENAME_IDENTIFIER2
''' ,_successflags= { ' CXXFLAGS ' : [ ' -fextended-identifiers ' ]}):
Compile = self . Compile
if Compile ( context , text = ' ' , main = main , msg = ' whether compiler accepts extended identifiers by default ' ) or \
Compile ( context , text = ' ' , main = main , msg = ' whether compiler accepts extended identifiers with -fextended-identifiers ' , successflags = _successflags ) :
return
user_settings = self . user_settings
user_settings . _dxx_extended_identifiers = user_settings . _dxx_disable_extended_identifiers
@_custom_test
2013-12-07 00:35:50 +00:00
def check_attribute_error ( self , context ) :
"""
2015-09-26 21:17:12 +00:00
Test whether the compiler accepts and properly implements gcc ' s function
attribute [ __attribute__ ( ( __error__ ) ) ] [ 1 ] .
A proper implementation will compile correctly if the function is
declared and , after optimizations are applied , the function is not
called . If this function attribute is not supported , then
DXX_ALWAYS_ERROR_FUNCTION results in link - time errors , rather than
compile - time errors .
This test will report failure if the optimizer does not remove the call
to the marked function .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Common - Function - Attributes . html #index-g_t_0040code_007berror_007d-function-attribute-3097
2013-12-07 00:35:50 +00:00
help : assume compiler supports __attribute__ ( ( error ) )
"""
2015-09-26 21:17:12 +00:00
self . _check_function_dce_attribute ( context , ' error ' )
def _check_function_dce_attribute ( self , context , attribute ) :
__attribute__ = ' __ %s __ ' % attribute
2013-12-07 00:35:50 +00:00
f = '''
2015-09-26 21:17:12 +00:00
void a ( ) __attribute__ ( ( % s ( " a called " ) ) ) ;
''' % __attribute__
macro_name = ' __attribute_ %s (M) ' % attribute
2016-08-13 22:20:13 +00:00
Compile = self . Compile
2016-08-13 22:20:13 +00:00
Define = context . sconf . Define
2016-08-13 22:20:13 +00:00
if Compile ( context , text = f , main = ' if( " 0 " [0]== \' 1 \' )a(); ' , msg = ' whether compiler optimizes function __attribute__(( %s )) ' % __attribute__ ) :
2016-08-13 22:20:13 +00:00
Define ( ' DXX_HAVE_ATTRIBUTE_ %s ' % attribute . upper ( ) )
macro_value = ' __attribute__(( %s (M))) ' % __attribute__
2013-12-07 00:35:50 +00:00
else :
2016-08-13 22:20:13 +00:00
Compile ( context , text = f , msg = ' whether compiler accepts function __attribute__(( %s )) ' % __attribute__ ) and \
Compile ( context , text = f , main = ' a(); ' , msg = ' whether compiler understands function __attribute__(( %s )) ' % __attribute__ , expect_failure = True )
2016-08-13 22:20:13 +00:00
macro_value = self . comment_not_supported
Define ( macro_name , macro_value )
2013-12-07 00:35:50 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_builtin_bswap ( self , context ,
_main = '''
( void ) __builtin_bswap64 ( static_cast < uint64_t > ( argc ) ) ;
( void ) __builtin_bswap32 ( static_cast < uint32_t > ( argc ) ) ;
#ifdef DXX_HAVE_BUILTIN_BSWAP16
/ * Added in gcc - 4.8 * /
( void ) __builtin_bswap16 ( static_cast < uint16_t > ( argc ) ) ;
#endif
''' ,
_successflags_bswap16 = { ' CPPDEFINES ' : [ ' DXX_HAVE_BUILTIN_BSWAP ' , ' DXX_HAVE_BUILTIN_BSWAP16 ' ] } ,
_successflags_bswap = { ' CPPDEFINES ' : [ ' DXX_HAVE_BUILTIN_BSWAP ' ] } ,
) :
2015-09-26 21:17:12 +00:00
"""
Test whether the compiler accepts the gcc byte swapping intrinsic
functions . These functions may be optimized into architecture - specific
swap instructions when the idiomatic swap is not .
u16 = ( u16 << 8 ) | ( u16 >> 8 ) ;
The 32 - bit version ( [ __builtin_bswap32 ] [ 1 ] ) and 64 - bit version
( [ __builtin_bswap64 ] [ 2 ] ) are present in all supported versions of
gcc . The 16 - bit version ( [ __builtin_bswap16 ] [ 3 ] ) was added in
gcc - 4.8 .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Other - Builtins . html #index-g_t_005f_005fbuiltin_005fbswap32-4135
[ 2 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Other - Builtins . html #index-g_t_005f_005fbuiltin_005fbswap64-4136
[ 3 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Other - Builtins . html #index-g_t_005f_005fbuiltin_005fbswap16-4134
"""
2014-09-27 23:14:50 +00:00
include = '''
2014-09-20 22:15:32 +00:00
#include <cstdint>
2014-09-27 23:14:50 +00:00
'''
2016-08-13 22:20:13 +00:00
Compile = self . Compile
if Compile ( context , text = include , main = _main , msg = ' whether compiler implements __builtin_bswap { 16,32,64} functions ' , successflags = _successflags_bswap16 ) or \
Compile ( context , text = include , main = _main , msg = ' whether compiler implements __builtin_bswap { 32,64} functions ' , successflags = _successflags_bswap ) :
2014-09-20 22:15:32 +00:00
return
2016-10-02 00:34:39 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_optimize_builtin_constant_p ' , " assume compiler optimizes __builtin_constant_p " ) )
2014-09-20 22:15:32 +00:00
@_custom_test
2013-12-07 00:35:50 +00:00
def check_builtin_constant_p ( self , context ) :
"""
2015-09-26 21:17:12 +00:00
Test whether the compiler accepts and properly implements gcc ' s
intrinsic [ __builtin_constant_p ] [ 1 ] . A proper implementation will
compile correctly if the intrinsic is recognized and , after
optimizations are applied , the intrinsic returns true for a constant
input and that return value is used to optimize away dead code .
If this intrinsic is not supported , or if applying optimizations
does not make the intrinsic report true , then the test reports
failure . A failure here disables some compile - time sanity checks .
This test is known to fail when optimizations are disabled . The failure
is not a bug in the test or in the compiler .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Other - Builtins . html #index-g_t_005f_005fbuiltin_005fconstant_005fp-4082
2013-12-07 00:35:50 +00:00
help : assume compiler supports compile - time __builtin_constant_p
"""
f = '''
int c ( int ) ;
2014-11-02 16:46:11 +00:00
static int a ( int b ) {
2013-12-07 00:35:50 +00:00
return __builtin_constant_p ( b ) ? 1 : % s ;
}
'''
2014-09-27 23:14:50 +00:00
main = ' return a(1) + a(2) '
2016-08-13 22:20:13 +00:00
Define = context . sconf . Define
2016-10-02 00:34:39 +00:00
if self . Link ( context , text = f % ' c(b) ' , main = main , msg = ' whether compiler optimizes __builtin_constant_p ' , calling_function = ' optimize_builtin_constant_p ' ) :
2016-08-13 22:20:13 +00:00
Define ( ' DXX_HAVE_BUILTIN_CONSTANT_P ' )
Define ( ' DXX_CONSTANT_TRUE(E) ' , ' (__builtin_constant_p((E)) && (E)) ' )
dxx_builtin_constant_p = ' __builtin_constant_p(A) '
2013-12-07 00:35:50 +00:00
else :
2016-08-13 22:20:13 +00:00
# This is present because it may be useful to see in the
# debug log. It is not expected to modify the build
# environment.
2014-09-27 23:14:50 +00:00
self . Compile ( context , text = f % ' 2 ' , main = main , msg = ' whether compiler accepts __builtin_constant_p ' )
2016-08-13 22:20:13 +00:00
dxx_builtin_constant_p = ' ((void)(A),0) '
Define ( ' dxx_builtin_constant_p(A) ' , dxx_builtin_constant_p )
2013-12-07 00:35:50 +00:00
@_custom_test
2015-01-12 00:26:03 +00:00
def check_builtin_expect ( self , context ) :
2015-09-26 21:17:12 +00:00
"""
Test whether the compiler accepts gcc ' s intrinsic
[ __builtin_expect ] [ 1 ] . This intrinsic is a hint to the optimizer ,
which it may ignore . The test does not try to detect whether the
optimizer respects such hints .
When this test succeeds , conditional tests can hint to the optimizer
which path should be considered hot .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Other - Builtins . html #index-g_t_005f_005fbuiltin_005fexpect-4083
"""
2015-01-12 00:26:03 +00:00
main = '''
return __builtin_expect ( argc == 1 , 1 ) ? 1 : 0 ;
'''
if self . Compile ( context , text = ' ' , main = main , msg = ' whether compiler accepts __builtin_expect ' ) :
2016-08-13 22:20:13 +00:00
likely = ' __builtin_expect(!!(A), 1) '
unlikely = ' __builtin_expect(!!(A), 0) '
2015-01-12 00:26:03 +00:00
else :
2016-08-13 22:20:13 +00:00
likely = unlikely = ' (!!(A)) '
Define = context . sconf . Define
Define ( ' likely(A) ' , likely )
Define ( ' unlikely(A) ' , unlikely )
2015-01-12 00:26:03 +00:00
@_custom_test
2016-04-06 03:34:13 +00:00
def check_builtin_file ( self , context ) :
if self . Compile ( context , text = '''
static void f ( const char * = __builtin_FILE ( ) , unsigned = __builtin_LINE ( ) )
{
}
''' , main= ' f(); ' , msg= ' whether compiler accepts __builtin_FILE, __builtin_LINE ' ):
context . sconf . Define ( ' DXX_HAVE_CXX_BUILTIN_FILE_LINE ' )
@_custom_test
2014-06-22 03:54:35 +00:00
def check_builtin_object_size ( self , context ) :
"""
2015-09-26 21:17:12 +00:00
Test whether the compiler accepts and optimizes gcc ' s intrinsic
[ __builtin_object_size ] [ 1 ] . If this intrinsic is optimized ,
compile - time checks can verify that the caller - specified constant
size of a variable does not exceed the compiler - determined size of
the variable . If the compiler cannot determine the size of the
variable , no compile - time check is done .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Object - Size - Checking . html #index-g_t_005f_005fbuiltin_005fobject_005fsize-3657
2014-06-22 03:54:35 +00:00
help : assume compiler supports __builtin_object_size
"""
f = '''
2015-09-26 21:17:12 +00:00
/ * a ( ) is never defined . An optimizing compiler will eliminate the
* attempt to call it , allowing the Link to succeed . A non - optimizing
* compiler will emit the call , and the Link will fail .
* /
2014-06-22 03:54:35 +00:00
int a ( ) ;
static inline int a ( char * c ) {
return __builtin_object_size ( c , 0 ) == 4 ? 1 : % s ;
}
2014-09-27 23:14:50 +00:00
'''
main = '''
2014-06-22 03:54:35 +00:00
char c [ 4 ] ;
return a ( c ) ;
'''
2014-09-27 23:14:50 +00:00
if self . Link ( context , text = f % ' a() ' , main = main , msg = ' whether compiler optimizes __builtin_object_size ' ) :
2014-06-22 03:54:35 +00:00
context . sconf . Define ( ' DXX_HAVE_BUILTIN_OBJECT_SIZE ' )
2014-08-01 03:08:14 +00:00
else :
2014-09-27 23:14:50 +00:00
self . Compile ( context , text = f % ' 2 ' , main = main , msg = ' whether compiler accepts __builtin_object_size ' )
2014-06-22 03:54:35 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_embedded_compound_statement ( self , context ,
_compound_statement_native = ( ' ' , ' ' ) ,
_compound_statement_emulated = ( ' [&] ' , ' () ' )
) :
2015-09-26 21:17:12 +00:00
"""
Test whether the compiler implements gcc ' s [statement expression][1]
extension . If this extension is present , statements can be used where
expressions are expected . If it is absent , it is emulated by defining ,
calling , and then discarding a lambda function . The compiler produces
better error messages for errors in statement expressions than for
errors in lambdas , so prefer statement expressions when they are
available .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Statement - Exprs . html
"""
2014-07-31 02:44:14 +00:00
f = '''
return ( { 1 + 2 ; } ) ;
'''
2015-12-03 03:26:49 +00:00
t = _compound_statement_native if self . Compile ( context , text = ' ' , main = f , msg = ' whether compiler understands embedded compound statements ' ) else _compound_statement_emulated
2016-08-13 22:20:13 +00:00
Define = context . sconf . Define
Define ( ' DXX_BEGIN_COMPOUND_STATEMENT ' , t [ 0 ] )
Define ( ' DXX_END_COMPOUND_STATEMENT ' , t [ 1 ] )
Define ( ' DXX_ALWAYS_ERROR_FUNCTION(F,S) ' , r ' ( DXX_BEGIN_COMPOUND_STATEMENT { \
2014-07-31 02:44:14 +00:00
void F ( ) __attribute_error ( S ) ; \
F ( ) ; \
2015-09-26 21:17:12 +00:00
} DXX_END_COMPOUND_STATEMENT ) ' , ' ' '
Declare a function named F and immediately call it . If gcc ' s
__attribute__ ( ( __error__ ) ) is supported , __attribute_error will expand
to use __attribute__ ( ( __error__ ) ) with the explanatory string S , causing
it to be a compilation error if this expression is not optimized out .
Use this macro to implement static assertions that depend on values that
are known to the optimizer , but are not considered " compile time
constant expressions " for the purpose of the static_assert intrinsic.
C + + 11 deleted functions cannot be used here because the compiler raises
an error for the call before the optimizer has an opportunity to delete
the call via a dead code elimination pass .
''' )
2014-07-31 02:44:14 +00:00
@_custom_test
2015-01-18 01:58:33 +00:00
def check_attribute_always_inline ( self , context ) :
"""
help : assume compiler supports __attribute__ ( ( always_inline ) )
"""
macro_name = ' __attribute_always_inline() '
macro_value = ' __attribute__((__always_inline__)) '
2015-09-19 23:04:35 +00:00
self . _check_macro ( context , macro_name = macro_name , macro_value = macro_value , test = ' %s static inline void a() {} ' % macro_name , main = ' a(); ' , msg = ' for function __attribute__((always_inline)) ' )
2015-01-25 05:32:45 +00:00
@_custom_test
2013-12-18 22:13:03 +00:00
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)) ' )
2014-12-24 03:34:33 +00:00
@_custom_test
def check_attribute_cold ( self , context ) :
"""
2015-09-26 21:17:12 +00:00
Test whether the compiler accepts gcc ' s function attribute
[ __attribute__ ( ( cold ) ) ] [ 1 ] . Use this to annotate functions which are
rarely called , such as error reporting functions .
[ 1 ] : https : / / gcc . gnu . org / onlinedocs / gcc / Common - Function - Attributes . html #index-g_t_0040code_007bcold_007d-function-attribute-3090
2014-12-24 03:34:33 +00:00
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)) ' )
2013-12-18 22:13:03 +00:00
@_custom_test
2013-06-27 02:35:22 +00:00
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)) ' )
2013-06-27 02:35:22 +00:00
@_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))) ' )
2013-12-18 21:47:20 +00:00
@_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)) ' )
2013-11-23 21:32:53 +00:00
@_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)) ' )
2013-10-27 22:13:04 +00:00
@_custom_test
2015-05-09 17:39:02 +00:00
def check_attribute_noreturn ( self , context ) :
"""
help : assume compiler supports __attribute__ ( ( noreturn ) )
"""
macro_name = ' __attribute_noreturn '
macro_value = ' __attribute__((noreturn)) '
2015-09-19 23:04:35 +00:00
self . _check_macro ( context , macro_name = macro_name , macro_value = macro_value , test = ' %s void a();void a() { for(;;);} ' % macro_name , main = ' a(); ' , msg = ' for function __attribute__((noreturn)) ' )
2015-05-09 17:39:02 +00:00
@_custom_test
2013-10-27 22:13:04 +00:00
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)) ' )
2015-02-05 03:03:50 +00:00
@_custom_test
def check_attribute_unused ( self , context ) :
"""
help : assume compiler supports __attribute__ ( ( unused ) )
"""
macro_name = ' __attribute_unused '
macro_value = ' __attribute__((unused)) '
self . _check_macro ( context , macro_name = macro_name , macro_value = macro_value , test = """
__attribute_unused
static void a ( ) { }
2016-12-05 00:26:08 +00:00
""" , msg= ' for function __attribute__((unused)) ' , successflags= { ' CXXFLAGS ' : [get_Werror_string(context.env[ ' CXXFLAGS ' ]) + ' unused ' ]})
2014-04-19 00:43:00 +00:00
@_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)) ' )
2015-09-26 21:17:12 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_attribute_warning ( self , context , _check_function_dce_attribute = _check_function_dce_attribute ) :
_check_function_dce_attribute ( self , context , ' warning ' )
2014-06-01 20:49:07 +00:00
def Cxx14Compile ( self , context , * args , * * kwargs ) :
2015-09-26 21:17:12 +00:00
"""
Test whether the compiler supports a C + + 14 feature . If the compiler
failed the test for C + + 14 support , then the test is not run , but a
message about skipping the test is printed and the test is assumed to
fail .
"""
2015-09-19 23:04:36 +00:00
self . __skip_missing_cxx_std ( self . _cxx_conformance_cxx14 , ' no C++14 support ' , kwargs )
2014-06-01 20:49:07 +00:00
return self . Compile ( context , * args , * * kwargs )
2015-09-19 23:04:36 +00:00
def __skip_missing_cxx_std ( self , level , text , kwargs ) :
2014-06-21 17:54:02 +00:00
if self . __cxx_conformance < level :
2015-09-19 23:04:36 +00:00
kwargs . setdefault ( ' skipped ' , text )
2015-09-19 23:04:36 +00:00
def _check_static_assert_method ( self , context , msg , f , testflags = { } , _tdict = { ' expr ' : ' true&&true ' } , _fdict = { ' expr ' : ' false||false ' } , * * kwargs ) :
_Compile = self . Compile
return _Compile ( context , text = f % _tdict , main = ' f(A()); ' , msg = msg % ' true ' , testflags = testflags , * * kwargs ) and \
_Compile ( context , text = f % _fdict , main = ' f(A()); ' , msg = msg % ' false ' , expect_failure = True , successflags = testflags , * * kwargs )
2013-11-26 05:03:00 +00:00
@_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
2014-06-21 17:54:02 +00:00
def check_cxx11_static_assert ( self , context , f ) :
2013-11-26 05:03:00 +00:00
"""
help : assume compiler supports C + + intrinsic static_assert
"""
2015-09-19 23:04:36 +00:00
return self . _check_static_assert_method ( context , ' for C++11 intrinsic static_assert when %s ' , f , testflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX11_STATIC_ASSERT ' ] } )
2013-11-26 05:03:00 +00:00
@_custom_test
def _check_static_assert ( self , context ) :
f = '''
2013-12-21 05:12:38 +00:00
#include "compiler-static_assert.h"
2015-07-09 03:12:45 +00:00
static_assert ( % ( expr ) s , " global " ) ;
struct A
{
static const bool value = % ( expr ) s ;
static_assert ( % ( expr ) s , " class literal " ) ;
static_assert ( A : : value , " class static " ) ;
A ( )
{
static_assert ( % ( expr ) s , " constructor literal " ) ;
static_assert ( value , " constructor static " ) ;
}
} ;
template < typename >
struct B
{
static const bool value = % ( expr ) s ;
static_assert ( % ( expr ) s , " template class literal " ) ;
static_assert ( value , " template class static " ) ;
B ( A a )
{
static_assert ( % ( expr ) s , " constructor literal " ) ;
static_assert ( value , " constructor self static " ) ;
static_assert ( A : : value , " constructor static " ) ;
static_assert ( a . value , " constructor member " ) ;
}
template < typename R >
B ( B < R > & & b )
{
static_assert ( % ( expr ) s , " template constructor literal " ) ;
static_assert ( value , " template constructor self static " ) ;
static_assert ( B < R > : : value , " template constructor static " ) ;
static_assert ( b . value , " template constructor member " ) ;
}
} ;
template < typename T >
static void f ( B < T > b )
{
static_assert ( % ( expr ) s , " template function literal " ) ;
static_assert ( B < T > : : value , " template function static " ) ;
static_assert ( b . value , " template function member " ) ;
}
void f ( A a ) ;
void f ( A a )
{
static_assert ( % ( expr ) s , " function literal " ) ;
static_assert ( A : : value , " function static " ) ;
static_assert ( a . value , " function member " ) ;
f ( B < long > ( B < int > ( a ) ) ) ;
}
2013-11-26 05:03:00 +00:00
'''
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 )
2016-02-06 22:12:55 +00:00
@_custom_test
def check_namespace_disambiguate ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX_DISAMBIGUATE_USING_NAMESPACE ' ] } ) :
self . Compile ( context , text = '''
namespace A
{
int a ;
}
using namespace A ;
namespace B
{
class A ;
}
using namespace B ;
''' , main= ' return A::a; ' , msg= ' whether compiler handles classes from " using namespace " ' , successflags=_successflags)
2014-06-01 20:49:07 +00:00
@_custom_test
2016-08-12 04:08:57 +00:00
def check_cxx11_required_features ( self , context , _features = __cxx11_required_features ) :
2015-08-03 03:11:25 +00:00
# First test all the features at once. If all work, then done.
# If any fail, then the configure run will stop.
2016-08-12 04:08:57 +00:00
_Compile = self . Compile
if _Compile ( context , text = _features . text , main = _features . main , msg = ' for required C++11 features ' ) :
2015-08-03 03:11:25 +00:00
return
# Some failed. Run each test separately and report to the user
# which ones failed.
2016-08-12 04:08:57 +00:00
failures = [ f . name for f in _features . features if not _Compile ( context , text = f . text , main = f . main , msg = ' for C++11 %s ' % f . name ) ]
raise SCons . Errors . StopError ( ( " C++ compiler does not support %s . " %
' , ' . join ( failures )
) if failures else ' C++ compiler supports each feature individually, but not all of them together. Please report this as a bug in the Rebirth configure script. ' )
2015-09-13 20:23:05 +00:00
def _show_pch_count_message ( self , context , which , user_setting ) :
count = user_setting if user_setting else 0
context . Display ( ' %s : checking when to pre-compile %s headers... %s \n ' % ( self . msgprefix , which , ( ' if used at least %u time %s ' % ( count , ' s ' if count > 1 else ' ' ) ) if count > 0 else ' never ' ) )
return count > 0
2015-10-03 17:17:49 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_pch_compile ' , " assume C++ compiler can create pre-compiled headers " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_pch_use ' , " assume C++ compiler can use pre-compiled headers " ) )
2013-11-28 04:21:20 +00:00
@_custom_test
2015-10-03 17:17:49 +00:00
def _check_pch ( self , context ,
2015-12-03 03:26:49 +00:00
_Test = _Test ,
2015-10-03 17:17:49 +00:00
_testflags_compile_pch = { ' CXXFLAGS ' : [ ' -x ' , ' c++-header ' ] } ,
_testflags_use_pch = { ' CXXFLAGS ' : [ ' -Winvalid-pch ' , ' -include ' , None ] }
) :
2013-11-28 04:21:20 +00:00
self . pch_flags = None
2015-09-13 20:23:05 +00:00
# Always evaluate both
co = self . _show_pch_count_message ( context , ' own ' , self . user_settings . pch )
cs = self . _show_pch_count_message ( context , ' system ' , self . user_settings . syspch )
2015-09-19 23:04:36 +00:00
context . did_show_result = True
2015-09-13 20:23:05 +00:00
if not co and not cs :
2013-11-28 04:21:20 +00:00
return
2015-09-13 20:23:05 +00:00
context . Display ( ' %s : checking when to compute pre-compiled header input *pch.cpp... %s \n ' % ( self . msgprefix , ' if missing ' if self . user_settings . pch_cpp_assume_unchanged else ' always ' ) )
2015-12-03 03:26:49 +00:00
result = _Test ( self , context , action = self . PCHAction ( context ) , text = ' ' , msg = ' whether compiler can create pre-compiled headers ' , testflags = _testflags_compile_pch , calling_function = ' pch_compile ' )
2015-10-03 17:17:49 +00:00
if not result :
raise SCons . Errors . StopError ( " C++ compiler cannot create pre-compiled headers. " )
_testflags_use_pch = _testflags_use_pch . copy ( )
_testflags_use_pch [ ' CXXFLAGS ' ] [ - 1 ] = str ( context . lastTarget ) [ : - 4 ]
result = self . Compile ( context , text = '''
/ * This symbol is defined in the PCH . If the PCH is included , this
* symbol will preprocess away to nothing . If the PCH is not included ,
* then the compiler is not using PCHs as expected .
* /
dxx_compiler_supports_pch
''' , msg= ' whether compiler uses pre-compiled headers ' , testflags=_testflags_use_pch, calling_function= ' pch_use ' )
if not result :
raise SCons . Errors . StopError ( " C++ compiler cannot use pre-compiled headers. " )
self . pch_flags = _testflags_compile_pch
2013-11-17 00:17:58 +00:00
@_custom_test
2015-05-06 02:11:03 +00:00
def _check_cxx11_explicit_delete ( self , context ) :
# clang 3.4 warns when a named parameter to a deleted function
# is not used, even though there is no body in which it could be
# used, so every named parameter to a deleted function is always
# unused.
f = ' int a(int %s )=delete; '
if self . check_cxx11_explicit_delete_named ( context , f ) :
# No bug: named parameters with explicitly deleted functions
# work correctly.
return
if self . check_cxx11_explicit_delete_named_unused ( context , f ) :
# Clang bug hit. Called function adds -Wno-unused-parameter
# to work around the bug, but affected users will not get
# warnings about parameters that are unused in regular
# functions.
return
2016-08-12 04:08:57 +00:00
raise SCons . Errors . StopError (
" C++ compiler rejects explicitly deleted functions with named parameters, even with -Wno-unused-parameter. " \
if self . check_cxx11_explicit_delete_anonymous ( context , f ) else \
" C++ compiler does not support explicitly deleted functions. "
)
2015-05-06 02:11:03 +00:00
@_implicit_test
def check_cxx11_explicit_delete_named ( self , context , f ) :
2013-11-17 00:17:58 +00:00
"""
2015-05-06 02:11:03 +00:00
help : assume compiler supports explicitly deleted functions with named parameters
2013-11-17 00:17:58 +00:00
"""
2015-09-19 23:04:36 +00:00
return self . Compile ( context , text = f % ' b ' , msg = ' for explicitly deleted functions with named parameters ' )
2015-05-06 02:11:03 +00:00
@_implicit_test
2015-12-03 03:26:49 +00:00
def check_cxx11_explicit_delete_named_unused ( self , context , f , _successflags = { ' CXXFLAGS ' : [ ' -Wno-unused-parameter ' ] } ) :
2015-05-06 02:11:03 +00:00
"""
help : assume compiler supports explicitly deleted functions with named parameters with - Wno - unused - parameter
"""
2015-12-03 03:26:49 +00:00
return self . Compile ( context , text = f % ' b ' , msg = ' for explicitly deleted functions with named parameters and -Wno-unused-parameter ' , successflags = _successflags )
2015-05-06 02:11:03 +00:00
@_implicit_test
def check_cxx11_explicit_delete_anonymous ( self , context , f ) :
"""
help : assume compiler supports explicitly deleted functions with anonymous parameters
"""
2015-09-19 23:04:36 +00:00
return self . Compile ( context , text = f % ' ' , msg = ' for explicitly deleted functions with anonymous parameters ' )
2013-12-24 04:53:59 +00:00
@_implicit_test
2015-12-03 03:26:49 +00:00
def check_cxx11_free_begin ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX11_BEGIN ' ] } , * * kwargs ) :
return self . Compile ( context , msg = ' for C++11 functions begin(), end() ' , successflags = _successflags , * * kwargs )
2014-01-18 18:02:02 +00:00
@_implicit_test
2014-09-27 23:14:50 +00:00
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 )
2014-01-18 18:02:02 +00:00
@_custom_test
def _check_free_begin_function ( self , context ) :
f = '''
#include "compiler-begin.h"
struct A {
typedef int * iterator ;
typedef const int * const_iterator ;
2014-09-27 23:14:50 +00:00
iterator begin ( ) { return 0 ; }
iterator end ( ) { return 0 ; }
const_iterator begin ( ) const { return 0 ; }
const_iterator end ( ) const { return 0 ; }
2014-01-18 18:02:02 +00:00
} ;
2014-09-27 23:14:50 +00:00
#define F(C){\
C int a [ 1 ] { 0 } ; \
C A b { } ; \
if ( begin ( a ) | | end ( a ) | | begin ( b ) | | end ( b ) ) return 1 ; \
2014-01-18 18:02:02 +00:00
}
'''
2014-09-27 23:14:50 +00:00
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 ) :
2014-01-18 18:02:02 +00:00
raise SCons . Errors . StopError ( " C++ compiler does not support free functions begin() and end(). " )
@_implicit_test
2015-12-03 03:26:49 +00:00
def check_cxx11_addressof ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX11_ADDRESSOF ' ] } , * * kwargs ) :
return self . Compile ( context , msg = ' for C++11 function addressof() ' , successflags = _successflags , * * kwargs )
2014-01-12 23:00:43 +00:00
@_implicit_test
2014-09-27 23:14:50 +00:00
def check_boost_addressof ( self , context , * * kwargs ) :
return self . Compile ( context , msg = ' for Boost.Utility function addressof() ' , successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_BOOST_ADDRESSOF ' ] } , * * kwargs )
2014-01-12 23:00:43 +00:00
@_custom_test
def _check_free_addressof_function ( self , context ) :
f = '''
#include "compiler-addressof.h"
struct A {
void operator & ( ) ;
} ;
'''
2014-09-27 23:14:50 +00:00
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 ) :
2014-01-12 23:00:43 +00:00
raise SCons . Errors . StopError ( " C++ compiler does not support free function addressof(). " )
2014-06-01 20:49:07 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_cxx14_exchange ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX14_EXCHANGE ' ] } ) :
2014-09-07 21:23:03 +00:00
f = '''
#include "compiler-exchange.h"
'''
2015-12-03 03:26:49 +00:00
self . Cxx14Compile ( context , text = f , main = ' return exchange(argc, 5) ' , msg = ' for C++14 exchange ' , successflags = _successflags )
2014-09-07 21:23:03 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_cxx14_integer_sequence ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX14_INTEGER_SEQUENCE ' ] } ) :
2014-06-01 20:49:07 +00:00
f = '''
#include <utility>
using std : : integer_sequence ;
using std : : index_sequence ;
'''
2015-12-03 03:26:49 +00:00
self . Cxx14Compile ( context , text = f , msg = ' for C++14 integer_sequence ' , successflags = _successflags )
2014-08-16 04:15:16 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_cxx14_make_unique ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_CXX14_MAKE_UNIQUE ' ] } ) :
2014-08-16 04:15:16 +00:00
f = '''
#include "compiler-make_unique.h"
2014-09-27 23:14:50 +00:00
'''
main = '''
2014-08-16 04:15:16 +00:00
make_unique < int > ( 0 ) ;
make_unique < int [ ] > ( 1 ) ;
'''
2015-12-03 03:26:49 +00:00
self . Cxx14Compile ( context , text = f , main = main , msg = ' for C++14 make_unique ' , successflags = _successflags )
2014-01-12 23:00:43 +00:00
@_implicit_test
2015-12-03 03:26:49 +00:00
def check_cxx11_inherit_constructor ( self , context , text , _macro_value = _quote_macro_value ( '''
2016-07-21 01:43:22 +00:00
/ * Use a typedef for the base type to avoid parsing issues when type
* B is a qualified name . Without this typedef , B = std : : BASE would
* expand as ` using std : : BASE : : std : : BASE ` , which causes a parsing error .
* The correct way to inherit from std : : BASE is ` using std : : BASE : : BASE ; ` .
* With using dxx_constructor_base_type = std : : BASE ; ` ,
* ` using dxx_constructor_base_type : : dxx_constructor_base_type ` produces
* the correct result .
* /
using dxx_constructor_base_type = B , ##__VA_ARGS__;
using dxx_constructor_base_type : : dxx_constructor_base_type ; ''' ),
2015-12-03 03:26:49 +00:00
* * kwargs ) :
2013-12-24 04:53:59 +00:00
"""
help : assume compiler supports inheriting constructors
"""
2015-05-09 17:39:02 +00:00
blacklist_clang_libcxx = '''
/ * Test for bug where clang + libc + + + constructor inheritance causes a
* compilation failure when returning nullptr .
*
* Works : gcc
* Works : clang + gcc libstdc + +
* Works : old clang + old libc + + ( cutoff date unknown ) .
* Works : new clang + new libc + + + unique_ptr < T >
* Fails : new clang + new libc + + + unique_ptr < T [ ] > ( v3 .6 .0 confirmed broken ) .
memory : 2676 : 32 : error : no type named ' type ' in ' std::__1::enable_if<false, std::__1::unique_ptr<int [], std::__1::default_delete<int []> >::__nat> ' ; ' enable_if ' cannot be used to disable this declaration
typename enable_if < __same_or_less_cv_qualified < _Pp , pointer > : : value , __nat > : : type = __nat ( ) ) _NOEXCEPT
^ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
. sconf_temp / conftest_43 . cpp : 26 : 11 : note : in instantiation of member function ' std::__1::unique_ptr<int [], std::__1::default_delete<int []> >::unique_ptr ' requested here
using B : : B ;
^
. sconf_temp / conftest_43 . cpp : 30 : 2 : note : while substituting deduced template arguments into function template ' I ' [ with _Pp = I ]
return nullptr ;
^
* /
#include <memory>
class I : std : : unique_ptr < int [ ] >
{
public :
typedef std : : unique_ptr < int [ ] > B ;
using B : : B ;
} ;
I a ( ) ;
I a ( )
{
return nullptr ;
}
'''
2015-12-03 03:26:49 +00:00
return _macro_value \
if self . Compile ( context , text = text . format ( leading_text = blacklist_clang_libcxx , macro_value = _macro_value ) , msg = ' for C++11 inherited constructors with good unique_ptr<T[]> support ' , * * kwargs ) \
else None
2013-12-24 04:53:59 +00:00
@_implicit_test
2015-12-03 03:26:49 +00:00
def check_cxx11_variadic_forward_constructor ( self , context , text , _macro_value = _quote_macro_value ( '''
2014-07-19 17:34:02 +00:00
template < typename . . . Args >
D ( Args & & . . . args ) :
2015-03-28 01:16:11 +00:00
B , ##__VA_ARGS__(std::forward<Args>(args)...) {}
2015-12-03 03:26:49 +00:00
''' ),**kwargs):
"""
help : assume compiler supports variadic template - based constructor forwarding
"""
return _macro_value \
if self . Compile ( context , text = text . format ( leading_text = ' #include <algorithm> \n ' , macro_value = _macro_value ) , msg = ' for C++11 variadic templates on constructors ' , * * kwargs ) \
else None
2013-12-24 04:53:59 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def _check_forward_constructor ( self , context , _text = '''
2015-09-19 23:04:35 +00:00
{ leading_text }
2015-12-03 03:26:49 +00:00
#define DXX_INHERIT_CONSTRUCTORS(D,B,...) {macro_value}
2013-12-24 04:53:59 +00:00
struct A { {
2014-09-27 23:14:50 +00:00
A ( int ) { { } }
2013-12-24 04:53:59 +00:00
} } ;
struct B : A { {
2015-12-03 03:26:49 +00:00
DXX_INHERIT_CONSTRUCTORS ( B , A ) ;
2013-12-24 04:53:59 +00:00
} } ;
2015-12-03 03:26:49 +00:00
''' ,
_macro_define = ' DXX_INHERIT_CONSTRUCTORS(D,B,...) ' ,
_methods = ( check_cxx11_inherit_constructor , check_cxx11_variadic_forward_constructor )
) :
for f in _methods :
macro_value = f ( self , context , text = _text , main = ' B(0) ' )
2013-12-24 04:53:59 +00:00
if macro_value :
2016-07-21 01:43:22 +00:00
context . sconf . Define ( _macro_define , macro_value , '''
Declare that derived type D inherits applicable constructors from base
type B . Use a variadic macro with the base type second so that types
such as std : : pair < int , int > pass through correctly without the need to
parenthesize them .
''' )
2015-09-19 23:04:35 +00:00
return
raise SCons . Errors . StopError ( " C++ compiler does not support constructor forwarding. " )
2014-08-02 04:51:07 +00:00
@_custom_test
2014-08-20 01:08:12 +00:00
def check_deep_tuple ( self , context ) :
text = '''
#include <tuple>
static inline std : : tuple < { type } > make ( ) { {
return std : : make_tuple ( { value } ) ;
} }
2014-09-27 23:14:50 +00:00
static void a ( ) { {
2014-08-20 01:08:12 +00:00
std : : tuple < { type } > t = make ( ) ;
( void ) t ;
} }
'''
count = 20
2016-08-13 22:20:13 +00:00
Compile = self . Compile
if Compile ( context , text = text . format ( type = ' , ' . join ( ( ' int ' , ) * count ) , value = ' , ' . join ( ( ' 0 ' , ) * count ) ) , main = ' a() ' , msg = ' whether compiler handles 20-element tuples ' ) :
2014-08-20 01:08:12 +00:00
return
count = 2
2015-12-03 03:26:49 +00:00
raise SCons . Errors . StopError (
" Compiler cannot handle tuples of 20 elements. Raise the template instantiation depth. " \
2016-08-13 22:20:13 +00:00
if Compile ( context , text = text . format ( type = ' , ' . join ( ( ' int ' , ) * count ) , value = ' , ' . join ( ( ' 0 ' , ) * count ) ) , main = ' a() ' , msg = ' whether compiler handles 2-element tuples ' ) \
2015-12-03 03:26:49 +00:00
else " Compiler cannot handle tuples of 2 elements. "
)
2014-09-07 19:26:17 +00:00
@_implicit_test
2016-10-08 23:24:21 +00:00
def check_poison_valgrind ( self , context ) :
2015-01-03 23:44:32 +00:00
'''
help : add Valgrind annotations ; wipe certain freed memory when running under Valgrind
'''
context . Message ( ' %s : checking %s ... ' % ( self . msgprefix , ' whether to use Valgrind poisoning ' ) )
r = ' valgrind ' in self . user_settings . poison
2014-09-07 19:26:17 +00:00
context . Result ( r )
2016-10-08 23:24:21 +00:00
self . _define_macro ( context , ' DXX_HAVE_POISON_VALGRIND ' , int ( r ) )
2014-09-07 19:26:17 +00:00
if not r :
return
text = '''
2016-10-08 23:24:21 +00:00
#define DXX_HAVE_POISON 1
2014-09-07 19:26:17 +00:00
#include "poison.h"
2014-09-27 23:14:50 +00:00
'''
main = '''
2014-09-07 19:26:17 +00:00
DXX_MAKE_MEM_UNDEFINED ( & argc , sizeof ( argc ) ) ;
'''
2016-10-08 23:24:21 +00:00
if self . Compile ( context , text = text , main = main , msg = ' whether Valgrind memcheck header works ' ) :
2014-09-07 19:26:17 +00:00
return True
raise SCons . Errors . StopError ( " Valgrind poison requested, but <valgrind/memcheck.h> does not work. " )
2015-01-03 23:44:32 +00:00
@_implicit_test
def check_poison_overwrite ( self , context ) :
'''
help : always wipe certain freed memory
'''
context . Message ( ' %s : checking %s ... ' % ( self . msgprefix , ' whether to use overwrite poisoning ' ) )
r = ' overwrite ' in self . user_settings . poison
context . Result ( r )
2016-10-08 23:24:21 +00:00
self . _define_macro ( context , ' DXX_HAVE_POISON_OVERWRITE ' , int ( r ) )
2015-01-03 23:44:32 +00:00
return r
2014-09-07 19:26:17 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def _check_poison_method ( self , context ,
2016-10-08 23:24:21 +00:00
_methods = ( check_poison_overwrite , check_poison_valgrind ) ,
2016-10-09 17:35:02 +00:00
poison = 0
2015-12-03 03:26:49 +00:00
) :
2015-09-26 21:17:12 +00:00
# Always run both checks. The user may want a program that
# always uses overwrite poisoning and, when running under
# Valgrind, marks the memory as undefined.
2015-12-03 03:26:49 +00:00
for f in _methods :
if f ( self , context ) :
2016-10-09 17:35:02 +00:00
poison = 1
self . _define_macro ( context , ' DXX_HAVE_POISON ' , poison )
2016-07-16 16:52:04 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_size_type_size ' , " assume size_t is formatted as `size_t` " ) )
2016-07-15 03:43:03 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_size_type_long ' , " assume size_t is formatted as `unsigned long` " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_size_type_int ' , " assume size_t is formatted as `unsigned int` " ) )
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_size_type_I64 ' , " assume size_t is formatted as `unsigned I64` " ) )
@_custom_test
def _check_size_type_format_modifier ( self , context , _text = '''
#include <cstddef>
#define DXX_PRI_size_type %s
__attribute_format_printf ( 1 , 2 )
void f ( const char * , . . . ) ;
2016-07-16 16:52:04 +00:00
void f ( const char * , . . . )
{
}
2016-07-15 03:43:03 +00:00
''' ,_main= '''
std : : size_t s = 0 ;
f ( " % " DXX_PRI_size_type , s ) ;
''' ):
'''
The test must declare a custom function to call with this format string .
gcc has hardcoded knowledge about how printf works , but that knowledge
on Mingw64 differs from the processing of
__attribute__ ( ( format ( printf , . . . ) ) ) . Mingw64 requires I64u for
__attribute__ ( ( format ) ) functions , but llu for printf . Mingw32 is
consistent in its use of u . Linux gcc is consistent in its use of
lu .
- - cut - -
#include <cstdio>
#include <cstddef>
__attribute__ ( ( format ( printf , 1 , 2 ) ) ) void f ( const char * , . . . ) ;
void a ( ) {
std : : size_t b = 0 ;
printf ( " % I64u " , b ) ;
f ( " % I64u " , b ) ;
printf ( " % llu " , b ) ;
f ( " % llu " , b ) ;
printf ( " %lu " , b ) ;
f ( " %lu " , b ) ;
printf ( " %u " , b ) ;
f ( " %u " , b ) ;
}
- - cut - -
$ x86_64 - w64 - mingw32 - g + + - 5.4 .0 - x c + + - S - Wformat - o / dev / null -
< stdin > : In function ' void a() ' :
< stdin > : 6 : 19 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long long unsigned int} ' [ - Wformat = ]
< stdin > : 9 : 13 : warning : unknown conversion type character ' l ' in format [ - Wformat = ]
< stdin > : 9 : 13 : warning : too many arguments for format [ - Wformat - extra - args ]
< stdin > : 10 : 17 : warning : format ' %lu ' expects argument of type ' long unsigned int ' , but argument 2 has type ' std::size_t { aka long long unsigned int} ' [ - Wformat = ]
< stdin > : 11 : 12 : warning : format ' %lu ' expects argument of type ' long unsigned int ' , but argument 2 has type ' std::size_t { aka long long unsigned int} ' [ - Wformat = ]
< stdin > : 12 : 16 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long long unsigned int} ' [ - Wformat = ]
< stdin > : 13 : 11 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long long unsigned int} ' [ - Wformat = ]
$ i686 - w64 - mingw32 - g + + - 5.4 .0 - x c + + - S - Wformat - o / dev / null -
< stdin > : In function ' void a() ' :
< stdin > : 7 : 14 : warning : format ' % I64u ' expects argument of type ' long long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
< stdin > : 8 : 18 : warning : format ' % llu ' expects argument of type ' long long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
< stdin > : 9 : 13 : warning : unknown conversion type character ' l ' in format [ - Wformat = ]
< stdin > : 9 : 13 : warning : too many arguments for format [ - Wformat - extra - args ]
< stdin > : 10 : 17 : warning : format ' %lu ' expects argument of type ' long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
< stdin > : 11 : 12 : warning : format ' %lu ' expects argument of type ' long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
$ mingw32 - g + + - 5.4 .0 - x c + + - S - Wformat - o / dev / null -
< stdin > : In function ' void a() ' :
< stdin > : 6 : 19 : warning : format ' % I64u ' expects argument of type ' long long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
< stdin > : 7 : 14 : warning : format ' % I64u ' expects argument of type ' long long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
< stdin > : 8 : 18 : warning : unknown conversion type character ' l ' in format [ - Wformat = ]
< stdin > : 8 : 18 : warning : too many arguments for format [ - Wformat - extra - args ]
< stdin > : 9 : 13 : warning : unknown conversion type character ' l ' in format [ - Wformat = ]
< stdin > : 9 : 13 : warning : too many arguments for format [ - Wformat - extra - args ]
< stdin > : 10 : 17 : warning : format ' %lu ' expects argument of type ' long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
< stdin > : 11 : 12 : warning : format ' %lu ' expects argument of type ' long unsigned int ' , but argument 2 has type ' std::size_t { aka unsigned int} ' [ - Wformat = ]
$ x86_64 - pc - linux - gnu - g + + - 5.4 .0 - x c + + - S - Wformat - o / dev / null -
< stdin > : In function ' void a() ' :
< stdin > : 6 : 19 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long unsigned int} ' [ - Wformat = ]
< stdin > : 7 : 14 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long unsigned int} ' [ - Wformat = ]
< stdin > : 8 : 18 : warning : format ' % llu ' expects argument of type ' long long unsigned int ' , but argument 2 has type ' std::size_t { aka long unsigned int} ' [ - Wformat = ]
< stdin > : 9 : 13 : warning : format ' % llu ' expects argument of type ' long long unsigned int ' , but argument 2 has type ' std::size_t { aka long unsigned int} ' [ - Wformat = ]
< stdin > : 12 : 16 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long unsigned int} ' [ - Wformat = ]
< stdin > : 13 : 11 : warning : format ' %u ' expects argument of type ' unsigned int ' , but argument 2 has type ' std::size_t { aka long unsigned int} ' [ - Wformat = ]
'''
# Test types in order of decreasing probability.
for how in (
# Linux
2016-07-16 16:52:04 +00:00
( ' z ' , ' size_type_size ' ) ,
2016-07-15 03:43:03 +00:00
( ' l ' , ' size_type_long ' ) ,
# Win64
( ' I64 ' , ' size_type_I64 ' ) ,
# Win32
( ' ' , ' size_type_int ' ) ,
) :
DXX_PRI_size_type = how [ 0 ]
f = ' " %s u " ' % DXX_PRI_size_type
if self . Compile ( context , text = _text % f , main = _main , msg = ' whether to format std::size_t with " %% %s u " ' % DXX_PRI_size_type , calling_function = how [ 1 ] ) :
context . sconf . Define ( ' DXX_PRI_size_type ' , f )
return
raise SCons . Errors . StopError ( " C++ compiler rejects all candidate format strings for std::size_t. " )
2017-02-08 23:34:41 +00:00
implicit_tests . append ( _implicit_test . RecordedTest ( ' check_compiler_accepts_useless_cast ' , " assume compiler accepts -Wuseless-cast " ) )
2015-01-12 00:26:03 +00:00
@_custom_test
2016-08-19 03:41:41 +00:00
def check_compiler_useless_cast ( self , context ) :
2016-10-30 18:37:28 +00:00
Compile = self . Compile
flags = { ' CXXFLAGS ' : [ get_Werror_string ( context . env [ ' CXXFLAGS ' ] ) + ' useless-cast ' ] }
if Compile ( context , text = '''
2017-02-08 23:34:41 +00:00
/ *
* SDL on Raspbian provokes a warning from - Wuseless - cast
*
* Reported - by : derhass < https : / / github . com / dxx - rebirth / dxx - rebirth / issues / 257 >
* /
2016-10-30 18:37:28 +00:00
#include <SDL_endian.h>
2017-02-08 23:34:41 +00:00
/ *
* gcc - 6. x ( all currently released versions ) create a useless cast when
* synthesizing constructor inheritance , then warn the user about the
* compiler - generated cast . Since the user did not write the cast in
* the source , the user cannot remove the cast to eliminate the warning .
*
* The only way to avoid the problem is to avoid using constructor
* inheritance in cases where the compiler would synthesize a useless
* cast .
*
* Reported - by : zicodxx < https : / / github . com / dxx - rebirth / dxx - rebirth / issues / 316 >
* /
class base
{
public :
base ( int & ) { }
} ;
class derived : public base
{
public :
using base : : base ;
} ;
2016-10-30 18:37:28 +00:00
''' , main= '''
2017-02-08 23:34:41 +00:00
derived d ( argc ) ;
2016-10-30 18:37:28 +00:00
return SDL_Swap32 ( argc ) ;
2017-02-08 23:34:41 +00:00
''' , msg= ' whether compiler argument -Wuseless-cast works with SDL and with constructor inheritance ' , successflags=flags):
2016-10-30 18:37:28 +00:00
return
2016-09-11 18:49:14 +00:00
# <=clang-3.7 does not understand -Wuseless-cast
2016-10-30 18:37:28 +00:00
# This test does not influence the compile environment, but is
# run to distinguish in the output whether the failure is
# because the compiler does not accept -Wuseless-cast or because
# SDL's headers provoke a warning from -Wuseless-cast.
2017-02-08 23:34:41 +00:00
Compile ( context , text = ' ' , main = ' ' , msg = ' whether compiler accepts -Wuseless-cast ' , testflags = flags , calling_function = ' compiler_accepts_useless_cast ' )
2016-09-11 18:49:14 +00:00
@_custom_test
def check_compiler_ptrdiff_cast_int ( self , context ) :
2016-08-19 03:41:41 +00:00
context . sconf . Define ( ' DXX_ptrdiff_cast_int ' , ' static_cast<int> '
if self . Compile ( context , text = '''
#include <cstdio>
''' , main= '''
char a [ 8 ] ;
snprintf ( a , sizeof ( a ) , " %.*s " , static_cast < int > ( & argv [ 0 ] [ 1 ] - & argv [ 0 ] [ 0 ] ) , " 0 " ) ;
''' , msg= ' whether to cast operator-(T*,T*) to int ' )
else ' ' ,
'''
For mingw32 - gcc - 5.4 .0 and x86_64 - pc - linux - gnu - gcc - 5.4 .0 , gcc defines
` long operator - ( T * , T * ) ` .
For mingw32 , gcc reports a useless cast converting ` long ` to ` int ` .
For x86_64 - pc - linux - gnu , gcc does not report a useless cast converting
2016-09-11 18:49:14 +00:00
` long ` to ` int ` .
2016-08-19 03:41:41 +00:00
Various parts of the code take the difference of two pointers , then cast
it to ` int ` to be passed as a parameter to ` snprintf ` field width
conversion ` . * ` . The field width conversion is defined to take ` int ` ,
so the cast is necessary to avoid a warning from ` - Wformat ` on
x86_64 - pc - linux - gnu , but is not necessary on mingw32 . However , the cast
2016-09-11 18:49:14 +00:00
causes a - Wuseless - cast warning on mingw32 . Resolve these conflicting
requirements by defining a macro that expands to ` static_cast < int > ` on
platforms where the cast is required and expands to nothing on platforms
where the cast is useless .
2016-08-19 03:41:41 +00:00
'''
)
@_custom_test
2015-12-03 03:26:49 +00:00
def check_strcasecmp_present ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_STRCASECMP ' ] } ) :
2015-01-12 00:26:03 +00:00
main = '''
return ! strcasecmp ( argv [ 0 ] , argv [ 0 ] + 1 ) & & ! strncasecmp ( argv [ 0 ] + 1 , argv [ 0 ] , 1 ) ;
'''
2015-12-03 03:26:49 +00:00
self . Compile ( context , text = ' #include <cstring> ' , main = main , msg = ' for strcasecmp ' , successflags = _successflags )
2016-03-18 02:05:16 +00:00
@_custom_test
def check_getaddrinfo_present ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_GETADDRINFO ' ] } ) :
self . Compile ( context , text = '''
#ifdef WIN32
2016-11-12 20:58:16 +00:00
#define DXX_WIN32_MINIMUM_WIN32_WINNT 0x0600
#if defined(_WIN32_WINNT) && (_WIN32_WINNT < DXX_WIN32_MINIMUM_WIN32_WINNT)
#undef _WIN32_WINNT
#endif
2016-03-18 02:05:16 +00:00
#ifndef _WIN32_WINNT
2016-11-12 20:58:16 +00:00
#define _WIN32_WINNT DXX_WIN32_MINIMUM_WIN32_WINNT
2016-03-18 02:05:16 +00:00
#endif
#include <winsock2.h>
#include <ws2tcpip.h>
#else
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#endif
''' , main= '''
addrinfo * res = nullptr ;
2016-11-12 20:58:16 +00:00
addrinfo hints { } ;
#ifdef AI_NUMERICSERV
/ * Test that it is defined to a term that can be used with bit - or * /
hints . ai_flags | = AI_NUMERICSERV ;
#endif
#if DXX_USE_IPv6
hints . ai_flags | = AI_V4MAPPED | AI_ALL ;
#endif
int i = getaddrinfo ( " " , " " , & hints , & res ) ;
2016-03-18 02:05:16 +00:00
( void ) i ;
freeaddrinfo ( res ) ;
return 0 ;
''' , msg= ' for getaddrinfo ' , successflags=_successflags)
2016-08-19 03:41:40 +00:00
@_custom_test
def check_timespec_present ( self , context , _successflags = { ' CPPDEFINES ' : [ ' DXX_HAVE_STRUCT_TIMESPEC ' ] } ) :
self . Compile ( context , text = '''
#include <time.h>
''' , main= '''
struct timespec ts ;
( void ) ts ;
return 0 ;
''' , msg= ' for struct timespec ' , successflags=_successflags)
2016-10-15 17:51:48 +00:00
__preferred_compiler_options = [
2015-09-19 23:04:35 +00:00
' -fvisibility=hidden ' ,
' -Wsuggest-attribute=noreturn ' ,
' -Wlogical-op ' ,
2016-09-15 01:50:56 +00:00
' -Wold-style-cast ' ,
2016-10-15 17:51:48 +00:00
]
__preferred_win32_linker_options = [
2015-09-19 23:04:35 +00:00
' -Wl,--large-address-aware ' ,
' -Wl,--dynamicbase ' ,
' -Wl,--nxcompat ' ,
2016-10-15 17:51:48 +00:00
]
2016-07-14 01:59:02 +00:00
if os . getenv ( ' SOURCE_DATE_EPOCH ' ) is None :
2016-10-15 17:51:48 +00:00
__preferred_win32_linker_options + = [ ' -Wl,--insert-timestamp ' ]
2015-12-03 03:26:49 +00:00
def __mangle_compiler_option_name ( opt ) :
return ' check_compiler_option %s ' % opt . replace ( ' - ' , ' _ ' ) . replace ( ' = ' , ' _ ' )
def __mangle_linker_option_name ( opt ) :
return ' check_linker_option %s ' % opt . replace ( ' - ' , ' _ ' ) . replace ( ' , ' , ' _ ' )
2015-09-19 23:04:35 +00:00
@_custom_test
2015-12-03 03:26:49 +00:00
def check_preferred_compiler_options ( self , context ,
ccopts = __preferred_compiler_options ,
ldopts = ( ) ,
2016-09-15 01:50:56 +00:00
_text = '''
/ * clang on OS X incorrectly diagnoses mistakes in Apple system headers
* even when - Wsystem - headers is not specified . Include one known buggy
* header here , so that the compilation will fail and the compiler will be
* marked as not supporting - Wold - style - cast .
* /
#if defined(__APPLE__) && defined(__MACH__)
#include <HIServices/Processes.h>
#endif
''' ,
2015-12-03 03:26:49 +00:00
_mangle_compiler_option_name = __mangle_compiler_option_name ,
_mangle_linker_option_name = __mangle_linker_option_name
) :
2015-09-19 23:04:35 +00:00
if self . user_settings . host_platform == ' win32 ' :
ldopts = self . __preferred_win32_linker_options
2016-08-13 22:20:13 +00:00
Compile = self . Compile
2016-08-17 04:44:22 +00:00
Link = self . Link
f , desc = ( Link , ' linker ' ) if ldopts else ( Compile , ' compiler ' )
2016-09-15 01:50:56 +00:00
if f ( context , text = _text , main = ' ' , msg = ' whether %s accepts preferred options ' % desc , successflags = { ' CXXFLAGS ' : ccopts , ' LINKFLAGS ' : ldopts } , calling_function = ' preferred_ %s _options ' % desc ) :
2015-09-19 23:04:35 +00:00
# Everything is supported. Skip individual tests.
return
# Compiler+linker together failed. Check if compiler alone will work.
2016-09-15 01:50:56 +00:00
if f is Compile or not Compile ( context , text = _text , main = ' ' , msg = ' whether compiler accepts preferred options ' , successflags = { ' CXXFLAGS ' : ccopts } ) :
2015-09-19 23:04:35 +00:00
# Compiler alone failed.
# Run down the individual compiler options to find any that work.
for opt in ccopts :
2016-09-15 01:50:56 +00:00
Compile ( context , text = _text , main = ' ' , msg = ' whether compiler accepts option %s ' % opt , successflags = { ' CXXFLAGS ' : ( opt , ) } , calling_function = _mangle_compiler_option_name ( opt ) [ 6 : ] )
2015-09-19 23:04:35 +00:00
# Run down the individual linker options to find any that work.
for opt in ldopts :
2016-09-15 01:50:56 +00:00
Link ( context , text = _text , main = ' ' , msg = ' whether linker accepts option %s ' % opt , successflags = { ' LINKFLAGS ' : ( opt , ) } , calling_function = _mangle_linker_option_name ( opt ) [ 6 : ] )
2015-09-19 23:04:35 +00:00
@classmethod
2015-12-03 03:26:49 +00:00
def register_preferred_compiler_options ( cls ,
ccopts = __preferred_compiler_options ,
2015-09-19 23:04:35 +00:00
# Always register target-specific tests on the class. Individual
# targets will decide whether to run the tests.
2015-12-03 03:26:49 +00:00
ldopts = __preferred_win32_linker_options ,
RecordedTest = Collector . RecordedTest ,
record = implicit_tests . append ,
_mangle_compiler_option_name = __mangle_compiler_option_name ,
_mangle_linker_option_name = __mangle_linker_option_name
) :
del cls . register_preferred_compiler_options
2015-09-19 23:04:35 +00:00
record ( RecordedTest ( ' check_preferred_linker_options ' , ' assume linker accepts preferred options ' ) )
2015-12-03 03:26:49 +00:00
mangle = _mangle_compiler_option_name
2015-09-19 23:04:35 +00:00
for opt in ccopts :
record ( RecordedTest ( mangle ( opt ) , ' assume compiler accepts %s ' % opt ) )
2015-12-03 03:26:49 +00:00
mangle = _mangle_linker_option_name
2015-09-19 23:04:35 +00:00
for opt in ldopts :
record ( RecordedTest ( mangle ( opt ) , ' assume linker accepts %s ' % opt ) )
2015-09-19 23:04:36 +00:00
assert cls . custom_tests [ 0 ] . name == cls . check_cxx_works . __name__ , cls . custom_tests [ 0 ] . name
assert cls . custom_tests [ - 1 ] . name == cls . _restore_cxx_prefix . __name__ , cls . custom_tests [ - 1 ] . name
# This must be the last custom test. It does not test the environment,
# but is responsible for reversing test-environment-specific changes made
# by check_cxx_works.
@_custom_test
def _restore_cxx_prefix ( self , context ) :
2015-10-24 03:13:11 +00:00
sconf = context . sconf
sconf . config_h_text = self . __commented_tool_versions + sconf . config_h_text
2015-09-19 23:04:36 +00:00
context . env [ ' CXXCOM ' ] = self . __cxx_com_prefix
context . did_show_result = True
2013-06-27 02:35:22 +00:00
2015-09-19 23:04:35 +00:00
ConfigureTests . register_preferred_compiler_options ( )
2015-06-20 03:50:46 +00:00
2015-11-01 21:15:39 +00:00
class cached_property ( object ) :
def __init__ ( self , f ) :
self . method = f
self . name = f . __name__
def __get__ ( self , instance , cls ) :
# This should never be accessed directly on the class.
assert instance is not None
name = self . name
d = instance . __dict__
# After the first access, Python should find the cached value in
# the instance dictionary instead of calling __get__ again.
assert name not in d
d [ name ] = r = self . method ( instance )
return r
class LazyObjectConstructor ( object ) :
2015-11-01 21:15:39 +00:00
def __lazy_objects ( self , source ,
cache = { } ,
__strip_extension = lambda _ , name , _splitext = os . path . splitext : _splitext ( name ) [ 0 ]
) :
2015-10-28 02:44:36 +00:00
env = self . env
2015-09-26 21:17:12 +00:00
# Use id because name needs to be hashable and have a 1-to-1
# mapping to source.
2015-10-28 02:44:36 +00:00
name = ( id ( env ) , id ( source ) )
2013-05-04 23:58:14 +00:00
try :
2015-09-19 23:04:35 +00:00
return cache [ name ]
2013-05-04 23:58:14 +00:00
except KeyError as e :
2015-09-19 23:04:35 +00:00
StaticObject = env . StaticObject
OBJSUFFIX = env [ ' OBJSUFFIX ' ]
builddir = self . user_settings . builddir
# Convert to a tuple so that attempting to modify a cached
# result raises an error.
2016-09-24 18:06:11 +00:00
value = tuple ( [
2016-10-15 00:53:18 +00:00
StaticObject ( target = ' %s %s %s ' % ( builddir , transform_target ( self , srcname ) , OBJSUFFIX ) , source = srcname ,
2016-11-26 22:51:47 +00:00
* * ( { } if transform_env is None else transform_env ( self , env ) )
2016-10-15 00:53:18 +00:00
) \
2016-09-24 18:06:11 +00:00
for s in source \
# This is a single iteration comprehension to work
# around the inability to assign variables as part of a
# normal comprehension. It iterates over one of two
# single element tuples. The choice of tuple is
# controlled by the isinstance check. Each single
2016-10-15 00:53:18 +00:00
# element tuple consists of (F, T, L) where F is the
# function to bind as `transform_target`, T is the
# function to bind as `transform_env` (or None), and L
# is an iterable to bind as `t`.
for transform_target , transform_env , t in ( \
( ( __strip_extension , None , ( s , ) ) , ) \
if isinstance ( s , str ) \
else ( ( \
s . get ( ' transform_target ' , __strip_extension ) , \
s . get ( ' transform_env ' , None ) , \
s [ ' source ' ] , \
) , ) \
) \
2016-09-24 18:06:11 +00:00
for srcname in t \
] )
2015-09-19 23:04:35 +00:00
cache [ name ] = value
2013-05-04 23:58:14 +00:00
return value
2015-10-28 02:44:36 +00:00
@staticmethod
2016-08-17 04:44:22 +00:00
def create_lazy_object_getter ( sources , __lazy_objects = __lazy_objects ) :
return lambda s , _f = __lazy_objects , _sources = sources : _f ( s , _sources )
2013-05-04 23:58:14 +00:00
2013-10-26 22:13:13 +00:00
class FilterHelpText :
2015-11-14 18:17:22 +00:00
_sconf_align = None
2013-10-26 22:13:13 +00:00
def __init__ ( self ) :
self . visible_arguments = [ ]
def FormatVariableHelpText ( self , env , opt , help , default , actual , aliases ) :
if not opt in self . visible_arguments :
return ' '
2014-08-05 02:43:29 +00:00
if not self . _sconf_align :
self . _sconf_align = len ( max ( ( s for s in self . visible_arguments if s [ : 6 ] == ' sconf_ ' ) , key = len ) )
2013-10-26 22:13:13 +00:00
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 ) )
2015-09-19 23:04:35 +00:00
return ( " { opt: %u } {help} {l} \n " % ( self . _sconf_align if opt [ : 6 ] == ' sconf_ ' else 15 ) ) . format ( opt = opt , help = help , l = ( " [ " + " ; " . join ( l ) + " ] " if l else ' ' ) )
2013-10-26 22:13:13 +00:00
2015-09-13 20:23:05 +00:00
class PCHManager ( object ) :
class ScannedFile :
def __init__ ( self , candidates ) :
self . candidates = candidates
2015-11-14 18:17:22 +00:00
syspch_cpp_filename = None
ownpch_cpp_filename = None
syspch_object_node = None
ownpch_object_node = None
required_pch_object_node = None
2015-09-13 20:23:05 +00:00
# Compile on first use, so that non-PCH builds skip the compile
_re_preproc_match = None
_re_include_match = None
_re_singleline_comments_sub = None
# Source files are tracked at class scope because all builds share
# the same source tree.
_cls_scanned_files = None
# Import required modules when the first PCHManager is created, then
# remove the hook and use object.__new__ to create PCHManager
# instances.
def __new__ ( cls , * args ) :
from re import compile as c
# Match C preprocessor directives that start with i or e,
# capture any arguments, and allow no arguments. This accepts:
# - #if
# - #ifdef
# - #ifndef
# - #else
# - #endif
# - #include
# - #error
cls . _re_preproc_match = c ( r ' # \ s*([ie] \ w+)(?: \ s+(.*))? ' ) . match
# Capture the argument in a #include statement, including the
# angle brackets or quotes.
#
# Rebirth currently has no computed includes, so ignore computed
# includes.
cls . _re_include_match = c ( r ' (<[^>]+>| " [^ " ]+ " ) ' ) . match
# Strip a single-line C++ style comment ("// Text") and any
# preceding whitespace. The C preprocessor will discard these
# when compiling the header. Discarding them from the
# environment Value will prevent rebuilding pch.cpp when the
# only change is in such a comment.
cls . _re_singleline_comments_sub = c ( r ' \ s*//.* ' ) . sub
# dict with key=filename with path, value=ScannedFile
cls . _cls_scanned_files = { }
from tempfile import mkstemp
cls . _tempfile_mkstemp = staticmethod ( mkstemp )
del cls . __new__
return cls . __new__ ( cls , * args )
def __init__ ( self , user_settings , env , pch_subdir , configure_pch_flags , common_pch_manager ) :
assert user_settings . syspch or user_settings . pch
self . user_settings = user_settings
self . env = env
# dict with key=fs.File, value=ScannedFile
self . _instance_scanned_files = { }
self . _common_pch_manager = common_pch_manager
2015-11-14 18:17:22 +00:00
syspch_cpp_filename = ownpch_cpp_filename = None
syspch_object_node = None
2015-09-13 20:23:05 +00:00
CXXFLAGS = env [ ' CXXFLAGS ' ] + configure_pch_flags [ ' CXXFLAGS ' ]
File = env . File
if user_settings . syspch :
self . syspch_cpp_filename = syspch_cpp_filename = os . path . join ( user_settings . builddir , pch_subdir , ' syspch.cpp ' )
self . syspch_cpp_node = File ( syspch_cpp_filename )
2015-10-03 17:17:49 +00:00
self . required_pch_object_node = self . syspch_object_node = syspch_object_node = env . StaticObject ( target = ' %s .gch ' % syspch_cpp_filename , source = self . syspch_cpp_node , CXXCOM = env . _dxx_cxxcom_no_ccache_prefix , CXXFLAGS = CXXFLAGS )
2015-09-13 20:23:05 +00:00
if user_settings . pch :
self . ownpch_cpp_filename = ownpch_cpp_filename = os . path . join ( user_settings . builddir , pch_subdir , ' ownpch.cpp ' )
self . ownpch_cpp_node = File ( ownpch_cpp_filename )
if syspch_object_node :
CXXFLAGS + = [ ' -include ' , syspch_cpp_filename , ' -Winvalid-pch ' ]
2015-10-03 17:17:49 +00:00
self . required_pch_object_node = self . ownpch_object_node = ownpch_object_node = env . StaticObject ( target = ' %s .gch ' % ownpch_cpp_filename , source = self . ownpch_cpp_node , CXXCOM = env . _dxx_cxxcom_no_ccache_prefix , CXXFLAGS = CXXFLAGS )
2015-09-13 20:23:05 +00:00
env . Depends ( ownpch_object_node , File ( os . path . join ( self . user_settings . builddir , ' dxxsconf.h ' ) ) )
if syspch_object_node :
env . Depends ( ownpch_object_node , syspch_object_node )
self . pch_CXXFLAGS = [ ' -include ' , ownpch_cpp_filename or syspch_cpp_filename , ' -Winvalid-pch ' ]
2015-09-26 21:17:14 +00:00
# If assume unchanged and the file exists, set __files_included
# to a dummy value. This bypasses scanning source files and
# guarantees that the text of pch.cpp is not changed. SCons
# will still recompile pch.cpp into a new .gch file if pch.cpp
# includes files that SCons recognizes as changed.
2015-09-13 20:23:05 +00:00
if user_settings . pch_cpp_assume_unchanged and \
( not syspch_cpp_filename or os . path . exists ( syspch_cpp_filename ) ) and \
( not ownpch_cpp_filename or os . path . exists ( ownpch_cpp_filename ) ) :
2015-09-26 21:17:14 +00:00
self . __files_included = True
else :
# collections.defaultdict with key from ScannedFile.candidates,
# value is a collections.Counter with key=tuple of preprocessor
# guards, value=count of times this header was included under
# that set of guards.
self . __files_included = defaultdict ( collections_counter )
2015-09-13 20:23:05 +00:00
self . __env_Program = env . Program
self . __env_StaticObject = env . StaticObject
env . Program = self . Program
env . StaticObject = self . StaticObject
def record_file ( self , env , source_file ) :
# Every scanned file goes into self._cls_scanned_files to
# prevent duplicate scanning from multiple targets.
f = self . _cls_scanned_files . get ( source_file , None )
if f is None :
self . _cls_scanned_files [ source_file ] = f = self . scan_file ( env , source_file )
self . _instance_scanned_files [ source_file ] = f
return f
# Scan a file for preprocessor directives related to conditional
# compilation and file inclusion.
#
# The #include directives found are eventually written to pch.cpp
# with their original preprocessor guards in place. Since the
# preprocessor guards are kept and the C preprocessor will evaluate
# them when compiling the header, this scanner does not attempt to
# track which branches are true and which are false.
#
# This scanner makes no attempt to normalize guard conditions. It
# considers each of these examples to be distinct guards, even
# though a full preprocessor will produce the same result for each:
#
# #if 1
# #if 2
# #if 3 < 5
#
# or:
#
# #ifdef A
# #if defined(A)
# #if (defined(A))
# #if !!defined(A)
#
# or:
#
# #ifndef A
# #else
# #endif
#
# #ifdef A
# #endif
#
# Include directives are followed only if the calling file will not
# be part of the output pch.cpp. When the calling file will be in
# pch.cpp, then the C preprocessor will include the called file, so
# there is no need to scan it for other headers to include.
#
# This scanner completely ignores pragmas, #define, #undef, and
# computed includes.
@classmethod
def scan_file ( cls , env , source_filenode ) :
match_preproc_directive = cls . _re_preproc_match
match_include_directive = cls . _re_include_match
preceding_line = None
lines_since_preprocessor = None
# defaultdict with key=name of header to include, value=set of
# preprocessor guards under which an include was seen. Set is
# used because duplicate inclusions from a single source file
# should not adjust the usage counter.
candidates = defaultdict ( set )
# List of currently active preprocessor guards
guard = [ ]
header_search_path = None
2016-08-17 04:44:22 +00:00
os_path_join = os . path . join
os_path_exists = os . path . exists
2015-09-13 20:23:05 +00:00
for line in map ( str . strip , source_filenode . get_contents ( ) . splitlines ( ) ) :
if preceding_line is not None :
# Basic support for line continuation.
2015-09-19 23:04:35 +00:00
line = ' %s %s ' % ( preceding_line [ : - 1 ] , line )
2015-09-13 20:23:05 +00:00
preceding_line = None
elif not line . startswith ( ' # ' ) :
# Allow unlimited non-preprocessor lines before the
# first preprocessor line. Once one preprocessor line
# is found, track how many lines since the most recent
# preprocessor line was seen. If too many
# non-preprocessor lines appear in a row, assume the
# scanner is now down in source text and move to the
# next file.
if lines_since_preprocessor is not None :
lines_since_preprocessor + = 1
if lines_since_preprocessor > 50 :
break
continue
lines_since_preprocessor = 0
# Joined lines are rare. Ignore complicated quoting.
if line [ - 1 ] == ' \\ ' :
preceding_line = line
continue
m = match_preproc_directive ( line )
if not m :
# Not a preprocessor directive or not a directive that
# this scanner handles.
continue
directive = m . group ( 1 )
if directive == ' include ' :
m = match_include_directive ( m . group ( 2 ) )
if not m :
# This directive is probably a computed include.
continue
name = m . group ( 1 )
bare_name = name [ 1 : - 1 ]
if name [ 0 ] == ' " ' :
# Canonicalize paths to non-system headers
if name == ' " dxxsconf.h " ' :
# Ignore generated header here. PCH generation
# will insert it in the right order.
continue
if header_search_path is None :
header_search_path = [
d for d in ( [ os . path . dirname ( str ( source_filenode ) ) ] + env [ ' CPPPATH ' ] )
# Filter out SDL paths
if not d . startswith ( ' / ' )
]
name = None
for d in header_search_path :
2016-08-17 04:44:22 +00:00
effective_name = os_path_join ( d , bare_name )
if os_path_exists ( effective_name ) :
2015-09-13 20:23:05 +00:00
name = effective_name
break
if name is None :
# name is None if:
# - A system header was included using quotes.
#
# - A game-specific header was included in a
# shared file. A game-specific preprocessor
# guard will prevent the preprocessor from
# including the file, but the PCH scan logic
# looks inside branches that the C preprocessor
# will evaluate as false.
continue
name = env . File ( name )
candidates [ name ] . add ( tuple ( guard ) )
elif directive == ' endif ' :
# guard should always be True here, but test to avoid
# ugly errors if scanning an ill-formed source file.
if guard :
guard . pop ( )
elif directive == ' else ' :
# #else is handled separately because it has no
# arguments
2015-09-19 23:04:35 +00:00
guard . append ( ' # %s ' % directive )
2015-09-13 20:23:05 +00:00
elif directive in (
' elif ' ,
' if ' ,
' ifdef ' ,
' ifndef ' ,
) :
2015-09-19 23:04:35 +00:00
guard . append ( ' # %s %s ' % ( directive , m . group ( 2 ) ) )
2015-09-13 20:23:05 +00:00
elif directive not in ( ' error ' , ) :
raise SCons . Errors . StopError ( " Scanning %s found unhandled C preprocessor directive %r " % ( str ( source_filenode ) , directive ) )
return cls . ScannedFile ( candidates )
def _compute_pch_text ( self ) :
self . _compute_indirect_includes ( )
own_header_inclusion_threshold = self . user_settings . pch
sys_header_inclusion_threshold = self . user_settings . syspch
2015-10-09 02:46:10 +00:00
configured_threshold = 0x7fffffff if self . user_settings . pch_cpp_exact_counts else None
2015-09-13 20:23:05 +00:00
# defaultdict with key=name of tuple of active preprocessor
# guards, value=tuple of (included file, count of times file was
# seen with this set of guards, count of times file would be
# included with this set of guards defined).
syscpp_includes = defaultdict ( list )
owncpp_includes = defaultdict ( list ) if own_header_inclusion_threshold else None
for included_file , usage_dict in self . __files_included . iteritems ( ) :
if isinstance ( included_file , str ) :
# System header
cpp_includes = syscpp_includes
name = included_file
threshold = own_header_inclusion_threshold if sys_header_inclusion_threshold is None else sys_header_inclusion_threshold
else :
# Own header
cpp_includes = owncpp_includes
2016-08-17 04:44:22 +00:00
name = ' " %s " ' % included_file
2015-09-13 20:23:05 +00:00
threshold = own_header_inclusion_threshold
if not threshold :
continue
2015-10-09 02:46:10 +00:00
count_threshold = configured_threshold or threshold
2015-09-13 20:23:05 +00:00
g = usage_dict . get ( ( ) , 0 )
# As a special case, if this header is included
# without any preprocessor guards, ignore all the
# conditional includes.
guards = \
[ ( ( ) , g ) ] if ( g > = threshold ) else \
sorted ( usage_dict . iteritems ( ) , reverse = True )
while guards :
preprocessor_guard_directives , local_count_seen = guards . pop ( )
total_count_seen = local_count_seen
2015-10-09 02:46:10 +00:00
if total_count_seen < count_threshold :
2015-09-13 20:23:05 +00:00
# If not eligible on its own, add in the count from
# preprocessor guards that are always true when this
# set of guards is true. Since the scanner does not
# normalize preprocessor directives, this is a
# conservative count of always-true guards.
g = preprocessor_guard_directives
2015-10-09 02:46:10 +00:00
while g and total_count_seen < count_threshold :
2015-09-13 20:23:05 +00:00
g = g [ : - 1 ]
total_count_seen + = usage_dict . get ( g , 0 )
if total_count_seen < threshold :
# If still not eligible, skip.
continue
cpp_includes [ preprocessor_guard_directives ] . append ( ( name , local_count_seen , total_count_seen ) )
if syscpp_includes :
self . __generated_syspch_lines = self . _compute_pch_generated_lines ( syscpp_includes )
if owncpp_includes :
self . __generated_ownpch_lines = self . _compute_pch_generated_lines ( owncpp_includes )
def _compute_pch_generated_lines ( self , cpp_includes ) :
generated_pch_lines = [ ]
# Append guarded #include directives for files which passed the
# usage threshold above. This loop could try to combine related
# preprocessor guards, but:
# - The C preprocessor handles the noncombined guards correctly.
# - As a native program optimized for text processing, the C
# preprocessor almost certainly handles guard checking faster
# than this script could handle guard merging.
# - This loop runs whenever pch.cpp might be regenerated, even
# if the result eventually shows that pch.cpp has not changed.
# The C preprocessor will only run over the file when it is
# actually changed and is processed to build a new .gch file.
for preprocessor_guard_directives , included_file_tuples in sorted ( cpp_includes . iteritems ( ) ) :
generated_pch_lines . append ( ' ' )
generated_pch_lines . extend ( preprocessor_guard_directives )
# local_count_seen is the direct usage count for this
# combination of preprocessor_guard_directives.
#
# total_count_seen is the sum of local_count_seen and all
# guards that are a superset of this
# preprocessor_guard_directives. The total stops counting
# when it reaches threshold, so it may undercount actual
# usage.
for ( name , local_count_seen , total_count_seen ) in sorted ( included_file_tuples ) :
2015-09-19 23:04:35 +00:00
generated_pch_lines . append ( ' #include %s \t // %u %u ' % ( name , local_count_seen , total_count_seen ) )
2015-09-13 20:23:05 +00:00
# d[2] == l if d is '#else' or d is '#elif'
# Only generate #endif when d is a '#if*' directive, since
# '#else/#elif' do not introduce a new scope.
generated_pch_lines . extend ( ' #endif ' for d in preprocessor_guard_directives if d [ 2 ] != ' l ' )
return generated_pch_lines
def _compute_indirect_includes ( self ) :
2015-10-09 02:46:10 +00:00
own_header_inclusion_threshold = None if self . user_settings . pch_cpp_exact_counts else self . user_settings . pch
2015-09-13 20:23:05 +00:00
# Count how many times each header is used for each preprocessor
# guard combination. After the outer loop finishes,
# files_included is a dictionary that maps the name of the
# include file to a collections.counter instance. The mapped
# counter maps a preprocessor guard to a count of how many times
# it was used.
#
# Given:
#
# a.cpp
# #include "a.h"
# #ifdef A
# #include "b.h"
# #endif
#
# b.cpp
# #include "b.h"
#
# files_included = {
# 'a.h' : { () : 1 }
# 'b.h' : {
# ('#ifdef A',) : 1, # From a.cpp
# () : 1, # From b.cpp
# }
# }
files_included = self . __files_included
for scanned_file in self . _instance_scanned_files . itervalues ( ) :
for included_file , guards in scanned_file . candidates . iteritems ( ) :
i = files_included [ included_file ]
for g in guards :
i [ g ] + = 1
# If own_header_inclusion_threshold == 1, then every header
# found will be listed in pch.cpp, so any indirect headers will
# be included by the C preprocessor.
if own_header_inclusion_threshold == 1 :
return
# For each include file which is below the threshold, scan it
# for includes which may end up above the threshold.
File = self . env . File
includes_to_check = sorted ( files_included . iterkeys ( ) , key = str )
while includes_to_check :
included_file = includes_to_check . pop ( )
if isinstance ( included_file , str ) :
# System headers are str. Internal headers are
# fs.File.
continue
guards = files_included [ included_file ]
unconditional_use_count = guards . get ( ( ) , 0 )
if unconditional_use_count > = own_header_inclusion_threshold and own_header_inclusion_threshold :
# Header will be unconditionally included in the PCH.
continue
f = self . record_file ( self . env , File ( included_file ) )
for nested_included_file , nested_guards in sorted ( f . candidates . iteritems ( ) , key = str ) :
if not isinstance ( included_file , str ) and not nested_included_file in files_included :
# If the header is a system header, it will be
# str. Skip system headers.
#
# Otherwise, if it has not been seen before,
# append it to includes_to_check for recursive
# scanning.
includes_to_check . append ( nested_included_file )
i = files_included [ nested_included_file ]
# If the nested header is included
# unconditionally, skip the generator.
for g in ( nested_guards if unconditional_use_count else ( a + b for a in guards for b in nested_guards ) ) :
i [ g ] + = 1
2015-09-26 21:17:12 +00:00
@classmethod
def write_pch_inclusion_file ( cls , target , source , env ) :
2015-09-13 20:23:05 +00:00
target = str ( target [ 0 ] )
2015-09-26 21:17:12 +00:00
fd , path = cls . _tempfile_mkstemp ( suffix = ' ' , prefix = ' %s . ' % os . path . basename ( target ) , dir = os . path . dirname ( target ) , text = True )
2015-09-13 20:23:05 +00:00
# source[0].get_contents() returns the comment-stripped form
os . write ( fd , source [ 0 ] . __generated_pch_text )
os . close ( fd )
os . rename ( path , target )
def StaticObject ( self , target , source , CXXFLAGS = None , * args , * * kwargs ) :
env = self . env
source = env . File ( source )
o = self . __env_StaticObject ( target = target , source = source , CXXFLAGS = self . pch_CXXFLAGS + ( env [ ' CXXFLAGS ' ] if CXXFLAGS is None else CXXFLAGS ) , * args , * * kwargs )
# Force an order dependency on the .gch file. It is never
# referenced by the command line or the source files, so SCons
# may not recognize it as an input.
env . Requires ( o , self . required_pch_object_node )
2015-09-26 21:17:14 +00:00
if self . __files_included is not True :
self . record_file ( env , source )
2015-09-13 20:23:05 +00:00
return o
def Program ( self , * args , * * kwargs ) :
self . _register_pch_commands ( )
if self . _common_pch_manager :
self . _common_pch_manager . _register_pch_commands ( )
return self . __env_Program ( * args , * * kwargs )
def _register_pch_commands ( self ) :
if self . __files_included :
# Common code calls this function once for each game which
# uses it. Only one call is necessary for proper operation.
# Ignore all later calls.
return
self . _compute_pch_text ( )
syspch_lines = self . __generated_syspch_lines
pch_begin_banner_template = '''
/ / BEGIN PCH GENERATED FILE
/ / % r
/ / Threshold = % u
2016-11-26 22:51:47 +00:00
/ / SConf generated header
#include "dxxsconf.h"
2015-09-13 20:23:05 +00:00
'''
pch_end_banner = ( '''
/ / END PCH GENERATED FILE
''' ,)
if self . syspch_object_node :
self . _register_write_pch_inclusion ( self . syspch_cpp_node ,
(
( pch_begin_banner_template % ( self . syspch_cpp_filename , self . user_settings . syspch ) , ) ,
syspch_lines ,
pch_end_banner ,
)
)
# ownpch.cpp will include syspch.cpp.gch from the command
# line, so clear syspch_lines to avoid repeating system
# includes in ownpch.cpp
syspch_lines = ( )
if self . ownpch_object_node :
self . _register_write_pch_inclusion ( self . ownpch_cpp_node ,
(
( pch_begin_banner_template % ( self . ownpch_cpp_filename , self . user_settings . pch ) , ) ,
( ' // System headers ' if syspch_lines else ' ' , ) ,
syspch_lines ,
( '''
/ / Own headers
''' ,),
self . __generated_ownpch_lines ,
pch_end_banner ,
)
)
def _register_write_pch_inclusion ( self , node , lineseq ) :
# The contents of pch.cpp are taken from the iterables in
# lineseq. Set the contents as an input Value instead of
# listing file nodes, so that pch.cpp is not rebuilt if a change
# to a source file does not change what headers are listed in
# pch.cpp.
#
# Strip C++ single line comments so that changes in comments are
# ignored when deciding whether pch.cpp needs to be rebuilt.
env = self . env
text = ' \n ' . join ( itertools . chain . from_iterable ( lineseq ) )
v = env . Value ( self . _re_singleline_comments_sub ( ' ' , text ) )
v . __generated_pch_text = text
2015-09-26 21:17:12 +00:00
# Use env.Command instead of env.Textfile or env.Substfile since
# the latter use their input both for freshness and as a data
# source. This Command writes the commented form of the Value
# to the file, but uses a comment-stripped form for SCons
# freshness checking.
2015-09-13 20:23:05 +00:00
env . Command ( node , v , self . write_pch_inclusion_file )
2013-05-04 23:58:14 +00:00
class DXXCommon ( LazyObjectConstructor ) :
2016-11-26 22:51:47 +00:00
# version number
VERSION_MAJOR = 0
VERSION_MINOR = 59
VERSION_MICRO = 100
DXX_VERSION_SEQ = ' , ' . join ( [ str ( VERSION_MAJOR ) , str ( VERSION_MINOR ) , str ( VERSION_MICRO ) ] )
2015-10-29 03:01:42 +00:00
pch_manager = None
2015-11-01 21:15:39 +00:00
@cached_property
2013-06-16 19:28:38 +00:00
def program_message_prefix ( self ) :
return ' %s . %d ' % ( self . PROGRAM_NAME , self . program_instance )
2013-05-11 23:52:32 +00:00
# Settings which affect how the files are compiled
class UserBuildSettings :
2015-09-13 20:23:05 +00:00
class IntVariable ( object ) :
def __new__ ( cls , key , help , default ) :
2015-11-01 21:15:40 +00:00
return ( key , help , default , cls . _validator , int )
2015-09-13 20:23:05 +00:00
@staticmethod
2015-11-01 21:15:40 +00:00
def _validator ( key , value , env ) :
2015-09-13 20:23:05 +00:00
try :
int ( value )
return True
except ValueError :
2015-11-01 21:15:40 +00:00
raise SCons . Errors . UserError ( ' Invalid value for integer-only option %s : %s . ' % ( key , value ) )
class UIntVariable ( IntVariable ) :
@staticmethod
def _validator ( key , value , env ) :
try :
if int ( value ) < 0 :
raise ValueError
return True
except ValueError :
raise SCons . Errors . UserError ( ' Invalid value for unsigned-integer-only option %s : %s . ' % ( key , value ) )
2013-05-05 18:52:00 +00:00
# Paths for the Videocore libs/includes on the Raspberry Pi
RPI_DEFAULT_VC_PATH = ' /opt/vc '
default_OGLES_LIB = ' GLES_CM '
2013-06-16 02:40:05 +00:00
_default_prefix = ' /usr/local '
2015-08-14 03:02:04 +00:00
__stdout_is_not_a_tty = None
2015-10-13 02:43:24 +00:00
__has_git_dir = None
2016-10-29 23:16:19 +00:00
# +/=
_dxx_extended_identifiers = ( r ' \ u012b ' , r ' \ u012f ' , r ' \ u013d ' )
_dxx_disable_extended_identifiers = ( ' _2b ' , ' _2f ' , ' _3d ' )
2016-10-29 23:16:16 +00:00
def default_poison ( self ) :
return ' overwrite ' if self . debug else ' none '
2013-05-11 23:52:32 +00:00
def default_builddir ( self ) :
2013-06-16 02:40:05 +00:00
builddir_prefix = self . builddir_prefix
builddir_suffix = self . builddir_suffix
2013-05-11 23:52:32 +00:00
default_builddir = builddir_prefix or ' '
if builddir_prefix is not None or builddir_suffix is not None :
2013-07-04 02:27:50 +00:00
fields = [
self . host_platform ,
2013-11-26 03:59:44 +00:00
os . path . basename ( self . CXX ) if self . CXX else None ,
2013-07-04 02:27:50 +00:00
]
2013-11-26 03:59:44 +00:00
compiler_flags = ' \n ' . join ( ( getattr ( self , attr ) or ' ' ) . strip ( ) for attr in [ ' CPPFLAGS ' , ' CXXFLAGS ' ] )
2013-07-04 02:27:07 +00:00
if compiler_flags :
2013-11-26 03:59:44 +00:00
# Mix in CRC of CXXFLAGS to get reasonable uniqueness
2013-05-18 02:37:19 +00:00
# when flags are changed. A full hash is
# unnecessary here.
2013-07-04 02:27:07 +00:00
crc = binascii . crc32 ( compiler_flags )
2013-06-08 01:38:05 +00:00
if crc < 0 :
crc = crc + 0x100000000
2013-07-04 02:27:50 +00:00
fields . append ( ' {:08x} ' . format ( crc ) )
2013-11-28 04:21:20 +00:00
if self . pch :
2015-09-13 20:23:05 +00:00
fields . append ( ' p %u ' % self . pch )
elif self . syspch :
fields . append ( ' sp %u ' % self . syspch )
2013-07-04 03:29:01 +00:00
fields . append ( ' ' . join ( a [ 1 ] if getattr ( self , a [ 0 ] ) else ( a [ 2 ] if len ( a ) > 2 else ' ' )
2013-05-11 23:52:32 +00:00
for a in (
( ' debug ' , ' dbg ' ) ,
2014-06-29 23:26:05 +00:00
( ' lto ' , ' lto ' ) ,
2013-05-11 23:52:32 +00:00
( ' editor ' , ' ed ' ) ,
2013-07-04 03:29:01 +00:00
( ' opengl ' , ' ogl ' , ' sdl ' ) ,
2013-05-11 23:52:32 +00:00
( ' opengles ' , ' es ' ) ,
( ' raspberrypi ' , ' rpi ' ) ,
2013-07-04 02:27:50 +00:00
) ) )
default_builddir + = ' - ' . join ( [ f for f in fields if f ] )
2013-05-11 23:52:32 +00:00
if builddir_suffix is not None :
default_builddir + = builddir_prefix
return default_builddir
2012-05-20 18:51:21 +00:00
def default_memdebug ( self ) :
return self . debug
2013-05-12 01:03:37 +00:00
# automatic setup for raspberrypi
def default_opengles ( self ) :
if self . raspberrypi :
2013-06-16 02:40:05 +00:00
return True
return False
2015-08-14 03:02:04 +00:00
@classmethod
def default_verbosebuild ( cls ) :
# Enable verbosebuild when the output is not directed to a
# terminal. When output is not a terminal, it is likely
# either a pager or a log file, both of which can readily
# handle the very long lines generated by verbose mode.
r = cls . __stdout_is_not_a_tty
if r is None :
isatty = getattr ( sys . stdout , ' isatty ' , None )
# If isatty is None, then assume output is a TTY.
cls . __stdout_is_not_a_tty = r = False if isatty is None else not isatty ( )
return r
2016-10-09 17:35:03 +00:00
def default_words_need_alignment ( self ) :
return self . raspberrypi
2013-05-12 01:03:37 +00:00
def selected_OGLES_LIB ( self ) :
if self . raspberrypi :
return ' GLESv2 '
return self . default_OGLES_LIB
2013-06-16 02:40:05 +00:00
def __default_DATA_DIR ( self ) :
2015-09-19 23:04:35 +00:00
return ' %s /share/games/ %s ' % ( self . prefix , self . _program . target )
2013-06-16 02:40:05 +00:00
def _generic_variable ( key , help , default ) :
return ( key , help , default )
2016-08-13 22:20:13 +00:00
def __get_configure_tests ( tests , _filter = lambda s : s . name [ 0 ] != ' _ ' ) :
2015-09-26 21:17:12 +00:00
# Construct combined list on first use, then cache it
# forever.
2015-09-13 21:02:19 +00:00
try :
return tests . __configure_tests
except AttributeError :
2016-08-13 22:20:13 +00:00
tests . __configure_tests = c = filter ( _filter , tests . implicit_tests + tests . custom_tests )
2015-09-13 21:02:19 +00:00
return c
2015-10-13 02:43:24 +00:00
@classmethod
def __get_has_git_dir ( cls ) :
r = cls . __has_git_dir
if r is None :
# SConstruct is always at the top of the repository.
# The user might have run `scons` from elsewhere and
# used `-f` to indicate this file, but a false negative
# is acceptable here.
cls . __has_git_dir = r = os . path . exists ( os . environ . get ( ' GIT_DIR ' , ' .git ' ) )
return r
2015-11-01 21:15:38 +00:00
def _options ( self ,
2016-08-13 22:20:13 +00:00
__get_configure_tests = __get_configure_tests ,
2015-11-01 21:15:38 +00:00
generic_variable = _generic_variable ,
BoolVariable = BoolVariable ,
EnumVariable = EnumVariable ,
conftests = ConfigureTests ,
getenv = os . environ . get
) :
2016-08-13 22:20:13 +00:00
tests = __get_configure_tests ( conftests )
expect_sconf_tuple = ( ' 0 ' , ' 1 ' , conftests . expect_sconf_success , conftests . expect_sconf_failure )
sconf_tuple = ( ' 0 ' , ' 1 ' , ' 2 ' , conftests . sconf_force_failure , conftests . sconf_force_success , conftests . sconf_assume_success )
2013-06-16 02:40:05 +00:00
return (
2013-06-27 02:35:22 +00:00
{
2015-09-13 21:02:19 +00:00
' variable ' : EnumVariable ,
2013-06-27 02:35:22 +00:00
' arguments ' : [
2015-09-13 21:02:19 +00:00
( ' expect_sconf_ %s ' % t . name [ 6 : ] ,
None ,
None ,
2016-08-13 22:20:13 +00:00
{ ' allowed_values ' : expect_sconf_tuple }
) for t in tests
2015-08-22 20:43:04 +00:00
] ,
} ,
{
2015-09-13 21:02:19 +00:00
' variable ' : EnumVariable ,
2015-08-22 20:43:04 +00:00
' arguments ' : [
2015-09-13 21:02:19 +00:00
( ' sconf_ %s ' % t . name [ 6 : ] ,
None ,
t . desc or ( ' assume result of %s ' % t . name ) ,
2016-08-13 22:20:13 +00:00
{ ' allowed_values ' : sconf_tuple }
) for t in tests
2013-06-27 02:35:22 +00:00
] ,
} ,
2013-06-16 02:40:05 +00:00
{
' variable ' : BoolVariable ,
' arguments ' : (
2015-08-22 20:43:04 +00:00
( ' record_sconf_results ' , False , ' write sconf results to dxxsconf.h ' ) ,
2013-06-16 02:40:05 +00:00
( ' raspberrypi ' , False , ' build for Raspberry Pi (automatically sets opengles and opengles_lib) ' ) ,
2015-10-13 02:43:24 +00:00
( ' git_describe_version ' , self . __get_has_git_dir ( ) , ' include git --describe in extra_version ' ) ,
2014-07-26 17:40:12 +00:00
( ' git_status ' , True , ' include git status ' ) ,
2014-09-13 22:10:24 +00:00
( ' versid_depend_all ' , False , ' rebuild vers_id.cpp if any object file changes ' ) ,
2013-06-16 02:40:05 +00:00
) ,
} ,
{
2015-09-13 21:02:19 +00:00
' variable ' : generic_variable ,
2013-06-16 02:40:05 +00:00
' 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) ' ) ,
2015-09-13 20:23:05 +00:00
) ,
} ,
{
2015-11-01 21:15:40 +00:00
' variable ' : self . UIntVariable ,
2015-09-13 20:23:05 +00:00
' arguments ' : (
2015-05-09 17:38:57 +00:00
( ' lto ' , 0 , ' enable gcc link time optimization ' ) ,
2015-09-13 20:23:05 +00:00
( ' pch ' , None , ' pre-compile own headers used at least this many times ' ) ,
( ' syspch ' , None , ' pre-compile system headers used at least this many times ' ) ,
2015-11-01 21:15:40 +00:00
( ' max_joysticks ' , 8 , ' maximum number of usable joysticks ' ) ,
( ' max_axes_per_joystick ' , 128 , ' maximum number of axes per joystick ' ) ,
( ' max_buttons_per_joystick ' , 128 , ' maximum number of buttons per joystick ' ) ,
( ' max_hats_per_joystick ' , 4 , ' maximum number of hats per joystick ' ) ,
2013-05-12 03:16:24 +00:00
) ,
} ,
{
2013-06-16 02:40:05 +00:00
' variable ' : BoolVariable ,
2013-05-12 03:16:24 +00:00
' arguments ' : (
2015-09-13 20:23:05 +00:00
( ' pch_cpp_assume_unchanged ' , False , ' assume text of *pch.cpp is unchanged ' ) ,
2015-10-09 02:46:10 +00:00
( ' pch_cpp_exact_counts ' , False , None ) ,
2014-11-17 04:02:25 +00:00
( ' check_header_includes ' , False , ' compile test each header (developer option) ' ) ,
2013-06-16 02:40:05 +00:00
( ' debug ' , False , ' build DEBUG binary which includes asserts, debugging output, cheats and more output ' ) ,
2012-05-20 18:51:21 +00:00
( ' memdebug ' , self . default_memdebug , ' build with malloc tracking ' ) ,
2013-06-16 02:40:05 +00:00
( ' opengl ' , True , ' build with OpenGL support ' ) ,
( ' opengles ' , self . default_opengles , ' build with OpenGL ES support ' ) ,
( ' editor ' , False , ' include editor into build (!EXPERIMENTAL!) ' ) ,
2015-07-14 02:42:12 +00:00
( ' sdl2 ' , False , ' use libSDL2+SDL2_mixer (!EXPERIMENTAL!) ' ) ,
2013-06-16 02:40:05 +00:00
( ' sdlmixer ' , True , ' build with SDL_Mixer support for sound and music (includes external music support) ' ) ,
2016-02-02 03:10:53 +00:00
( ' ipv6 ' , False , ' enable UDP/IPv6 for multiplayer ' ) ,
2013-06-16 02:40:05 +00:00
( ' use_udp ' , True , ' enable UDP support ' ) ,
( ' use_tracker ' , True , ' enable Tracker support (requires UDP) ' ) ,
2015-08-14 03:02:04 +00:00
( ' verbosebuild ' , self . default_verbosebuild , ' print out all compiler/linker messages during building ' ) ,
2016-10-09 17:35:03 +00:00
( ' words_need_alignment ' , self . default_words_need_alignment , ' align words at load (needed for many non-x86 systems) ' ) ,
2015-10-18 21:01:18 +00:00
( ' register_compile_target ' , True , ' report compile targets to SCons core ' ) ,
2015-10-18 21:01:18 +00:00
( ' register_cpp_output_targets ' , None , None ) ,
2015-09-26 21:17:13 +00:00
# This is intentionally undocumented. If a bug
# report includes a log with this set to False, the
# reporter will be asked to provide a log with the
# value set to True. Try to prevent the extra round
# trip by hiding the option.
( ' show_tool_version ' , True , None )
2013-05-12 03:16:24 +00:00
) ,
} ,
{
2015-09-13 21:02:19 +00:00
' variable ' : generic_variable ,
2013-05-12 03:16:24 +00:00
' arguments ' : (
2015-09-19 23:04:35 +00:00
( ' CHOST ' , getenv ( ' CHOST ' ) , ' CHOST of output ' ) ,
( ' CXX ' , getenv ( ' CXX ' ) , ' C++ compiler command ' ) ,
( ' PKG_CONFIG ' , getenv ( ' PKG_CONFIG ' ) , ' PKG_CONFIG to run (Linux only) ' ) ,
( ' RC ' , getenv ( ' RC ' ) , ' Windows resource compiler command ' ) ,
2013-11-14 23:01:25 +00:00
( ' extra_version ' , None , ' text to append to version, such as VCS identity ' ) ,
2015-01-03 23:44:32 +00:00
( ' ccache ' , None , ' path to ccache ' ) ,
( ' distcc ' , None , ' path to distcc ' ) ,
2015-09-19 23:04:35 +00:00
( ' distcc_hosts ' , getenv ( ' DISTCC_HOSTS ' ) , ' hosts to distribute compilation ' ) ,
2013-11-14 23:01:25 +00:00
) ,
} ,
{
2015-09-13 21:02:19 +00:00
' variable ' : generic_variable ,
2013-11-14 23:01:25 +00:00
' stack ' : ' ' ,
' arguments ' : (
2015-09-19 23:04:35 +00:00
( ' CPPFLAGS ' , getenv ( ' CPPFLAGS ' ) , ' C preprocessor flags ' ) ,
( ' CXXFLAGS ' , getenv ( ' CXXFLAGS ' ) , ' C++ compiler flags ' ) ,
( ' LDFLAGS ' , getenv ( ' LDFLAGS ' ) , ' Linker flags ' ) ,
( ' LIBS ' , getenv ( ' LIBS ' ) , ' Libraries to link ' ) ,
2017-01-01 23:19:21 +00:00
# These are intentionally undocumented. They are
# meant for developers who know the implications of
# using them.
( ' CPPFLAGS_unchecked ' , None , None ) ,
( ' CXXFLAGS_unchecked ' , None , None ) ,
( ' LINKFLAGS_unchecked ' , None , None ) ,
2013-05-12 03:16:24 +00:00
) ,
} ,
{
2015-09-13 21:02:19 +00:00
' variable ' : EnumVariable ,
2013-06-16 02:40:05 +00:00
' arguments ' : (
2016-10-09 17:35:03 +00:00
( ' host_endian ' , None , ' endianness of host platform ' , { ' allowed_values ' : ( ' little ' , ' big ' ) } ) ,
2016-08-13 22:20:13 +00:00
( ' host_platform ' , ' linux ' if sys . platform == ' linux2 ' else sys . platform , ' cross-compile to specified platform ' , { ' allowed_values ' : ( ' win32 ' , ' darwin ' , ' linux ' ) } ) ,
2015-01-03 23:44:32 +00:00
) ,
} ,
{
' variable ' : ListVariable ,
' arguments ' : (
2016-10-29 23:16:16 +00:00
( ' poison ' , self . default_poison , ' method for poisoning free memory ' , { ' names ' : ( ' valgrind ' , ' overwrite ' ) } ) ,
2013-06-16 02:40:05 +00:00
) ,
} ,
{
2015-09-13 21:02:19 +00:00
' variable ' : generic_variable ,
2013-06-16 02:40:05 +00:00
' arguments ' : (
( ' builddir_prefix ' , None , ' prefix to generated build directory ' ) ,
( ' builddir_suffix ' , None , ' suffix to generated build directory ' ) ,
2013-05-12 03:16:24 +00:00
# This must be last so that default_builddir will
# have access to other properties.
2013-06-16 02:40:05 +00:00
( ' builddir ' , self . default_builddir , ' build in specified directory ' ) ,
2013-05-12 03:16:24 +00:00
) ,
} ,
)
2015-11-01 21:15:38 +00:00
_generic_variable = staticmethod ( _generic_variable )
2013-10-26 22:15:14 +00:00
@staticmethod
def _names ( name , prefix ) :
2014-08-05 02:41:42 +00:00
return [ ' %s %s %s ' % ( p , ' _ ' if p else ' ' , name ) for p in prefix ]
2013-07-04 03:29:04 +00:00
def __init__ ( self , program = None ) :
self . _program = program
2016-08-20 22:28:43 +00:00
def register_variables ( self , prefix , variables , filtered_help ) :
2013-10-26 18:13:28 +00:00
self . known_variables = [ ]
2015-11-01 21:15:39 +00:00
append_known_variable = self . known_variables . append
add_variable = variables . Add
2013-06-16 02:40:05 +00:00
for grp in self . _options ( ) :
variable = grp [ ' variable ' ]
2013-11-14 23:01:25 +00:00
stack = grp . get ( ' stack ' , None )
2013-06-16 02:40:05 +00:00
for opt in grp [ ' arguments ' ] :
( name , value , help ) = opt [ 0 : 3 ]
kwargs = opt [ 3 ] if len ( opt ) > 3 else { }
2014-08-05 02:41:42 +00:00
if name not in variables . keys ( ) :
2015-09-26 21:17:13 +00:00
if help is not None :
filtered_help . visible_arguments . append ( name )
2015-11-01 21:15:39 +00:00
add_variable ( variable ( key = name , help = help , default = None if callable ( value ) else value , * * kwargs ) )
2013-10-26 22:15:14 +00:00
names = self . _names ( name , prefix )
for n in names :
2013-07-04 03:29:04 +00:00
if n not in variables . keys ( ) :
2015-11-01 21:15:39 +00:00
add_variable ( variable ( key = n , help = help , default = None , * * kwargs ) )
2014-08-05 02:41:42 +00:00
if not name in names :
names . append ( name )
2015-11-01 21:15:39 +00:00
append_known_variable ( ( names , name , value , stack ) )
2013-11-14 23:01:25 +00:00
if stack :
2014-08-05 02:41:42 +00:00
for n in names :
2015-11-01 21:15:39 +00:00
add_variable ( self . _generic_variable ( key = ' %s _stop ' % n , help = None , default = None ) )
2013-10-26 18:13:28 +00:00
def read_variables ( self , variables , d ) :
2014-08-05 02:41:42 +00:00
for ( namelist , cname , dvalue , stack ) in self . known_variables :
2013-11-14 23:01:25 +00:00
value = None
found_value = False
2013-10-26 18:13:28 +00:00
for n in namelist :
try :
2013-11-14 23:01:25 +00:00
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
2015-09-19 23:04:35 +00:00
if d . get ( ' %s _stop ' % n , None ) :
2013-11-14 23:01:25 +00:00
break
continue
value = v
2013-10-26 18:13:28 +00:00
break
except KeyError as e :
pass
2013-11-14 23:01:25 +00:00
if not found_value :
value = dvalue
2013-10-26 18:13:28 +00:00
if callable ( value ) :
value = value ( )
2014-08-05 02:41:42 +00:00
setattr ( self , cname , value )
2013-03-16 18:24:52 +00:00
if self . builddir != ' ' and self . builddir [ - 1 : ] != ' / ' :
self . builddir + = ' / '
2013-05-12 03:16:24 +00:00
def clone ( self ) :
clone = DXXCommon . UserBuildSettings ( None )
2013-06-16 02:40:05 +00:00
for grp in clone . _options ( ) :
2013-05-12 03:16:24 +00:00
for o in grp [ ' arguments ' ] :
name = o [ 0 ]
value = getattr ( self , name )
setattr ( clone , name , value )
return clone
2013-05-11 23:52:32 +00:00
class UserInstallSettings :
2013-06-16 02:40:05 +00:00
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 ' ) ,
) ,
} ,
)
2013-05-11 23:52:32 +00:00
class UserSettings ( UserBuildSettings , UserInstallSettings ) :
2013-06-16 02:40:05 +00:00
def _options ( self ) :
return DXXCommon . UserBuildSettings . _options ( self ) + DXXCommon . UserInstallSettings . _options ( self )
2013-02-24 23:33:20 +00:00
# Base class for platform-specific settings processing
class _PlatformSettings :
2016-01-21 03:57:38 +00:00
tools = ( ' g++ ' , ' gnulink ' )
2013-03-16 23:15:01 +00:00
ogllibs = ' '
2016-09-14 01:59:32 +00:00
platform_objects = ( )
2013-06-16 17:01:47 +00:00
def __init__ ( self , program , user_settings ) :
self . __program = program
self . user_settings = user_settings
@property
def env ( self ) :
return self . __program . env
2013-02-24 23:33:20 +00:00
# Settings to apply to mingw32 builds
class Win32PlatformSettings ( _PlatformSettings ) :
2015-03-25 01:59:21 +00:00
ogllibs = ( ' opengl32 ' , )
2016-01-21 03:57:38 +00:00
tools = ( ' mingw ' , )
2013-03-02 21:25:47 +00:00
def adjust_environment ( self , program , env ) :
2016-08-14 23:47:27 +00:00
env . Append (
CPPDEFINES = [ ' _WIN32 ' , ' WIN32_LEAN_AND_MEAN ' ] ,
LINKFLAGS = [ ' -mwindows ' ] ,
)
2013-02-24 23:33:20 +00:00
class DarwinPlatformSettings ( _PlatformSettings ) :
2016-02-23 04:18:13 +00:00
# Darwin targets include Objective-C (not Objective-C++) code to
# access Apple-specific functionality. Add 'gcc' to the target
# list to support this.
#
# Darwin targets need a special linker, because OS X uses
# frameworks instead of standard libraries. Using `gnulink`
# omits framework-related arguments, causing the linker to skip
# including required libraries. SCons's `applelink` target
# understands these quirks and ensures that framework-related
# arguments are included.
tools = ( ' gcc ' , ' g++ ' , ' applelink ' )
2013-03-02 21:25:47 +00:00
def adjust_environment ( self , program , env ) :
2015-10-29 03:01:42 +00:00
library_frameworks = os . path . join ( os . getenv ( " HOME " ) , ' Library/Frameworks ' )
env . Append (
2016-08-19 03:41:40 +00:00
CPPDEFINES = [ ' __unix__ ' ] ,
2015-10-29 03:01:42 +00:00
CPPPATH = [ os . path . join ( library_frameworks , ' SDL.framework/Headers ' ) , ' /Library/Frameworks/SDL.framework/Headers ' ] ,
FRAMEWORKS = [ ' ApplicationServices ' , ' Cocoa ' , ' SDL ' ] ,
FRAMEWORKPATH = [ library_frameworks , ' /System/Library/Frameworks/ApplicationServices.framework/Versions/A/Frameworks ' ] ,
2016-09-20 07:18:50 +00:00
LINKFLAGS = [ ' -Wl,-rpath,@loader_path/../Frameworks ' ] , # Allow libraries & frameworks to go in app bundle
2015-10-29 03:01:42 +00:00
)
if self . user_settings . opengl or self . user_settings . opengles :
2013-04-20 23:56:13 +00:00
env . Append ( FRAMEWORKS = [ ' OpenGL ' ] )
2013-02-24 23:33:20 +00:00
# Settings to apply to Linux builds
class LinuxPlatformSettings ( _PlatformSettings ) :
2015-10-27 03:04:49 +00:00
@property
def ogllibs ( self ) :
user_settings = self . user_settings
return ( user_settings . opengles_lib , ' EGL ' ) if user_settings . opengles else ( ' GL ' , ' GLU ' )
2016-08-17 04:44:22 +00:00
@staticmethod
def get_platform_objects ( _empty = ( ) ) :
return _empty
2013-03-02 21:25:47 +00:00
def adjust_environment ( self , program , env ) :
2015-10-29 03:01:42 +00:00
env . Append (
CXXFLAGS = [ ' -pthread ' ] ,
)
2013-02-24 20:35:12 +00:00
2015-10-29 03:01:42 +00:00
def __init__ ( self , __program_instance = itertools . count ( 1 ) ) :
self . program_instance = next ( __program_instance )
2013-10-25 23:58:05 +00:00
2015-11-17 03:31:21 +00:00
def create_header_targets ( self , __shared_header_file_list = [ ] , __shared_cpp_dict = { } ) :
2014-11-17 04:02:25 +00:00
fs = SCons . Node . FS . get_default_fs ( )
builddir = self . user_settings . builddir
2015-10-13 02:43:24 +00:00
env = self . env
2015-11-17 03:31:21 +00:00
try :
check_header_includes = __shared_cpp_dict [ builddir ]
except KeyError :
check_header_includes = env . File ( os . path . join ( builddir , ' check_header_includes.cpp ' ) )
2015-10-13 02:43:24 +00:00
# Generate the list once, on first use. Any other targets
# will reuse it.
#
# Touch the file into existence. It is always empty, but
# must exist and have an extension of '.cpp'.
2015-11-17 03:31:21 +00:00
check_header_includes = env . Textfile ( target = check_header_includes , source = env . Value ( '''
2015-10-13 02:43:24 +00:00
/ * This file is always empty . It is only present to act as the source
* file for SCons targets that test individual headers .
* /
''' ))
2015-11-17 03:31:21 +00:00
__shared_cpp_dict [ builddir ] = check_header_includes
2016-08-12 04:08:57 +00:00
# This is ugly, but all other known methods are worse. The
# object file needs to depend on the header that created it
# and recursively depend on headers that contributed to the
# header that created it. Adding a dependency using
# env.Depend creates the first-level dependency, but does
# not recurse into the header. Overriding
# get_found_includes will cause SCons to recurse in the
# desired way.
#
# Calling get_found_includes requires parameters that are
# not easily obtained here. There does not appear to be a
# way to patch the node's dependency list to insert the
# header other than to override get_found_includes.
#
# Newer SCons applied a performance optimization based on
# using Python __slots__. Using __slots__ makes it a
# runtime error to overwrite get_found_includes on the
# instance, so overwrite the class method instead. However,
# some instances of the class may not want this hook, so
# keep a whitelist of nodes for which the hook is active.
c = check_header_includes [ 0 ] . __class__
# If the attribute is missing, this is the first time that
# an env.Textfile has been created for this purpose. Create
# the attribute so that subsequent passes do not add extra
# copies of the same hook.
#
# Since a flag attribute is needed anyway, make it useful by
# storing the hook whitelist in it.
if not hasattr ( c , ' _dxx_node_header_target_set ' ) :
c . _dxx_node_header_target_set = set ( )
def __get_found_includes ( node , env , scanner , path ) :
# Always call the original and always return at
# least what it returned. If this node is in the
# whitelist, then also apply the hook logic.
# Otherwise, this is a pass-through.
r = node . __get_found_includes ( env , scanner , path )
return ( r + [ fs . File ( env [ ' DXX_EFFECTIVE_SOURCE ' ] ) ] ) \
if node in node . _dxx_node_header_target_set else r
c . __get_found_includes = c . get_found_includes
c . get_found_includes = __get_found_includes
# Update the whitelist here, outside the hasattr block.
# This is necessary so that the hook is created once, but
# every env.Textfile created by this block is whitelisted.
c . _dxx_node_header_target_set . add ( check_header_includes [ 0 ] )
2015-11-17 03:31:21 +00:00
if not __shared_header_file_list :
2015-09-26 21:17:13 +00:00
headers = Git . pcall ( [ ' ls-files ' , ' -z ' , ' -- ' , ' *.h ' ] ) . out
2015-10-13 02:43:25 +00:00
if not headers :
g = Git . pcall ( [ ' --version ' ] , stderr = subprocess . STDOUT )
2016-08-12 04:08:57 +00:00
raise SCons . Errors . StopError (
" `git ls-files` failed. `git --version` failed. Check that Git is installed. " \
if g . returncode else \
" `git ls-files` failed, but `git --version` works. Check that scons is run from a Git repository. "
)
2015-10-13 02:43:24 +00:00
# Filter out OS X related directories. Files in those
# directories assume they are only ever built on OS X, so
# they unconditionally include headers specific to OS X.
2014-11-17 04:02:25 +00:00
excluded_directories = (
' common/arch/cocoa/ ' ,
' common/arch/carbon/ ' ,
)
2015-10-13 02:43:25 +00:00
__shared_header_file_list . extend ( [ h for h in headers . split ( ' \0 ' ) if h and not h . startswith ( excluded_directories ) ] )
if not __shared_header_file_list :
raise SCons . Errors . StopError ( " `git ls-files` found headers, but none can be checked. " )
2014-11-17 04:02:25 +00:00
dirname = os . path . join ( builddir , self . srcdir )
2015-10-13 02:43:24 +00:00
Depends = env . Depends
StaticObject = env . StaticObject
CPPFLAGS_template = env [ ' CPPFLAGS ' ]
2015-11-24 04:05:35 +00:00
CPPFLAGS_no_sconf = CPPFLAGS_template + [ ' -g0 ' , ' -include ' , ' $DXX_EFFECTIVE_SOURCE ' ]
CPPFLAGS_with_sconf = [ ' -include ' , ' dxxsconf.h ' ] + CPPFLAGS_no_sconf
CXXCOMSTR = env . __header_check_output_COMSTR
CXXFLAGS = env [ ' CXXFLAGS ' ]
target = os . path . join ( ' %s /chi/$ {DXX_EFFECTIVE_SOURCE} %s ' % ( dirname , env [ ' OBJSUFFIX ' ] ) )
2015-10-13 02:43:25 +00:00
for name in __shared_header_file_list :
2014-11-17 04:02:25 +00:00
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
2015-10-13 02:43:24 +00:00
# Compiler feature headers cannot include dxxsconf.h because
# it confuses the dependency resolver when SConf runs.
# Calling code must include dxxsconf.h before including the
# compiler feature header, so add the inclusion here.
#
# For best test coverage, only headers that must avoid
# including dxxsconf.h receive an implicit include. Any
# header which needs dxxsconf.h and can include it without
# side effects must do so.
2015-11-24 04:05:35 +00:00
StaticObject ( target = target ,
CPPFLAGS = CPPFLAGS_with_sconf if name [ : 24 ] == ' common/include/compiler- ' else CPPFLAGS_no_sconf ,
CXXCOMSTR = CXXCOMSTR ,
CXXFLAGS = CXXFLAGS ,
DXX_EFFECTIVE_SOURCE = name ,
source = check_header_includes )
2014-11-17 04:02:25 +00:00
2015-11-01 21:15:39 +00:00
def _cpp_output_StaticObject ( self , target = None , source = None , DXX_EFFECTIVE_SOURCE = ' $SOURCE ' , * args , * * kwargs ) :
2015-11-24 04:05:35 +00:00
CPPFLAGS = kwargs . get ( ' CPPFLAGS ' , None )
2015-10-18 21:01:18 +00:00
CXXFLAGS = kwargs . get ( ' CXXFLAGS ' , None )
env = self . env
OBJSUFFIX = env [ ' OBJSUFFIX ' ]
StaticObject = env . __cpp_output_StaticObject
StaticObject (
target = ' %s .i ' % ( target [ : - len ( OBJSUFFIX ) ] if target . endswith ( OBJSUFFIX ) else target ) ,
source = source , OBJSUFFIX = ' .i ' ,
# Bypass ccache
CXXCOM = env . _dxx_cxxcom_no_prefix ,
2015-11-24 04:05:35 +00:00
CPPFLAGS = ( env [ ' CPPFLAGS ' ] if CPPFLAGS is None else CPPFLAGS ) ,
2015-10-18 21:01:18 +00:00
CXXFLAGS = ( env [ ' CXXFLAGS ' ] if CXXFLAGS is None else CXXFLAGS ) + [ ' -E ' ] ,
CXXCOMSTR = env . __generate_cpp_output_COMSTR ,
2015-11-01 21:15:39 +00:00
DXX_EFFECTIVE_SOURCE = DXX_EFFECTIVE_SOURCE ,
2015-10-18 21:01:18 +00:00
)
2015-11-24 04:05:35 +00:00
return StaticObject ( target = target , source = source , DXX_EFFECTIVE_SOURCE = DXX_EFFECTIVE_SOURCE , * args , * * kwargs )
2015-10-18 21:01:18 +00:00
2015-11-01 21:15:39 +00:00
def create_special_target_nodes ( self , archive ) :
2015-10-18 21:01:18 +00:00
user_settings = self . user_settings
2015-10-18 21:01:18 +00:00
if user_settings . register_cpp_output_targets :
env = self . env
env . __cpp_output_StaticObject = env . StaticObject
env . StaticObject = self . _cpp_output_StaticObject
2015-11-01 21:15:39 +00:00
if user_settings . check_header_includes :
# Create header targets before creating the PCHManager, so that
# create_header_targets() does not call the PCHManager
# StaticObject hook.
self . create_header_targets ( )
2015-09-13 20:23:05 +00:00
configure_pch_flags = archive . configure_pch_flags
2015-10-18 21:01:18 +00:00
if configure_pch_flags :
self . pch_manager = PCHManager ( self . user_settings , self . env , self . srcdir , configure_pch_flags , archive . pch_manager )
2013-10-25 23:58:05 +00:00
2015-09-19 23:04:35 +00:00
@staticmethod
2016-10-21 02:16:47 +00:00
def _quote_cppdefine ( s , f = repr , b2a_hex = binascii . b2a_hex ) :
2013-11-16 22:59:17 +00:00
r = ' '
prior = False
2015-04-16 01:48:15 +00:00
for c in f ( s ) :
2013-11-16 22:59:17 +00:00
# No xdigit support in str
2014-09-02 22:09:44 +00:00
if c in ' ()*+,-./:=[]_ ' or ( c . isalnum ( ) and not ( prior and ( c . isdigit ( ) or c in ' abcdefABCDEF ' ) ) ) :
2013-11-16 22:59:17 +00:00
r + = c
2014-09-02 22:09:44 +00:00
elif c == ' \n ' :
r + = r ' \ n '
2013-11-16 22:59:17 +00:00
else :
2015-09-19 23:04:35 +00:00
r + = r ' \\ x %s ' % b2a_hex ( c )
2013-11-16 22:59:17 +00:00
prior = True
2014-09-02 22:09:44 +00:00
continue
prior = False
2015-09-19 23:04:35 +00:00
return ' \\ " %s \\ " ' % r
2013-11-16 22:59:17 +00:00
2016-10-21 02:16:47 +00:00
@staticmethod
2016-10-29 23:16:19 +00:00
def _encode_cppdefine_for_identifier ( user_settings , s , b2a_base64 = binascii . b2a_base64 ) :
extended_identifiers = user_settings . _dxx_extended_identifiers
return ' " %s " ' % b2a_base64 ( s ) \
. rstrip ( ) \
. replace ( ' + ' , extended_identifiers [ 0 ] ) \
. replace ( ' / ' , extended_identifiers [ 1 ] ) \
. replace ( ' = ' , extended_identifiers [ 2 ] )
2016-10-21 02:16:47 +00:00
2013-02-25 00:02:01 +00:00
def prepare_environment ( self ) :
# Prettier build messages......
2015-01-11 05:08:30 +00:00
# Move target to end of C++ source command
2014-09-02 22:09:07 +00:00
target_string = ' -o $TARGET '
2015-09-19 23:04:36 +00:00
env = self . env
2015-12-03 03:26:49 +00:00
# Get traditional compiler environment variables
for cc in ( ' CXX ' , ' RC ' ) :
value = getattr ( self . user_settings , cc )
if value is not None :
env [ cc ] = value
# Expand $CXX immediately.
# $CCFLAGS is never used. Remove it.
cxxcom = env [ ' CXXCOM ' ] \
. replace ( ' $CXX ' , ' %s ' % env [ ' CXX ' ] ) \
. replace ( ' $CCFLAGS ' , ' ' )
2014-09-02 22:09:07 +00:00
if target_string + ' ' in cxxcom :
2015-09-19 23:04:35 +00:00
cxxcom = ' %s %s ' % ( cxxcom . replace ( target_string , ' ' ) , target_string )
2015-09-19 23:04:36 +00:00
env . _dxx_cxxcom_no_prefix = cxxcom
2015-10-03 17:17:49 +00:00
distcc_path = self . user_settings . distcc
distcc_cxxcom = ( ' %s %s ' % ( distcc_path , cxxcom ) ) if distcc_path else cxxcom
env . _dxx_cxxcom_no_ccache_prefix = distcc_cxxcom
ccache_path = self . user_settings . ccache
2015-01-11 05:08:30 +00:00
# Add ccache/distcc only for compile, not link
2015-10-03 17:17:49 +00:00
if ccache_path :
cxxcom = ' %s %s ' % ( ccache_path , cxxcom )
2015-09-19 23:04:35 +00:00
if distcc_path is not None :
penv = self . env [ ' ENV ' ]
if distcc_path :
penv [ ' CCACHE_PREFIX ' ] = distcc_path
elif distcc_path is not None :
penv . pop ( ' CCACHE_PREFIX ' , None )
2015-10-03 17:17:49 +00:00
elif distcc_path :
cxxcom = distcc_cxxcom
2015-12-03 03:26:49 +00:00
# Expand $LINK immediately.
linkcom = env [ ' LINKCOM ' ] . replace ( ' $LINK ' , ' %s ' % env [ ' LINK ' ] )
2015-01-11 05:08:30 +00:00
# Move target to end of link command
if target_string + ' ' in linkcom :
2015-09-19 23:04:35 +00:00
linkcom = ' %s %s ' % ( linkcom . replace ( target_string , ' ' ) , target_string )
2015-01-11 05:08:30 +00:00
# Add $CXXFLAGS to link command
cxxflags = ' $CXXFLAGS '
if ' ' + cxxflags not in linkcom :
linkflags = ' $LINKFLAGS '
linkcom = linkcom . replace ( linkflags , cxxflags + linkflags )
2015-11-01 21:15:38 +00:00
env . Replace (
CXXCOM = cxxcom ,
LINKCOM = linkcom ,
)
2015-01-11 05:08:30 +00:00
# Custom DISTCC_HOSTS per target
distcc_hosts = self . user_settings . distcc_hosts
if distcc_hosts is not None :
2015-11-01 21:15:38 +00:00
env [ ' ENV ' ] [ ' DISTCC_HOSTS ' ] = distcc_hosts
2015-10-18 21:01:18 +00:00
if self . user_settings . verbosebuild :
2015-11-24 04:05:35 +00:00
env . __header_check_output_COMSTR = None
2015-11-01 21:15:38 +00:00
env . __generate_cpp_output_COMSTR = None
2015-10-18 21:01:18 +00:00
else :
target = self . target [ : 3 ]
format_tuple = ( target , self . user_settings . builddir or ' . ' )
2015-11-24 04:05:35 +00:00
env . __header_check_output_COMSTR = " CHK %s %s $DXX_EFFECTIVE_SOURCE " % format_tuple
2015-11-01 21:15:39 +00:00
env . __generate_cpp_output_COMSTR = " CPP %s %s $DXX_EFFECTIVE_SOURCE " % format_tuple
2015-11-01 21:15:38 +00:00
env . Replace (
CXXCOMSTR = " CXX %s %s $SOURCE " % format_tuple ,
2015-10-18 21:01:18 +00:00
# `builddir` is implicit since $TARGET is the full path to
# the output
2015-11-01 21:15:38 +00:00
LINKCOMSTR = " LD %s $TARGET " % target ,
2016-07-16 16:52:04 +00:00
RCCOMSTR = " RC %s %s $SOURCE " % format_tuple ,
2015-11-01 21:15:38 +00:00
)
2013-02-25 00:02:01 +00:00
2014-09-17 02:09:53 +00:00
Werror = get_Werror_string ( self . user_settings . CXXFLAGS )
2015-11-01 21:15:38 +00:00
env . Prepend ( CXXFLAGS = [
2016-03-02 02:52:44 +00:00
' -ftabstop=4 ' ,
2015-06-13 22:42:21 +00:00
' -Wall ' ,
2016-05-22 17:50:13 +00:00
' -Wshadow ' ,
2015-06-13 22:42:21 +00:00
Werror + ' extra ' ,
2016-01-03 20:21:35 +00:00
Werror + ' format=2 ' ,
2015-06-13 22:42:21 +00:00
Werror + ' missing-braces ' ,
Werror + ' missing-include-dirs ' ,
2016-01-03 20:21:35 +00:00
Werror + ' uninitialized ' ,
2015-06-13 22:42:21 +00:00
Werror + ' undef ' ,
Werror + ' pointer-arith ' ,
2015-06-13 22:42:21 +00:00
Werror + ' cast-qual ' ,
2015-06-13 22:42:21 +00:00
Werror + ' missing-declarations ' ,
Werror + ' redundant-decls ' ,
Werror + ' vla ' ,
] )
2015-10-29 03:01:42 +00:00
env . Append (
CXXFLAGS = [ ' -funsigned-char ' ] ,
CPPPATH = [ ' common/include ' , ' common/main ' , ' . ' , self . user_settings . builddir ] ,
CPPFLAGS = SCons . Util . CLVar ( ' -Wno-sign-compare ' ) ,
)
2015-12-03 03:26:49 +00:00
add_flags = { }
2015-10-29 03:01:42 +00:00
if self . user_settings . editor :
2015-12-03 03:26:49 +00:00
add_flags [ ' CPPPATH ' ] = [ ' common/include/editor ' ]
CLVar = SCons . Util . CLVar
for flags in ( ' CPPFLAGS ' , ' CXXFLAGS ' , ' LIBS ' ) :
2013-05-18 02:15:01 +00:00
value = getattr ( self . user_settings , flags )
if value is not None :
2015-12-03 03:26:49 +00:00
add_flags [ flags ] = CLVar ( value )
2013-06-01 23:46:05 +00:00
if self . user_settings . LDFLAGS :
2015-12-03 03:26:49 +00:00
add_flags [ ' LINKFLAGS ' ] = CLVar ( self . user_settings . LDFLAGS )
env . Append ( * * add_flags )
2014-06-29 23:26:05 +00:00
if self . user_settings . lto :
2015-10-29 03:01:42 +00:00
env . Append ( CXXFLAGS = [
2015-07-18 21:01:56 +00:00
# clang does not support =N syntax
( ' -flto= %s ' % self . user_settings . lto ) if self . user_settings . lto > 1 else ' -flto ' ,
2015-10-29 03:01:42 +00:00
] )
2013-02-25 00:02:01 +00:00
2015-11-01 21:15:39 +00:00
@cached_property
def platform_settings ( self ) :
2013-02-25 00:02:01 +00:00
# windows or *nix?
2013-12-05 23:01:34 +00:00
platform_name = self . user_settings . host_platform
2015-11-01 21:15:39 +00:00
message ( self , " compiling on %s for %s into %s %s " % ( sys . platform , platform_name , self . user_settings . builddir or ' . ' ,
2015-12-04 03:36:33 +00:00
( ' with prefix list %s ' % str ( self . _argument_prefix_list ) ) if self . _argument_prefix_list else ' ' ) )
2015-11-01 21:15:39 +00:00
return (
self . Win32PlatformSettings if platform_name == ' win32 ' else (
self . DarwinPlatformSettings if platform_name == ' darwin ' else
self . LinuxPlatformSettings
)
) ( self , self . user_settings )
@cached_property
def env ( self ) :
platform_settings = self . platform_settings
2013-04-08 06:35:48 +00:00
# Acquire environment object...
2016-01-21 03:57:38 +00:00
env = Environment ( ENV = os . environ , tools = platform_settings . tools + ( ' textfile ' , ) )
2015-11-01 21:15:39 +00:00
platform_settings . adjust_environment ( self , env )
return env
2013-02-25 00:02:01 +00:00
def process_user_settings ( self ) :
env = self . env
2015-12-03 03:26:49 +00:00
user_settings = self . user_settings
2013-02-25 00:02:01 +00:00
2015-04-17 02:38:12 +00:00
env . Prepend ( CXXFLAGS = [ ' -g ' , ' -O2 ' ] )
2013-05-03 22:52:57 +00:00
# Raspberry Pi?
2015-12-03 03:26:49 +00:00
if user_settings . raspberrypi :
rpi_vc_path = user_settings . rpi_vc_path
2015-09-19 23:04:35 +00:00
message ( self , " Raspberry Pi: using VideoCore libs in %r " % rpi_vc_path )
2015-10-29 03:01:42 +00:00
env . Append (
2016-10-09 17:35:03 +00:00
CPPDEFINES = [ ' RPI ' ] ,
2015-03-20 23:56:18 +00:00
# use CPPFLAGS -isystem instead of CPPPATH because these those header files
2015-03-20 20:34:43 +00:00
# are not very clean and would trigger some warnings we usually consider as
# errors. Using them as system headers will make gcc ignoring any warnings.
2015-10-29 03:01:42 +00:00
CPPFLAGS = [
2015-09-19 23:04:35 +00:00
' -isystem= %s /include ' % rpi_vc_path ,
' -isystem= %s /include/interface/vcos/pthreads ' % rpi_vc_path ,
' -isystem= %s /include/interface/vmcs_host/linux ' % rpi_vc_path ,
2015-10-29 03:01:42 +00:00
] ,
LIBPATH = ' %s /lib ' % rpi_vc_path ,
LIBS = [ ' bcm_host ' ] ,
)
2013-05-03 22:52:57 +00:00
2013-03-03 01:03:33 +00:00
class DXXArchive ( DXXCommon ) :
2015-10-29 03:01:42 +00:00
PROGRAM_NAME = ' DXX-Archive '
_argument_prefix_list = None
2013-03-03 01:03:33 +00:00
srcdir = ' common '
target = ' dxx-common '
2016-08-19 03:41:40 +00:00
def get_objects_common ( self ,
__get_objects_common = DXXCommon . create_lazy_object_getter ( [ os . path . join ( srcdir , f ) for f in [
2012-11-11 22:12:51 +00:00
' 2d/2dsline.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/bitblt.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/bitmap.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/box.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/canvas.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/circle.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/disc.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/gpixel.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/line.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/pixel.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/rect.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/rle.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/scalec.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/draw.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/globvars.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/instance.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/matrix.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/points.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/rod.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/setup.cpp ' ,
2015-07-18 21:01:56 +00:00
' arch/sdl/event.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/sdl/joy.cpp ' ,
2015-07-18 21:01:56 +00:00
' arch/sdl/key.cpp ' ,
2015-07-18 21:01:56 +00:00
' arch/sdl/mouse.cpp ' ,
2015-07-04 21:01:17 +00:00
' arch/sdl/timer.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/sdl/window.cpp ' ,
2015-06-07 16:20:46 +00:00
' main/cli.cpp ' ,
' main/cmd.cpp ' ,
' main/cvar.cpp ' ,
2012-11-11 22:12:51 +00:00
' maths/fixc.cpp ' ,
2012-11-11 22:12:51 +00:00
' maths/rand.cpp ' ,
2012-11-11 22:12:51 +00:00
' maths/tables.cpp ' ,
2012-11-11 22:12:51 +00:00
' maths/vecmat.cpp ' ,
2015-07-18 21:01:56 +00:00
' mem/mem.cpp ' ,
2012-11-11 22:12:51 +00:00
' misc/error.cpp ' ,
2015-07-18 21:01:56 +00:00
' misc/hash.cpp ' ,
2012-11-11 22:12:51 +00:00
' misc/hmp.cpp ' ,
2012-11-11 22:12:51 +00:00
' misc/ignorecase.cpp ' ,
2012-11-11 22:12:51 +00:00
' misc/strutil.cpp ' ,
2017-01-01 23:19:21 +00:00
' misc/vgrphys.cpp ' ,
' misc/vgwphys.cpp ' ,
2012-11-11 22:12:51 +00:00
' texmap/ntmap.cpp ' ,
2012-11-11 22:12:51 +00:00
' texmap/scanline.cpp '
2013-03-03 01:03:33 +00:00
]
2016-08-19 03:41:40 +00:00
] ) , \
__get_objects_use_sdl1 = DXXCommon . create_lazy_object_getter ( [ os . path . join ( srcdir , f ) for f in [
2015-07-14 02:42:12 +00:00
' arch/sdl/rbaudio.cpp ' ,
]
2013-03-24 23:03:46 +00:00
] )
2016-08-19 03:41:40 +00:00
) :
value = list ( __get_objects_common ( self ) )
extend = value . extend
if not self . user_settings . sdl2 :
extend ( __get_objects_use_sdl1 ( self ) )
extend ( self . platform_settings . get_platform_objects ( ) )
return value
2016-08-17 04:44:22 +00:00
get_objects_editor = DXXCommon . create_lazy_object_getter ( [ os . path . join ( srcdir , f ) for f in [
2015-07-25 23:10:47 +00:00
' editor/autosave.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/func.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/button.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/checkbox.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/dialog.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/file.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/gadget.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/icon.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/inputbox.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/keypad.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/keypress.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/listbox.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/menu.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/menubar.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/message.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/radio.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/scroll.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/ui.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/uidraw.cpp ' ,
2012-11-17 06:14:09 +00:00
' ui/userbox.cpp '
2013-03-03 01:03:33 +00:00
]
2013-03-24 23:03:46 +00:00
] )
2013-03-03 01:03:33 +00:00
# for non-ogl
2016-08-17 04:44:22 +00:00
get_objects_arch_sdl = DXXCommon . create_lazy_object_getter ( [ os . path . join ( srcdir , f ) for f in [
2014-11-16 19:14:51 +00:00
' 3d/clipper.cpp ' ,
2012-11-11 22:12:51 +00:00
' texmap/tmapflat.cpp '
2013-03-03 01:03:33 +00:00
]
2015-03-22 19:32:14 +00:00
] )
# for ogl
2016-08-17 04:44:22 +00:00
get_objects_arch_ogl = DXXCommon . create_lazy_object_getter ( [ os . path . join ( srcdir , f ) for f in [
2015-03-22 20:19:06 +00:00
' arch/ogl/ogl_extensions.cpp ' ,
' arch/ogl/ogl_sync.cpp '
2015-03-22 19:32:14 +00:00
]
2013-03-24 23:03:46 +00:00
] )
2016-08-17 04:44:22 +00:00
get_objects_arch_sdlmixer = DXXCommon . create_lazy_object_getter ( [ os . path . join ( srcdir , f ) for f in [
2012-11-11 22:12:51 +00:00
' arch/sdl/digi_mixer_music.cpp ' ,
2013-03-03 01:03:33 +00:00
]
2013-03-24 23:03:46 +00:00
] )
2015-11-07 21:56:00 +00:00
class Win32PlatformSettings ( DXXCommon . Win32PlatformSettings ) :
2016-08-17 04:44:22 +00:00
get_platform_objects = LazyObjectConstructor . create_lazy_object_getter ( [
2012-11-11 22:12:51 +00:00
' common/arch/win32/messagebox.cpp '
2013-05-05 04:01:03 +00:00
] )
2015-11-07 21:56:00 +00:00
class DarwinPlatformSettings ( DXXCommon . DarwinPlatformSettings ) :
2016-08-17 04:44:22 +00:00
get_platform_objects = LazyObjectConstructor . create_lazy_object_getter ( [
2014-11-24 03:53:02 +00:00
' common/arch/cocoa/messagebox.mm ' ,
2014-11-23 12:25:42 +00:00
' common/arch/cocoa/SDLMain.m '
] )
2013-05-12 03:16:24 +00:00
def __init__ ( self , user_settings ) :
2013-03-03 01:03:33 +00:00
DXXCommon . __init__ ( self )
2013-05-12 03:16:24 +00:00
self . user_settings = user_settings . clone ( )
2015-10-18 21:01:18 +00:00
if not user_settings . register_compile_target :
return
2013-03-03 01:03:33 +00:00
self . prepare_environment ( )
self . process_user_settings ( )
2013-07-27 22:16:14 +00:00
self . configure_environment ( )
2015-11-01 21:15:39 +00:00
self . create_special_target_nodes ( self )
2016-10-02 00:34:50 +00:00
ToolchainInformation . show_partial_environ ( self . env , lambda s , _message = message , _self = self : _message ( self , s ) )
2013-07-27 22:16:14 +00:00
def configure_environment ( self ) :
fs = SCons . Node . FS . get_default_fs ( )
2016-11-26 22:51:47 +00:00
user_settings = self . user_settings
builddir = fs . Dir ( user_settings . builddir or ' . ' )
tests = ConfigureTests ( self . program_message_prefix , user_settings , self . platform_settings )
2013-07-27 22:16:14 +00:00
log_file = fs . File ( ' sconf.log ' , builddir )
2017-01-01 23:19:21 +00:00
env = self . env
conf = env . Configure ( custom_tests = {
2015-09-13 21:02:19 +00:00
k . name : getattr ( tests , k . name ) for k in tests . custom_tests
2013-07-27 22:16:14 +00:00
} ,
conf_dir = fs . Dir ( ' .sconf_temp ' , builddir ) ,
log_file = log_file ,
config_h = fs . File ( ' dxxsconf.h ' , builddir ) ,
clean = False ,
help = False
)
2013-11-28 04:21:20 +00:00
self . configure_added_environment_flags = tests . successful_flags
self . configure_pch_flags = None
2013-07-27 22:16:14 +00:00
if not conf . env :
return
2015-09-13 21:02:19 +00:00
cc_env_strings = tests . ForceVerboseLog ( conf . env )
2013-07-27 22:16:14 +00:00
try :
for k in tests . custom_tests :
2015-09-13 21:02:19 +00:00
getattr ( conf , k . name ) ( )
2013-07-27 22:16:14 +00:00
except SCons . Errors . StopError as e :
2015-09-19 23:04:35 +00:00
raise SCons . Errors . StopError ( ' {e0} See {log_file} for details. ' . format ( e0 = e . args [ 0 ] , log_file = log_file ) , * e . args [ 1 : ] )
2015-09-13 21:02:19 +00:00
cc_env_strings . restore ( conf . env )
2016-11-26 22:51:47 +00:00
if user_settings . pch :
conf . Define ( ' DXX_VERSION_SEQ ' , self . DXX_VERSION_SEQ )
if user_settings . record_sconf_results :
2015-09-19 23:04:35 +00:00
conf . config_h_text + = '''
/ *
% s
* /
''' % ' \n ' .join([ ' check_ %s = %s ' % (n,v) for n,v in tests._sconf_results])
2015-11-01 21:15:39 +00:00
conf . Finish ( )
2013-11-28 04:21:20 +00:00
self . configure_pch_flags = tests . pch_flags
2017-01-01 23:19:21 +00:00
add_flags = self . configure_added_environment_flags
CLVar = SCons . Util . CLVar
for flags in ( ' CPPFLAGS ' , ' CXXFLAGS ' , ' LINKFLAGS ' ) :
value = getattr ( user_settings , ' %s _unchecked ' % flags )
if value :
add_flags [ flags ] + = CLVar ( value )
env . MergeFlags ( self . configure_added_environment_flags )
2013-03-03 01:03:33 +00:00
2013-03-03 00:53:35 +00:00
class DXXProgram ( DXXCommon ) :
2013-03-16 21:30:13 +00:00
static_archive_construction = { }
2013-03-24 23:03:46 +00:00
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 ] ) )
2016-11-26 22:51:47 +00:00
def _apply_env_version_seq ( self , env , _empty = { } ) :
return _empty if self . user_settings . pch else { ' CPPDEFINES ' : env [ ' CPPDEFINES ' ] + [ ( ' DXX_VERSION_SEQ ' , self . DXX_VERSION_SEQ ) ] }
2016-08-17 04:44:22 +00:00
get_objects_similar_arch_ogl = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 19:49:59 +00:00
' source ' : [ os . path . join ( ' similar ' , f ) for f in [
2012-11-11 22:12:51 +00:00
' arch/ogl/gr.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/ogl/ogl.cpp ' ,
2013-03-03 01:03:33 +00:00
]
2013-04-21 19:49:59 +00:00
] ,
' transform_target ' : _apply_target_name ,
} ] )
2016-08-17 04:44:22 +00:00
get_objects_similar_arch_sdl = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 19:49:59 +00:00
' source ' : [ os . path . join ( ' similar ' , f ) for f in [
2012-11-11 22:12:51 +00:00
' arch/sdl/gr.cpp ' ,
2013-03-03 01:03:33 +00:00
]
2013-04-21 19:49:59 +00:00
] ,
' transform_target ' : _apply_target_name ,
} ] )
2016-08-17 04:44:22 +00:00
get_objects_similar_arch_sdlmixer = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 19:49:59 +00:00
' source ' : [ os . path . join ( ' similar ' , f ) for f in [
2012-11-11 22:12:51 +00:00
' arch/sdl/digi_mixer.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/sdl/jukebox.cpp '
2013-03-03 01:03:33 +00:00
]
2013-04-21 19:49:59 +00:00
] ,
' transform_target ' : _apply_target_name ,
} ] )
2016-08-17 04:44:22 +00:00
__get_objects_common = DXXCommon . create_lazy_object_getter ( [ {
2016-10-15 00:53:18 +00:00
' source ' : [ os . path . join ( ' similar ' , f ) for f in (
2012-11-11 22:12:51 +00:00
' 2d/font.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/palette.cpp ' ,
2012-11-11 22:12:51 +00:00
' 2d/pcx.cpp ' ,
2012-11-11 22:12:51 +00:00
' 3d/interp.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/sdl/digi.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/sdl/digi_audio.cpp ' ,
2012-11-11 22:12:51 +00:00
' arch/sdl/init.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/ai.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/aipath.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/automap.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/bm.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/cntrlcen.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/collide.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/config.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/console.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/controls.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/credits.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/digiobj.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/effects.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/endlevel.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/fireball.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/fuelcen.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/fvi.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/game.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gamecntl.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gamefont.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gamemine.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gamerend.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gamesave.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gameseg.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gameseq.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gauges.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/hostage.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/hud.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/iff.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/kconfig.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/kmatrix.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/laser.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/lighting.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/menu.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/mglobal.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/mission.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/morph.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/multi.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/multibot.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/newdemo.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/newmenu.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/object.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/paging.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/physics.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/piggy.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/player.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/polyobj.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/powerup.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/render.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/robot.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/scores.cpp ' ,
2014-10-04 17:53:09 +00:00
' main/segment.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/slew.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/songs.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/state.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/switch.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/terrain.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/texmerge.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/text.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/titles.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/vclip.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/wall.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/weapon.cpp ' ,
2012-11-11 22:12:51 +00:00
' misc/args.cpp ' ,
2016-10-15 00:53:18 +00:00
)
2013-04-21 19:49:59 +00:00
] ,
' transform_target ' : _apply_target_name ,
2016-10-15 00:53:18 +00:00
} , {
' source ' : (
' similar/main/inferno.cpp ' ,
2016-10-21 02:16:47 +00:00
) ,
2016-11-26 22:51:47 +00:00
' transform_env ' : lambda self , env : { ' CPPDEFINES ' : env [ ' CPPDEFINES ' ] + env . __dxx_CPPDEFINE_SHAREPATH + env . __dxx_CPPDEFINE_git_version } ,
2016-10-21 02:16:47 +00:00
' transform_target ' : _apply_target_name ,
} , {
' source ' : (
2016-10-15 00:53:18 +00:00
' similar/misc/physfsx.cpp ' ,
) ,
2016-11-26 22:51:47 +00:00
' transform_env ' : lambda self , env : { ' CPPDEFINES ' : env [ ' CPPDEFINES ' ] + env . __dxx_CPPDEFINE_SHAREPATH } ,
2016-10-15 00:53:18 +00:00
' transform_target ' : _apply_target_name ,
2016-10-15 00:53:19 +00:00
} , {
' source ' : (
' similar/main/playsave.cpp ' ,
) ,
' transform_env ' : _apply_env_version_seq ,
' transform_target ' : _apply_target_name ,
2016-10-15 00:53:18 +00:00
}
] )
2016-08-17 04:44:22 +00:00
get_objects_editor = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 19:49:59 +00:00
' source ' : [ os . path . join ( ' similar ' , f ) for f in [
2012-11-17 06:14:09 +00:00
' editor/centers.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/curves.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/dumpmine.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/eglobal.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/elight.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/eobject.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/eswitch.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/group.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/info.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/kbuild.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/kcurve.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/kfuncs.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/kgame.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/khelp.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/kmine.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/ksegmove.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/ksegsel.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/ksegsize.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/ktmap.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/kview.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/med.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/meddraw.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/medmisc.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/medrobot.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/medsel.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/medwall.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/mine.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/objpage.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/segment.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/seguvs.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/texpage.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/texture.cpp ' ,
2013-03-03 01:03:33 +00:00
]
2013-03-03 01:03:33 +00:00
] ,
' transform_target ' : _apply_target_name ,
} ] )
2013-03-03 00:53:35 +00:00
class UserSettings ( DXXCommon . UserSettings ) :
2013-07-04 03:29:02 +00:00
@property
def BIN_DIR ( self ) :
2013-03-03 00:53:35 +00:00
# installation path
2015-09-19 23:04:35 +00:00
return ' %s /bin ' % self . prefix
2013-03-03 00:53:35 +00:00
# Settings to apply to mingw32 builds
2015-01-03 23:44:32 +00:00
class Win32PlatformSettings ( DXXCommon . Win32PlatformSettings ) :
2013-06-16 17:01:47 +00:00
def __init__ ( self , program , user_settings ) :
DXXCommon . Win32PlatformSettings . __init__ ( self , program , user_settings )
2013-03-03 00:53:35 +00:00
user_settings . sharepath = ' '
def adjust_environment ( self , program , env ) :
DXXCommon . Win32PlatformSettings . adjust_environment ( self , program , env )
2013-06-02 21:46:32 +00:00
rcbasename = os . path . join ( program . srcdir , ' arch/win32/ %s ' % program . target )
2016-08-17 04:44:22 +00:00
self . platform_objects = [ ( env . RES ( target = ' %s %s %s ' % ( program . user_settings . builddir , rcbasename , env [ " OBJSUFFIX " ] ) , source = ' %s .rc ' % rcbasename ) ) ]
2017-01-11 03:03:19 +00:00
env . Prepend (
CXXFLAGS = [ ' -fno-omit-frame-pointer ' ] ,
)
2015-10-29 03:01:42 +00:00
env . Append (
CPPPATH = [ os . path . join ( program . srcdir , ' arch/win32/include ' ) ] ,
LIBS = [ ' glu32 ' , ' wsock32 ' , ' ws2_32 ' , ' winmm ' , ' mingw32 ' , ' SDLmain ' , ' SDL ' ] ,
)
2013-03-03 00:53:35 +00:00
# Settings to apply to Apple builds
2015-01-03 23:44:32 +00:00
class DarwinPlatformSettings ( DXXCommon . DarwinPlatformSettings ) :
2013-06-16 17:01:47 +00:00
def __init__ ( self , program , user_settings ) :
DXXCommon . DarwinPlatformSettings . __init__ ( self , program , user_settings )
2013-03-03 00:53:35 +00:00
user_settings . sharepath = ' '
def adjust_environment ( self , program , env ) :
DXXCommon . DarwinPlatformSettings . adjust_environment ( self , program , env )
2015-09-19 23:04:35 +00:00
VERSION = ' %s . %s ' % ( program . VERSION_MAJOR , program . VERSION_MINOR )
2013-05-05 23:20:06 +00:00
if ( program . VERSION_MICRO ) :
2015-09-19 23:04:35 +00:00
VERSION + = ' . %s ' % program . VERSION_MICRO
2015-11-01 21:15:38 +00:00
env . Replace (
VERSION_NUM = VERSION ,
VERSION_NAME = ' %s v %s ' % ( program . PROGRAM_NAME , VERSION ) ,
)
2013-03-03 00:53:35 +00:00
# Settings to apply to Linux builds
2015-01-03 23:44:32 +00:00
class LinuxPlatformSettings ( DXXCommon . LinuxPlatformSettings ) :
2013-06-16 17:01:47 +00:00
def __init__ ( self , program , user_settings ) :
DXXCommon . LinuxPlatformSettings . __init__ ( self , program , user_settings )
2015-01-03 23:44:32 +00:00
if user_settings . sharepath and user_settings . sharepath [ - 1 ] != ' / ' :
user_settings . sharepath + = ' / '
2013-03-03 00:53:35 +00:00
2016-08-19 03:41:40 +00:00
def get_objects_common ( self ,
__get_objects_common = __get_objects_common ,
__get_objects_use_udp = DXXCommon . create_lazy_object_getter ( [ {
2016-10-15 00:53:19 +00:00
' source ' : (
2016-08-19 03:41:40 +00:00
' similar/main/net_udp.cpp ' ,
2016-10-15 00:53:19 +00:00
) ,
' transform_env ' : _apply_env_version_seq ,
2016-08-19 03:41:40 +00:00
' transform_target ' : _apply_target_name ,
} ] )
) :
value = list ( __get_objects_common ( self ) )
2015-09-19 23:04:35 +00:00
extend = value . extend
if self . user_settings . use_udp :
2016-08-19 03:41:40 +00:00
extend ( __get_objects_use_udp ( self ) )
2015-09-19 23:04:35 +00:00
extend ( self . platform_settings . platform_objects )
return value
2016-08-20 22:28:43 +00:00
def __init__ ( self , prefix , variables , filtered_help ) :
2013-10-26 18:13:28 +00:00
self . variables = variables
2013-06-16 19:07:58 +00:00
self . _argument_prefix_list = prefix
2013-03-03 00:53:35 +00:00
DXXCommon . __init__ ( self )
2016-10-21 02:16:47 +00:00
git_describe_version = Git . compute_extra_version ( ) . describe
2016-06-01 01:52:45 +00:00
extra_version = ' v %s . %s . %s ' % ( self . VERSION_MAJOR , self . VERSION_MINOR , self . VERSION_MICRO )
if git_describe_version and not ( extra_version == git_describe_version or extra_version [ 1 : ] == git_describe_version ) :
extra_version + = ' ' + git_describe_version
print ( ' ===== %s %s ===== ' % ( self . PROGRAM_NAME , extra_version ) )
2016-01-26 03:45:07 +00:00
self . user_settings = user_settings = self . UserSettings ( program = self )
2016-08-20 22:28:43 +00:00
user_settings . register_variables ( prefix , variables , filtered_help )
2013-10-26 18:13:28 +00:00
def init ( self , substenv ) :
self . user_settings . read_variables ( self . variables , substenv )
2016-01-26 03:45:07 +00:00
archive = DXXProgram . static_archive_construction . get ( self . user_settings . builddir , None )
if archive is None :
DXXProgram . static_archive_construction [ self . user_settings . builddir ] = archive = DXXArchive ( self . user_settings )
2015-10-18 21:01:18 +00:00
if self . user_settings . register_compile_target :
2016-01-26 03:45:07 +00:00
self . prepare_environment ( archive )
2015-10-18 21:01:18 +00:00
self . process_user_settings ( )
2013-03-03 00:53:35 +00:00
self . register_program ( )
2016-08-20 22:28:43 +00:00
return self . variables . GenerateHelpText ( self . env )
2013-03-03 00:53:35 +00:00
2016-10-15 00:53:19 +00:00
def prepare_environment ( self , archive ) :
2013-03-03 00:53:35 +00:00
DXXCommon . prepare_environment ( self )
2015-11-01 21:15:38 +00:00
env = self . env
env . MergeFlags ( archive . configure_added_environment_flags )
2015-11-01 21:15:39 +00:00
self . create_special_target_nodes ( archive )
2016-10-15 00:53:18 +00:00
env . __dxx_CPPDEFINE_SHAREPATH = [ ( ' SHAREPATH ' , self . _quote_cppdefine ( self . user_settings . sharepath , f = str ) ) ]
2015-11-01 21:15:38 +00:00
env . Append (
2015-10-29 03:01:42 +00:00
CPPDEFINES = [
2016-01-26 03:45:07 +00:00
self . env_CPPDEFINES ,
2013-11-28 23:23:18 +00:00
# For PRIi64
2015-10-29 03:01:42 +00:00
( ' __STDC_FORMAT_MACROS ' , ) ,
] ,
CPPPATH = [ os . path . join ( self . srcdir , ' main ' ) ] ,
2015-11-01 21:15:39 +00:00
LIBS = [ ' m ' ] ,
2015-10-29 03:01:42 +00:00
)
2013-03-03 00:53:35 +00:00
2013-12-04 01:43:19 +00:00
def register_program ( self ) :
2015-10-18 21:01:18 +00:00
exe_target = self . user_settings . program_name
if not exe_target :
exe_target = os . path . join ( self . srcdir , self . target )
if self . user_settings . editor :
exe_target + = ' -editor '
exe_target = os . path . join ( self . user_settings . builddir , exe_target )
2016-10-15 17:51:48 +00:00
env = self . env
PROGSUFFIX = env [ ' PROGSUFFIX ' ]
2015-10-18 21:01:18 +00:00
if PROGSUFFIX and not exe_target . endswith ( PROGSUFFIX ) :
exe_target + = PROGSUFFIX
if self . user_settings . register_compile_target :
exe_target = self . _register_program ( exe_target )
if self . user_settings . register_install_target :
self . _register_install ( self . shortname , exe_target )
2016-10-15 17:51:48 +00:00
ToolchainInformation . show_partial_environ ( env , lambda s , _message = message , _self = self : _message ( self , s ) )
2013-12-04 01:43:19 +00:00
2015-10-18 21:01:18 +00:00
def _register_program ( self , exe_target ) :
2013-03-10 19:33:57 +00:00
env = self . env
2016-10-29 23:16:19 +00:00
user_settings = self . user_settings
static_archive_construction = self . static_archive_construction [ user_settings . builddir ]
2016-08-17 04:44:22 +00:00
objects = static_archive_construction . get_objects_common ( )
2016-10-29 23:16:19 +00:00
git_describe_version = Git . compute_extra_version ( ) if user_settings . git_describe_version else Git . UnknownExtraVersion
env . __dxx_CPPDEFINE_git_version = [ ( ' DXX_git_commit ' , git_describe_version . revparse_HEAD ) , ( ' DXX_git_describe ' , self . _encode_cppdefine_for_identifier ( user_settings , git_describe_version . describe ) ) ]
2016-08-17 04:44:22 +00:00
objects . extend ( self . get_objects_common ( ) )
2016-10-29 23:16:19 +00:00
if user_settings . sdlmixer :
2016-08-17 04:44:22 +00:00
objects . extend ( static_archive_construction . get_objects_arch_sdlmixer ( ) )
objects . extend ( self . get_objects_similar_arch_sdlmixer ( ) )
2016-10-29 23:16:19 +00:00
if user_settings . opengl or user_settings . opengles :
2013-06-01 23:46:05 +00:00
env . Append ( LIBS = self . platform_settings . ogllibs )
2016-08-17 04:44:22 +00:00
static_objects_arch = static_archive_construction . get_objects_arch_ogl
objects_similar_arch = self . get_objects_similar_arch_ogl
2013-03-03 01:03:33 +00:00
else :
2016-08-17 04:44:22 +00:00
static_objects_arch = static_archive_construction . get_objects_arch_sdl
objects_similar_arch = self . get_objects_similar_arch_sdl
objects . extend ( static_objects_arch ( ) )
objects . extend ( objects_similar_arch ( ) )
2016-10-29 23:16:19 +00:00
if user_settings . editor :
2016-08-17 04:44:22 +00:00
objects . extend ( self . get_objects_editor ( ) )
objects . extend ( static_archive_construction . get_objects_editor ( ) )
2014-09-13 22:10:24 +00:00
versid_build_environ = [ ' CXX ' , ' CPPFLAGS ' , ' CXXFLAGS ' , ' LINKFLAGS ' ]
versid_cppdefines = env [ ' CPPDEFINES ' ] [ : ]
2016-10-29 23:16:19 +00:00
extra_version = user_settings . extra_version
2014-07-26 17:40:12 +00:00
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
2016-10-21 02:16:47 +00:00
git_describe_version_describe_output = git_describe_version . describe
if git_describe_version_describe_output and not ( extra_version and ( extra_version == git_describe_version_describe_output or ( extra_version [ 0 ] == ' v ' and extra_version [ 1 : ] == git_describe_version_describe_output ) ) ) :
2014-07-26 17:40:12 +00:00
# Suppress duplicate output
if extra_version :
extra_version + = ' '
2016-10-21 02:16:47 +00:00
extra_version + = git_describe_version_describe_output
2016-07-21 01:43:22 +00:00
get_version_head = StaticSubprocess . get_version_head
ld_path = ToolchainInformation . get_tool_path ( env , ' ld ' ) [ 1 ]
_quote_cppdefine = self . _quote_cppdefine
versid_cppdefines . extend ( [ ( ' DESCENT_ %s ' % k , _quote_cppdefine ( env . get ( k , ' ' ) ) ) for k in versid_build_environ ] )
versid_cppdefines . extend ( (
# VERSION_EXTRA is special. Format it as a string so that
# it can be pasted into g_descent_version (in vers_id.cpp)
# and look normal when printed as part of the startup
# banner. Since it is pasted into g_descent_version, it is
# NOT included in versid_build_environ as an independent
# field.
( ' DESCENT_VERSION_EXTRA ' , _quote_cppdefine ( extra_version , f = str ) ) ,
( ' DESCENT_CXX_version ' , _quote_cppdefine ( get_version_head ( env [ ' CXX ' ] ) ) ) ,
( ' DESCENT_LINK ' , _quote_cppdefine ( ld_path ) ) ,
( ' DESCENT_LINK_version ' , _quote_cppdefine ( get_version_head ( ld_path ) ) ) ,
2016-10-21 02:16:47 +00:00
( ' DESCENT_git_status ' , _quote_cppdefine ( git_describe_version . status ) ) ,
( ' DESCENT_git_diffstat ' , _quote_cppdefine ( git_describe_version . diffstat_HEAD ) ) ,
2016-07-21 01:43:22 +00:00
) )
versid_build_environ . extend ( (
' CXX_version ' ,
' LINK ' ,
' LINK_version ' ,
' git_status ' ,
' git_diffstat ' ,
) )
2016-01-19 04:29:34 +00:00
versid_cppdefines . append ( ( ' DXX_RBE " (A) " ' , ' " %s " ' % ' ' . join ( [ ' A( %s ) ' % k for k in versid_build_environ ] ) ) )
2015-01-03 23:44:32 +00:00
versid_environ = self . env [ ' ENV ' ] . copy ( )
# Direct mode conflicts with __TIME__
versid_environ [ ' CCACHE_NODIRECT ' ] = 1
2015-10-13 02:43:24 +00:00
versid_cpp = ' similar/main/vers_id.cpp '
2016-10-29 23:16:19 +00:00
versid_obj = env . StaticObject ( target = ' %s %s %s ' % ( user_settings . builddir , self . _apply_target_name ( versid_cpp ) , self . env [ " OBJSUFFIX " ] ) , source = versid_cpp , CPPDEFINES = versid_cppdefines , ENV = versid_environ )
if user_settings . versid_depend_all :
2015-10-13 02:43:24 +00:00
# Optional fake dependency to force vers_id to rebuild so
# that it picks up the latest timestamp.
env . Depends ( versid_obj , objects )
objects . append ( versid_obj )
2013-03-10 19:33:57 +00:00
# finally building program...
2015-10-18 21:01:18 +00:00
return env . Program ( target = exe_target , source = objects )
def _register_install ( self , dxxstr , exe_node ) :
env = self . env
2013-12-05 23:01:34 +00:00
if self . user_settings . host_platform != ' darwin ' :
2015-09-19 23:04:35 +00:00
install_dir = ' %s %s ' % ( self . user_settings . DESTDIR or ' ' , self . user_settings . BIN_DIR )
2013-12-06 03:54:52 +00:00
env . Install ( install_dir , exe_node )
2013-05-05 22:55:16 +00:00
env . Alias ( ' install ' , install_dir )
2013-03-10 19:33:57 +00:00
else :
2013-05-05 23:20:06 +00:00
syspath = sys . path [ : ]
2013-03-03 01:03:33 +00:00
cocoa = ' common/arch/cocoa '
2013-05-05 23:20:06 +00:00
sys . path + = [ cocoa ]
2013-03-10 19:33:57 +00:00
import tool_bundle
2013-05-05 23:20:06 +00:00
sys . path = syspath
2013-03-10 19:33:57 +00:00
tool_bundle . TOOL_BUNDLE ( env )
2015-09-19 23:04:35 +00:00
env . MakeBundle ( os . path . join ( self . user_settings . builddir , ' %s .app ' % self . PROGRAM_NAME ) , exe_node ,
2014-12-05 01:27:06 +00:00
' free. %s -rebirth ' % dxxstr , os . path . join ( cocoa , ' Info.plist ' ) ,
2013-03-10 19:33:57 +00:00
typecode = ' APPL ' , creator = ' DCNT ' ,
2013-05-05 23:20:06 +00:00
icon_file = os . path . join ( cocoa , ' %s -rebirth.icns ' % dxxstr ) ,
2014-12-05 00:39:45 +00:00
resources = [ [ os . path . join ( self . srcdir , s ) , s ] for s in [ ' English.lproj/InfoPlist.strings ' ] ] )
2013-03-10 19:33:57 +00:00
2013-03-02 20:53:47 +00:00
class D1XProgram ( DXXProgram ) :
PROGRAM_NAME = ' D1X-Rebirth '
2016-08-17 04:44:22 +00:00
target = \
2013-03-02 20:53:47 +00:00
srcdir = ' d1x-rebirth '
2013-12-04 01:43:19 +00:00
shortname = ' d1x '
2016-01-26 03:45:07 +00:00
env_CPPDEFINES = ( ' DXX_BUILD_DESCENT_I ' , )
2013-03-02 20:53:47 +00:00
# general source files
2016-08-19 03:41:40 +00:00
def get_objects_common ( self ,
__get_dxx_objects_common = DXXProgram . get_objects_common , \
__get_dsx_objects_common = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 17:58:04 +00:00
' source ' : [ os . path . join ( srcdir , f ) for f in [
2012-11-11 22:12:51 +00:00
' main/bmread.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/custom.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/snddecom.cpp ' ,
2013-03-02 20:53:47 +00:00
]
2013-04-21 17:58:04 +00:00
] ,
} ] )
2016-08-19 03:41:40 +00:00
) :
value = __get_dxx_objects_common ( self )
value . extend ( __get_dsx_objects_common ( self ) )
2015-09-19 23:04:35 +00:00
return value
2013-03-02 20:53:47 +00:00
# for editor
2016-08-19 03:41:40 +00:00
def get_objects_editor ( self ,
__get_dxx_objects_editor = DXXProgram . get_objects_editor ,
__get_dsx_objects_editor = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 17:59:38 +00:00
' source ' : [ os . path . join ( srcdir , f ) for f in [
2012-11-11 22:12:51 +00:00
' main/hostage.cpp ' ,
2012-11-17 06:14:09 +00:00
' editor/ehostage.cpp ' ,
2013-03-02 20:53:47 +00:00
]
2013-04-21 17:59:38 +00:00
] ,
2016-08-19 03:41:40 +00:00
} ] ) ) :
value = list ( __get_dxx_objects_editor ( self ) )
value . extend ( __get_dsx_objects_editor ( self ) )
2015-09-19 23:04:35 +00:00
return value
2013-03-02 20:53:47 +00:00
2013-02-25 00:02:01 +00:00
class D2XProgram ( DXXProgram ) :
PROGRAM_NAME = ' D2X-Rebirth '
2016-08-17 04:44:22 +00:00
target = \
2013-03-02 20:53:47 +00:00
srcdir = ' d2x-rebirth '
2013-12-04 01:43:19 +00:00
shortname = ' d2x '
2016-01-26 03:45:07 +00:00
env_CPPDEFINES = ( ' DXX_BUILD_DESCENT_II ' , )
2013-03-02 22:41:05 +00:00
2013-02-25 00:02:01 +00:00
# general source files
2016-08-19 03:41:40 +00:00
def get_objects_common ( self ,
__get_dxx_objects_common = DXXProgram . get_objects_common , \
__get_dsx_objects_common = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 17:58:04 +00:00
' source ' : [ os . path . join ( srcdir , f ) for f in [
2012-11-11 22:12:51 +00:00
' libmve/decoder8.cpp ' ,
2012-11-11 22:12:51 +00:00
' libmve/decoder16.cpp ' ,
2012-11-11 22:12:51 +00:00
' libmve/mve_audio.cpp ' ,
2012-11-11 22:12:51 +00:00
' libmve/mvelib.cpp ' ,
2012-11-11 22:12:51 +00:00
' libmve/mveplay.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/escort.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/gamepal.cpp ' ,
2012-11-11 22:12:51 +00:00
' main/movie.cpp ' ,
2012-11-11 22:12:51 +00:00
' misc/physfsrwops.cpp ' ,
2013-03-03 02:20:54 +00:00
]
2013-04-21 17:58:04 +00:00
] ,
2016-08-19 03:41:40 +00:00
} ] ) ) :
value = __get_dxx_objects_common ( self )
value . extend ( __get_dsx_objects_common ( self ) )
2015-09-19 23:04:35 +00:00
return value
2006-12-19 02:25:50 +00:00
2013-02-25 00:02:01 +00:00
# for editor
2016-08-19 03:41:40 +00:00
def get_objects_editor ( self ,
__get_dxx_objects_editor = DXXProgram . get_objects_editor , \
__get_dsx_objects_editor = DXXCommon . create_lazy_object_getter ( [ {
2013-04-21 17:59:38 +00:00
' source ' : [ os . path . join ( srcdir , f ) for f in [
2012-11-11 22:12:51 +00:00
' main/bmread.cpp ' ,
2013-03-03 02:20:54 +00:00
]
2013-04-21 17:59:38 +00:00
] ,
2016-08-19 03:41:40 +00:00
} ] ) ) :
value = list ( __get_dxx_objects_editor ( self ) )
value . extend ( __get_dsx_objects_editor ( self ) )
2015-09-19 23:04:35 +00:00
return value
2006-12-19 02:25:50 +00:00
2016-12-10 17:51:07 +00:00
def register_program ( program , other_program , variables , filtered_help , append , _itertools_product = itertools . product ) :
2013-12-04 01:43:19 +00:00
s = program . shortname
2015-03-12 02:21:21 +00:00
l = [ v for ( k , v ) in ARGLIST if k == s or k == ' dxx ' ] or [ other_program . shortname not in ARGUMENTS ]
2016-08-20 22:28:43 +00:00
# Legacy case: build one configuration.
2013-05-05 23:12:52 +00:00
if len ( l ) == 1 :
try :
2016-08-20 22:28:43 +00:00
if not int ( l [ 0 ] ) :
# If the user specifies an integer that evaluates to
# False, this configuration is disabled. This allows
# the user to build only one game, instead of both.
2016-12-10 17:51:07 +00:00
return
2016-08-20 22:28:43 +00:00
# Coerce to an empty string, then reuse the case for stacked
# profiles. This is slightly less efficient, but reduces
Unfold register_program comprehension
The folded form combined multiple targets into one, which is not the
desired result.
'''
unfolded
l=['gcc5,', 'mingw5,'] e='gcc5,' p=('gcc5', '')
l=['gcc5,', 'mingw5,'] e='mingw5,' p=('mingw5', '')
folded
l=['gcc5,', 'mingw5,'] e='mingw5,' p=('gcc5', '', 'mingw5', '')
'''
import itertools
l = ['gcc5,', 'mingw5,']
print('unfolded')
for e in l:
for p in itertools.product(*[v.split('+') for v in e.split(',')]):
print("l=%r e=%r p=%r" % (l, e, p))
print('folded')
for p in itertools.product(*[
v.split('+') \
for e in l \
for v in e.split(',') \
]):
print("l=%r e=%r p=%r" % (l, e, p))
2016-09-07 04:24:33 +00:00
# maintenance by not having multiple sites that call
2016-08-20 22:28:43 +00:00
# program().
l = [ ' ' ]
2013-05-05 23:12:52 +00:00
except ValueError :
# If not an integer, treat this as a configuration profile.
pass
2013-12-04 02:18:43 +00:00
seen = set ( )
2016-08-20 22:28:43 +00:00
add_seen = seen . add
Unfold register_program comprehension
The folded form combined multiple targets into one, which is not the
desired result.
'''
unfolded
l=['gcc5,', 'mingw5,'] e='gcc5,' p=('gcc5', '')
l=['gcc5,', 'mingw5,'] e='mingw5,' p=('mingw5', '')
folded
l=['gcc5,', 'mingw5,'] e='mingw5,' p=('gcc5', '', 'mingw5', '')
'''
import itertools
l = ['gcc5,', 'mingw5,']
print('unfolded')
for e in l:
for p in itertools.product(*[v.split('+') for v in e.split(',')]):
print("l=%r e=%r p=%r" % (l, e, p))
print('folded')
for p in itertools.product(*[
v.split('+') \
for e in l \
for v in e.split(',') \
]):
print("l=%r e=%r p=%r" % (l, e, p))
2016-09-07 04:24:33 +00:00
for e in l :
for prefix in _itertools_product ( * [ v . split ( ' + ' ) for v in e . split ( ' , ' ) ] ) :
2014-10-16 01:56:30 +00:00
duplicates = set ( )
2016-08-20 22:28:43 +00:00
# This would be simpler if set().add(V) returned the result
# of `V not in self`, as seen before the add. Instead, it
# always returns None.
#
# Test for presence, then add if not present. add() always
# returns None, which coerces to False in boolean context.
# This is slightly inefficient since we know the right hand
# side will always be True after negation, so we ought to be
# able to skip testing its truth value. However, the
# alternative is to use a wrapper function that checks
# membership, adds the value, and returns the result of the
# test. Calling such a function is even less efficient.
#
# In most cases, this loop is not run often enough for the
# efficiency to matter.
prefix = tuple ( [
p for p in prefix \
if ( p not in duplicates and not duplicates . add ( p ) )
] )
2013-12-04 02:18:43 +00:00
if prefix in seen :
continue
2016-08-20 22:28:43 +00:00
add_seen ( prefix )
append ( program ( tuple ( ( ' %s %s %s ' % ( s , ' _ ' if p else ' ' , p ) for p in prefix ) ) + prefix , variables , filtered_help ) )
2006-12-19 02:25:50 +00:00
2016-12-10 17:51:07 +00:00
def main ( register_program , _d1xp = D1XProgram , _d2xp = D2XProgram ) :
2016-08-20 22:28:43 +00:00
variables = Variables ( [ v for k , v in ARGLIST if k == ' site ' ] or [ ' site-local.py ' ] , ARGUMENTS )
filtered_help = FilterHelpText ( )
2016-12-10 17:51:07 +00:00
dxx = [ ]
register_program ( _d1xp , _d2xp , variables , filtered_help , dxx . append )
register_program ( _d2xp , _d1xp , variables , filtered_help , dxx . append )
2016-08-20 22:28:43 +00:00
substenv = SCons . Environment . SubstitutionEnvironment ( )
variables . FormatVariableHelpText = filtered_help . FormatVariableHelpText
variables . Update ( substenv )
2006-12-19 02:25:50 +00:00
# show some help when running scons -h
2016-08-20 22:28:43 +00:00
Help ( """ DXX-Rebirth, SConstruct file help:
2006-12-19 02:25:50 +00:00
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 .
2016-08-20 22:28:43 +00:00
2006-12-19 02:25:50 +00:00
Extra options ( add them to command line , like ' scons extraoption=value ' ) :
2013-06-16 02:40:05 +00:00
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
2014-10-10 02:58:05 +00:00
dxx = VALUE Equivalent to d1x = VALUE d2x = VALUE
2016-08-20 22:28:43 +00:00
""" + \
2016-10-02 00:34:50 +00:00
' ' . join ( [ ' %s : \n %s ' % ( d . program_message_prefix , d . init ( substenv ) ) for d in dxx ] )
2016-08-20 22:28:43 +00:00
)
if not dxx :
return
unknown = variables . UnknownVariables ( )
# Delete known unregistered variables
unknown . pop ( ' d1x ' , None )
unknown . pop ( ' d2x ' , None )
unknown . pop ( ' dxx ' , None )
unknown . pop ( ' site ' , None )
ignore_unknown_variables = unknown . pop ( ' ignore_unknown_variables ' , ' 0 ' )
if unknown :
# Protect user from misspelled options by reporting an error.
# Provide a way for the user to override the check, which might
# be necessary if the user is setting an option that is only
# understood by older (or newer) versions of SConstruct.
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. ' +
2014-10-10 02:58:05 +00:00
' ' . join ( [ ' \n \t %s ' % k for k in unknown . keys ( ) ] ) +
' \n Remove unknown values or set ignore_unknown_variables=1 to continue. ' )
2013-02-21 00:20:26 +00:00
2016-08-20 22:28:43 +00:00
main ( register_program )