1#!/usr/bin/env python
2
3EnsureSConsVersion(0, 98, 1)
4
5# System
6import glob
7import os
8import pickle
9import sys
10from collections import OrderedDict
11
12# Local
13import methods
14import gles_builders
15from platform_methods import run_in_subprocess
16
17# scan possible build platforms
18
19platform_list = []  # list of platforms
20platform_opts = {}  # options for each platform
21platform_flags = {}  # flags for each platform
22
23active_platforms = []
24active_platform_ids = []
25platform_exporters = []
26platform_apis = []
27
28for x in sorted(glob.glob("platform/*")):
29    if not os.path.isdir(x) or not os.path.exists(x + "/detect.py"):
30        continue
31    tmppath = "./" + x
32
33    sys.path.insert(0, tmppath)
34    import detect
35
36    if os.path.exists(x + "/export/export.cpp"):
37        platform_exporters.append(x[9:])
38    if os.path.exists(x + "/api/api.cpp"):
39        platform_apis.append(x[9:])
40    if detect.is_active():
41        active_platforms.append(detect.get_name())
42        active_platform_ids.append(x)
43    if detect.can_build():
44        x = x.replace("platform/", "")  # rest of world
45        x = x.replace("platform\\", "")  # win32
46        platform_list += [x]
47        platform_opts[x] = detect.get_opts()
48        platform_flags[x] = detect.get_flags()
49    sys.path.remove(tmppath)
50    sys.modules.pop("detect")
51
52methods.save_active_platforms(active_platforms, active_platform_ids)
53
54custom_tools = ["default"]
55
56platform_arg = ARGUMENTS.get("platform", ARGUMENTS.get("p", False))
57
58if os.name == "nt" and (platform_arg == "android" or ARGUMENTS.get("use_mingw", False)):
59    custom_tools = ["mingw"]
60elif platform_arg == "javascript":
61    # Use generic POSIX build toolchain for Emscripten.
62    custom_tools = ["cc", "c++", "ar", "link", "textfile", "zip"]
63
64env_base = Environment(tools=custom_tools)
65if "TERM" in os.environ:
66    env_base["ENV"]["TERM"] = os.environ["TERM"]
67env_base.AppendENVPath("PATH", os.getenv("PATH"))
68env_base.AppendENVPath("PKG_CONFIG_PATH", os.getenv("PKG_CONFIG_PATH"))
69env_base.disabled_modules = []
70env_base.use_ptrcall = False
71env_base.module_version_string = ""
72env_base.msvc = False
73
74env_base.__class__.disable_module = methods.disable_module
75
76env_base.__class__.add_module_version_string = methods.add_module_version_string
77
78env_base.__class__.add_source_files = methods.add_source_files
79env_base.__class__.use_windows_spawn_fix = methods.use_windows_spawn_fix
80env_base.__class__.split_lib = methods.split_lib
81
82env_base.__class__.add_shared_library = methods.add_shared_library
83env_base.__class__.add_library = methods.add_library
84env_base.__class__.add_program = methods.add_program
85env_base.__class__.CommandNoCache = methods.CommandNoCache
86env_base.__class__.disable_warnings = methods.disable_warnings
87
88env_base["x86_libtheora_opt_gcc"] = False
89env_base["x86_libtheora_opt_vc"] = False
90
91# avoid issues when building with different versions of python out of the same directory
92env_base.SConsignFile(".sconsign{0}.dblite".format(pickle.HIGHEST_PROTOCOL))
93
94# Build options
95
96customs = ["custom.py"]
97
98profile = ARGUMENTS.get("profile", False)
99if profile:
100    if os.path.isfile(profile):
101        customs.append(profile)
102    elif os.path.isfile(profile + ".py"):
103        customs.append(profile + ".py")
104
105opts = Variables(customs, ARGUMENTS)
106
107# Target build options
108opts.Add("arch", "Platform-dependent architecture (arm/arm64/x86/x64/mips/...)", "")
109opts.Add(EnumVariable("bits", "Target platform bits", "default", ("default", "32", "64")))
110opts.Add("p", "Platform (alias for 'platform')", "")
111opts.Add("platform", "Target platform (%s)" % ("|".join(platform_list),), "")
112opts.Add(EnumVariable("target", "Compilation target", "debug", ("debug", "release_debug", "release")))
113opts.Add(EnumVariable("optimize", "Optimization type", "speed", ("speed", "size")))
114opts.Add(BoolVariable("tools", "Build the tools (a.k.a. the Godot editor)", True))
115opts.Add(BoolVariable("use_lto", "Use link-time optimization", False))
116opts.Add(BoolVariable("use_precise_math_checks", "Math checks use very precise epsilon (debug option)", False))
117
118# Components
119opts.Add(BoolVariable("deprecated", "Enable deprecated features", True))
120opts.Add(BoolVariable("gdscript", "Enable GDScript support", True))
121opts.Add(BoolVariable("minizip", "Enable ZIP archive support using minizip", True))
122opts.Add(BoolVariable("xaudio2", "Enable the XAudio2 audio driver", False))
123opts.Add("custom_modules", "A list of comma-separated directory paths containing custom modules to build.", "")
124
125# Advanced options
126opts.Add(BoolVariable("verbose", "Enable verbose output for the compilation", False))
127opts.Add(BoolVariable("progress", "Show a progress indicator during compilation", True))
128opts.Add(EnumVariable("warnings", "Level of compilation warnings", "all", ("extra", "all", "moderate", "no")))
129opts.Add(BoolVariable("werror", "Treat compiler warnings as errors", False))
130opts.Add(BoolVariable("dev", "If yes, alias for verbose=yes warnings=extra werror=yes", False))
131opts.Add("extra_suffix", "Custom extra suffix added to the base filename of all generated binary files", "")
132opts.Add(BoolVariable("vsproj", "Generate a Visual Studio solution", False))
133opts.Add(EnumVariable("macports_clang", "Build using Clang from MacPorts", "no", ("no", "5.0", "devel")))
134opts.Add(
135    BoolVariable(
136        "split_libmodules",
137        "Split intermediate libmodules.a in smaller chunks to prevent exceeding linker command line size (forced to True when using MinGW)",
138        False,
139    )
140)
141opts.Add(BoolVariable("disable_3d", "Disable 3D nodes for a smaller executable", False))
142opts.Add(BoolVariable("disable_advanced_gui", "Disable advanced GUI nodes and behaviors", False))
143opts.Add(BoolVariable("no_editor_splash", "Don't use the custom splash screen for the editor", False))
144opts.Add("system_certs_path", "Use this path as SSL certificates default for editor (for package maintainers)", "")
145
146# Thirdparty libraries
147# opts.Add(BoolVariable('builtin_assimp', "Use the built-in Assimp library", True))
148opts.Add(BoolVariable("builtin_bullet", "Use the built-in Bullet library", True))
149opts.Add(BoolVariable("builtin_certs", "Use the built-in SSL certificates bundles", True))
150opts.Add(BoolVariable("builtin_enet", "Use the built-in ENet library", True))
151opts.Add(BoolVariable("builtin_freetype", "Use the built-in FreeType library", True))
152opts.Add(BoolVariable("builtin_libogg", "Use the built-in libogg library", True))
153opts.Add(BoolVariable("builtin_libpng", "Use the built-in libpng library", True))
154opts.Add(BoolVariable("builtin_libtheora", "Use the built-in libtheora library", True))
155opts.Add(BoolVariable("builtin_libvorbis", "Use the built-in libvorbis library", True))
156opts.Add(BoolVariable("builtin_libvpx", "Use the built-in libvpx library", True))
157opts.Add(BoolVariable("builtin_libwebp", "Use the built-in libwebp library", True))
158opts.Add(BoolVariable("builtin_wslay", "Use the built-in wslay library", True))
159opts.Add(BoolVariable("builtin_mbedtls", "Use the built-in mbedTLS library", True))
160opts.Add(BoolVariable("builtin_miniupnpc", "Use the built-in miniupnpc library", True))
161opts.Add(BoolVariable("builtin_opus", "Use the built-in Opus library", True))
162opts.Add(BoolVariable("builtin_pcre2", "Use the built-in PCRE2 library", True))
163opts.Add(BoolVariable("builtin_pcre2_with_jit", "Use JIT compiler for the built-in PCRE2 library", True))
164opts.Add(BoolVariable("builtin_recast", "Use the built-in Recast library", True))
165opts.Add(BoolVariable("builtin_squish", "Use the built-in squish library", True))
166opts.Add(BoolVariable("builtin_xatlas", "Use the built-in xatlas library", True))
167opts.Add(BoolVariable("builtin_zlib", "Use the built-in zlib library", True))
168opts.Add(BoolVariable("builtin_zstd", "Use the built-in Zstd library", True))
169
170# Compilation environment setup
171opts.Add("CXX", "C++ compiler")
172opts.Add("CC", "C compiler")
173opts.Add("LINK", "Linker")
174opts.Add("CCFLAGS", "Custom flags for both the C and C++ compilers")
175opts.Add("CFLAGS", "Custom flags for the C compiler")
176opts.Add("CXXFLAGS", "Custom flags for the C++ compiler")
177opts.Add("LINKFLAGS", "Custom flags for the linker")
178
179# add platform specific options
180
181for k in platform_opts.keys():
182    opt_list = platform_opts[k]
183    for o in opt_list:
184        opts.Add(o)
185
186# Update the environment now as the "custom_modules" option may be
187# defined in a file rather than specified via the command line.
188opts.Update(env_base)
189
190# Detect modules.
191modules_detected = OrderedDict()
192module_search_paths = ["modules"]  # Built-in path.
193
194if env_base["custom_modules"]:
195    paths = env_base["custom_modules"].split(",")
196    for p in paths:
197        try:
198            module_search_paths.append(methods.convert_custom_modules_path(p))
199        except ValueError as e:
200            print(e)
201            sys.exit(255)
202
203for path in module_search_paths:
204    # Note: custom modules can override built-in ones.
205    modules_detected.update(methods.detect_modules(path))
206    include_path = os.path.dirname(path)
207    if include_path:
208        env_base.Prepend(CPPPATH=[include_path])
209
210# Add module options
211for name, path in modules_detected.items():
212    enabled = True
213    sys.path.insert(0, path)
214    import config
215
216    try:
217        enabled = config.is_enabled()
218    except AttributeError:
219        pass
220    sys.path.remove(path)
221    sys.modules.pop("config")
222    opts.Add(BoolVariable("module_" + name + "_enabled", "Enable module '%s'" % (name,), enabled))
223
224methods.write_modules(modules_detected)
225
226# Update the environment again after all the module options are added.
227opts.Update(env_base)
228Help(opts.GenerateHelpText(env_base))
229
230# add default include paths
231
232env_base.Prepend(CPPPATH=["#"])
233
234# configure ENV for platform
235env_base.platform_exporters = platform_exporters
236env_base.platform_apis = platform_apis
237
238if env_base["use_precise_math_checks"]:
239    env_base.Append(CPPDEFINES=["PRECISE_MATH_CHECKS"])
240
241if env_base["target"] == "debug":
242    env_base.Append(CPPDEFINES=["DEBUG_MEMORY_ALLOC", "DISABLE_FORCED_INLINE"])
243
244    # The two options below speed up incremental builds, but reduce the certainty that all files
245    # will properly be rebuilt. As such, we only enable them for debug (dev) builds, not release.
246
247    # To decide whether to rebuild a file, use the MD5 sum only if the timestamp has changed.
248    # http://scons.org/doc/production/HTML/scons-user/ch06.html#idm139837621851792
249    env_base.Decider("MD5-timestamp")
250    # Use cached implicit dependencies by default. Can be overridden by specifying `--implicit-deps-changed` in the command line.
251    # http://scons.org/doc/production/HTML/scons-user/ch06s04.html
252    env_base.SetOption("implicit_cache", 1)
253
254if env_base["no_editor_splash"]:
255    env_base.Append(CPPDEFINES=["NO_EDITOR_SPLASH"])
256
257if not env_base["deprecated"]:
258    env_base.Append(CPPDEFINES=["DISABLE_DEPRECATED"])
259
260env_base.platforms = {}
261
262selected_platform = ""
263
264if env_base["platform"] != "":
265    selected_platform = env_base["platform"]
266elif env_base["p"] != "":
267    selected_platform = env_base["p"]
268    env_base["platform"] = selected_platform
269else:
270    # Missing `platform` argument, try to detect platform automatically
271    if sys.platform.startswith("linux"):
272        selected_platform = "x11"
273    elif sys.platform == "darwin":
274        selected_platform = "osx"
275    elif sys.platform == "win32":
276        selected_platform = "windows"
277    else:
278        print("Could not detect platform automatically. Supported platforms:")
279        for x in platform_list:
280            print("\t" + x)
281        print("\nPlease run SCons again and select a valid platform: platform=<string>")
282
283    if selected_platform != "":
284        print("Automatically detected platform: " + selected_platform)
285        env_base["platform"] = selected_platform
286
287if selected_platform in ["linux", "bsd", "linuxbsd"]:
288    if selected_platform == "linuxbsd":
289        # Alias for forward compatibility.
290        print('Platform "linuxbsd" is still called "x11" in Godot 3.2.x. Building for platform "x11".')
291    # Alias for convenience.
292    selected_platform = "x11"
293    env_base["platform"] = selected_platform
294
295if selected_platform in platform_list:
296    tmppath = "./platform/" + selected_platform
297    sys.path.insert(0, tmppath)
298    import detect
299
300    if "create" in dir(detect):
301        env = detect.create(env_base)
302    else:
303        env = env_base.Clone()
304
305    # Compilation DB requires SCons 3.1.1+.
306    from SCons import __version__ as scons_raw_version
307
308    scons_ver = env._get_major_minor_revision(scons_raw_version)
309
310    if scons_ver >= (4, 0, 0):
311        env.Tool("compilation_db")
312        env.Alias("compiledb", env.CompilationDatabase())
313
314    if env["dev"]:
315        env["verbose"] = True
316        env["warnings"] = "extra"
317        env["werror"] = True
318
319    if env["vsproj"]:
320        env.vs_incs = []
321        env.vs_srcs = []
322
323        def AddToVSProject(sources):
324            for x in sources:
325                if type(x) == type(""):
326                    fname = env.File(x).path
327                else:
328                    fname = env.File(x)[0].path
329                pieces = fname.split(".")
330                if len(pieces) > 0:
331                    basename = pieces[0]
332                    basename = basename.replace("\\\\", "/")
333                    if os.path.isfile(basename + ".h"):
334                        env.vs_incs = env.vs_incs + [basename + ".h"]
335                    elif os.path.isfile(basename + ".hpp"):
336                        env.vs_incs = env.vs_incs + [basename + ".hpp"]
337                    if os.path.isfile(basename + ".c"):
338                        env.vs_srcs = env.vs_srcs + [basename + ".c"]
339                    elif os.path.isfile(basename + ".cpp"):
340                        env.vs_srcs = env.vs_srcs + [basename + ".cpp"]
341
342        env.AddToVSProject = AddToVSProject
343
344    env.extra_suffix = ""
345
346    if env["extra_suffix"] != "":
347        env.extra_suffix += "." + env["extra_suffix"]
348
349    # Environment flags
350    CCFLAGS = env.get("CCFLAGS", "")
351    env["CCFLAGS"] = ""
352    env.Append(CCFLAGS=str(CCFLAGS).split())
353
354    CFLAGS = env.get("CFLAGS", "")
355    env["CFLAGS"] = ""
356    env.Append(CFLAGS=str(CFLAGS).split())
357
358    CXXFLAGS = env.get("CXXFLAGS", "")
359    env["CXXFLAGS"] = ""
360    env.Append(CXXFLAGS=str(CXXFLAGS).split())
361
362    LINKFLAGS = env.get("LINKFLAGS", "")
363    env["LINKFLAGS"] = ""
364    env.Append(LINKFLAGS=str(LINKFLAGS).split())
365
366    # Platform specific flags
367    flag_list = platform_flags[selected_platform]
368    for f in flag_list:
369        if not (f[0] in ARGUMENTS):  # allow command line to override platform flags
370            env[f[0]] = f[1]
371
372    # Must happen after the flags definition, so that they can be used by platform detect
373    detect.configure(env)
374
375    # Set our C and C++ standard requirements.
376    # Prepending to make it possible to override
377    # This needs to come after `configure`, otherwise we don't have env.msvc.
378    if not env.msvc:
379        # Specifying GNU extensions support explicitly, which are supported by
380        # both GCC and Clang. This mirrors GCC and Clang's current default
381        # compile flags if no -std is specified.
382        env.Prepend(CFLAGS=["-std=gnu11"])
383        env.Prepend(CXXFLAGS=["-std=gnu++14"])
384    else:
385        # MSVC doesn't have clear C standard support, /std only covers C++.
386        # We apply it to CCFLAGS (both C and C++ code) in case it impacts C features.
387        env.Prepend(CCFLAGS=["/std:c++14"])
388
389    # Configure compiler warnings
390    if env.msvc:
391        # Truncations, narrowing conversions, signed/unsigned comparisons...
392        disable_nonessential_warnings = ["/wd4267", "/wd4244", "/wd4305", "/wd4018", "/wd4800"]
393        if env["warnings"] == "extra":
394            env.Append(CCFLAGS=["/Wall"])  # Implies /W4
395        elif env["warnings"] == "all":
396            env.Append(CCFLAGS=["/W3"] + disable_nonessential_warnings)
397        elif env["warnings"] == "moderate":
398            env.Append(CCFLAGS=["/W2"] + disable_nonessential_warnings)
399        else:  # 'no'
400            env.Append(CCFLAGS=["/w"])
401        # Set exception handling model to avoid warnings caused by Windows system headers.
402        env.Append(CCFLAGS=["/EHsc"])
403        if env["werror"]:
404            env.Append(CCFLAGS=["/WX"])
405        # Force to use Unicode encoding
406        env.Append(MSVC_FLAGS=["/utf8"])
407    else:  # Rest of the world
408        version = methods.get_compiler_version(env) or [-1, -1]
409
410        shadow_local_warning = []
411        all_plus_warnings = ["-Wwrite-strings"]
412
413        if methods.using_gcc(env):
414            env.Append(CCFLAGS=["-Wno-misleading-indentation"])
415            if version[0] >= 7:
416                shadow_local_warning = ["-Wshadow-local"]
417
418        if env["warnings"] == "extra":
419            # Note: enable -Wimplicit-fallthrough for Clang (already part of -Wextra for GCC)
420            # once we switch to C++11 or later (necessary for our FALLTHROUGH macro).
421            env.Append(CCFLAGS=["-Wall", "-Wextra", "-Wno-unused-parameter"] + all_plus_warnings + shadow_local_warning)
422            env.Append(CXXFLAGS=["-Wctor-dtor-privacy", "-Wnon-virtual-dtor"])
423            if methods.using_gcc(env):
424                env.Append(
425                    CCFLAGS=[
426                        "-Walloc-zero",
427                        "-Wduplicated-branches",
428                        "-Wduplicated-cond",
429                        "-Wstringop-overflow=4",
430                        "-Wlogical-op",
431                    ]
432                )
433                env.Append(CXXFLAGS=["-Wnoexcept", "-Wplacement-new=1"])
434                if version[0] >= 9:
435                    env.Append(CCFLAGS=["-Wattribute-alias=2"])
436        elif env["warnings"] == "all":
437            env.Append(CCFLAGS=["-Wall"] + shadow_local_warning)
438        elif env["warnings"] == "moderate":
439            env.Append(CCFLAGS=["-Wall", "-Wno-unused"] + shadow_local_warning)
440        else:  # 'no'
441            env.Append(CCFLAGS=["-w"])
442        if env["werror"]:
443            env.Append(CCFLAGS=["-Werror"])
444        else:  # always enable those errors
445            env.Append(CCFLAGS=["-Werror=return-type"])
446
447    if hasattr(detect, "get_program_suffix"):
448        suffix = "." + detect.get_program_suffix()
449    else:
450        suffix = "." + selected_platform
451
452    if env["target"] == "release":
453        if env["tools"]:
454            print("Tools can only be built with targets 'debug' and 'release_debug'.")
455            sys.exit(255)
456        suffix += ".opt"
457        env.Append(CPPDEFINES=["NDEBUG"])
458
459    elif env["target"] == "release_debug":
460        if env["tools"]:
461            suffix += ".opt.tools"
462        else:
463            suffix += ".opt.debug"
464    else:
465        if env["tools"]:
466            suffix += ".tools"
467        else:
468            suffix += ".debug"
469
470    if env["arch"] != "":
471        suffix += "." + env["arch"]
472    elif env["bits"] == "32":
473        suffix += ".32"
474    elif env["bits"] == "64":
475        suffix += ".64"
476
477    suffix += env.extra_suffix
478
479    sys.path.remove(tmppath)
480    sys.modules.pop("detect")
481
482    modules_enabled = OrderedDict()
483    env.module_icons_paths = []
484    env.doc_class_path = {}
485
486    for name, path in modules_detected.items():
487        if not env["module_" + name + "_enabled"]:
488            continue
489        sys.path.insert(0, path)
490        env.current_module = name
491        import config
492
493        # can_build changed number of arguments between 3.0 (1) and 3.1 (2),
494        # so try both to preserve compatibility for 3.0 modules
495        can_build = False
496        try:
497            can_build = config.can_build(env, selected_platform)
498        except TypeError:
499            print(
500                "Warning: module '%s' uses a deprecated `can_build` "
501                "signature in its config.py file, it should be "
502                "`can_build(env, platform)`." % x
503            )
504            can_build = config.can_build(selected_platform)
505        if can_build:
506            config.configure(env)
507            # Get doc classes paths (if present)
508            try:
509                doc_classes = config.get_doc_classes()
510                doc_path = config.get_doc_path()
511                for c in doc_classes:
512                    env.doc_class_path[c] = path + "/" + doc_path
513            except:
514                pass
515            # Get icon paths (if present)
516            try:
517                icons_path = config.get_icons_path()
518                env.module_icons_paths.append(path + "/" + icons_path)
519            except:
520                # Default path for module icons
521                env.module_icons_paths.append(path + "/" + "icons")
522            modules_enabled[name] = path
523
524        sys.path.remove(path)
525        sys.modules.pop("config")
526
527    env.module_list = modules_enabled
528
529    methods.update_version(env.module_version_string)
530
531    env["PROGSUFFIX"] = suffix + env.module_version_string + env["PROGSUFFIX"]
532    env["OBJSUFFIX"] = suffix + env["OBJSUFFIX"]
533    # (SH)LIBSUFFIX will be used for our own built libraries
534    # LIBSUFFIXES contains LIBSUFFIX and SHLIBSUFFIX by default,
535    # so we need to append the default suffixes to keep the ability
536    # to link against thirdparty libraries (.a, .so, .lib, etc.).
537    if os.name == "nt":
538        # On Windows, only static libraries and import libraries can be
539        # statically linked - both using .lib extension
540        env["LIBSUFFIXES"] += [env["LIBSUFFIX"]]
541    else:
542        env["LIBSUFFIXES"] += [env["LIBSUFFIX"], env["SHLIBSUFFIX"]]
543    env["LIBSUFFIX"] = suffix + env["LIBSUFFIX"]
544    env["SHLIBSUFFIX"] = suffix + env["SHLIBSUFFIX"]
545
546    if env.use_ptrcall:
547        env.Append(CPPDEFINES=["PTRCALL_ENABLED"])
548    if env["tools"]:
549        env.Append(CPPDEFINES=["TOOLS_ENABLED"])
550    if env["disable_3d"]:
551        if env["tools"]:
552            print(
553                "Build option 'disable_3d=yes' cannot be used with 'tools=yes' (editor), "
554                "only with 'tools=no' (export template)."
555            )
556            sys.exit(255)
557        else:
558            env.Append(CPPDEFINES=["_3D_DISABLED"])
559    if env["gdscript"]:
560        env.Append(CPPDEFINES=["GDSCRIPT_ENABLED"])
561    if env["disable_advanced_gui"]:
562        if env["tools"]:
563            print(
564                "Build option 'disable_advanced_gui=yes' cannot be used with 'tools=yes' (editor), "
565                "only with 'tools=no' (export template)."
566            )
567            sys.exit(255)
568        else:
569            env.Append(CPPDEFINES=["ADVANCED_GUI_DISABLED"])
570    if env["minizip"]:
571        env.Append(CPPDEFINES=["MINIZIP_ENABLED"])
572
573    editor_module_list = ["regex"]
574    for x in editor_module_list:
575        if not env["module_" + x + "_enabled"]:
576            if env["tools"]:
577                print(
578                    "Build option 'module_" + x + "_enabled=no' cannot be used with 'tools=yes' (editor), "
579                    "only with 'tools=no' (export template)."
580                )
581                sys.exit(255)
582
583    if not env["verbose"]:
584        methods.no_verbose(sys, env)
585
586    if not env["platform"] == "server":  # FIXME: detect GLES3
587        env.Append(
588            BUILDERS={
589                "GLES3_GLSL": env.Builder(
590                    action=run_in_subprocess(gles_builders.build_gles3_headers), suffix="glsl.gen.h", src_suffix=".glsl"
591                )
592            }
593        )
594        env.Append(
595            BUILDERS={
596                "GLES2_GLSL": env.Builder(
597                    action=run_in_subprocess(gles_builders.build_gles2_headers), suffix="glsl.gen.h", src_suffix=".glsl"
598                )
599            }
600        )
601
602    scons_cache_path = os.environ.get("SCONS_CACHE")
603    if scons_cache_path != None:
604        CacheDir(scons_cache_path)
605        print("Scons cache enabled... (path: '" + scons_cache_path + "')")
606
607    Export("env")
608
609    # build subdirs, the build order is dependent on link order.
610
611    SConscript("core/SCsub")
612    SConscript("servers/SCsub")
613    SConscript("scene/SCsub")
614    SConscript("editor/SCsub")
615    SConscript("drivers/SCsub")
616
617    SConscript("platform/SCsub")
618    SConscript("modules/SCsub")
619    SConscript("main/SCsub")
620
621    SConscript("platform/" + selected_platform + "/SCsub")  # build selected platform
622
623    # Microsoft Visual Studio Project Generation
624    if env["vsproj"]:
625        env["CPPPATH"] = [Dir(path) for path in env["CPPPATH"]]
626        methods.generate_vs_project(env, GetOption("num_jobs"))
627        methods.generate_cpp_hint_file("cpp.hint")
628
629    # Check for the existence of headers
630    conf = Configure(env)
631    if "check_c_headers" in env:
632        for header in env["check_c_headers"]:
633            if conf.CheckCHeader(header[0]):
634                env.AppendUnique(CPPDEFINES=[header[1]])
635
636elif selected_platform != "":
637    if selected_platform == "list":
638        print("The following platforms are available:\n")
639    else:
640        print('Invalid target platform "' + selected_platform + '".')
641        print("The following platforms were detected:\n")
642
643    for x in platform_list:
644        print("\t" + x)
645
646    print("\nPlease run SCons again and select a valid platform: platform=<string>")
647
648    if selected_platform == "list":
649        # Exit early to suppress the rest of the built-in SCons messages
650        sys.exit(0)
651    else:
652        sys.exit(255)
653
654# The following only makes sense when the 'env' is defined, and assumes it is.
655if "env" in locals():
656    methods.show_progress(env)
657    # TODO: replace this with `env.Dump(format="json")`
658    # once we start requiring SCons 4.0 as min version.
659    methods.dump(env)
660