[iOS/macOS] Add option to automatically build (and sign / archive) bundles.

This commit is contained in:
bruvzg 2023-12-11 20:50:44 +02:00
parent dfe226b933
commit 94238d0462
No known key found for this signature in database
GPG Key ID: 7960FCF39844EC38
9 changed files with 498 additions and 69 deletions

View File

@ -182,7 +182,6 @@ opts.Add(BoolVariable("debug_symbols", "Build with debugging symbols", False))
opts.Add(BoolVariable("separate_debug_symbols", "Extract debugging symbols to a separate file", False)) opts.Add(BoolVariable("separate_debug_symbols", "Extract debugging symbols to a separate file", False))
opts.Add(EnumVariable("lto", "Link-time optimization (production builds)", "none", ("none", "auto", "thin", "full"))) opts.Add(EnumVariable("lto", "Link-time optimization (production builds)", "none", ("none", "auto", "thin", "full")))
opts.Add(BoolVariable("production", "Set defaults to build Godot for use in production", False)) opts.Add(BoolVariable("production", "Set defaults to build Godot for use in production", False))
opts.Add(BoolVariable("generate_apk", "Generate an APK/AAB after building Android library by calling Gradle", False))
opts.Add(BoolVariable("threads", "Enable threading support", True)) opts.Add(BoolVariable("threads", "Enable threading support", True))
# Components # Components

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-dyld-environment-variables</key>
<true/>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
<key>com.apple.security.cs.disable-executable-page-protection</key>
<true/>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
<key>com.apple.security.device.camera</key>
<true/>
<key>com.apple.security.get-task-allow</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,199 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>English</string>
<key>CFBundleExecutable</key>
<string>Godot</string>
<key>CFBundleName</key>
<string>Godot</string>
<key>CFBundleIconFile</key>
<string>Godot.icns</string>
<key>CFBundleIdentifier</key>
<string>org.godotengine.godot</string>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>$short_version</string>
<key>CFBundleSignature</key>
<string>godot</string>
<key>CFBundleVersion</key>
<string>$version</string>
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required to capture audio.</string>
<key>NSCameraUsageDescription</key>
<string>Camera access is required to capture video.</string>
<key>NSRequiresAquaSystemAppearance</key>
<false/>
<key>NSHumanReadableCopyright</key>
<string>© 2007-present Juan Linietsky, Ariel Manzur &amp; Godot Engine contributors</string>
<key>CFBundleSupportedPlatforms</key>
<array>
<string>MacOSX</string>
</array>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.developer-tools</string>
<key>LSMinimumSystemVersion</key>
<string>10.12</string>
<key>LSMinimumSystemVersionByArchitecture</key>
<dict>
<key>x86_64</key>
<string>10.12</string>
</dict>
<key>NSHighResolutionCapable</key>
<true/>
<key>CFBundleDocumentTypes</key>
<array>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.tscn</string>
</array>
<key>NSExportableTypes</key>
<array>
<string>public.tscn</string>
</array>
</dict>
<dict>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSItemContentTypes</key>
<array>
<string>public.godot</string>
</array>
<key>NSExportableTypes</key>
<array>
<string>public.godot</string>
</array>
</dict>
</array>
<key>UTExportedTypeDeclarations</key>
<array>
<dict>
<key>UTTypeIdentifier</key>
<string>public.tscn</string>
<key>UTTypeReferenceURL</key>
<string></string>
<key>UTTypeDescription</key>
<string>Godot Engine scene</string>
<key>UTTypeIconFile</key>
<string>Scene.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>scn</string>
<string>tscn</string>
<string>escn</string>
</array>
<key>public.mime-type</key>
<string>application/x-godot-scene</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>public.gd</string>
<key>UTTypeReferenceURL</key>
<string></string>
<key>UTTypeDescription</key>
<string>GDScript script</string>
<key>UTTypeIconFile</key>
<string>GDScript.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.script</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>gd</string>
</array>
<key>public.mime-type</key>
<string>application/x-gdscript</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>public.res</string>
<key>UTTypeReferenceURL</key>
<string></string>
<key>UTTypeDescription</key>
<string>Godot Engine resource</string>
<key>UTTypeIconFile</key>
<string>Resource.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>res</string>
<string>tres</string>
</array>
<key>public.mime-type</key>
<string>application/x-godot-resource</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>public.gdshader</string>
<key>UTTypeReferenceURL</key>
<string></string>
<key>UTTypeDescription</key>
<string>Godot Engine shader</string>
<key>UTTypeIconFile</key>
<string>Shader.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.script</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>gdshader</string>
</array>
<key>public.mime-type</key>
<string>application/x-godot-shader</string>
</dict>
</dict>
<dict>
<key>UTTypeIdentifier</key>
<string>public.godot</string>
<key>UTTypeReferenceURL</key>
<string></string>
<key>UTTypeDescription</key>
<string>Godot Engine project</string>
<key>UTTypeIconFile</key>
<string>Project.icns</string>
<key>UTTypeConformsTo</key>
<array>
<string>public.data</string>
</array>
<key>UTTypeTagSpecification</key>
<dict>
<key>public.filename-extension</key>
<array>
<string>godot</string>
</array>
<key>public.mime-type</key>
<string>application/x-godot-project</string>
</dict>
</dict>
</array>
</dict>
</plist>

