Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
edea7dc
feat(ios): add full iOS Simulator support
gumaciel Mar 28, 2026
2ef8280
feat(ios): implement universal binary merging for iOS support (v2)
gumaciel Mar 28, 2026
1a88e6e
chore(build): ensure clean XCFramework rebuild in SConstruct
gumaciel Mar 28, 2026
45cef7d
fix: add null check and weak import attribute for load_all_fmod_plugi…
gumaciel Mar 29, 2026
4827afc
chore: add build_libs/ to .gitignore
gumaciel Apr 1, 2026
8e56741
Merge remote-tracking branch 'upstream/master' into feature/ios-simul…
gumaciel Apr 1, 2026
dd29099
feat: add weak symbol for iOS plugin loading and update demo project …
gumaciel Apr 5, 2026
cd35527
feat: add iOS XCFramework support for FMOD and Godot-cpp libraries an…
gumaciel Apr 9, 2026
29c8824
fix: prevent double-suffix error on Windows by explicitly handling SH…
gumaciel Apr 9, 2026
1fac1db
refactor: simplify library target naming and suffix handling in SCons…
gumaciel Apr 9, 2026
ed7afcc
fix: rename iOS to ios (case-sensitive Linux fix) and fix SConstruct …
gumaciel Apr 9, 2026
03bbbe3
fix(ios): universal dylib naming and casing for reliable xcframework …
gumaciel Apr 9, 2026
5cb1fb5
chore: add arch=arm64 flag to iOS build configurations in CI workflows
gumaciel Apr 10, 2026
8cc30a0
Fix lipo error in release workflow by using FMOD xcframeworks directly
gumaciel Apr 10, 2026
582773e
build(ios): integrate dsymutil for dSYM generation in xcframework
gumaciel May 3, 2026
c693402
build(ios): resolve merge conflict and integrate dSYM generation into…
gumaciel May 7, 2026
8084226
Merge pull request #2 from gumaciel/fix/dsym-generation
gumaciel May 7, 2026
a37eba6
Fix formatting in user guide for audio drivers (#432)
Fernando-S Apr 27, 2026
285d03c
Updated .gitignore to exclude Visual Studio solution files generated …
horacv Apr 27, 2026
3040e3e
Fix compiling with GCC 13+ (#410)
pmdevita Apr 27, 2026
0f8b33f
Call `mixerSuspend` and `mixerResume` when moving to/from background …
raggy Apr 27, 2026
315d0c6
merge: sync feature/ios-simulator-support with upstream/master
gumaciel May 7, 2026
9f854b5
build(ios): ensure dSYMs are preserved and captured in artifacts
gumaciel May 7, 2026
0bf136e
build(ios): add dSYM diagnostics and use absolute paths
gumaciel May 7, 2026
c3dcb41
build(ios): generate dSYMs in merge-xcframework job
gumaciel May 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions .github/actions/create-native-build/action.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ inputs:
description: The password for fmod account used to download fmod binaries.
shell:
description: The shell used by the runner.
artifact-suffix:
description: Optional suffix for the artifact name.
default: ""
runs:
using: composite
steps:
Expand Down Expand Up @@ -106,6 +109,6 @@ runs:
- name: Upload Artifact
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.platform }}-${{ inputs.target }}
path: ${{ env.TARGET_PATH }}${{ inputs.platform }}/**/*.*
name: ${{ inputs.platform }}-${{ inputs.target }}${{ inputs.artifact-suffix }}
path: ${{ env.TARGET_PATH }}${{ inputs.platform }}/**/*
if-no-files-found: error
2 changes: 2 additions & 0 deletions .github/workflows/check_pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -142,13 +142,15 @@ jobs:
os: "macos-14"
platform: ios
target: template_debug
flags: arch=arm64
fmod-executable-suffix: ios.dmg
shell: bash

- name: iOS Release Compilation
os: "macos-14"
platform: ios
target: template_release
flags: arch=arm64
fmod-executable-suffix: ios.dmg
shell: bash

Expand Down
112 changes: 107 additions & 5 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -123,16 +123,54 @@ jobs:
os: "macos-14"
platform: ios
target: template_debug
flags: arch=arm64
fmod-executable-suffix: ios.dmg
shell: bash

- name: iOS Release Compilation
os: "macos-14"
platform: ios
target: template_release
flags: arch=arm64
fmod-executable-suffix: ios.dmg
shell: bash

