@ -30,6 +30,8 @@ ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16, 23: 16, 25: 19 } # minimal API le
@@ -30,6 +30,8 @@ ANDROID_NDK_API_MIN = { 10: 3, 19: 16, 20: 16, 23: 16, 25: 19 } # minimal API le
ANDROID_STPCPY_API_MIN = 21 # stpcpy() introduced in SDK 21
ANDROID_64BIT_API_MIN = 21 # minimal API level that supports 64-bit targets
NSWITCH_ENVVARS = [ ' DEVKITPRO ' ]
# This class does support ONLY r10e and r19c/r20 NDK
class Android :
ctx = None # waf context
@ -348,14 +350,100 @@ class Android:
@@ -348,14 +350,100 @@ class Android:
ldflags + = [ ' -march=armv5te ' ]
return ldflags
class NintendoSwitch :
ctx = None # waf context
arch = " arm64 "
dkp_dir = None
portlibs_dir = None
dka64_dir = None
libnx_dir = None
def __init__ ( self , ctx ) :
self . ctx = ctx
for i in NSWITCH_ENVVARS :
self . dkp_dir = os . getenv ( i )
if self . dkp_dir != None :
break
else :
ctx . fatal ( ' Set %s environment variable pointing to the DEVKITPRO home! ' %
' or ' . join ( NSWITCH_ENVVARS ) )
self . dkp_dir = os . path . abspath ( self . dkp_dir )
self . dka64_dir = os . path . join ( self . dkp_dir , ' devkitA64 ' )
if not os . path . exists ( self . dka64_dir ) :
ctx . fatal ( ' devkitA64 not found in ` %s `. Install devkitA64! ' % self . dka64_dir )
self . libnx_dir = os . path . join ( self . dkp_dir , ' libnx ' )
if not os . path . exists ( self . libnx_dir ) :
ctx . fatal ( ' libnx not found in ` %s `. Install libnx! ' % self . libnx_dir )
self . portlibs_dir = os . path . join ( self . dkp_dir , ' portlibs ' , ' switch ' )
if not os . path . exists ( self . portlibs_dir ) :
ctx . fatal ( ' No Switch libraries found in ` %s `! ' % self . portlibs_dir )
def gen_toolchain_prefix ( self ) :
return ' aarch64-none-elf- '
def gen_gcc_toolchain_path ( self ) :
return os . path . join ( self . dka64_dir , ' bin ' , self . gen_toolchain_prefix ( ) )
def cc ( self ) :
return self . gen_gcc_toolchain_path ( ) + ' gcc '
def cxx ( self ) :
return self . gen_gcc_toolchain_path ( ) + ' g++ '
def strip ( self ) :
return self . gen_gcc_toolchain_path ( ) + ' strip '
def pkgconfig ( self ) :
# counter-intuitively, this motherfucker is in portlibs/switch/bin
return os . path . join ( self . portlibs_dir , ' bin ' , self . gen_toolchain_prefix ( ) + ' pkg-config ' )
def cflags ( self , cxx = False ) :
cflags = [ ]
# arch flags
cflags + = [ ' -D__SWITCH__ ' , ' -march=armv8-a+crc+crypto ' , ' -mtune=cortex-a57 ' , ' -mtp=soft ' , ' -ftls-model=local-exec ' , ' -fPIE ' ]
# help the linker out
cflags + = [ ' -ffunction-sections ' , ' -fdata-sections ' ]
# base include dirs
cflags + = [ ' -isystem %s /include ' % self . libnx_dir , ' -I %s /include ' % self . portlibs_dir ]
if cxx :
# while these are supported, they could fuck up the crappy dynamic linker
cflags + = [ ' -fno-exceptions ' , ' -fno-rtti ' ]
# the game wants GNU extensions
cflags + = [ ' -std=gnu++17 ' , ' -D_GNU_SOURCE ' ]
else :
cflags + = [ ' -std=gnu11 ' , ' -D_GNU_SOURCE ' ]
return cflags
# they go before object list
def linkflags ( self ) :
linkflags = [ ' -fPIE ' , ' -specs= %s /switch.specs ' % self . libnx_dir ]
# libsolder only supports sysv hashes and we need to build everything with -rdynamic
linkflags + = [ ' -Wl,--hash-style=sysv ' , ' -rdynamic ' ]
# avoid pulling in and exposing mesa's internals, that crashes it for some god forsaken reason
linkflags + = [ ' -Wl,--exclude-libs=libglapi.a ' , ' -Wl,--exclude-libs=libdrm_nouveau.a ' ]
return linkflags
def ldflags ( self ) :
# NOTE: shared libraries should be built without standard libs, so that they could import their contents from the NRO,
# but executables, including the SDL2 sanity check, will generally require libstdc++ and libm, which we will add manually
ldflags = [ ] # ['-lm', '-lstdc++']
return ldflags
def options ( opt ) :
android = opt . add_option_group ( ' Android options ' )
android . add_option ( ' --android ' , action = ' store ' , dest = ' ANDROID_OPTS ' , default = None ,
xc = opt . add_option_group ( ' Cross compile options' )
xc . add_option ( ' --android ' , action = ' store ' , dest = ' ANDROID_OPTS ' , default = None ,
help = ' enable building for android, format: --android=<arch>,<toolchain>,<api>, example: --android=armeabi-v7a-hard,4.9,9 ' )
magx = opt . add_option_group ( ' MotoMAGX options ' )
magx . add_option ( ' --enable-magx ' , action = ' store_true ' , dest = ' MAGX ' , default = False ,
help = ' enable targetting for MotoMAGX phones [default: %d efault] ' )
xc . add_option ( ' --enable-magx ' , action = ' store_true ' , dest = ' MAGX ' , default = False ,
help = ' enable building for Motorola MAGX [default: %d efault] ' )
xc . add_option ( ' --enable-msvc-wine ' , action = ' store_true ' , dest = ' MSVC_WINE ' , default = False ,
help = ' enable building with MSVC using Wine [default: %d efault] ' )
xc . add_option ( ' --nswitch ' , action = ' store_true ' , dest = ' NSWITCH ' , default = False ,
help = ' enable building for Nintendo Switch [default: %d efault] ' )
def configure ( conf ) :
if conf . options . ANDROID_OPTS :
@ -389,18 +477,42 @@ def configure(conf):
@@ -389,18 +477,42 @@ def configure(conf):
conf . msg ( ' ... C/C++ flags ' , ' ' . join ( android . cflags ( ) ) . replace ( android . ndk_home , ' $NDK/ ' ) )
conf . msg ( ' ... link flags ' , ' ' . join ( android . linkflags ( ) ) . replace ( android . ndk_home , ' $NDK/ ' ) )
conf . msg ( ' ... ld flags ' , ' ' . join ( android . ldflags ( ) ) . replace ( android . ndk_home , ' $NDK/ ' ) )
# conf.env.ANDROID_OPTS = android
conf . env . DEST_OS2 = ' android '
elif conf . options . MAGX :
# useless to change toolchain path, as toolchain meant to be placed in this path
toolchain_path = ' /opt/toolchains/motomagx/arm-eabi2/lib/ '
conf . env . INCLUDES_MAGX = [ toolchain_path + i for i in [ ' ezx-z6/include ' , ' qt-2.3.8/include ' ] ]
conf . env . LIBPATH_MAGX = [ toolchain_path + i for i in [ ' ezx-z6/lib ' , ' qt-2.3.8/lib ' ] ]
conf . env . LINKFLAGS_MAGX = [ ' -Wl,-rpath-link= ' + i for i in conf . env . LIBPATH_MAGX ]
elif conf . options . MSVC_WINE :
try :
toolchain_path = conf . environ [ ' MSVC_WINE_PATH ' ]
except KeyError :
conf . fatal ( ' Set MSVC_WINE_PATH environment variable to the MSVC toolchain root! ' )
conf . environ [ ' CC ' ] = conf . environ [ ' CXX ' ] = os . path . join ( toolchain_path , ' bin ' , conf . env . MSVC_TARGETS [ 0 ] , ' cl ' )
conf . environ [ ' LINK_CXX ' ] = os . path . join ( toolchain_path , ' bin ' , conf . env . MSVC_TARGETS [ 0 ] , ' link ' )
conf . environ [ ' AR ' ] = os . path . join ( toolchain_path , ' bin ' , conf . env . MSVC_TARGETS [ 0 ] , ' lib ' )
conf . environ [ ' WINRC ' ] = os . path . join ( toolchain_path , ' bin ' , conf . env . MSVC_TARGETS [ 0 ] , ' rc ' )
conf . env . DEST_OS = ' win32 '
conf . env . DEST_CPU = conf . env . MSVC_TARGETS [ 0 ]
conf . env . COMPILER_CXX = conf . env . COMPILER_CC = ' msvc '
elif conf . options . NSWITCH :
conf . nswitch = nswitch = NintendoSwitch ( conf )
conf . environ [ ' CC ' ] = nswitch . cc ( )
conf . environ [ ' CXX ' ] = nswitch . cxx ( )
conf . environ [ ' STRIP ' ] = nswitch . strip ( )
conf . env . PKGCONFIG = nswitch . pkgconfig ( )
conf . env . CFLAGS + = nswitch . cflags ( )
conf . env . CXXFLAGS + = nswitch . cflags ( True )
conf . env . LINKFLAGS + = nswitch . linkflags ( )
conf . env . LDFLAGS + = nswitch . ldflags ( )
conf . env . HAVE_M = True
conf . env . LIB_M = [ ' m ' ]
conf . env . DEST_OS = ' nswitch '
conf . env . MAGX = conf . options . MAGX
MACRO_TO_DESTOS = OrderedDict ( { ' __ANDROID__ ' : ' android ' } )
conf . env . MSVC_WINE = conf . options . MSVC_WINE
MACRO_TO_DESTOS = OrderedDict ( { ' __ANDROID__ ' : ' android ' , ' __SWITCH__ ' : ' nswitch ' } )
for k in c_config . MACRO_TO_DESTOS :
MACRO_TO_DESTOS [ k ] = c_config . MACRO_TO_DESTOS [ k ] # ordering is important
c_config . MACRO_TO_DESTOS = MACRO_TO_DESTOS
@ -433,11 +545,17 @@ compiler_cxx_configure = getattr(compiler_cxx, 'configure')
@@ -433,11 +545,17 @@ compiler_cxx_configure = getattr(compiler_cxx, 'configure')
compiler_c_configure = getattr ( compiler_c , ' configure ' )
def patch_compiler_cxx_configure ( conf ) :
compiler_cxx_configure ( conf )
if not conf . env . MSVC_WINE :
compiler_cxx_configure ( conf )
else :
conf . load ( ' msvc ' , funs = ' no_autodetect ' )
post_compiler_cxx_configure ( conf )
def patch_compiler_c_configure ( conf ) :
compiler_c_configure ( conf )
if not conf . env . MSVC_WINE :
compiler_c_configure ( conf )
else :
conf . load ( ' msvc ' , funs = ' no_autodetect ' )
post_compiler_c_configure ( conf )
setattr ( compiler_cxx , ' configure ' , patch_compiler_cxx_configure )