diff --git a/SConstruct b/SConstruct
index 04564ad149b..47a039fb148 100644
--- a/SConstruct
+++ b/SConstruct
@@ -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(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("generate_apk", "Generate an APK/AAB after building Android library by calling Gradle", False))
opts.Add(BoolVariable("threads", "Enable threading support", True))
# Components
diff --git a/misc/dist/macos/editor_debug.entitlements b/misc/dist/macos/editor_debug.entitlements
new file mode 100644
index 00000000000..ff3f5891213
--- /dev/null
+++ b/misc/dist/macos/editor_debug.entitlements
@@ -0,0 +1,22 @@
+
+
+
+
+ com.apple.security.cs.allow-dyld-environment-variables
+
+ com.apple.security.cs.allow-jit
+
+ com.apple.security.cs.allow-unsigned-executable-memory
+
+ com.apple.security.cs.disable-executable-page-protection
+
+ com.apple.security.cs.disable-library-validation
+
+ com.apple.security.device.audio-input
+
+ com.apple.security.device.camera
+
+ com.apple.security.get-task-allow
+
+
+
diff --git a/misc/dist/macos/editor_info_plist.template b/misc/dist/macos/editor_info_plist.template
new file mode 100644
index 00000000000..4b5399c3451
--- /dev/null
+++ b/misc/dist/macos/editor_info_plist.template
@@ -0,0 +1,199 @@
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+ CFBundleExecutable
+ Godot
+ CFBundleName
+ Godot
+ CFBundleIconFile
+ Godot.icns
+ CFBundleIdentifier
+ org.godotengine.godot
+ CFBundleInfoDictionaryVersion
+ 6.0
+ CFBundlePackageType
+ APPL
+ CFBundleShortVersionString
+ $short_version
+ CFBundleSignature
+ godot
+ CFBundleVersion
+ $version
+ NSMicrophoneUsageDescription
+ Microphone access is required to capture audio.
+ NSCameraUsageDescription
+ Camera access is required to capture video.
+ NSRequiresAquaSystemAppearance
+
+ NSHumanReadableCopyright
+ © 2007-present Juan Linietsky, Ariel Manzur & Godot Engine contributors
+ CFBundleSupportedPlatforms
+
+ MacOSX
+
+ NSPrincipalClass
+ NSApplication
+ LSApplicationCategoryType
+ public.app-category.developer-tools
+ LSMinimumSystemVersion
+ 10.12
+ LSMinimumSystemVersionByArchitecture
+
+ x86_64
+ 10.12
+
+ NSHighResolutionCapable
+
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeRole
+ Editor
+ LSItemContentTypes
+
+ public.tscn
+
+ NSExportableTypes
+
+ public.tscn
+
+
+
+ CFBundleTypeRole
+ Editor
+ LSItemContentTypes
+
+ public.godot
+
+ NSExportableTypes
+
+ public.godot
+
+
+
+ UTExportedTypeDeclarations
+
+
+ UTTypeIdentifier
+ public.tscn
+ UTTypeReferenceURL
+
+ UTTypeDescription
+ Godot Engine scene
+ UTTypeIconFile
+ Scene.icns
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ scn
+ tscn
+ escn
+
+ public.mime-type
+ application/x-godot-scene
+
+
+
+ UTTypeIdentifier
+ public.gd
+ UTTypeReferenceURL
+
+ UTTypeDescription
+ GDScript script
+ UTTypeIconFile
+ GDScript.icns
+ UTTypeConformsTo
+
+ public.script
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ gd
+
+ public.mime-type
+ application/x-gdscript
+
+
+
+ UTTypeIdentifier
+ public.res
+ UTTypeReferenceURL
+
+ UTTypeDescription
+ Godot Engine resource
+ UTTypeIconFile
+ Resource.icns
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ res
+ tres
+
+ public.mime-type
+ application/x-godot-resource
+
+
+
+ UTTypeIdentifier
+ public.gdshader
+ UTTypeReferenceURL
+
+ UTTypeDescription
+ Godot Engine shader
+ UTTypeIconFile
+ Shader.icns
+ UTTypeConformsTo
+
+ public.script
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ gdshader
+
+ public.mime-type
+ application/x-godot-shader
+
+
+
+ UTTypeIdentifier
+ public.godot
+ UTTypeReferenceURL
+
+ UTTypeDescription
+ Godot Engine project
+ UTTypeIconFile
+ Project.icns
+ UTTypeConformsTo
+
+ public.data
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ godot
+
+ public.mime-type
+ application/x-godot-project
+
+
+
+
+
diff --git a/platform/android/detect.py b/platform/android/detect.py
index b396e5eb2d6..8976e218b37 100644
--- a/platform/android/detect.py
+++ b/platform/android/detect.py
@@ -28,6 +28,7 @@ def get_opts():
"android-" + str(get_min_target_api()),
),
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),
]
diff --git a/platform/ios/SCsub b/platform/ios/SCsub
index d7c950967c0..5a57f3840bd 100644
--- a/platform/ios/SCsub
+++ b/platform/ios/SCsub
@@ -2,6 +2,62 @@
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 = [
"godot_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)
+
+if env["generate_bundle"]:
+ generate_bundle_command = env.Command("generate_bundle", [], generate_bundle)
+ command = env.AlwaysBuild(generate_bundle_command)
+ env.Depends(command, [combine_command])
diff --git a/platform/ios/detect.py b/platform/ios/detect.py
index 23f688501b8..f8468e3d9e6 100644
--- a/platform/ios/detect.py
+++ b/platform/ios/detect.py
@@ -23,6 +23,7 @@ def get_opts():
from SCons.Variables import BoolVariable
return [
+ ("vulkan_sdk_path", "Path to the Vulkan SDK", ""),
(
"IOS_TOOLCHAIN_PATH",
"Path to iOS toolchain",
@@ -31,6 +32,7 @@ def get_opts():
("IOS_SDK_PATH", "Path to the iOS SDK", ""),
BoolVariable("ios_simulator", "Build for iOS Simulator", False),
("ios_triple", "Triple for ios toolchain", ""),
+ BoolVariable("generate_bundle", "Generate an APP bundle after building iOS/macOS binaries", False),
]
diff --git a/platform/macos/SCsub b/platform/macos/SCsub
index 5a93c3a09ff..559e5c3f1f0 100644
--- a/platform/macos/SCsub
+++ b/platform/macos/SCsub
@@ -2,8 +2,99 @@
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 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 = [
"os_macos.mm",
@@ -33,3 +124,8 @@ prog = env.add_program("#bin/godot", files)
if env["debug_symbols"] and env["separate_debug_symbols"]:
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])
diff --git a/platform/macos/detect.py b/platform/macos/detect.py
index 0d1e40fb3d0..54eeb833fae 100644
--- a/platform/macos/detect.py
+++ b/platform/macos/detect.py
@@ -1,7 +1,7 @@
import os
import sys
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
@@ -33,6 +33,12 @@ def get_opts():
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),
("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"):
# Validate arch.
supported_arches = ["x86_64", "arm64"]
@@ -274,32 +239,11 @@ def configure(env: "Environment"):
env.Append(LINKFLAGS=["-framework", "Metal", "-framework", "IOSurface"])
if not env["use_volk"]:
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 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/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:
+ if mvk_path != "":
+ env.Append(LINKFLAGS=["-L" + os.path.join(mvk_path, "macos-arm64_x86_64")])
+ else:
print(
"MoltenVK SDK installation directory not found, use 'vulkan_sdk_path' SCons parameter to specify SDK path."
)
diff --git a/platform_methods.py b/platform_methods.py
index 8b2c62ad4a3..a05298bfa5a 100644
--- a/platform_methods.py
+++ b/platform_methods.py
@@ -140,3 +140,108 @@ def generate_export_icons(platform_path, platform_name):
wf = export_path + "/" + name + "_svg.gen.h"
with open(wf, "w") as svgw:
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 ""