- name: iOS Simulator x86_64 Debug Compilation
os: "macos-14"
platform: ios
target: template_debug
flags: ios_simulator=yes arch=x86_64
fmod-executable-suffix: ios.dmg
artifact-suffix: "-sim-x64"
shell: bash

- name: iOS Simulator x86_64 Release Compilation
os: "macos-14"
platform: ios
target: template_release
flags: ios_simulator=yes arch=x86_64
fmod-executable-suffix: ios.dmg
artifact-suffix: "-sim-x64"
shell: bash

- name: iOS Simulator arm64 Debug Compilation
os: "macos-14"
platform: ios
target: template_debug
flags: ios_simulator=yes arch=arm64
fmod-executable-suffix: ios.dmg
artifact-suffix: "-sim-arm64"
shell: bash

- name: iOS Simulator arm64 Release Compilation
os: "macos-14"
platform: ios
target: template_release
flags: ios_simulator=yes arch=arm64
fmod-executable-suffix: ios.dmg
artifact-suffix: "-sim-arm64"
shell: bash

steps:
- name: Checkout
uses: actions/checkout@v4
Expand All @@ -158,9 +196,73 @@ jobs:
fmod-user: ${{ secrets.FMODUSER }}
fmod-password: ${{ secrets.FMODPASS }}
shell: ${{ matrix.shell }}
artifact-suffix: ${{ matrix.artifact-suffix }}

package-godot-addon:
merge-ios-xcframework:
name: Merge iOS XCFrameworks (${{ matrix.target }})
needs: [build]
runs-on: "macos-14"
strategy:
matrix:
target: [template_debug, template_release]
steps:
- name: Download iOS Device
uses: actions/download-artifact@v4
with:
name: ios-${{ matrix.target }}
path: ios-device

- name: Download iOS Simulator x64
uses: actions/download-artifact@v4
with:
name: ios-${{ matrix.target }}-sim-x64
path: ios-sim-x64

- name: Download iOS Simulator arm64
uses: actions/download-artifact@v4
with:
name: ios-${{ matrix.target }}-sim-arm64
path: ios-sim-arm64

- name: Merge Simulator Binaries (Dynamic)
run: |
mkdir -p ios-sim-universal
SIM_X64=$(find ios-sim-x64 -name "${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.x86_64.simulator.dylib" | head -n 1)
SIM_ARM64=$(find ios-sim-arm64 -name "${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.arm64.simulator.dylib" | head -n 1)
echo "SIM_X64: $SIM_X64"
echo "SIM_ARM64: $SIM_ARM64"
lipo -create "$SIM_X64" "$SIM_ARM64" -output ios-sim-universal/libGodotFmod.ios.${{ matrix.target }}.simulator.dylib

- name: Create Universal XCFramework (Bridge)
run: |
DEVICE_LIB=$(find ios-device -name "${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.arm64.dylib" | head -n 1)
SIM_LIB=ios-sim-universal/libGodotFmod.ios.${{ matrix.target }}.simulator.dylib
echo "DEVICE_LIB: $DEVICE_LIB"
echo "SIM_LIB: $SIM_LIB"

# Generate dSYMs for both libraries
dsymutil "$DEVICE_LIB" -o "${DEVICE_LIB}.dSYM"
dsymutil "$SIM_LIB" -o "${SIM_LIB}.dSYM"

