diff --git a/.github/workflows/package.yml b/.github/workflows/package.yml
index e65f1a82b..179acf8d6 100644
--- a/.github/workflows/package.yml
+++ b/.github/workflows/package.yml
@@ -63,36 +63,24 @@ jobs:
run: |
rcodesign sign --for-notarization \
--p12-file ${{ env.APPLE_CERT_PATH }} --p12-password ${{ secrets.APPLE_CERT_PASSWORD }} \
- artifact/addons/sentry/bin/macos/libsentry.macos.debug.framework
+ artifact/addons/sentry/bin/macos/libsentry.macos.debug.dylib
rcodesign sign --for-notarization \
--p12-file ${{ env.APPLE_CERT_PATH }} --p12-password ${{ secrets.APPLE_CERT_PASSWORD }} \
- artifact/addons/sentry/bin/macos/libsentry.macos.release.framework
+ artifact/addons/sentry/bin/macos/libsentry.macos.release.dylib
rcodesign sign --for-notarization \
--p12-file ${{ env.APPLE_CERT_PATH }} --p12-password ${{ secrets.APPLE_CERT_PASSWORD }} \
- artifact/addons/sentry/bin/macos/Sentry.framework
+ artifact/addons/sentry/bin/macos/libSentry.dylib
- name: Notarize macOS binaries
if: env.DO_CODESIGN == '1'
run: |
cd artifact/addons/sentry/bin/macos/
- zip -r libsentry.macos.debug.zip libsentry.macos.debug.framework
+ zip -r sentry-godot-dylibs.zip libsentry.macos.debug.dylib libsentry.macos.release.dylib libSentry.dylib
rcodesign notary-submit --wait \
--api-key-file ${{ env.APPLE_API_KEY_PATH }} \
- libsentry.macos.debug.zip
- rm libsentry.macos.debug.zip
-
- zip -r libsentry.macos.release.zip libsentry.macos.release.framework
- rcodesign notary-submit --wait \
- --api-key-file ${{ env.APPLE_API_KEY_PATH }} \
- libsentry.macos.release.zip
- rm libsentry.macos.release.zip
-
- zip -r Sentry.framework.zip Sentry.framework
- rcodesign notary-submit --wait \
- --api-key-file ${{ env.APPLE_API_KEY_PATH }} \
- Sentry.framework.zip
- rm Sentry.framework.zip
+ sentry-godot-dylibs.zip
+ rm sentry-godot-dylibs.zip
- name: Prepare artifact
shell: bash
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9d228cf78..a20eb92ff 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Improvements
+
+- Switched from shipping frameworks to dylibs on macOS to avoid Windows symlink issues and prevent TestFlight rejections caused by malformed framework bundles ([#468](https://github.com/getsentry/sentry-godot/pull/468))
+
### Dependencies
- Bump Sentry Android from v8.28.0 to v8.29.0 ([#465](https://github.com/getsentry/sentry-godot/pull/465))
diff --git a/SConstruct b/SConstruct
index 2a74004b1..acc2ea4eb 100644
--- a/SConstruct
+++ b/SConstruct
@@ -80,7 +80,6 @@ arch = env["arch"]
# Register tools
env.Tool("copy")
env.Tool("separate_debug_symbols")
-env.Tool("plist")
# Restore original ARGUMENTS and add custom options to environment
ARGUMENTS.clear()
@@ -149,8 +148,8 @@ if internal_sdk == SDK.COCOA:
env = SConscript("modules/sentry-cocoa.SConscript", exports=["env"])
# Deploy Sentry Cocoa dependency to project directory.
- deploy_cocoa_xcframework = env.DeploySentryCocoa(out_dir)
- Default(deploy_cocoa_xcframework)
+ deploy_cocoa = env.DeploySentryCocoa(out_dir)
+ Default(deploy_cocoa)
# *** Build GDExtension library.
@@ -200,10 +199,11 @@ if platform == "ios":
extra += ".simulator"
temp_dir = "project/addons/sentry/bin/ios/temp"
- lib_name = f"libsentry.{platform}.{build_type}.{arch}{extra}"
- lib_path = f"{temp_dir}/{lib_name}.dylib"
+ lib_name = f"libsentry.{platform}.{build_type}.{arch}{extra}.dylib"
+ lib_path = f"{temp_dir}/{lib_name}"
library = env.SharedLibrary(lib_path, source=sources)
+ Depends(library, deploy_cocoa)
Default(library)
# Generate XCFramework for iOS GDExtension libs if requested
@@ -224,23 +224,13 @@ if platform == "ios":
elif platform == "macos":
# *** Build macOS shared library.
- lib_name = f"libsentry.{platform}.{build_type}{extra}"
- lib_path = f"{out_dir}/{lib_name}.framework/{lib_name}"
+ lib_name = f"libsentry.{platform}.{build_type}{extra}.dylib"
+ lib_path = f"{out_dir}/{lib_name}"
library = env.SharedLibrary(lib_path, source=sources)
+ Depends(library, deploy_cocoa)
Default(library)
- # Create Info.plist
- plist_path = f"{out_dir}/{lib_name}.framework/Resources/Info.plist"
- plist = env.FrameworkPlist(File(plist_path), File("SConstruct"),
- bundle_executable=lib_name,
- bundle_identifier=f"io.sentry.SentryForGodot.{build_type}",
- bundle_version=VERSION,
- bundle_platforms=["MacOSX"]
- )
- Depends(plist, library)
- Default(plist)
-
else:
# *** Build shared library on other platforms.
@@ -248,8 +238,8 @@ else:
if env["threads"] is False:
extra += ".nothreads"
- lib_name = f"libsentry.{platform}.{build_type}.{arch}{extra}"
- lib_path = f"{out_dir}/{lib_name}{shlib_suffix}"
+ lib_name = f"libsentry.{platform}.{build_type}.{arch}{extra}{shlib_suffix}"
+ lib_path = f"{out_dir}/{lib_name}"
library = env.SharedLibrary(lib_path, source=sources)
Default(library)
@@ -260,10 +250,10 @@ else:
if env["debug_symbols"] and env["separate_debug_symbols"]:
# Note: Windows/MSVC separates by default.
if platform in ["macos", "ios"]:
- dsym_path = f"{out_dir}/dSYMs/{lib_name}.framework.dSYM"
+ dsym_path = f"{out_dir}/dSYMs/{lib_name}.dSYM"
env.SeparateDebugSymbols(Dir(dsym_path), library)
elif platform in ["linux", "android"]:
- symbols_path = f"{lib_path}.debug"
+ symbols_path = f"{out_dir}/{lib_name}.debug"
env.SeparateDebugSymbols(File(symbols_path), library)
diff --git a/modules/sentry-cocoa.SConscript b/modules/sentry-cocoa.SConscript
index 0d5195718..f3d5169dd 100644
--- a/modules/sentry-cocoa.SConscript
+++ b/modules/sentry-cocoa.SConscript
@@ -73,70 +73,6 @@ def detect_xcode():
Exit(1)
-def flatten_framework(framework_path):
- """Flatten macOS framework by removing symlinks and versioned structure"""
- framework_path = Path(framework_path)
- if not framework_path.exists():
- print(f"ERROR: Framework not found at {framework_path}")
- Exit(1)
-
- versions_dir = framework_path / "Versions"
- if not versions_dir.exists():
- # Framework doesn't need flattening.
- return
-
- print(f"Flattening framework structure: {framework_path}")
-
- # 1. Remove all symlinks from the root of the framework
- for item_path in framework_path.iterdir():
- if item_path.is_symlink():
- print(f" Removing symlink: {item_path.name}")
- item_path.unlink()
-
- # 2. Move Versions/A/* to root of the framework
- version_a_dir = versions_dir / "A"
- if version_a_dir.exists():
- print(" Moving contents from Versions/A/ to root")
- for item_path in version_a_dir.iterdir():
- dest_path = framework_path / item_path.name
-
- # Handle potential conflicts
- if dest_path.exists():
- print(f" WARNING: {item_path.name} already exists at root, removing old version")
- remove_if_exists(dest_path)
-
- shutil.move(str(item_path), str(dest_path))
- print(f" Moved: {item_path.name}")
- else:
- print(f" WARNING: Versions/A directory not found in {framework_path}")
-
- # 3. Remove Versions/ directory
- print(" Removing Versions directory")
- shutil.rmtree(versions_dir)
-
- # 4. Patch the binary's install name for flattened structure
- binary_name = framework_path.name.replace('.framework', '')
- binary_path = framework_path / binary_name
-
- if not binary_path.exists():
- print(f"ERROR: Framework binary not found at {binary_path}")
- Exit(1)
-
- print(f" Patching install name for binary: {binary_name}")
- install_name = f"@rpath/{binary_name}.framework/{binary_name}"
- cmd = ["install_name_tool", "-id", install_name, str(binary_path)]
-
- try:
- subprocess.run(cmd, capture_output=True, text=True, check=True)
- print(f" Successfully updated install name to: {install_name}")
- except subprocess.CalledProcessError as e:
- print(f" WARNING: Failed to update install name: {e}")
- print(f" stdout: {e.stdout}")
- print(f" stderr: {e.stderr}")
-
- print(f"Framework flattening completed: {framework_path}")
-
-
def update_cocoa_framework():
"""Updates Sentry Cocoa to the latest version."""
project_root = Path(env.Dir("#").abspath)
@@ -160,7 +96,8 @@ def update_cocoa_framework():
if stored_version == cocoa_version:
# Check if framework actually exists
xcframework_path = cocoa_dir / "Sentry-Dynamic.xcframework"
- if xcframework_path.exists():
+ headers_path = cocoa_dir / "macos_include" / "Sentry"
+ if xcframework_path.exists() and headers_path.exists():
should_download = False
print(f"Detected Sentry Cocoa SDK v{cocoa_version} – up-to-date!")
except:
@@ -184,9 +121,6 @@ def update_cocoa_framework():
print(f"Extracting {zip_path}")
extract_zip_with_symlinks(zip_path, cocoa_dir)
- # NOTE: We need to flatten macOS slice due to issues with symlinks on Windows.
- flatten_framework(cocoa_dir / "Sentry-Dynamic.xcframework" / "macos-arm64_x86_64" / "Sentry.framework")
-
zip_path.unlink() # delete file
version_file.write_text(cocoa_version)
@@ -196,6 +130,29 @@ def update_cocoa_framework():
print(f"ERROR: Failed to download Sentry framework: {e}.")
Exit(1)
+ # Prepare headers
+ print("Preparing headers for macOS...")
+ headers_dest = cocoa_dir / "macos_include" / "Sentry"
+ headers_dest.mkdir(parents=True, exist_ok=True)
+ macos_framework = cocoa_dir / "Sentry-Dynamic.xcframework" / "macos-arm64_x86_64" / "Sentry.framework"
+
+ if macos_framework.exists():
+ # Copy Headers
+ headers_source = macos_framework / "Headers"
+ if headers_source.exists():
+ for header_file in headers_source.glob("*.h"):
+ shutil.copy2(header_file, headers_dest)
+ print(f" Copied {len(list(headers_source.glob('*.h')))} headers from {headers_source}")
+
+ # Copy PrivateHeaders
+ private_headers_source = macos_framework / "PrivateHeaders"
+ if private_headers_source.exists():
+ for header_file in private_headers_source.glob("*.h"):
+ shutil.copy2(header_file, headers_dest)
+ print(f" Copied {len(list(private_headers_source.glob('*.h')))} private headers from {private_headers_source}")
+ else:
+ print(f"WARNING: macOS framework not found at {macos_framework}")
+
update_cocoa_framework()
@@ -224,29 +181,38 @@ if platform in ["macos", "ios"]:
if platform == "macos":
framework_dir = xcframework_path / "macos-arm64_x86_64/Sentry.framework"
+ env.Append(
+ CPPPATH=f"{project_root}/modules/sentry-cocoa/macos_include/",
+ LIBPATH=f"{project_root}/project/addons/sentry/bin/macos/",
+ LIBS=["Sentry"],
+ LINKFLAGS=[
+ # Load libSentry.dylib from same dir as GDExtension.
+ "-Wl,-rpath,@loader_path/"
+ ]
+ )
else:
if ios_simulator:
framework_dir = xcframework_path / "ios-arm64_x86_64-simulator/Sentry.framework"
else:
framework_dir = xcframework_path / "ios-arm64/Sentry.framework"
- if not framework_dir.exists():
- print(f"ERROR: Sentry.framework is missing at {framework_dir}.")
- Exit(1)
+ if not framework_dir.exists():
+ print(f"ERROR: iOS framework slice is missing at {framework_dir}.")
+ Exit(1)
- framework_container_dir = framework_dir.parent.absolute()
+ framework_container_dir = framework_dir.parent.absolute()
- env.Append(
- CPPFLAGS=["-F" + str(framework_container_dir)],
- LINKFLAGS=[
- "-framework", "Sentry",
- "-F" + str(framework_container_dir),
- # Allow extension to find framework in addons/sentry/ directory.
- "-Wl,-rpath,@loader_path/..",
- ]
- )
+ env.Append(
+ CPPFLAGS=["-F" + str(framework_container_dir)],
+ LINKFLAGS=[
+ "-framework", "Sentry",
+ "-F" + str(framework_container_dir),
+ # Allow extension to find framework in addons/sentry/ directory.
+ "-Wl,-rpath,@loader_path/..",
+ ]
+ )
- print(f"Added {platform} Sentry dynamic framework: {framework_dir}")
+ print(f"Added {platform} Sentry dynamic framework: {framework_dir}")
# *** Export pseudo-builders to create xcframeworks
@@ -402,20 +368,41 @@ def DeploySentryCocoa(self, target_dir):
elif platform == "macos":
source_framework = source_xcframework / "macos-arm64_x86_64/Sentry.framework"
- target_framework = target_dir_path / "Sentry.framework"
- # Copy only the binary and "Resources" dir -- we don't need to export headers or modules.
- commands.append(
- env.Copy(File(target_framework / "Sentry"), File(source_framework / "Sentry"))
- )
+ def patch_install_name_action(target, source, env):
+ """Patch install name after copy"""
+ lib_path_str = str(target[0])
+ print(f" Patching install name for binary: {lib_name}")
+ try:
+ new_rpath = "@rpath/libSentry.dylib"
+ cmd = ["install_name_tool", "-id", new_rpath, lib_path_str]
+ subprocess.run(cmd, capture_output=True, text=True, check=True)
+ print(f" Successfully updated install name to: {new_rpath}")
+ except subprocess.CalledProcessError as e:
+ print(f" WARNING: Failed to update install name: {e}")
+ print(f" stdout: {e.stdout}")
+ print(f" stderr: {e.stderr}")
+ return 1
+ return 0
+
+ # Copy the framework binary as dylib, and patch install name.
+ lib_name = "libSentry.dylib"
+ lib_path = target_dir_path / lib_name
commands.append(
- env.Copy(Dir(target_framework / "Resources"), Dir(source_framework / "Resources"))
+ env.Command(
+ File(lib_path),
+ File(source_framework / "Versions/A/Sentry"),
+ [
+ Copy("$TARGET", "$SOURCE"),
+ patch_install_name_action
+ ]
+ )
)
# Debug symbols
commands.append(
env.Copy(
- Dir(target_dir_path / "dSYMs" / "Sentry.framework.dSYM"),
+ Dir(target_dir_path / "dSYMs" / "libSentry.dylib.dSYM"),
Dir(source_xcframework / "macos-arm64_x86_64" / "dSYMs" / "Sentry.framework.dSYM")
)
)
diff --git a/project/project.godot b/project/project.godot
index b51ea54ed..07d82ffe7 100644
--- a/project/project.godot
+++ b/project/project.godot
@@ -57,4 +57,3 @@ options/dsn="https://3f1e095cf2e14598a0bd5b4ff324f712@o447951.ingest.us.sentry.i
options/attach_scene_tree=true
logger/include_variables=true
experimental/attach_screenshot=true
-experimental/enable_logs=true
diff --git a/site_scons/site_tools/plist.py b/site_scons/site_tools/plist.py
deleted file mode 100644
index a6a305e74..000000000
--- a/site_scons/site_tools/plist.py
+++ /dev/null
@@ -1,60 +0,0 @@
-"""
-Tool to generate Info.plist.
-"""
-
-import os
-from SCons.Script import Builder, Action
-
-
-def generate_framework_plist(target, source, env):
- bundle_executable = env.get("bundle_executable", "MyFramework")
- bundle_identifier = env.get("bundle_identifier", "com.example.MyFramework")
- bundle_name = env.get("bundle_name", bundle_executable)
- bundle_version_string = env.get("bundle_version", "1.0")
- bundle_version = bundle_version_string.split("-", 1)[0]
- bundle_platforms = env.get("bundle_platforms", ["MacOSX"])
- bundle_package_type = env.get("bundle_package_type", "FMWK") # FMWK or BNDL
- bundle_min_system = env.get("bundle_min_system", env.get("macos_deployment_target", "10.13"))
-
- platforms_content = "\n".join(f" {p}" for p in bundle_platforms)
-
- content = f"""
-
-
-
- CFBundleExecutable
- {bundle_executable}
- CFBundleIdentifier
- {bundle_identifier}
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- {bundle_name}
- CFBundlePackageType
- {bundle_package_type}
- CFBundleShortVersionString
- {bundle_version_string}
- CFBundleSupportedPlatforms
-
-{platforms_content}
-
- CFBundleVersion
- {bundle_version}
- LSMinimumSystemVersion
- {bundle_min_system}
-
-"""
-
- with open(str(target[0]), "w") as f:
- f.write(content)
-
- return None
-
-
-def generate(env):
- plist_builder = Builder(action=Action(generate_framework_plist, cmdstr="Generating Info.plist for $TARGET"))
- env.Append(BUILDERS={"FrameworkPlist": plist_builder})
-
-
-def exists(env):
- return True
diff --git a/src/manifest.gdextension b/src/manifest.gdextension
index 0ea5fa826..08466f7a4 100644
--- a/src/manifest.gdextension
+++ b/src/manifest.gdextension
@@ -5,8 +5,8 @@ compatibility_minimum = "{compatibility_minimum}"
[libraries]
-macos.debug = "res://addons/sentry/bin/macos/libsentry.macos.debug.framework"
-macos.release = "res://addons/sentry/bin/macos/libsentry.macos.release.framework"
+macos.debug = "res://addons/sentry/bin/macos/libsentry.macos.debug.dylib"
+macos.release = "res://addons/sentry/bin/macos/libsentry.macos.release.dylib"
windows.debug.x86_64 = "res://addons/sentry/bin/windows/x86_64/libsentry.windows.debug.x86_64.dll"
windows.release.x86_64 = "res://addons/sentry/bin/windows/x86_64/libsentry.windows.release.x86_64.dll"
@@ -61,11 +61,11 @@ windows.x86_32 = {
}
macos.debug = {
- "res://addons/sentry/bin/macos/Sentry.framework" : ""
+ "res://addons/sentry/bin/macos/libSentry.dylib" : ""
}
macos.release = {
- "res://addons/sentry/bin/macos/Sentry.framework" : ""
+ "res://addons/sentry/bin/macos/libSentry.dylib" : ""
}
ios.debug = {