Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
godotengine
GitHub Repository: godotengine/godot
Path: blob/master/platform_methods.py
20879 views
1
import os
2
import platform
3
import shutil
4
import subprocess
5
import sys
6
7
import methods
8
9
# NOTE: The multiprocessing module is not compatible with SCons due to conflict on cPickle
10
11
12
compatibility_platform_aliases = {
13
"osx": "macos",
14
"iphone": "ios",
15
"x11": "linuxbsd",
16
"javascript": "web",
17
}
18
19
# CPU architecture options.
20
architectures = ["x86_32", "x86_64", "arm32", "arm64", "rv64", "ppc64", "wasm32", "loongarch64"]
21
architecture_aliases = {
22
"x86": "x86_32",
23
"x64": "x86_64",
24
"amd64": "x86_64",
25
"armv7": "arm32",
26
"armv8": "arm64",
27
"arm64v8": "arm64",
28
"aarch64": "arm64",
29
"rv": "rv64",
30
"riscv": "rv64",
31
"riscv64": "rv64",
32
"ppc64le": "ppc64",
33
"loong64": "loongarch64",
34
}
35
36
37
def detect_arch():
38
host_machine = platform.machine().lower()
39
if host_machine in architectures:
40
return host_machine
41
elif host_machine in architecture_aliases.keys():
42
return architecture_aliases[host_machine]
43
elif "86" in host_machine:
44
# Catches x86, i386, i486, i586, i686, etc.
45
return "x86_32"
46
else:
47
methods.print_warning(f'Unsupported CPU architecture: "{host_machine}". Falling back to x86_64.')
48
return "x86_64"
49
50
51
def validate_arch(arch, platform_name, supported_arches):
52
if arch not in supported_arches:
53
methods.print_error(
54
'Unsupported CPU architecture "%s" for %s. Supported architectures are: %s.'
55
% (arch, platform_name, ", ".join(supported_arches))
56
)
57
sys.exit(255)
58
59
60
def get_build_version(short):
61
import version
62
63
name = "custom_build"
64
if os.getenv("BUILD_NAME") is not None:
65
name = os.getenv("BUILD_NAME")
66
v = "%d.%d" % (version.major, version.minor)
67
if version.patch > 0:
68
v += ".%d" % version.patch
69
status = version.status
70
if not short:
71
if os.getenv("GODOT_VERSION_STATUS") is not None:
72
status = str(os.getenv("GODOT_VERSION_STATUS"))
73
v += ".%s.%s" % (status, name)
74
return v
75
76
77
def lipo(prefix, suffix):
78
from pathlib import Path
79
80
target_bin = ""
81
lipo_command = ["lipo", "-create"]
82
arch_found = 0
83
84
for arch in architectures:
85
bin_name = prefix + "." + arch + suffix
86
if Path(bin_name).is_file():
87
target_bin = bin_name
88
lipo_command += [bin_name]
89
arch_found += 1
90
91
if arch_found > 1:
92
target_bin = prefix + ".fat" + suffix
93
lipo_command += ["-output", target_bin]
94
subprocess.run(lipo_command)
95
96
return target_bin
97
98
99
def get_mvk_sdk_path(osname):
100
def int_or_zero(i):
101
try:
102
return int(i)
103
except (TypeError, ValueError):
104
return 0
105
106
def ver_parse(a):
107
return [int_or_zero(i) for i in a.split(".")]
108
109
dirname = os.path.expanduser("~/VulkanSDK")
110
if not os.path.exists(dirname):
111
return ""
112
113
ver_min = ver_parse("1.3.231.0")
114
ver_num = ver_parse("0.0.0.0")
115
files = os.listdir(dirname)
116
lib_name_out = dirname
117
for file in files:
118
if os.path.isdir(os.path.join(dirname, file)):
119
ver_comp = ver_parse(file)
120
if ver_comp > ver_num and ver_comp >= ver_min:
121
# Try new SDK location.
122
lib_name = os.path.join(os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework/" + osname + "/")
123
if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
124
ver_num = ver_comp
125
lib_name_out = os.path.join(os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework")
126
else:
127
# Try old SDK location.
128
lib_name = os.path.join(
129
os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework/" + osname + "/"
130
)
131
if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
132
ver_num = ver_comp
133
lib_name_out = os.path.join(os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework")
134
135
return lib_name_out
136
137
138
def detect_mvk(env, osname):
139
mvk_list = [
140
get_mvk_sdk_path(osname),
141
"/opt/homebrew/Frameworks/MoltenVK.xcframework",
142
"/usr/local/homebrew/Frameworks/MoltenVK.xcframework",
143
"/opt/local/Frameworks/MoltenVK.xcframework",
144
]
145
if env["vulkan_sdk_path"] != "":
146
mvk_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"]))
147
mvk_list.insert(
148
0,
149
os.path.join(os.path.expanduser(env["vulkan_sdk_path"]), "macOS/lib/MoltenVK.xcframework"),
150
)
151
mvk_list.insert(
152
0,
153
os.path.join(os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework"),
154
)
155
156
for mvk_path in mvk_list:
157
if mvk_path and os.path.isfile(os.path.join(mvk_path, f"{osname}/libMoltenVK.a")):
158
print(f"MoltenVK found at: {mvk_path}")
159
return mvk_path
160
161
return ""
162
163
164
def combine_libs_apple_embedded(target, source, env):
165
lib_path = target[0].srcnode().abspath
166
if "osxcross" in env:
167
libtool = "$APPLE_TOOLCHAIN_PATH/usr/bin/${apple_target_triple}libtool"
168
else:
169
libtool = "$APPLE_TOOLCHAIN_PATH/usr/bin/libtool"
170
env.Execute(
171
libtool + ' -static -o "' + lib_path + '" ' + " ".join([('"' + lib.srcnode().abspath + '"') for lib in source])
172
)
173
174
175
def generate_bundle_apple_embedded(platform, framework_dir, framework_dir_sim, use_mkv, target, source, env):
176
bin_dir = env.Dir("#bin").abspath
177
178
# Template bundle.
179
app_prefix = "godot." + platform
180
rel_prefix = "libgodot." + platform + "." + "template_release"
181
dbg_prefix = "libgodot." + platform + "." + "template_debug"
182
if env.dev_build:
183
app_prefix += ".dev"
184
rel_prefix += ".dev"
185
dbg_prefix += ".dev"
186
if env["precision"] == "double":
187
app_prefix += ".double"
188
rel_prefix += ".double"
189
dbg_prefix += ".double"
190
191
# Lipo template libraries.
192
#
193
# env.extra_suffix contains ".simulator" when building for simulator,
194
# but it's undesired when calling lipo()
195
extra_suffix = env.extra_suffix.replace(".simulator", "")
196
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, extra_suffix + ".a")
197
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, extra_suffix + ".a")
198
rel_target_bin_sim = lipo(bin_dir + "/" + rel_prefix, ".simulator" + extra_suffix + ".a")
199
dbg_target_bin_sim = lipo(bin_dir + "/" + dbg_prefix, ".simulator" + extra_suffix + ".a")
200
# Assemble Xcode project bundle.
201
app_dir = env.Dir("#bin/" + platform + "_xcode").abspath
202
templ = env.Dir("#misc/dist/apple_embedded_xcode").abspath
203
if os.path.exists(app_dir):
204
shutil.rmtree(app_dir)
205
shutil.copytree(templ, app_dir)
206
if rel_target_bin != "":
207
print(f' Copying "{platform}" release framework')
208
shutil.copy(
209
rel_target_bin, app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir + "/libgodot.a"
210
)
211
if dbg_target_bin != "":
212
print(f' Copying "{platform}" debug framework')
213
shutil.copy(
214
dbg_target_bin, app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir + "/libgodot.a"
215
)
216
if rel_target_bin_sim != "":
217
print(f' Copying "{platform}" (simulator) release framework')
218
shutil.copy(
219
rel_target_bin_sim,
220
app_dir + "/libgodot." + platform + ".release.xcframework/" + framework_dir_sim + "/libgodot.a",
221
)
222
if dbg_target_bin_sim != "":
223
print(f' Copying "{platform}" (simulator) debug framework')
224
shutil.copy(
225
dbg_target_bin_sim,
226
app_dir + "/libgodot." + platform + ".debug.xcframework/" + framework_dir_sim + "/libgodot.a",
227
)
228
229
# Remove other platform xcframeworks
230
for entry in os.listdir(app_dir):
231
if entry.startswith("libgodot.") and entry.endswith(".xcframework"):
232
parts = entry.split(".")
233
if len(parts) >= 3 and parts[1] != platform:
234
full_path = os.path.join(app_dir, entry)
235
shutil.rmtree(full_path)
236
237
if use_mkv:
238
mvk_path = detect_mvk(env, "ios-arm64")
239
if mvk_path != "":
240
shutil.copytree(mvk_path, app_dir + "/MoltenVK.xcframework")
241
242
# ZIP Xcode project bundle.
243
zip_dir = env.Dir("#bin/" + (app_prefix + extra_suffix).replace(".", "_")).abspath
244
shutil.make_archive(zip_dir, "zip", root_dir=app_dir)
245
shutil.rmtree(app_dir)
246
247
248
def setup_swift_builder(env, apple_platform, sdk_path, current_path, bridging_header_filename, all_swift_files):
249
from SCons.Script import Action, Builder
250
251
if apple_platform == "macos":
252
target_suffix = "macosx10.9"
253
254
elif apple_platform == "ios":
255
target_suffix = "ios14.0" # iOS 14.0 needed for SwiftUI lifecycle
256
257
elif apple_platform == "iossimulator":
258
target_suffix = "ios14.0-simulator" # iOS 14.0 needed for SwiftUI lifecycle
259
260
elif apple_platform == "visionos":
261
target_suffix = "xros26.0"
262
263
elif apple_platform == "visionossimulator":
264
target_suffix = "xros26.0-simulator"
265
266
else:
267
raise Exception("Invalid platform argument passed to detect_darwin_sdk_path")
268
269
swiftc_target = env["arch"] + "-apple-" + target_suffix
270
271
env["ALL_SWIFT_FILES"] = all_swift_files
272
env["CURRENT_PATH"] = current_path
273
if "SWIFT_FRONTEND" in env and env["SWIFT_FRONTEND"] != "":
274
frontend_path = env["SWIFT_FRONTEND"]
275
elif "osxcross" not in env:
276
frontend_path = "$APPLE_TOOLCHAIN_PATH/usr/bin/swift-frontend"
277
else:
278
frontend_path = None
279
280
if frontend_path is None:
281
raise Exception("Swift frontend path is not set. Please set SWIFT_FRONTEND.")
282
283
bridging_header_path = current_path + "/" + bridging_header_filename
284
env["SWIFTC"] = frontend_path + " -frontend -c" # Swift compiler
285
env["SWIFTCFLAGS"] = [
286
"-cxx-interoperability-mode=default",
287
"-emit-object",
288
"-target",
289
swiftc_target,
290
"-sdk",
291
sdk_path,
292
"-import-objc-header",
293
bridging_header_path,
294
"-swift-version",
295
"6",
296
"-parse-as-library",
297
"-module-name",
298
"godot_swift_module",
299
"-I./", # Pass the current directory as the header root so bridging headers can include files from any point of the hierarchy
300
]
301
302
if "osxcross" in env:
303
env.Append(
304
SWIFTCFLAGS=[
305
"-resource-dir",
306
"/root/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib/swift",
307
]
308
)
309
310
if env["debug_symbols"]:
311
env.Append(SWIFTCFLAGS=["-g"])
312
313
if env["optimize"] in ["speed", "speed_trace"]:
314
env.Append(SWIFTCFLAGS=["-O"])
315
316
elif env["optimize"] == "size":
317
env.Append(SWIFTCFLAGS=["-Osize"])
318
319
elif env["optimize"] in ["debug", "none"]:
320
env.Append(SWIFTCFLAGS=["-Onone"])
321
322
def generate_swift_action(source, target, env, for_signature):
323
fullpath_swift_files = [env["CURRENT_PATH"] + "/" + file for file in env["ALL_SWIFT_FILES"]]
324
fullpath_swift_files.remove(source[0].abspath)
325
326
fullpath_swift_files_string = '"' + '" "'.join(fullpath_swift_files) + '"'
327
compile_command = "$SWIFTC " + fullpath_swift_files_string + " -primary-file $SOURCE -o $TARGET $SWIFTCFLAGS"
328
329
swift_comdstr = env.get("SWIFTCOMSTR")
330
if swift_comdstr is not None:
331
swift_action = Action(compile_command, cmdstr=swift_comdstr)
332
else:
333
swift_action = Action(compile_command)
334
335
return swift_action
336
337
# Define Builder for Swift files
338
swift_builder = Builder(
339
generator=generate_swift_action, suffix=env["OBJSUFFIX"], src_suffix=".swift", emitter=methods.redirect_emitter
340
)
341
342
env.Append(BUILDERS={"Swift": swift_builder})
343
env["BUILDERS"]["Library"].add_src_builder("Swift")
344
env["BUILDERS"]["Object"].add_action(".swift", Action(generate_swift_action, generator=1))
345
346