xcodebuild -create-xcframework \
-library "$DEVICE_LIB" -debug-symbols "$(pwd)/${DEVICE_LIB}.dSYM" \
-library "$SIM_LIB" -debug-symbols "$(pwd)/${SIM_LIB}.dSYM" \
-output ${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.xcframework

- name: Copy FMOD XCFrameworks
run: |
cp -R ios-device/fmod*.xcframework .

- name: Upload Merged Artifacts
uses: actions/upload-artifact@v4
with:
name: ios-merged-${{ matrix.target }}
path: |
${{ env.TARGET_NAME }}.ios.${{ matrix.target }}.xcframework
fmod*.xcframework

package-godot-addon:
needs: [build, merge-ios-xcframework]
strategy:
matrix:
include:
Expand Down Expand Up @@ -235,14 +337,14 @@ jobs:
- name: Download iOS template_debug libraries
uses: actions/download-artifact@v4
with:
name: ios-template_debug
path: demo/addons/fmod/libs/iOS/
name: ios-merged-template_debug
path: demo/addons/fmod/libs/ios/

- name: Download iOS template_release libraries
uses: actions/download-artifact@v4
with:
name: ios-template_release
path: demo/addons/fmod/libs/iOS/
name: ios-merged-template_release
path: demo/addons/fmod/libs/ios/

- name: Download android editor libraries
uses: actions/download-artifact@v4
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,7 @@ config.log
**/*.editor

demo/addons/fmod/libs/android/aar/
build_libs/

*.pdb
.vs
103 changes: 89 additions & 14 deletions SConstruct
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ env = SConscript("godot-cpp/SConstruct")

# Add those directory manually, so we can skip the godot_cpp directory when including headers in C++ files
source_path = [
os.path.join("godot-cpp", "include"),
os.path.join("godot-cpp", "gen", "include"),
os.path.join("godot-cpp", "include","godot_cpp"),
os.path.join("godot-cpp", "gen", "include","godot_cpp")
]
Expand Down Expand Up @@ -96,16 +98,24 @@ elif env["platform"] == "windows":
env.Append(CCFLAGS=["/FS", "/Zi"])

elif env["platform"] == "ios":
libfmod = 'libfmod%s_iphoneos.a' % lfix
libfmodstudio = 'libfmodstudio%s_iphoneos.a' % lfix

env.Append(CPPPATH=[env['fmod_lib_dir'] + 'ios/core/inc/', env['fmod_lib_dir'] + 'ios/studio/inc/'])
env.Append(LIBPATH=[env['fmod_lib_dir'] + 'ios/core/lib/', env['fmod_lib_dir'] + 'ios/studio/lib/'])
ios_platform = "iphonesimulator" if env["ios_simulator"] else "iphoneos"
libfmod = 'libfmod%s_%s.a' % (lfix, ios_platform)
libfmodstudio = 'libfmodstudio%s_%s.a' % (lfix, ios_platform)

# Use the local libs/ios folder which should exist via symlink
fmod_ios_inc = 'libs/ios/'
if not os.path.exists(fmod_ios_inc):
# Fallback to the provided prefix if symlink doesn't exist
fmod_ios_inc = env['fmod_lib_dir'] + 'ios/'

env.Append(CPPPATH=[fmod_ios_inc + 'core/inc/', fmod_ios_inc + 'studio/inc/'])
env.Append(LIBPATH=[fmod_ios_inc + 'core/lib/', fmod_ios_inc + 'studio/lib/'])
env.Append(LIBS=[libfmod, libfmodstudio])

env.Append(LINKFLAGS=[
'-Wl,-undefined,dynamic_lookup', "-miphoneos-version-min=" + env["ios_min_version"]
])
ios_link_flags = ['-Wl,-undefined,dynamic_lookup']
if not env["ios_simulator"]:
ios_link_flags.append("-miphoneos-version-min=" + env["ios_min_version"])
env.Append(LINKFLAGS=ios_link_flags)

elif env["platform"] == "android":
libfmod = 'libfmod%s.so' % lfix
Expand Down Expand Up @@ -137,18 +147,26 @@ if env["platform"] == "macos":
env["target"]
)
else:
target = "{}.{}{}".format(
ios_sim_suffix = ".simulator" if env["platform"] == "ios" and env.get("ios_simulator", False) else ""
target = "{}.{}{}{}".format(
target,
env["arch"],
ios_sim_suffix,
env["SHLIBSUFFIX"]
)
target_out = target

library = env.SharedLibrary(target=target, source=sources)


def sys_exec(args):
proc = subprocess.Popen(args, stdout=subprocess.PIPE, text=True)
print("Running command: " + " ".join(args))
proc = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
(out, err) = proc.communicate()
if proc.returncode != 0:
print("Error executing command: " + " ".join(args))
print("Error output: " + err)
Exit(proc.returncode)
return out.rstrip("\r\n").lstrip()


Expand All @@ -162,13 +180,70 @@ if env["platform"] == "ios":
)

def create_xcframework(self, arg, env, executor = None):
sys_exec(["xcodebuild", "-create-xcframework", "-library", target, "-output", xcframework_path])
sys_exec(["rm", target])
sys_exec(["/usr/libexec/PlistBuddy", "-c", "Add :MinimumOSVersion string " + env["ios_min_version"], "{}/Info.plist".format(xcframework_path)])
if os.path.exists(xcframework_path):
shutil.rmtree(xcframework_path)

# Base target path without architecture or simulator suffix
base_target = "{}{}/{}.{}.{}".format(
target_path, env["platform"], target_name, env["platform"], env["target"]
)

# We want to find the device and simulator libraries for this target
# e.g. for ios: .arm64.dylib and .simulator.dylib
libs = []

# Device library (try .arm64.dylib, then fall back to .universal.dylib if found)
device_lib_arm64 = "{}.arm64{}".format(base_target, env["SHLIBSUFFIX"])
device_lib_universal = "{}.universal{}".format(base_target, env["SHLIBSUFFIX"])

if os.path.exists(device_lib_arm64):
libs.append(device_lib_arm64)
elif os.path.exists(device_lib_universal):
libs.append(device_lib_universal)

# Simulator library (prefer universal simulator if it exists)
sim_universal = "{}.simulator{}".format(base_target, env["SHLIBSUFFIX"])
sim_universal_alt = "{}.universal.simulator{}".format(base_target, env["SHLIBSUFFIX"])

if os.path.exists(sim_universal):
libs.append(sim_universal)
elif os.path.exists(sim_universal_alt):
libs.append(sim_universal_alt)
else:
# Fallback to arch-specific simulator build if universal doesn't exist
sim_arch = "{}.{}.simulator{}".format(base_target, env["arch"], env["SHLIBSUFFIX"])
if os.path.exists(sim_arch):
libs.append(sim_arch)

if not libs:
print("No libraries found to create xcframework for target: " + base_target)
return

cmd = ["xcodebuild", "-create-xcframework"]
dsym_paths = []
for lib in libs:
dsym_path = os.path.abspath(lib + ".dSYM")
abs_lib = os.path.abspath(lib)
sys_exec(["dsymutil", abs_lib, "-o", dsym_path])
if os.path.exists(dsym_path):
cmd.extend(["-library", abs_lib, "-debug-symbols", dsym_path])
else:
cmd.extend(["-library", abs_lib])
dsym_paths.append(dsym_path)

cmd.extend(["-output", xcframework_path])
sys_exec(cmd)

# Add MinimumOSVersion to Info.plist
plutil_cmd = ["/usr/libexec/PlistBuddy", "-c", "Add :MinimumOSVersion string " + env["ios_min_version"], "{}/Info.plist".format(xcframework_path)]
sys_exec(plutil_cmd)

create_xcframework_action = Action('', create_xcframework)

AddPostAction(library, create_xcframework_action)
# Only create xcframework on device builds; simulator builds produce
# individual dylibs that the CI merge-ios-xcframework job combines.
if not env.get("ios_simulator", False):
AddPostAction(library, create_xcframework_action)


def copy_fmod_libraries(self, arg, env, executor = None):
Expand Down
7 changes: 6 additions & 1 deletion demo/addons/fmod/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,9 @@
*.so
*.so.*
*.xcframework
!libs/
!libs/
!libs/**/*.a
!libs/**/*.dylib
!libs/**/*.xcframework
!libs/**/*.dll
!libs/**/*.so
10 changes: 5 additions & 5 deletions demo/addons/fmod/fmod.gdextension
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[configuration]
entry_symbol = "fmod_library_init"
compatibility_minimum = 4.5
compatibility_minimum = 4.1

[libraries]
windows.editor.x86_64 = "res://addons/fmod/libs/windows/libGodotFmod.windows.editor.x86_64.dll"
Expand Down Expand Up @@ -92,10 +92,10 @@ android.release.arm64 = {
"libs/android/arm64/libfmodstudio.so": "",
}
ios.debug = {
"libs/ios/libfmodL_iphoneos.a": "",
"libs/ios/libfmodstudioL_iphoneos.a": ""
"libs/ios/fmodL.xcframework": "",
"libs/ios/fmodstudioL.xcframework": ""
}
ios.release = {
"libs/ios/libfmod_iphoneos.a": "",
"libs/ios/libfmodstudio_iphoneos.a": ""
"libs/ios/fmod.xcframework": "",
"libs/ios/fmodstudio.xcframework": ""
}
Loading
Loading