-
-
Notifications
You must be signed in to change notification settings - Fork 69
Expand file tree
/
Copy pathSConstruct
More file actions
251 lines (241 loc) · 16.1 KB
/
Copy pathSConstruct
File metadata and controls
251 lines (241 loc) · 16.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
# Build script for NVGT using the scons build system.
# NVGT - NonVisual Gaming Toolkit (https://nvgt.dev)
# Copyright (c) 2022-2025 Sam Tupy
# license: zlib
import os, multiprocessing, tempfile
Help("""
Available custom build switches for NVGT:
copylibs=0 or 1 (default 1): Copy shared libraries to release/lib after building?
debug=0 or 1 (default 0): Include debug symbols in the resulting binaries?
deps=build, download, or unmanaged (default download): How to fetch dependencies required to build NVGT? build = use vcpkg to build from source, download = download prebuilt binaries from nvgt.dev if newer than existing, unmanaged = assume dependencies are in place.
deps_path=path: Optional location where dependencies are stored? Defaults to a folder named after the platform in the repository root.
no_upx=0 or 1 (default 1): Disable UPX stubs?
no_plugins=0 or 1 (default 0): Disable the plugin system entirely?
no_shared_plugins=0 or 1 (default 0): Only compile plugins statically?
no_stubs=0 or 1 (default 0): Disable compilation of all stubs?
no_user=0 or 1 (default 0): Pretend that the user directory doesn't exist?
no_<plugname>_plugin=1: Disable a plugin by name.
static_<plugname>_plugin=1: Cause the given plugin to be linked statically if possible.
stub_obfuscation=0 or 1 (default 0): Obfuscate some Angelscript function registration strings in the resulting stubs? Could make them bigger.
warnings (0 or 1, default 0): enable compiler warnings?
warnings_as_errors (0 or 1, default 0): treat compiler warnings as errors?
You can also run scons install (for now only on windows) to install the build into C:/nvgt. STILL WIP!
Note that custom switches or targets may be added by any plugin SConscript and may not be documented here.
""")
# setup
if ARGUMENTS.get("target", "") == "android":
env = Environment(tools=['mingw'])
else:
env = Environment()
# Prevent scons from wiping out the environment for certain tools, e.g. scan-build
env["CC"] = os.getenv("CC") or env["CC"]
env["CXX"] = os.getenv("CXX") or env["CXX"]
env["ENV"].update(x for x in os.environ.items() if x[0].startswith("CCC_"))
Decider('content-timestamp')
env.Alias("install", "c:/nvgt")
SConscript("build/upx_sconscript.py", exports = ["env"])
SConscript("build/version_sconscript.py", exports = ["env"])
env.SetOption("num_jobs", multiprocessing.cpu_count())
SConscript("build/osdev_sconscript.py", exports = ["env"])
SConscript("vcpkg/_SConscript", exports = ["env"])
if ARGUMENTS.get("debug", "0") == "1":
env.Tool('compilation_db')
cdb = env.CompilationDatabase()
Alias('cdb', cdb)
# Platform setup and system libraries
common_libs = ["PocoJSON", "PocoNet", "PocoNetSSL", "PocoUtil", "PocoXML", "PocoCrypto", "PocoFoundation", "expat", "angelscript", "SDL3_ttf", "freetype", "bz2", "enet", "reactphysics3d", "ssl", "crypto", "utf8proc", "pcre2-8", "vorbisfile", "vorbisenc", "vorbis", "ogg", "opusfile", "opusenc", "opus", "tinyexpr", "tiny-aes-c", "ffi"]
if env["NVGT_TARGET"] == "windows":
deb_rel_flags = ["/MTd", "/Od", "/Z7"] if ARGUMENTS.get("debug", "0") == "1" else ["/MT", "/O2"]
env.Append(CCFLAGS = ["/EHsc", "/J", "/utf-8", "/Gy", "/std:c++20", "/GF", "/Zc:inline", "/bigobj", "/permissive-", "/W3" if ARGUMENTS.get("warnings", "0") == "1" else "", "/WX" if ARGUMENTS.get("warnings_as_errors", "0") == "1" else ""] + deb_rel_flags)
env.Append(LINKFLAGS = ["/NOEXP", "/NOIMPLIB"], no_import_lib = 1)
env.Append(LIBS = ["Kernel32", "User32", "imm32", "OneCoreUAP", "dinput8", "dxguid", "gdi32", "winspool", "shell32", "iphlpapi", "ole32", "oleaut32", "delayimp", "uuid", "comdlg32", "advapi32", "netapi32", "winmm", "version", "crypt32", "bcrypt", "normaliz", "wldap32", "ws2_32", "ntdll"])
else:
env.Append(CXXFLAGS = ["-fms-extensions", "-std=c++20", "-fpermissive", "-O0" if ARGUMENTS.get("debug", 0) == "1" else "-O3", "-Wno-narrowing", "-Wno-int-to-pointer-cast", "-Wno-delete-incomplete", "-Wno-unused-result", "-g" if ARGUMENTS.get("debug", 0) == "1" else "", "-Wall" if ARGUMENTS.get("warnings", "0") == "1" else "", "-Wextra" if ARGUMENTS.get("warnings", "0") == "1" else "", "-Werror" if ARGUMENTS.get("warnings_as_errors", "0") == "1" else ""], LIBS = ["m"])
if env["NVGT_TARGET"] == "macos":
# homebrew paths and other libraries/flags for MacOS
env.Append(CCFLAGS = ["-mmacosx-version-min=12.0", "-arch", "arm64", "-arch", "x86_64"], LINKFLAGS = ["-arch", "arm64", "-arch", "x86_64"])
env["FRAMEWORKPREFIX"] = "-weak_framework"
elif env["NVGT_TARGET"] == "ios":
import subprocess
env["ENV"]["SDKROOT"] = subprocess.check_output(["xcrun", "-sdk", "iphoneos", "--show-sdk-path"]).decode().strip()
env.Append(CCFLAGS = ["-arch", "arm64", "-xobjective-c++"], LINKFLAGS = ["-arch", "arm64"], LIBS=["mysofa", "pffft"])
env["FRAMEWORKPREFIX"] = "-weak_framework"
elif env["NVGT_TARGET"] == "linux":
# enable the gold linker, strip the resulting binaries, and add /usr/local/lib to the libpath because it seems we aren't finding libraries unless we do manually.
env.Append(CPPPATH = ["lindev/include", "/usr/local/include"], LIBPATH = ["lindev/lib", "/usr/local/lib", "/usr/lib/x86_64-linux-gnu"], LINKFLAGS = ["-fuse-ld=gold", "-g" if ARGUMENTS.get("debug", 0) == "1" else "-s"])
elif env["NVGT_TARGET"] == "android":
SConscript("build/android_sconscript.py", exports = ["env"])
env.Append(LIBS = common_libs + ["z", "GLESv1_CM", "GLESv2", "OpenSLES", "log", "android"])
env.Append(CPPDEFINES = ["POCO_STATIC", "POCO_NO_AUTOMATIC_LIBS", "UNIVERSAL_SPEECH_STATIC", "DEBUG" if ARGUMENTS.get("debug", "0") == "1" else "NDEBUG", "UNICODE"])
env.Append(CPPPATH = ["#ASAddon/include", "#dep"], LIBPATH = ["#build/lib"])
env["PLUGIN_DEST_DIR"] = "#release/lib_android" if env["NVGT_TARGET"] == "android" else "#release/lib"
# plugins
static_plugins = []
static_plugins_object = None
plugin_env = env.Clone()
if ARGUMENTS.get("no_plugins", "0") == "0":
try:
# First, read the list of static plugins we wish to link if available.
with open(os.path.join("user", "static_plugins"), "r") as f:
lines = f.readlines()
for l in lines:
if not l or l.startswith("#"): continue
static_plugins.append(l.strip())
except FileNotFoundError: pass
if env["NVGT_TARGET"] == "android":
plugin_env.Append(CXXFLAGS = ["-fPIC"])
plugin_env["SHLIBPREFIX"] = ""
# Then loop through all known plugins and build them.
for s in Glob("plugin/*/_SConscript") + Glob("plugin/*/SConscript") + Glob("extra/plugin/integrated/*/_SConscript") + Glob("extra/plugin/integrated/*/SConscript"):
plugname = str(s).split(os.path.sep)[-2]
if ARGUMENTS.get(f"no_{plugname}_plugin", "0") == "1": continue
if ARGUMENTS.get(f"static_{plugname}_plugin", "0") == "1" and not plugname in static_plugins: static_plugins.append(plugname)
# Build the plugin.
# A list of static libraries NVGT should link with is returned if the plugin generates any.
plug = SConscript(s, variant_dir = f"build/obj_plugin/{plugname}", duplicate = 0, exports = {"env": plugin_env, "nvgt_env": env})
if plug and plugname in static_plugins: env.Append(LIBS = plug)
# Finally generate nvgt_plugins.cpp
static_plugins_path = os.path.join(tempfile.gettempdir(), "nvgt_plugins")
if len(static_plugins) > 0:
with open(static_plugins_path + ".cpp", "w") as f:
f.write("#define NVGT_LOAD_STATIC_PLUGINS\n#include <nvgt_plugin.h>\n")
for plugin in static_plugins: f.write(f"static_plugin({plugin})" + "\n")
static_plugins_object = env.Object(static_plugins_path, static_plugins_path + ".cpp", CPPPATH = env["CPPPATH"] + ["#src"])
# Project libraries
env.Append(LIBS = ["deps"] + common_libs + ["zs" if env["NVGT_TARGET"] == "windows" else "z", "SDL3", "phonon", "ASAddon"])
if env["NVGT_TARGET"] == "windows": env.Append(LIBS = ["UniversalSpeechStatic"])
# nvgt itself
sources = [str(i)[4:] for i in Glob("src/*.cpp")]
if env["NVGT_TARGET"] != "android" and "android.cpp" in sources:
sources.remove("android.cpp")
if env["NVGT_TARGET"] != "windows" and "win.cpp" in sources: sources.remove("win.cpp")
if env["NVGT_TARGET"] != "linux" and "linux.cpp" in sources: sources.remove("linux.cpp")
if "version.cpp" in sources: sources.remove("version.cpp")
env.Command(target = "src/version.cpp", source = ["src/" + i for i in sources], action = env["generate_version"])
version_object = env.Object("build/obj_src/version", "src/version.cpp") # Things get weird if we do this after VariantDir.
VariantDir("build/obj_src", "src", duplicate = 0)
env.Append(CPPDEFINES = ["NVGT_BUILDING", "NO_OBFUSCATE"])
if env["NVGT_TARGET"] == "windows":
deb_rel_flags = ["/DEBUG", "/INCREMENTAL:NO"] if ARGUMENTS.get("debug", "0") == "1" else ["/OPT:ICF=3"]
env.Append(CPPDEFINES = ["_SILENCE_CXX20_OLD_SHARED_PTR_ATOMIC_SUPPORT_DEPRECATION_WARNING"], LINKFLAGS = ["/ignore:4099", "/delayload:phonon.dll"] + deb_rel_flags)
elif env["NVGT_TARGET"] in ("macos", "ios"):
sources.append("apple.mm")
# We must link Apple frameworks here rather than above in the system libraries section to insure that they don't get linked with random plugins.
env.Append(FRAMEWORKS = ["AudioToolbox", "AVFoundation", "CoreAudio", "CoreFoundation", "CoreHaptics", "CoreMedia", "CoreVideo", "GameController", "IOKit", "Metal", "QuartzCore", "Security"])
if env["NVGT_TARGET"] == "macos":
env.Append(FRAMEWORKS = ["AppKit", "Carbon", "Cocoa", "ForceFeedback", "UniformTypeIdentifiers"])
env.Append(LINKFLAGS = ["-Wl,-rpath,'@loader_path',-rpath,'@loader_path/lib',-rpath,'@loader_path/../Frameworks',-dead_strip_dylibs", "-mmacosx-version-min=14.0"])
else:
env.Append(FRAMEWORKS = ["CoreBluetooth", "CoreGraphics", "CoreMotion", "Foundation", "OpenGLES", "UIKit"])
env.Append(CCFLAGS = ["-miphoneos-version-min=16.0"], LINKFLAGS = ["-miphoneos-version-min=16.0"])
env.Append(LIBS = ["objc"])
elif env["NVGT_TARGET"] == "linux":
env.Append(LINKFLAGS = ["-Wl,-rpath,'$$ORIGIN/.',-rpath,'$$ORIGIN/lib'"])
if ARGUMENTS.get("no_user", "0") == "0":
if os.path.isfile("user/nvgt_config.h"):
env.Append(CPPDEFINES = ["NVGT_USER_CONFIG"])
for s in ["_SConscript", "SConscript"]:
if os.path.isfile(f"user/{s}"):
SConscript(f"user/{s}", exports = {"plugin_env": plugin_env, "nvgt_env": env})
break # only execute one script from here
SConscript("ASAddon/_SConscript", variant_dir = "build/obj_ASAddon", duplicate = 0, exports = "env")
SConscript("dep/_SConscript", variant_dir = "build/obj_dep", duplicate = 0, exports = "env")
# We'll clone the environment for stubs now so that we can then add any extra libraries that are not needed for stubs to the main nvgt environment.
stub_env = env.Clone(PROGSUFFIX = ".bin")
if env["NVGT_TARGET"] == "windows":
env.Append(LINKFLAGS = ["/delayload:plist-2.0.dll", "/delayload:archive.dll"])
env.Append(LIBS = ["plist-2.0", "archive"])
env["no_import_lib"] = 0
elif env["NVGT_TARGET"] == "android": env["no_import_lib"] = 1
elif env["NVGT_TARGET"] != "ios": env.Append(LIBS = ["plist-2.0", "archive"])
extra_objects = [version_object]
if static_plugins_object: extra_objects.append(static_plugins_object)
if env["NVGT_TARGET"] not in ["android", "ios"]:
if ARGUMENTS.get("debug", "0") == "1": env["PDB"] = "#build/debug/nvgt.pdb"
nvgt = env.Program("release/nvgt", env.Object([os.path.join("build/obj_src", s) for s in sources]) + extra_objects)
if env["NVGT_TARGET"] == "macos":
# On Mac OS, we need to run install_name_tool to modify the paths of any dynamic libraries we link.
for lib in ["plist-2.0", "archive"]: env.AddPostAction(nvgt, lambda target, source, env: env.Execute(f"install_name_tool -change lib/lib{lib}.dylib @rpath/lib{lib}.dylib " + str(target[0])))
if env["NVGT_TARGET"] == "windows":
# Only on windows we must go through the frustrating hastle of compiling a version of nvgt with no console E. the windows subsystem.
# It is at least set up so that we only need to recompile one object
if "nvgt.cpp" in sources: sources.remove("nvgt.cpp")
if ARGUMENTS.get("debug", "0") == "1": env["PDB"] = "#build/debug/nvgtw.pdb"
nvgtw = env.Program("release/nvgtw", env.Object([os.path.join("build/obj_src", s) for s in sources]) + [env.Object("build/obj_src/nvgtw", "build/obj_src/nvgt.cpp", CPPDEFINES = ["$CPPDEFINES", "NVGT_WIN_APP"]), extra_objects], LINKFLAGS = ["$LINKFLAGS", "/subsystem:windows"])
sources.append("nvgt.cpp")
# Todo: Properly implement the install target on other platforms
env.Install("c:/nvgt", nvgt)
env.Install("c:/nvgt", nvgtw)
env.Install("c:/nvgt", "#release/include")
env.Install("c:/nvgt", "#release/lib")
elif env["NVGT_TARGET"] == "android":
# We need to build both the runner and the stub.
# First, the runner.
runner_dest = "jni/libs/runner/arm64-v8a"
runner_lib = env.SharedLibrary(os.path.join(runner_dest, "libmain"), env.Object([os.path.join("build/obj_src", s) for s in sources]) + extra_objects)
# Next, the stub.
stub_dest = "jni/libs/stub/arm64-v8a"
android_stub_env = env.Clone()
android_stub_env.Append(CPPDEFINES = ["NVGT_STUB"])
VariantDir("build/obj_stub", "src", duplicate = 0)
libgame = env.SharedLibrary(os.path.join(stub_dest, "libgame"), android_stub_env.Object([os.path.join("build/obj_stub", s) for s in sources]) + extra_objects)
# Copy dependencies to both directories.
libcxx_path = os.path.join(env["NDK_HOME"], "toolchains", "llvm", "prebuilt", env["NDK_HOST_TAG"], "sysroot", "usr", "lib", "aarch64-linux-android", "libc++_shared.so")
android_deps = [runner_lib, libgame]
for d in [runner_dest, stub_dest]:
android_deps.extend(env.Install(d, libcxx_path))
android_deps.extend(env.Install(d, os.path.join(env["NVGT_OSDEV_PATH"], "lib/libSDL3.so")))
android_deps.extend(env.Install(d, os.path.join(env["NVGT_OSDEV_PATH"], "lib/libphonon.so")))
# Finally, trigger Gradle to package the .bin and the APK
def run_gradle(target, source, env):
import subprocess
original_dir = os.getcwd()
os.chdir("jni")
try:
subprocess.check_call(env["GRADLE_CMD"] + ["assembleStubRelease", "assembleRunnerRelease"])
finally:
os.chdir(original_dir)
apk_alias = env.Alias("android_apk", android_deps)
#env.AddPostAction(apk_alias, Action(run_gradle, "Packaging Android Stub and Runner..."))
#env.Default(apk_alias)
# stubs
def fix_stub(target, source, env):
"""On windows, we replace the first 2 bytes of a stub with 'NV' to stop some sort of antivirus scan upon script compile that makes it take a bit longer. We do the same on MacOS because otherwise
apple's notarization service detects the stub as an unsigned binary and fails.
Stubs must be unsigned until the nvgt scripter signs their compiled games."""
for t in target:
if not str(t).endswith(".bin"): continue
with open(str(t), "rb+") as f:
f.seek(0)
f.write(b"NV")
f.close()
if ARGUMENTS.get("no_stubs", "0") == "0" and env["NVGT_TARGET"] not in ("android"):
stub_platform = env["NVGT_TARGET"] if env["NVGT_TARGET"] != "macos" else "mac"
VariantDir("build/obj_stub", "src", duplicate = 0)
stub_env.Append(CPPDEFINES = ["NVGT_STUB"])
if env["NVGT_TARGET"] == "windows": stub_env.Append(LINKFLAGS = ["/subsystem:windows"])
if ARGUMENTS.get("stub_obfuscation", "0") == "1": stub_env["CPPDEFINES"].remove("NO_OBFUSCATE")
stub_objects = stub_env.Object([os.path.join("build/obj_stub", s) for s in sources]) + extra_objects
if ARGUMENTS.get("debug", "0") == "1": stub_env["PDB"] = f"#build/debug/nvgt_{stub_platform}.pdb"
stub = stub_env.Program(f"release/stub/nvgt_{stub_platform}", stub_objects)
stub_env.AddPostAction(stub, fix_stub)
if env["NVGT_TARGET"] == "windows":
env.Install("c:/nvgt/stub", stub)
if "upx" in env:
stub_u = stub_env.UPX(f"release/stub/nvgt_{stub_platform}_upx.bin", stub)
stub_env.AddPostAction(stub_u, fix_stub)
env.Install("c:/nvgt/stub", stub_u)
stublibs = list(stub_env["LIBS"])
if "angelscript" in stublibs:
stublibs[stublibs.index("angelscript")] = "angelscript_nc"
if ARGUMENTS.get("debug", "0") == "1": stub_env["PDB"] = f"#build/debug/nvgt_{stub_platform}_nc.pdb"
stub_nc = stub_env.Program(f"release/stub/nvgt_{stub_platform}_nc", stub_objects, LIBS = stublibs)
stub_env.AddPostAction(stub_nc, fix_stub)
if env["NVGT_TARGET"] == "windows":
env.Install("c:/nvgt/stub", stub_nc)
if "upx" in env:
stub_nc_u = stub_env.UPX(f"release/stub/nvgt_{stub_platform}_nc_upx.bin", stub_nc)
stub_env.AddPostAction(stub_nc_u, fix_stub)
env.Install("c:/nvgt/stub", stub_nc_u)
if ARGUMENTS.get("copylibs", "1") == "1":
env["NVGT_OSDEV_COPY_LIBS"](env)