Skip to content

Commit

Permalink
fix: Xcode build with iOS native support disabled (#1352)
Browse files Browse the repository at this point in the history
  • Loading branch information
bitsandfoxes authored Jun 5, 2023
1 parent 797f53b commit d545adf
Show file tree
Hide file tree
Showing 13 changed files with 379 additions and 70 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ package-dev/Plugins/*/Sentry/crashpad_handler*

# required to be excluded on all platforms because we don't want Unity to copy it during build
!package-dev/**/SentryNativeBridge.m.meta
!package-dev/**/SentryNativeBridgeNoOp.m.meta

# Android SDK files
package-dev/Plugins/Android/Sentry~/*
Expand Down
5 changes: 3 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@

## Unreleased

### Fixes
### Fixes

- Fixed Xcode linking error with the SDK disabled ([#1352](https://github.com/getsentry/sentry-unity/pull/1352))
- Fixed an issue triggering an error `Failed to find the closing bracket` when using custom gradle files ([#1359](https://github.com/getsentry/sentry-unity/pull/1359))
- Fixes Android native integration in the gradle output project for builds with Unity 2022.3 and newer ([#1354](https://github.com/getsentry/sentry-unity/pull/1354))
- Fixed the Android native integration for builds with Unity 2022.3 and newer ([#1354](https://github.com/getsentry/sentry-unity/pull/1354))

### Dependencies

Expand Down
67 changes: 67 additions & 0 deletions package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
NS_ASSUME_NONNULL_BEGIN

// macOS only
int SentryNativeBridgeLoadLibrary() { return 0; }
void *_Nullable SentryNativeBridgeOptionsNew() { return nil; }
void SentryNativeBridgeOptionsSetString(void *options, const char *name, const char *value) { }
void SentryNativeBridgeOptionsSetInt(void *options, const char *name, int32_t value) { }
void SentryNativeBridgeStartWithOptions(void *options) { }

int SentryNativeBridgeCrashedLastRun() { return 0; }

void SentryNativeBridgeClose() { }

void SentryNativeBridgeAddBreadcrumb(
const char *timestamp, const char *message, const char *type, const char *category, int level) { }

void SentryNativeBridgeSetExtra(const char *key, const char *value) { }

void SentryNativeBridgeSetTag(const char *key, const char *value) { }

void SentryNativeBridgeUnsetTag(const char *key) { }

void SentryNativeBridgeSetUser(
const char *email, const char *userId, const char *ipAddress, const char *username) { }

void SentryNativeBridgeUnsetUser() { }

char *SentryNativeBridgeGetInstallationId() { return NULL; }

void SentryNativeBridgeWriteScope( // clang-format off
// // const char *AppStartTime,
// const char *AppBuildType,
// // const char *OperatingSystemRawDescription,
// int DeviceProcessorCount,
// const char *DeviceCpuDescription,
// const char *DeviceTimezone,
// int8_t DeviceSupportsVibration,
// const char *DeviceName,
// int8_t DeviceSimulator,
// const char *DeviceDeviceUniqueIdentifier,
// const char *DeviceDeviceType,
// // const char *DeviceModel,
// // long DeviceMemorySize,
int32_t GpuId,
const char *GpuName,
const char *GpuVendorName,
int32_t GpuMemorySize,
const char *GpuNpotSupport,
const char *GpuVersion,
const char *GpuApiType,
int32_t GpuMaxTextureSize,
int8_t GpuSupportsDrawCallInstancing,
int8_t GpuSupportsRayTracing,
int8_t GpuSupportsComputeShaders,
int8_t GpuSupportsGeometryShaders,
const char *GpuVendorId,
int8_t GpuMultiThreadedRendering,
const char *GpuGraphicsShaderLevel,
const char *EditorVersion,
const char *UnityInstallMode,
const char *UnityTargetFrameRate,
const char *UnityCopyTextureSupport,
const char *UnityRenderingThreadingMode
) // clang-format on
{ }

NS_ASSUME_NONNULL_END
78 changes: 78 additions & 0 deletions package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

63 changes: 49 additions & 14 deletions src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs
Original file line number Diff line number Diff line change
Expand Up @@ -51,45 +51,80 @@ internal static void AddSentryToXcodeProject(SentryUnityOptions? options,
{
logger.LogWarning("iOS native support disabled because Sentry has not been configured. " +
"You can do that through the editor: {0}", SentryWindow.EditorMenuPath);

SetupNoOpBridge(logger, pathToProject);
return;
}

if (!IsNativeSupportEnabled(options, logger))
{
var main = File.ReadAllText(Path.Combine(pathToProject, SentryXcodeProject.MainPath));
if (NativeMain.ContainsSentry(main, logger))
var mainPath = Path.Combine(pathToProject, SentryXcodeProject.MainPath);
if (File.Exists(mainPath))
{
throw new BuildFailedException(
"The iOS native support has been disabled but the exported project has been modified " +
"during a previous build. Select 'Replace' when exporting the project to create a clean project.");
var main = File.ReadAllText(mainPath);
if (NativeMain.ContainsSentry(main, logger))
{
throw new BuildFailedException(
"The iOS native support has been disabled but the exported project has been modified " +
"during a previous build. Select 'Replace' when exporting the project to create a clean project.");
}
}

SetupNoOpBridge(logger, pathToProject);
return;
}

logger.LogInfo("Attempting to add Sentry to the Xcode project.");
SetupSentry(options, cliOptions, logger, pathToProject);
}

internal static void SetupNoOpBridge(IDiagnosticLogger logger, string pathToProject)
{
try
{
// The Sentry.xcframework ends in '~' to stop Unity from trying to import the .frameworks.
// Otherwise, Unity tries to export those with the XCode build.
var frameworkPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", "Sentry.xcframework~"));
CopyFramework(frameworkPath, Path.Combine(pathToProject, "Frameworks", "Sentry.xcframework"), logger);
logger.LogDebug("Copying the NoOp bride to the output project.");

var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", "SentryNativeBridge.m"));
CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), "SentryNativeBridge.m"), logger);
// The Unity SDK expects the bridge to be there. The Xcode build will break during linking otherwise.
var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.NoOpBridgeName));
CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger);

using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject);
sentryXcodeProject.AddSentryNativeBridge();
}
catch (Exception e)
{
logger.LogError("Failed to add the Sentry NoOp bridge to the output project.", e);
}
}

internal static void SetupSentry(SentryUnityOptions options,
SentryCliOptions? cliOptions,
IDiagnosticLogger logger,
string pathToProject)
{
logger.LogInfo("Attempting to add Sentry to the Xcode project.");

try
{
// The Sentry.xcframework ends in '~' to hide it from Unity. Otherwise, Unity tries to export it with the XCode build.
var frameworkPath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.FrameworkName + "~"));
CopyFramework(frameworkPath, Path.Combine(pathToProject, "Frameworks", SentryXcodeProject.FrameworkName), logger);

var nativeBridgePath = Path.GetFullPath(Path.Combine("Packages", SentryPackageInfo.GetName(), "Plugins", "iOS", SentryXcodeProject.BridgeName));
CopyFile(nativeBridgePath, Path.Combine(pathToProject, "Libraries", SentryPackageInfo.GetName(), SentryXcodeProject.BridgeName), logger);

using var sentryXcodeProject = SentryXcodeProject.Open(pathToProject, logger);
sentryXcodeProject.AddSentryFramework();
sentryXcodeProject.AddSentryNativeBridge();
sentryXcodeProject.AddNativeOptions(options, NativeOptions.CreateFile);
sentryXcodeProject.AddSentryToMain(options);

if (cliOptions?.IsValid(logger, EditorUserBuildSettings.development) is true)
if (cliOptions != null && cliOptions.IsValid(logger, EditorUserBuildSettings.development))
{
logger.LogInfo("Automatic symbol upload enabled. Adding script to build phase.");

SentryCli.CreateSentryProperties(pathToProject, cliOptions, options);
SentryCli.SetupSentryCli(pathToProject, RuntimePlatform.OSXEditor);
sentryXcodeProject.AddBuildPhaseSymbolUpload(logger, cliOptions);
sentryXcodeProject.AddBuildPhaseSymbolUpload(cliOptions);
}
else if (options.Il2CppLineNumberSupportEnabled)
{
Expand Down
33 changes: 28 additions & 5 deletions src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ internal class SentryXcodeProject : IDisposable
{
internal const string FrameworkName = "Sentry.xcframework";
internal const string BridgeName = "SentryNativeBridge.m";
internal const string NoOpBridgeName = "SentryNativeBridgeNoOp.m";
internal const string OptionsName = "SentryOptions.m";
internal const string SymbolUploadPhaseName = "SymbolUpload";

Expand All @@ -41,6 +42,7 @@ internal class SentryXcodeProject : IDisposable
fi
";

private readonly IDiagnosticLogger? _logger = null;
private readonly Type _pbxProjectType = null!; // Set in constructor or throws
private readonly Type _pbxProjectExtensionsType = null!; // Set in constructor or throws
private readonly object _project = null!; // Set in constructor or throws
Expand All @@ -51,8 +53,9 @@ internal class SentryXcodeProject : IDisposable
private string _mainTargetGuid = null!; // Set when opening the project
private string _unityFrameworkTargetGuid = null!; // Set when opening the project

internal SentryXcodeProject(string projectRoot)
internal SentryXcodeProject(string projectRoot, IDiagnosticLogger? logger = null)
{
_logger = logger;
var xcodeAssembly = Assembly.Load("UnityEditor.iOS.Extensions.Xcode");
_pbxProjectType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.PBXProject");
_pbxProjectExtensionsType = xcodeAssembly.GetType("UnityEditor.iOS.Xcode.Extensions.PBXProjectExtensions");
Expand All @@ -64,16 +67,18 @@ internal SentryXcodeProject(string projectRoot)
.Invoke(null, new[] { _projectRoot });
}

public static SentryXcodeProject Open(string path)
public static SentryXcodeProject Open(string path, IDiagnosticLogger? logger = null)
{
var xcodeProject = new SentryXcodeProject(path);
var xcodeProject = new SentryXcodeProject(path, logger);
xcodeProject.ReadFromProjectFile();

return xcodeProject;
}

internal void ReadFromProjectFile()
{
_logger?.LogInfo("Reading the Xcode project file.");

if (!File.Exists(_projectPath))
{
throw new FileNotFoundException("Could not locate generated Xcode project at", _projectPath);
Expand All @@ -85,10 +90,14 @@ internal void ReadFromProjectFile()
.Invoke(_project, null);
_unityFrameworkTargetGuid = (string)_pbxProjectType.GetMethod("GetUnityFrameworkTargetGuid", BindingFlags.Public | BindingFlags.Instance)
.Invoke(_project, null);

_logger?.LogDebug("Successfully read the Xcode project file.");
}

public void AddSentryFramework()
{
_logger?.LogInfo("Adding the Sentry framework.");

var relativeFrameworkPath = Path.Combine("Frameworks", FrameworkName);
var frameworkGuid = (string)_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance)
.Invoke(_project, new object[] { relativeFrameworkPath, relativeFrameworkPath, 1 }); // 1 is PBXSourceTree.Source
Expand Down Expand Up @@ -116,16 +125,22 @@ public void AddSentryFramework()
var addFileToBuildSectionMethod = _pbxProjectType.GetMethod("AddFileToBuildSection", new[] { typeof(string), typeof(string), typeof(string) });
addFileToBuildSectionMethod.Invoke(_project, new object[] { _mainTargetGuid, mainBuildPhaseGuid, frameworkGuid });
addFileToBuildSectionMethod.Invoke(_project, new object[] { _unityFrameworkTargetGuid, unityFrameworkBuildPhaseGuid, frameworkGuid });

_logger?.LogDebug("Successfully added the Sentry framework.");
}

public void AddSentryNativeBridge()
{
_logger?.LogInfo("Adding the Sentry Native Bridge.");

var relativeBridgePath = Path.Combine("Libraries", SentryPackageInfo.GetName(), BridgeName);
var bridgeGuid = (string)_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance)
.Invoke(_project, new object[] { relativeBridgePath, relativeBridgePath, 1 }); // 1 is PBXSourceTree.Source

_pbxProjectType.GetMethod("AddFileToBuild", BindingFlags.Public | BindingFlags.Instance)
.Invoke(_project, new[] { _unityFrameworkTargetGuid, bridgeGuid });

_logger?.LogDebug("Successfully added the Sentry Native Bridge.");
}

// Used for testing
Expand All @@ -135,11 +150,13 @@ internal void SetSearchPathBuildProperty(string path)
.Invoke(_project, new object[] { new[] { _mainTargetGuid, _unityFrameworkTargetGuid }, "FRAMEWORK_SEARCH_PATHS", path });
}

public void AddBuildPhaseSymbolUpload(IDiagnosticLogger? logger, SentryCliOptions sentryCliOptions)
public void AddBuildPhaseSymbolUpload(SentryCliOptions sentryCliOptions)
{
_logger?.LogInfo("Adding automated debug symbol upload script to build phase.");

if (MainTargetContainsSymbolUploadBuildPhase())
{
logger?.LogDebug("Build phase '{0}' already added.", SymbolUploadPhaseName);
_logger?.LogDebug("Build phase '{0}' already added.", SymbolUploadPhaseName);
return;
}

Expand All @@ -152,13 +169,19 @@ public void AddBuildPhaseSymbolUpload(IDiagnosticLogger? logger, SentryCliOption

_pbxProjectType.GetMethod("AddShellScriptBuildPhase", new[] { typeof(string), typeof(string), typeof(string), typeof(string) })
.Invoke(_project, new object[] { _mainTargetGuid, SymbolUploadPhaseName, "/bin/sh", uploadScript });

_logger?.LogDebug("Successfully added automated debug symbol upload script to build phase.");
}

public void AddNativeOptions(SentryUnityOptions options, Action<string, SentryUnityOptions> nativeOptionFileCreation)
{
_logger?.LogInfo("Adding native options.");

nativeOptionFileCreation.Invoke(Path.Combine(_projectRoot, _optionsPath), options);
_pbxProjectType.GetMethod("AddFile", BindingFlags.Public | BindingFlags.Instance)
.Invoke(_project, new object[] { _optionsPath, _optionsPath, 1 }); // 1 is PBXSourceTree.Source

_logger?.LogDebug("Successfully added native options.");
}

public void AddSentryToMain(SentryUnityOptions options) =>
Expand Down
Loading

0 comments on commit d545adf

Please sign in to comment.