View File

@ -28,6 +28,7 @@ def get_opts():
"android-" + str(get_min_target_api()), "android-" + str(get_min_target_api()),
), ),
BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False), BoolVariable("store_release", "Editor build for Google Play Store (for official builds only)", False),
BoolVariable("generate_apk", "Generate an APK/AAB after building Android library by calling Gradle", False),
] ]

View File

@ -2,6 +2,62 @@
Import("env") Import("env")
import os, json
from platform_methods import run_in_subprocess, architectures, lipo, get_build_version, detect_mvk
import subprocess
import shutil
def generate_bundle(target, source, env):
bin_dir = Dir("#bin").abspath
# Template bundle.
app_prefix = "godot." + env["platform"]
rel_prefix = "libgodot." + env["platform"] + "." + "template_release"
dbg_prefix = "libgodot." + env["platform"] + "." + "template_debug"
if env.dev_build:
app_prefix += ".dev"
rel_prefix += ".dev"
dbg_prefix += ".dev"
if env["precision"] == "double":
app_prefix += ".double"
rel_prefix += ".double"
dbg_prefix += ".double"
# Lipo template libraries.
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, env.extra_suffix + ".a")
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, env.extra_suffix + ".a")
rel_target_bin_sim = lipo(bin_dir + "/" + rel_prefix, ".simulator" + env.extra_suffix + ".a")
dbg_target_bin_sim = lipo(bin_dir + "/" + dbg_prefix, ".simulator" + env.extra_suffix + ".a")
# Assemble Xcode project bundle.
app_dir = Dir("#bin/ios_xcode").abspath
templ = Dir("#misc/dist/ios_xcode").abspath
if os.path.exists(app_dir):
shutil.rmtree(app_dir)
shutil.copytree(templ, app_dir)
if rel_target_bin != "":
shutil.copy(rel_target_bin, app_dir + "/libgodot.ios.release.xcframework/ios-arm64/libgodot.a")
if dbg_target_bin != "":
shutil.copy(dbg_target_bin, app_dir + "/libgodot.ios.debug.xcframework/ios-arm64/libgodot.a")
if rel_target_bin_sim != "":
shutil.copy(
rel_target_bin_sim, app_dir + "/libgodot.ios.release.xcframework/ios-arm64_x86_64-simulator/libgodot.a"
)
if dbg_target_bin_sim != "":
shutil.copy(
dbg_target_bin_sim, app_dir + "/libgodot.ios.debug.xcframework/ios-arm64_x86_64-simulator/libgodot.a"
)
mvk_path = detect_mvk(env, "ios-arm64")
if mvk_path != "":
shutil.copytree(mvk_path, app_dir + "/MoltenVK.xcframework")
# ZIP Xcode project bundle.
zip_dir = Dir("#bin/" + (app_prefix + env.extra_suffix).replace(".", "_")).abspath
shutil.make_archive(zip_dir, "zip", root_dir=app_dir)
shutil.rmtree(app_dir)
ios_lib = [ ios_lib = [
"godot_ios.mm", "godot_ios.mm",
"os_ios.mm", "os_ios.mm",
@ -42,3 +98,8 @@ def combine_libs(target=None, source=None, env=None):
combine_command = env_ios.Command("#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], combine_libs) combine_command = env_ios.Command("#bin/libgodot" + env_ios["LIBSUFFIX"], [ios_lib] + env_ios["LIBS"], combine_libs)
if env["generate_bundle"]:
generate_bundle_command = env.Command("generate_bundle", [], generate_bundle)
command = env.AlwaysBuild(generate_bundle_command)
env.Depends(command, [combine_command])

View File

@ -23,6 +23,7 @@ def get_opts():
from SCons.Variables import BoolVariable from SCons.Variables import BoolVariable
return [ return [
("vulkan_sdk_path", "Path to the Vulkan SDK", ""),
( (
"IOS_TOOLCHAIN_PATH", "IOS_TOOLCHAIN_PATH",
"Path to iOS toolchain", "Path to iOS toolchain",
@ -31,6 +32,7 @@ def get_opts():
("IOS_SDK_PATH", "Path to the iOS SDK", ""), ("IOS_SDK_PATH", "Path to the iOS SDK", ""),
BoolVariable("ios_simulator", "Build for iOS Simulator", False), BoolVariable("ios_simulator", "Build for iOS Simulator", False),
("ios_triple", "Triple for ios toolchain", ""), ("ios_triple", "Triple for ios toolchain", ""),
BoolVariable("generate_bundle", "Generate an APP bundle after building iOS/macOS binaries", False),
] ]

View File

@ -2,8 +2,99 @@
Import("env") Import("env")
from platform_methods import run_in_subprocess import os, json
from platform_methods import run_in_subprocess, architectures, lipo, get_build_version
import platform_macos_builders import platform_macos_builders
import subprocess
import shutil
def generate_bundle(target, source, env):
bin_dir = Dir("#bin").abspath
if env.editor_build:
# Editor bundle.
prefix = "godot." + env["platform"] + "." + env["target"]
if env.dev_build:
prefix += ".dev"
if env["precision"] == "double":
prefix += ".double"
# Lipo editor executable.
target_bin = lipo(bin_dir + "/" + prefix, env.extra_suffix)
# Assemble .app bundle and update version info.
app_dir = Dir("#bin/" + (prefix + env.extra_suffix).replace(".", "_") + ".app").abspath
templ = Dir("#misc/dist/macos_tools.app").abspath
if os.path.exists(app_dir):
shutil.rmtree(app_dir)
shutil.copytree(templ, app_dir, ignore=shutil.ignore_patterns("Contents/Info.plist"))
if not os.path.isdir(app_dir + "/Contents/MacOS"):
os.mkdir(app_dir + "/Contents/MacOS")
if target_bin != "":
shutil.copy(target_bin, app_dir + "/Contents/MacOS/Godot")
version = get_build_version(False)
short_version = get_build_version(True)
with open(Dir("#misc/dist/macos").abspath + "/editor_info_plist.template", "rt") as fin:
with open(app_dir + "/Contents/Info.plist", "wt") as fout:
for line in fin:
line = line.replace("$version", version)
line = line.replace("$short_version", short_version)
fout.write(line)
# Sign .app bundle.
if env["bundle_sign_identity"] != "":
sign_command = [
"codesign",
"-s",
env["bundle_sign_identity"],
"--deep",
"--force",
"--options=runtime",
"--entitlements",
]
if env.dev_build:
sign_command += [Dir("#misc/dist/macos").abspath + "/editor_debug.entitlements"]
else:
sign_command += [Dir("#misc/dist/macos").abspath + "/editor.entitlements"]
sign_command += [app_dir]
subprocess.run(sign_command)
else:
# Template bundle.
app_prefix = "godot." + env["platform"]
rel_prefix = "godot." + env["platform"] + "." + "template_release"
dbg_prefix = "godot." + env["platform"] + "." + "template_debug"
if env.dev_build:
app_prefix += ".dev"
rel_prefix += ".dev"
dbg_prefix += ".dev"
if env["precision"] == "double":
app_prefix += ".double"
rel_prefix += ".double"
dbg_prefix += ".double"
# Lipo template executables.
rel_target_bin = lipo(bin_dir + "/" + rel_prefix, env.extra_suffix)
dbg_target_bin = lipo(bin_dir + "/" + dbg_prefix, env.extra_suffix)
# Assemble .app bundle.
app_dir = Dir("#bin/macos_template.app").abspath
templ = Dir("#misc/dist/macos_template.app").abspath
if os.path.exists(app_dir):
shutil.rmtree(app_dir)
shutil.copytree(templ, app_dir)
if not os.path.isdir(app_dir + "/Contents/MacOS"):
os.mkdir(app_dir + "/Contents/MacOS")
if rel_target_bin != "":
shutil.copy(rel_target_bin, app_dir + "/Contents/MacOS/godot_macos_release.universal")
if dbg_target_bin != "":
shutil.copy(dbg_target_bin, app_dir + "/Contents/MacOS/godot_macos_debug.universal")
# ZIP .app bundle.
zip_dir = Dir("#bin/" + (app_prefix + env.extra_suffix).replace(".", "_")).abspath
shutil.make_archive(zip_dir, "zip", root_dir=bin_dir, base_dir="macos_template.app")
shutil.rmtree(app_dir)
files = [ files = [
"os_macos.mm", "os_macos.mm",
@ -33,3 +124,8 @@ prog = env.add_program("#bin/godot", files)
if env["debug_symbols"] and env["separate_debug_symbols"]: if env["debug_symbols"] and env["separate_debug_symbols"]:
env.AddPostAction(prog, run_in_subprocess(platform_macos_builders.make_debug_macos)) env.AddPostAction(prog, run_in_subprocess(platform_macos_builders.make_debug_macos))
if env["generate_bundle"]:
generate_bundle_command = env.Command("generate_bundle", [], generate_bundle)
command = env.AlwaysBuild(generate_bundle_command)
env.Depends(command, [prog])

View File

@ -1,7 +1,7 @@
import os import os
import sys import sys
from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang from methods import detect_darwin_sdk_path, get_compiler_version, is_vanilla_clang
from platform_methods import detect_arch from platform_methods import detect_arch, detect_mvk
from typing import TYPE_CHECKING from typing import TYPE_CHECKING
@ -33,6 +33,12 @@ def get_opts():
BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False), BoolVariable("use_tsan", "Use LLVM/GCC compiler thread sanitizer (TSAN)", False),
BoolVariable("use_coverage", "Use instrumentation codes in the binary (e.g. for code coverage)", False), BoolVariable("use_coverage", "Use instrumentation codes in the binary (e.g. for code coverage)", False),
("angle_libs", "Path to the ANGLE static libraries", ""), ("angle_libs", "Path to the ANGLE static libraries", ""),
(
"bundle_sign_identity",
"The 'Full Name', 'Common Name' or SHA-1 hash of the signing identity used to sign editor .app bundle.",
"-",
),
BoolVariable("generate_bundle", "Generate an APP bundle after building iOS/macOS binaries", False),
] ]
@ -53,47 +59,6 @@ def get_flags():
] ]
def get_mvk_sdk_path():
def int_or_zero(i):
try:
return int(i)
except:
return 0
def ver_parse(a):
return [int_or_zero(i) for i in a.split(".")]
dirname = os.path.expanduser("~/VulkanSDK")
if not os.path.exists(dirname):
return ""
ver_min = ver_parse("1.3.231.0")
ver_num = ver_parse("0.0.0.0")
files = os.listdir(dirname)
lib_name_out = dirname
for file in files:
if os.path.isdir(os.path.join(dirname, file)):
ver_comp = ver_parse(file)
if ver_comp > ver_num and ver_comp >= ver_min:
# Try new SDK location.
lib_name = os.path.join(
os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework/macos-arm64_x86_64/"
)
if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
ver_num = ver_comp
lib_name_out = lib_name
else:
# Try old SDK location.
lib_name = os.path.join(
os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/"
)
if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
ver_num = ver_comp
lib_name_out = lib_name
return lib_name_out
def configure(env: "Environment"): def configure(env: "Environment"):
# Validate arch. # Validate arch.
supported_arches = ["x86_64", "arm64"] supported_arches = ["x86_64", "arm64"]
@ -274,32 +239,11 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"]) env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"])
if not env["use_volk"]: if not env["use_volk"]:
env.Append(LINKFLAGS=["-lMoltenVK"]) env.Append(LINKFLAGS=["-lMoltenVK"])
mvk_found = False mvk_path = detect_mvk(env, "macos-arm64_x86_64")
mvk_list = [get_mvk_sdk_path(), "/opt/homebrew/lib", "/usr/local/homebrew/lib", "/opt/local/lib"] if mvk_path != "":
if env["vulkan_sdk_path"] != "": env.Append(LINKFLAGS=["-L" + os.path.join(mvk_path, "macos-arm64_x86_64")])
mvk_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"])) else:
mvk_list.insert(
0,
os.path.join(
os.path.expanduser(env["vulkan_sdk_path"]), "macOS/lib/MoltenVK.xcframework/macos-arm64_x86_64/"
),
)
mvk_list.insert(
0,
os.path.join(
os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework/macos-arm64_x86_64/"
),
)
for mvk_path in mvk_list:
if mvk_path and os.path.isfile(os.path.join(mvk_path, "libMoltenVK.a")):
mvk_found = True
print("MoltenVK found at: " + mvk_path)
env.Append(LINKFLAGS=["-L" + mvk_path])
break
if not mvk_found:
print( print(
"MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path." "MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path."
) )

