diff --git a/.gitignore b/.gitignore index c9a388230..554132bad 100644 --- a/.gitignore +++ b/.gitignore @@ -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~/* diff --git a/CHANGELOG.md b/CHANGELOG.md index 79e6b336b..8048bdb8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m b/package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m new file mode 100644 index 000000000..a219b2847 --- /dev/null +++ b/package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m @@ -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 diff --git a/package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m.meta b/package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m.meta new file mode 100644 index 000000000..3ed55417a --- /dev/null +++ b/package-dev/Plugins/iOS/SentryNativeBridgeNoOp.m.meta @@ -0,0 +1,78 @@ +fileFormatVersion: 2 +guid: 271a0c0b7f7454854b080b1d97da8570 +PluginImporter: + externalObjects: {} + serializedVersion: 2 + iconMap: {} + executionOrder: {} + defineConstraints: [] + isPreloaded: 0 + isOverridable: 1 + isExplicitlyReferenced: 0 + validateReferences: 1 + platformData: + - first: + : Any + second: + enabled: 0 + settings: + Exclude Editor: 1 + Exclude Linux64: 1 + Exclude OSXUniversal: 1 + Exclude Win: 1 + Exclude Win64: 1 + Exclude iOS: 1 + - first: + Any: + second: + enabled: 0 + settings: {} + - first: + Editor: Editor + second: + enabled: 0 + settings: + CPU: AnyCPU + DefaultValueInitialized: true + OS: AnyOS + - first: + Standalone: Linux64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + Standalone: OSXUniversal + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + Standalone: Win + second: + enabled: 0 + settings: + CPU: x86 + - first: + Standalone: Win64 + second: + enabled: 0 + settings: + CPU: x86_64 + - first: + iPhone: iOS + second: + enabled: 0 + settings: + AddToEmbeddedBinaries: false + CPU: AnyCPU + CompileFlags: + FrameworkDependencies: + - first: + tvOS: tvOS + second: + enabled: 1 + settings: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs index 1fb47c56e..8c3dd381d 100644 --- a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs +++ b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs @@ -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) { diff --git a/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs b/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs index 4c61baee0..cb1d20852 100644 --- a/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs +++ b/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs @@ -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"; @@ -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 @@ -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"); @@ -64,9 +67,9 @@ 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; @@ -74,6 +77,8 @@ public static SentryXcodeProject Open(string path) 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); @@ -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 @@ -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 @@ -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; } @@ -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 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) => diff --git a/src/Sentry.Unity.Editor/Android/AndroidSdkSetup.cs b/src/Sentry.Unity.Editor/Android/AndroidSdkSetup.cs index 8319ad5c9..62ba0e255 100644 --- a/src/Sentry.Unity.Editor/Android/AndroidSdkSetup.cs +++ b/src/Sentry.Unity.Editor/Android/AndroidSdkSetup.cs @@ -30,7 +30,7 @@ internal void AddAndroidSdk() } _logger.LogInfo("Copying the Android SDK to '{0}'.", _targetAndroidSdkPath); - CopyDirectory(_androidSdkPath, _targetAndroidSdkPath); + SentryFileUtil.CopyDirectory(_androidSdkPath, _targetAndroidSdkPath); } public void RemoveAndroidSdk() @@ -41,29 +41,5 @@ public void RemoveAndroidSdk() Directory.Delete(_targetAndroidSdkPath, true); } } - - private static void CopyDirectory(string sourceDirectory, string destinationDirectory) - { - var directory = new DirectoryInfo(sourceDirectory); - if (!directory.Exists) - { - throw new DirectoryNotFoundException($"Source directory not found: {directory.FullName}"); - } - - var subDirectories = directory.GetDirectories(); - Directory.CreateDirectory(destinationDirectory); - - foreach (var file in directory.GetFiles()) - { - var targetFilePath = Path.Combine(destinationDirectory, file.Name); - file.CopyTo(targetFilePath); - } - - foreach (var subDirectory in subDirectories) - { - var newDestinationDir = Path.Combine(destinationDirectory, subDirectory.Name); - CopyDirectory(subDirectory.FullName, newDestinationDir); - } - } } } diff --git a/src/Sentry.Unity.Editor/Properties/AssemblyInfo.cs b/src/Sentry.Unity.Editor/Properties/AssemblyInfo.cs index 9a6fb9ece..8f9c22a30 100644 --- a/src/Sentry.Unity.Editor/Properties/AssemblyInfo.cs +++ b/src/Sentry.Unity.Editor/Properties/AssemblyInfo.cs @@ -1,4 +1,6 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Sentry.Unity.Editor.iOS")] +[assembly: InternalsVisibleTo("Sentry.Unity.Editor.iOS.Tests")] [assembly: InternalsVisibleTo("Sentry.Unity.Editor.Tests")] + diff --git a/src/Sentry.Unity.Editor/SentryFileUtil.cs b/src/Sentry.Unity.Editor/SentryFileUtil.cs new file mode 100644 index 000000000..d7f245cfd --- /dev/null +++ b/src/Sentry.Unity.Editor/SentryFileUtil.cs @@ -0,0 +1,31 @@ +using System.IO; + +namespace Sentry.Unity.Editor +{ + public static class SentryFileUtil + { + internal static void CopyDirectory(string sourceDirectory, string destinationDirectory) + { + var directory = new DirectoryInfo(sourceDirectory); + if (!directory.Exists) + { + throw new DirectoryNotFoundException($"Source directory not found: {directory.FullName}"); + } + + var subDirectories = directory.GetDirectories(); + Directory.CreateDirectory(destinationDirectory); + + foreach (var file in directory.GetFiles()) + { + var targetFilePath = Path.Combine(destinationDirectory, file.Name); + file.CopyTo(targetFilePath); + } + + foreach (var subDirectory in subDirectories) + { + var newDestinationDir = Path.Combine(destinationDirectory, subDirectory.Name); + CopyDirectory(subDirectory.FullName, newDestinationDir); + } + } + } +} diff --git a/test/Scripts.Tests/package-release.zip.snapshot b/test/Scripts.Tests/package-release.zip.snapshot index c41ab85db..fec6572ee 100644 --- a/test/Scripts.Tests/package-release.zip.snapshot +++ b/test/Scripts.Tests/package-release.zip.snapshot @@ -35,6 +35,8 @@ Plugins/macOS/Sentry/Sentry.dylib.dSYM.meta Plugins/macOS/Sentry/Sentry.dylib.meta Plugins/iOS/SentryNativeBridge.m Plugins/iOS/SentryNativeBridge.m.meta +Plugins/iOS/SentryNativeBridgeNoOp.m +Plugins/iOS/SentryNativeBridgeNoOp.m.meta Plugins/iOS/Sentry.xcframework~/ios-arm64/ Plugins/iOS/Sentry.xcframework~/ios-arm64_x86_64-simulator/ Plugins/iOS/Sentry.xcframework~/Info.plist diff --git a/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs index dabfd4b36..ee5b7f943 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/BuildPostProcessorTests.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Reflection; using NUnit.Framework; using Sentry.Unity.Tests.SharedClasses; using UnityEditor.Build; @@ -12,7 +13,7 @@ public class BuildPostProcessorTests private string _testDirectoryRoot = null!; private string _testFrameworkPath = null!; private string _testFilePath = null!; - private string _xcodeProjectPath = null!; + private string _outputProjectPath = null!; [SetUp] public void Setup() @@ -20,31 +21,91 @@ public void Setup() _testDirectoryRoot = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString().Substring(0, 4)); _testFrameworkPath = Path.Combine(_testDirectoryRoot, "Test.framework"); _testFilePath = Path.Combine(_testDirectoryRoot, "Test.m"); - _xcodeProjectPath = Path.Combine(_testDirectoryRoot, "XcodeProject"); + _outputProjectPath = Path.Combine(_testDirectoryRoot, "XcodeProject"); Directory.CreateDirectory(_testDirectoryRoot); Directory.CreateDirectory(_testFrameworkPath); File.Create(_testFilePath).Close(); - Directory.CreateDirectory(_xcodeProjectPath); + + // Test setup for output Xcode project + var testXcodeProjectPath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestFiles", "2019_4"); + SentryFileUtil.CopyDirectory(testXcodeProjectPath, _outputProjectPath); } [TearDown] public void TearDown() => Directory.Delete(_testDirectoryRoot, true); [Test] - public void AddSentryToXcodeProject_OptionsNull_LogsAndReturn() + public void AddSentryToXcodeProject_OptionsNull_LogsAndCopiesNoOpBridge() { - var options = new SentryUnityOptions { Enabled = false }; var testLogger = new TestLogger(); - BuildPostProcess.AddSentryToXcodeProject(null, null, testLogger, string.Empty); + BuildPostProcess.AddSentryToXcodeProject(null, null, testLogger, _outputProjectPath); + Assert.IsFalse(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Info && + log.message.Contains("Attempting to add Sentry to the Xcode project."))); // Sanity check Assert.IsTrue(testLogger.Logs.Any(log => log.logLevel == SentryLevel.Warning && log.message.Contains("iOS native support disabled because Sentry has not been configured."))); + + var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); + + Assert.IsTrue(File.Exists(noOpBridgePath)); + StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK + } + + [Test] + public void AddSentryToXcodeProject_SdkDisabled_LogsAndCopiesNoOpBridge() + { + var options = new SentryUnityOptions { Enabled = false }; + var testLogger = new TestLogger(); + + BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _outputProjectPath); + Assert.IsFalse(testLogger.Logs.Any(log => log.logLevel == SentryLevel.Info && - log.message.Contains("Attempting to add Sentry to the Xcode project."))); + log.message.Contains("Attempting to add Sentry to the Xcode project."))); // Sanity check + Assert.IsTrue(testLogger.Logs.Any(log => + log.logLevel == SentryLevel.Warning && + log.message.Contains("Sentry SDK has been disabled. There will be no iOS native support."))); + + var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); + + Assert.IsTrue(File.Exists(noOpBridgePath)); + StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK + } + + [Test] + public void SetupNoOpBridge_CopiesNoOpBridgeToOutput() + { + BuildPostProcess.SetupNoOpBridge(new TestLogger(), _outputProjectPath); + + var noOpBridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); + + Assert.IsTrue(File.Exists(noOpBridgePath)); + StringAssert.DoesNotContain("[SentrySDK", File.ReadAllText(noOpBridgePath)); // The NoOp bridge does not call into the Cocoa SDK + } + + [Test] + public void SetupSentry_CopiesFrameworkAndBridge() + { + var options = new SentryUnityOptions(); + var testLogger = new TestLogger(); + + BuildPostProcess.SetupSentry(options, null, testLogger, _outputProjectPath); + + var bridgePath = Path.Combine(_outputProjectPath, "Libraries", SentryPackageInfo.GetName(), + SentryXcodeProject.BridgeName); + var frameworkPath = Path.Combine(_outputProjectPath, "Frameworks", SentryXcodeProject.FrameworkName); + + Assert.IsTrue(File.Exists(bridgePath)); + Assert.IsTrue(Directory.Exists(frameworkPath)); + + StringAssert.Contains("[SentrySDK", File.ReadAllText(bridgePath)); // Sanity check } [Test] @@ -82,7 +143,7 @@ public void IsNativeSupportEnabled_IosNativeSupportDisabled_LogsAndReturnsFalse( [Test] public void AddSentryToXcodeProject_NativeSupportDisabledButMainAlreadyModified_ThrowsBuildFailedException() { - var file = new FileInfo(Path.Combine(_xcodeProjectPath, SentryXcodeProject.MainPath)); + var file = new FileInfo(Path.Combine(_outputProjectPath, SentryXcodeProject.MainPath)); file.Directory?.Create(); File.WriteAllText(file.FullName, NativeMain.Include); var options = new SentryUnityOptions @@ -93,15 +154,15 @@ public void AddSentryToXcodeProject_NativeSupportDisabledButMainAlreadyModified_ var testLogger = new TestLogger(); Assert.Throws(() => - BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _xcodeProjectPath)); + BuildPostProcess.AddSentryToXcodeProject(options, null, testLogger, _outputProjectPath)); - Directory.Delete(_xcodeProjectPath, true); + Directory.Delete(_outputProjectPath, true); } [Test] public void CopyFrameworkToXcodeProject_CopyFramework_DirectoryExists() { - var targetPath = Path.Combine(_xcodeProjectPath, "SomeDirectory", "Test.framework"); + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"); BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, new TestLogger()); @@ -112,7 +173,7 @@ public void CopyFrameworkToXcodeProject_CopyFramework_DirectoryExists() public void CopyFramework_FrameworkAlreadyExists_LogsSkipMessage() { var testLogger = new TestLogger(); - var targetPath = Path.Combine(_xcodeProjectPath, "SomeDirectory", "Test.framework"); + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"); BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, testLogger); BuildPostProcess.CopyFramework(_testFrameworkPath, targetPath, testLogger); @@ -126,19 +187,18 @@ public void CopyFramework_FrameworkAlreadyExists_LogsSkipMessage() public void CopyFramework_SourceMissing_ThrowsDirectoryNotFoundException() => Assert.Throws(() => BuildPostProcess.CopyFramework("non-existent-path.framework", - Path.Combine(_xcodeProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); + Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); [Test] public void CopyFramework_FailedToCopy_ThrowsDirectoryNotFoundException() => Assert.Throws(() => BuildPostProcess.CopyFramework("non-existent-path.framework", - Path.Combine(_xcodeProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); - + Path.Combine(_outputProjectPath, "SomeDirectory", "Test.framework"), new TestLogger())); [Test] public void CopyFile_CopyFile_FileExists() { - var targetPath = Path.Combine(_xcodeProjectPath, "SomeDirectory", "Test.m"); + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.m"); BuildPostProcess.CopyFile(_testFilePath, targetPath, new TestLogger()); @@ -149,7 +209,7 @@ public void CopyFile_CopyFile_FileExists() public void CopyFile_FileAlreadyExists_LogsSkipMessage() { var testLogger = new TestLogger(); - var targetPath = Path.Combine(_xcodeProjectPath, "SomeDirectory", "Test.m"); + var targetPath = Path.Combine(_outputProjectPath, "SomeDirectory", "Test.m"); BuildPostProcess.CopyFile(_testFilePath, targetPath, testLogger); BuildPostProcess.CopyFile(_testFilePath, targetPath, testLogger); @@ -163,12 +223,12 @@ public void CopyFile_FileAlreadyExists_LogsSkipMessage() public void CopyFile_SourceMissing_ThrowsFileNotFoundException() => Assert.Throws(() => BuildPostProcess.CopyFile("non-existent-path.m", - Path.Combine(_xcodeProjectPath, "NewDirectory", "Test.m"), new TestLogger())); + Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); [Test] public void CopyFile_FailedToCopyFile_ThrowsFileNotFoundException() => Assert.Throws(() => BuildPostProcess.CopyFile("non-existent-path.m", - Path.Combine(_xcodeProjectPath, "NewDirectory", "Test.m"), new TestLogger())); + Path.Combine(_outputProjectPath, "NewDirectory", "Test.m"), new TestLogger())); } } diff --git a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs index a0be8d82e..5ec9adc40 100644 --- a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs +++ b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs @@ -1,7 +1,9 @@ using System.IO; +using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using NUnit.Framework; +using NUnit.Framework.Internal; using Sentry.Extensibility; using Sentry.Unity.Tests.SharedClasses; @@ -27,7 +29,7 @@ public Fixture() }; } - public SentryXcodeProject GetSut() => new(ProjectRoot); + public SentryXcodeProject GetSut() => new(ProjectRoot, TestLogger); } private Fixture _fixture = new(); @@ -139,7 +141,7 @@ public void AddBuildPhaseSymbolUpload_CleanXcodeProject_BuildPhaseSymbolUploadAd xcodeProject.ReadFromProjectFile(); var didContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase(); - xcodeProject.AddBuildPhaseSymbolUpload(_fixture.Options.DiagnosticLogger, new SentryCliOptions()); + xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); var doesContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase(); Assert.IsFalse(didContainUploadPhase); @@ -153,13 +155,15 @@ public void AddBuildPhaseSymbolUpload_PhaseAlreadyAdded_LogsAndDoesNotAddAgain() var xcodeProject = _fixture.GetSut(); xcodeProject.ReadFromProjectFile(); - xcodeProject.AddBuildPhaseSymbolUpload(_fixture.Options.DiagnosticLogger, new SentryCliOptions()); - xcodeProject.AddBuildPhaseSymbolUpload(_fixture.Options.DiagnosticLogger, new SentryCliOptions()); + xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); + xcodeProject.AddBuildPhaseSymbolUpload(new SentryCliOptions()); var actualBuildPhaseOccurence = Regex.Matches(xcodeProject.ProjectToString(), Regex.Escape(SentryXcodeProject.SymbolUploadPhaseName)).Count; - Assert.AreEqual(1, _fixture.TestLogger.Logs.Count); + Assert.IsTrue(_fixture.TestLogger.Logs.Any(log => + log.logLevel == SentryLevel.Debug && + log.message.Contains("already added."))); // Sanity check Assert.AreEqual(expectedBuildPhaseOccurence, actualBuildPhaseOccurence); } } diff --git a/test/Sentry.Unity.Editor.iOS.Tests/TestFiles/2019_4/MainApp/main.mm b/test/Sentry.Unity.Editor.iOS.Tests/TestFiles/2019_4/MainApp/main.mm new file mode 100644 index 000000000..41df4c790 --- /dev/null +++ b/test/Sentry.Unity.Editor.iOS.Tests/TestFiles/2019_4/MainApp/main.mm @@ -0,0 +1,29 @@ +#include + +UnityFramework* UnityFrameworkLoad() +{ + NSString* bundlePath = nil; + bundlePath = [[NSBundle mainBundle] bundlePath]; + bundlePath = [bundlePath stringByAppendingString: @"/Frameworks/UnityFramework.framework"]; + + NSBundle* bundle = [NSBundle bundleWithPath: bundlePath]; + if ([bundle isLoaded] == false) [bundle load]; + + UnityFramework* ufw = [bundle.principalClass getInstance]; + if (![ufw appController]) + { + // unity is not initialized + [ufw setExecuteHeader: &_mh_execute_header]; + } + return ufw; +} + +int main(int argc, char* argv[]) +{ + @autoreleasepool + { + id ufw = UnityFrameworkLoad(); + [ufw runUIApplicationMainWithArgc: argc argv: argv]; + return 0; + } +}