1import os 2import platform 3import sys 4from methods import get_compiler_version, using_gcc, using_clang 5 6 7def is_active(): 8 return True 9 10 11def get_name(): 12 return "X11" 13 14 15def can_build(): 16 17 if os.name != "posix" or sys.platform == "darwin": 18 return False 19 20 # Check the minimal dependencies 21 x11_error = os.system("pkg-config --version > /dev/null") 22 if x11_error: 23 return False 24 25 x11_error = os.system("pkg-config x11 --modversion > /dev/null ") 26 if x11_error: 27 return False 28 29 x11_error = os.system("pkg-config xcursor --modversion > /dev/null ") 30 if x11_error: 31 print("xcursor not found.. x11 disabled.") 32 return False 33 34 x11_error = os.system("pkg-config xinerama --modversion > /dev/null ") 35 if x11_error: 36 print("xinerama not found.. x11 disabled.") 37 return False 38 39 x11_error = os.system("pkg-config xrandr --modversion > /dev/null ") 40 if x11_error: 41 print("xrandr not found.. x11 disabled.") 42 return False 43 44 x11_error = os.system("pkg-config xrender --modversion > /dev/null ") 45 if x11_error: 46 print("xrender not found.. x11 disabled.") 47 return False 48 49 x11_error = os.system("pkg-config xi --modversion > /dev/null ") 50 if x11_error: 51 print("xi not found.. Aborting.") 52 return False 53 54 return True 55 56 57def get_opts(): 58 from SCons.Variables import BoolVariable, EnumVariable 59 60 return [ 61 BoolVariable("use_llvm", "Use the LLVM compiler", False), 62 BoolVariable("use_lld", "Use the LLD linker", False), 63 BoolVariable("use_thinlto", "Use ThinLTO", False), 64 BoolVariable("use_static_cpp", "Link libgcc and libstdc++ statically for better portability", False), 65 BoolVariable("use_ubsan", "Use LLVM/GCC compiler undefined behavior sanitizer (UBSAN)", False), 66 BoolVariable("use_asan", "Use LLVM/GCC compiler address sanitizer (ASAN))", False), 67 BoolVariable("use_lsan", "Use LLVM/GCC compiler leak sanitizer (LSAN))", False), 68 BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN))", False), 69 BoolVariable("pulseaudio", "Detect and use PulseAudio", True), 70 BoolVariable("udev", "Use udev for gamepad connection callbacks", False), 71 EnumVariable("debug_symbols", "Add debugging symbols to release builds", "yes", ("yes", "no", "full")), 72 BoolVariable("separate_debug_symbols", "Create a separate file containing debugging symbols", False), 73 BoolVariable("touch", "Enable touch events", True), 74 BoolVariable("execinfo", "Use libexecinfo on systems where glibc is not available", False), 75 ] 76 77 78def get_flags(): 79 80 return [] 81 82 83def configure(env): 84 85 ## Build type 86 87 if env["target"] == "release": 88 if env["optimize"] == "speed": # optimize for speed (default) 89 env.Prepend(CCFLAGS=["-O3"]) 90 else: # optimize for size 91 env.Prepend(CCFLAGS=["-Os"]) 92 93 if env["debug_symbols"] == "yes": 94 env.Prepend(CCFLAGS=["-g1"]) 95 if env["debug_symbols"] == "full": 96 env.Prepend(CCFLAGS=["-g2"]) 97 98 elif env["target"] == "release_debug": 99 if env["optimize"] == "speed": # optimize for speed (default) 100 env.Prepend(CCFLAGS=["-O2"]) 101 else: # optimize for size 102 env.Prepend(CCFLAGS=["-Os"]) 103 env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) 104 105 if env["debug_symbols"] == "yes": 106 env.Prepend(CCFLAGS=["-g1"]) 107 if env["debug_symbols"] == "full": 108 env.Prepend(CCFLAGS=["-g2"]) 109 110 elif env["target"] == "debug": 111 env.Prepend(CCFLAGS=["-g3"]) 112 env.Prepend(CPPDEFINES=["DEBUG_ENABLED"]) 113 env.Append(LINKFLAGS=["-rdynamic"]) 114 115 ## Architecture 116 117 is64 = sys.maxsize > 2 ** 32 118 if env["bits"] == "default": 119 env["bits"] = "64" if is64 else "32" 120 121 ## Compiler configuration 122 123 if "CXX" in env and "clang" in os.path.basename(env["CXX"]): 124 # Convenience check to enforce the use_llvm overrides when CXX is clang(++) 125 env["use_llvm"] = True 126 127 if env["use_llvm"]: 128 if "clang++" not in os.path.basename(env["CXX"]): 129 env["CC"] = "clang" 130 env["CXX"] = "clang++" 131 env["LINK"] = "clang++" 132 env.Append(CPPDEFINES=["TYPED_METHOD_BIND"]) 133 env.extra_suffix = ".llvm" + env.extra_suffix 134 135 if env["use_lld"]: 136 if env["use_llvm"]: 137 env.Append(LINKFLAGS=["-fuse-ld=lld"]) 138 if env["use_thinlto"]: 139 # A convenience so you don't need to write use_lto too when using SCons 140 env["use_lto"] = True 141 else: 142 print("Using LLD with GCC is not supported yet, try compiling with 'use_llvm=yes'.") 143 sys.exit(255) 144 145 if env["use_ubsan"] or env["use_asan"] or env["use_lsan"] or env["use_tsan"]: 146 env.extra_suffix += "s" 147 148 if env["use_ubsan"]: 149 env.Append(CCFLAGS=["-fsanitize=undefined"]) 150 env.Append(LINKFLAGS=["-fsanitize=undefined"]) 151 152 if env["use_asan"]: 153 env.Append(CCFLAGS=["-fsanitize=address"]) 154 env.Append(LINKFLAGS=["-fsanitize=address"]) 155 156 if env["use_lsan"]: 157 env.Append(CCFLAGS=["-fsanitize=leak"]) 158 env.Append(LINKFLAGS=["-fsanitize=leak"]) 159 160 if env["use_tsan"]: 161 env.Append(CCFLAGS=["-fsanitize=thread"]) 162 env.Append(LINKFLAGS=["-fsanitize=thread"]) 163 164 if env["use_lto"]: 165 if not env["use_llvm"] and env.GetOption("num_jobs") > 1: 166 env.Append(CCFLAGS=["-flto"]) 167 env.Append(LINKFLAGS=["-flto=" + str(env.GetOption("num_jobs"))]) 168 else: 169 if env["use_lld"] and env["use_thinlto"]: 170 env.Append(CCFLAGS=["-flto=thin"]) 171 env.Append(LINKFLAGS=["-flto=thin"]) 172 else: 173 env.Append(CCFLAGS=["-flto"]) 174 env.Append(LINKFLAGS=["-flto"]) 175 176 if not env["use_llvm"]: 177 env["RANLIB"] = "gcc-ranlib" 178 env["AR"] = "gcc-ar" 179 180 env.Append(CCFLAGS=["-pipe"]) 181 env.Append(LINKFLAGS=["-pipe"]) 182 183 # Check for gcc version >= 6 before adding -no-pie 184 version = get_compiler_version(env) or [-1, -1] 185 if using_gcc(env): 186 if version[0] >= 6: 187 env.Append(CCFLAGS=["-fpie"]) 188 env.Append(LINKFLAGS=["-no-pie"]) 189 # Do the same for clang should be fine with Clang 4 and higher 190 if using_clang(env): 191 if version[0] >= 4: 192 env.Append(CCFLAGS=["-fpie"]) 193 env.Append(LINKFLAGS=["-no-pie"]) 194 195 ## Dependencies 196 197 env.ParseConfig("pkg-config x11 --cflags --libs") 198 env.ParseConfig("pkg-config xcursor --cflags --libs") 199 env.ParseConfig("pkg-config xinerama --cflags --libs") 200 env.ParseConfig("pkg-config xrandr --cflags --libs") 201 env.ParseConfig("pkg-config xrender --cflags --libs") 202 env.ParseConfig("pkg-config xi --cflags --libs") 203 204 if env["touch"]: 205 env.Append(CPPDEFINES=["TOUCH_ENABLED"]) 206 207 # FIXME: Check for existence of the libs before parsing their flags with pkg-config 208 209 # freetype depends on libpng and zlib, so bundling one of them while keeping others 210 # as shared libraries leads to weird issues 211 if env["builtin_freetype"] or env["builtin_libpng"] or env["builtin_zlib"]: 212 env["builtin_freetype"] = True 213 env["builtin_libpng"] = True 214 env["builtin_zlib"] = True 215 216 if not env["builtin_freetype"]: 217 env.ParseConfig("pkg-config freetype2 --cflags --libs") 218 219 if not env["builtin_libpng"]: 220 env.ParseConfig("pkg-config libpng16 --cflags --libs") 221 222 if not env["builtin_bullet"]: 223 # We need at least version 2.89 224 import subprocess 225 226 bullet_version = subprocess.check_output(["pkg-config", "bullet", "--modversion"]).strip() 227 if str(bullet_version) < "2.89": 228 # Abort as system bullet was requested but too old 229 print( 230 "Bullet: System version {0} does not match minimal requirements ({1}). Aborting.".format( 231 bullet_version, "2.89" 232 ) 233 ) 234 sys.exit(255) 235 env.ParseConfig("pkg-config bullet --cflags --libs") 236 237 if False: # not env['builtin_assimp']: 238 # FIXME: Add min version check 239 env.ParseConfig("pkg-config assimp --cflags --libs") 240 241 if not env["builtin_enet"]: 242 env.ParseConfig("pkg-config libenet --cflags --libs") 243 244 if not env["builtin_squish"]: 245 env.ParseConfig("pkg-config libsquish --cflags --libs") 246 247 if not env["builtin_zstd"]: 248 env.ParseConfig("pkg-config libzstd --cflags --libs") 249 250 # Sound and video libraries 251 # Keep the order as it triggers chained dependencies (ogg needed by others, etc.) 252 253 if not env["builtin_libtheora"]: 254 env["builtin_libogg"] = False # Needed to link against system libtheora 255 env["builtin_libvorbis"] = False # Needed to link against system libtheora 256 env.ParseConfig("pkg-config theora theoradec --cflags --libs") 257 else: 258 list_of_x86 = ["x86_64", "x86", "i386", "i586"] 259 if any(platform.machine() in s for s in list_of_x86): 260 env["x86_libtheora_opt_gcc"] = True 261 262 if not env["builtin_libvpx"]: 263 env.ParseConfig("pkg-config vpx --cflags --libs") 264 265 if not env["builtin_libvorbis"]: 266 env["builtin_libogg"] = False # Needed to link against system libvorbis 267 env.ParseConfig("pkg-config vorbis vorbisfile --cflags --libs") 268 269 if not env["builtin_opus"]: 270 env["builtin_libogg"] = False # Needed to link against system opus 271 env.ParseConfig("pkg-config opus opusfile --cflags --libs") 272 273 if not env["builtin_libogg"]: 274 env.ParseConfig("pkg-config ogg --cflags --libs") 275 276 if not env["builtin_libwebp"]: 277 env.ParseConfig("pkg-config libwebp --cflags --libs") 278 279 if not env["builtin_mbedtls"]: 280 # mbedTLS does not provide a pkgconfig config yet. See https://github.com/ARMmbed/mbedtls/issues/228 281 env.Append(LIBS=["mbedtls", "mbedcrypto", "mbedx509"]) 282 283 if not env["builtin_wslay"]: 284 env.ParseConfig("pkg-config libwslay --cflags --libs") 285 286 if not env["builtin_miniupnpc"]: 287 # No pkgconfig file so far, hardcode default paths. 288 env.Prepend(CPPPATH=["/usr/include/miniupnpc"]) 289 env.Append(LIBS=["miniupnpc"]) 290 291 # On Linux wchar_t should be 32-bits 292 # 16-bit library shouldn't be required due to compiler optimisations 293 if not env["builtin_pcre2"]: 294 env.ParseConfig("pkg-config libpcre2-32 --cflags --libs") 295 296 ## Flags 297 298 if os.system("pkg-config --exists alsa") == 0: # 0 means found 299 print("Enabling ALSA") 300 env.Append(CPPDEFINES=["ALSA_ENABLED", "ALSAMIDI_ENABLED"]) 301 # Don't parse --cflags, we don't need to add /usr/include/alsa to include path 302 env.ParseConfig("pkg-config alsa --libs") 303 else: 304 print("ALSA libraries not found, disabling driver") 305 306 if env["pulseaudio"]: 307 if os.system("pkg-config --exists libpulse") == 0: # 0 means found 308 print("Enabling PulseAudio") 309 env.Append(CPPDEFINES=["PULSEAUDIO_ENABLED"]) 310 env.ParseConfig("pkg-config --cflags --libs libpulse") 311 else: 312 print("PulseAudio development libraries not found, disabling driver") 313 314 if platform.system() == "Linux": 315 env.Append(CPPDEFINES=["JOYDEV_ENABLED"]) 316 317 if env["udev"]: 318 if os.system("pkg-config --exists libudev") == 0: # 0 means found 319 print("Enabling udev support") 320 env.Append(CPPDEFINES=["UDEV_ENABLED"]) 321 env.ParseConfig("pkg-config libudev --cflags --libs") 322 else: 323 print("libudev development libraries not found, disabling udev support") 324 325 # Linkflags below this line should typically stay the last ones 326 if not env["builtin_zlib"]: 327 env.ParseConfig("pkg-config zlib --cflags --libs") 328 329 env.Prepend(CPPPATH=["#platform/x11"]) 330 env.Append(CPPDEFINES=["X11_ENABLED", "UNIX_ENABLED", "OPENGL_ENABLED", "GLES_ENABLED"]) 331 env.Append(LIBS=["GL", "pthread"]) 332 333 if platform.system() == "Linux": 334 env.Append(LIBS=["dl"]) 335 336 if platform.system().find("BSD") >= 0: 337 env["execinfo"] = True 338 339 if env["execinfo"]: 340 env.Append(LIBS=["execinfo"]) 341 342 if not env["tools"]: 343 import subprocess 344 import re 345 346 linker_version_str = subprocess.check_output([env.subst(env["LINK"]), "-Wl,--version"]).decode("utf-8") 347 gnu_ld_version = re.search("^GNU ld [^$]*(\d+\.\d+)$", linker_version_str, re.MULTILINE) 348 if not gnu_ld_version: 349 print( 350 "Warning: Creating template binaries enabled for PCK embedding is currently only supported with GNU ld" 351 ) 352 else: 353 if float(gnu_ld_version.group(1)) >= 2.30: 354 env.Append(LINKFLAGS=["-T", "platform/x11/pck_embed.ld"]) 355 else: 356 env.Append(LINKFLAGS=["-T", "platform/x11/pck_embed.legacy.ld"]) 357 358 ## Cross-compilation 359 360 if is64 and env["bits"] == "32": 361 env.Append(CCFLAGS=["-m32"]) 362 env.Append(LINKFLAGS=["-m32", "-L/usr/lib/i386-linux-gnu"]) 363 elif not is64 and env["bits"] == "64": 364 env.Append(CCFLAGS=["-m64"]) 365 env.Append(LINKFLAGS=["-m64", "-L/usr/lib/i686-linux-gnu"]) 366 367 # Link those statically for portability 368 if env["use_static_cpp"]: 369 env.Append(LINKFLAGS=["-static-libgcc", "-static-libstdc++"]) 370