View File

@ -140,3 +140,108 @@ def generate_export_icons(platform_path, platform_name):
wf = export_path + "/" + name + "_svg.gen.h" wf = export_path + "/" + name + "_svg.gen.h"
with open(wf, "w") as svgw: with open(wf, "w") as svgw:
svgw.write(svg_str) svgw.write(svg_str)
def get_build_version(short):
import version
name = "custom_build"
if os.getenv("BUILD_NAME") != None:
name = os.getenv("BUILD_NAME")
v = "%d.%d" % (version.major, version.minor)
if version.patch > 0:
v += ".%d" % version.patch
status = version.status
if not short:
if os.getenv("GODOT_VERSION_STATUS") != None:
status = str(os.getenv("GODOT_VERSION_STATUS"))
v += ".%s.%s" % (status, name)
return v
def lipo(prefix, suffix):
from pathlib import Path
target_bin = ""
lipo_command = ["lipo", "-create"]
arch_found = 0
for arch in architectures:
bin_name = prefix + "." + arch + suffix
if Path(bin_name).is_file():
target_bin = bin_name
lipo_command += [bin_name]
arch_found += 1
if arch_found > 1:
target_bin = prefix + ".fat" + suffix
lipo_command += ["-output", target_bin]
subprocess.run(lipo_command)
return target_bin
def get_mvk_sdk_path(osname):
def int_or_zero(i):
try:
return int(i)
except:
return 0
def ver_parse(a):
return [int_or_zero(i) for i in a.split(".")]
dirname = os.path.expanduser("~/VulkanSDK")
if not os.path.exists(dirname):
return ""
ver_min = ver_parse("1.3.231.0")
ver_num = ver_parse("0.0.0.0")
files = os.listdir(dirname)
lib_name_out = dirname
for file in files:
if os.path.isdir(os.path.join(dirname, file)):
ver_comp = ver_parse(file)
if ver_comp > ver_num and ver_comp >= ver_min:
# Try new SDK location.
lib_name = os.path.join(os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework/" + osname + "/")
if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
ver_num = ver_comp
lib_name_out = os.path.join(os.path.join(dirname, file), "macOS/lib/MoltenVK.xcframework")
else:
# Try old SDK location.
lib_name = os.path.join(
os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework/" + osname + "/"
)
if os.path.isfile(os.path.join(lib_name, "libMoltenVK.a")):
ver_num = ver_comp
lib_name_out = os.path.join(os.path.join(dirname, file), "MoltenVK/MoltenVK.xcframework")
return lib_name_out
def detect_mvk(env, osname):
mvk_list = [
get_mvk_sdk_path(osname),
"/opt/homebrew/Frameworks/MoltenVK.xcframework",
"/usr/local/homebrew/Frameworks/MoltenVK.xcframework",
"/opt/local/Frameworks/MoltenVK.xcframework",
]
if env["vulkan_sdk_path"] != "":
mvk_list.insert(0, os.path.expanduser(env["vulkan_sdk_path"]))
mvk_list.insert(
0,
os.path.join(os.path.expanduser(env["vulkan_sdk_path"]), "macOS/lib/MoltenVK.xcframework"),
)
mvk_list.insert(
0,
os.path.join(os.path.expanduser(env["vulkan_sdk_path"]), "MoltenVK/MoltenVK.xcframework"),
)
for mvk_path in mvk_list:
if mvk_path and os.path.isfile(os.path.join(mvk_path, osname + "/libMoltenVK.a")):
mvk_found = True
print("MoltenVK found at: " + mvk_path)
return mvk_path
return ""