diff --git a/CHANGELOG.md b/CHANGELOG.md
index a816c409e..73a732c2f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,10 @@
## Unreleased
+### Features
+
+- Automated symbols upload for iOS builds when bitcode is disabled ([#443](https://github.com/getsentry/sentry-unity/pull/443))
+
### Fixes
- Sentry no longer requires Xcode projects to be exported on macOS ([#442](https://github.com/getsentry/sentry-unity/pull/442))
diff --git a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs
index 6556da88e..942d03d87 100644
--- a/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs
+++ b/src/Sentry.Unity.Editor.iOS/BuildPostProcess.cs
@@ -17,6 +17,7 @@ public static void OnPostProcessBuild(BuildTarget target, string pathToProject)
}
var options = ScriptableSentryUnityOptions.LoadSentryUnityOptions(BuildPipeline.isBuildingPlayer);
+ var logger = options?.DiagnosticLogger ?? new UnityLogger(new SentryUnityOptions());
try
{
@@ -27,22 +28,48 @@ public static void OnPostProcessBuild(BuildTarget target, string pathToProject)
if (options?.Validate() != true)
{
- new UnityLogger(new SentryOptions()).LogWarning("Failed to validate Sentry Options. Native support disabled.");
+ logger.LogWarning("Failed to validate Sentry Options. Native support disabled.");
return;
}
if (!options.IosNativeSupportEnabled)
{
- options.DiagnosticLogger?.LogDebug("iOS Native support disabled through the options.");
+ logger.LogDebug("iOS Native support disabled through the options.");
return;
}
sentryXcodeProject.AddNativeOptions(options);
sentryXcodeProject.AddSentryToMain(options);
+
+ var sentryCliOptions = SentryCliOptions.LoadCliOptions();
+ if (!sentryCliOptions.UploadSymbols)
+ {
+ logger.LogDebug("Automated symbols upload has been disabled.");
+ return;
+ }
+
+ if (EditorUserBuildSettings.development && !sentryCliOptions.UploadDevelopmentSymbols)
+ {
+ logger.LogDebug("Automated symbols upload for development builds has been disabled.");
+ return;
+ }
+
+ if (!sentryCliOptions.Validate(logger))
+ {
+ logger.LogWarning("sentry-cli validation failed. Symbols will not be uploaded." +
+ "\nYou can disable this warning by disabling the automated symbols upload under " +
+ "Tools -> Sentry -> Editor");
+ return;
+ }
+
+ SentryCli.CreateSentryProperties(pathToProject, sentryCliOptions);
+ SentryCli.AddExecutableToXcodeProject(pathToProject);
+
+ sentryXcodeProject.AddBuildPhaseSymbolUpload(logger);
}
catch (Exception e)
{
- options?.DiagnosticLogger?.LogError("Failed to add the Sentry framework to the generated Xcode project", e);
+ logger.LogError("Failed to add the Sentry framework to the generated Xcode project", e);
}
}
diff --git a/src/Sentry.Unity.Editor.iOS/Sentry.Unity.Editor.iOS.csproj b/src/Sentry.Unity.Editor.iOS/Sentry.Unity.Editor.iOS.csproj
index ad79c341d..e2b679a3d 100644
--- a/src/Sentry.Unity.Editor.iOS/Sentry.Unity.Editor.iOS.csproj
+++ b/src/Sentry.Unity.Editor.iOS/Sentry.Unity.Editor.iOS.csproj
@@ -7,7 +7,7 @@
-
+
diff --git a/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs b/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs
index 7f3d656e6..b4c62d2f6 100644
--- a/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs
+++ b/src/Sentry.Unity.Editor.iOS/SentryXcodeProject.cs
@@ -1,5 +1,7 @@
using System;
using System.IO;
+using System.Linq;
+using Sentry.Extensibility;
using UnityEditor.iOS.Xcode;
using UnityEditor.iOS.Xcode.Extensions;
@@ -8,6 +10,7 @@ namespace Sentry.Unity.Editor.iOS
internal class SentryXcodeProject : IDisposable
{
private const string FrameworkName = "Sentry.framework";
+ internal const string SymbolUploadPhaseName = "SymbolUpload";
private readonly string _mainPath = Path.Combine("MainApp", "main.mm");
private readonly string _optionsPath = Path.Combine("MainApp", "SentryOptions.m");
@@ -71,9 +74,37 @@ public void AddSentryFramework()
_project.SetBuildProperty(unityFrameworkTargetGuid, "FRAMEWORK_SEARCH_PATHS", "$(inherited)");
_project.AddBuildProperty(unityFrameworkTargetGuid, "FRAMEWORK_SEARCH_PATHS", "$(PROJECT_DIR)/Frameworks/");
+ _project.SetBuildProperty(mainTargetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym");
+ _project.SetBuildProperty(unityFrameworkTargetGuid, "DEBUG_INFORMATION_FORMAT", "dwarf-with-dsym");
+
_project.AddBuildProperty(mainTargetGuid, "OTHER_LDFLAGS", "-ObjC");
}
+ public void AddBuildPhaseSymbolUpload(IDiagnosticLogger? logger)
+ {
+ if (MainTargetContainsSymbolUploadBuildPhase())
+ {
+ logger?.LogDebug("Build phase '{0}' already added.", SymbolUploadPhaseName);
+ return;
+ }
+
+ var mainTargetGuid = _project.GetUnityMainTargetGuid();
+ _project.AddShellScriptBuildPhase(mainTargetGuid,
+ SymbolUploadPhaseName,
+ "/bin/sh",
+ $@"export SENTRY_PROPERTIES=sentry.properties
+if [ ""$ENABLE_BITCODE"" = ""NO"" ] ; then
+ echo ""Bitcode is disabled - Uploading symbols""
+ ERROR=$(./{SentryCli.SentryCliMacOS} upload-dif $BUILT_PRODUCTS_DIR > ./sentry-symbols-upload.log 2>&1 &)
+ if [ ! $? -eq 0 ] ; then
+ echo ""warning: sentry-cli - $ERROR""
+ fi
+else
+ echo ""Bitcode is enabled - Skipping symbols upload""
+fi"
+ );
+ }
+
public void AddNativeOptions(SentryUnityOptions options)
{
_nativeOptions.CreateFile(Path.Combine(_projectRoot, _optionsPath), options);
@@ -83,6 +114,12 @@ public void AddNativeOptions(SentryUnityOptions options)
public void AddSentryToMain(SentryUnityOptions options) =>
_nativeMain.AddSentry(Path.Combine(_projectRoot, _mainPath), options.DiagnosticLogger);
+ internal bool MainTargetContainsSymbolUploadBuildPhase()
+ {
+ var allBuildPhases = _project.GetAllBuildPhasesForTarget(_project.GetUnityMainTargetGuid());
+ return allBuildPhases.Any(buildPhase => _project.GetBuildPhaseName(buildPhase) == SymbolUploadPhaseName);
+ }
+
internal string ProjectToString() => _project.WriteToString();
public void Dispose() => _project.WriteToFile(_projectPath);
diff --git a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
index da33357e6..72978445c 100644
--- a/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
+++ b/src/Sentry.Unity.Editor/Android/AndroidManifestConfiguration.cs
@@ -149,7 +149,9 @@ internal void SetupSymbolsUpload(string basePath)
if (!sentryCliOptions.Validate(logger))
{
- logger.LogWarning("Loading sentry-cli configuration failed. Symbols will not be uploaded");
+ logger.LogWarning("sentry-cli validation failed. Symbols will not be uploaded." +
+ "\nYou can disable this warning by disabling the automated symbols upload under " +
+ "Tools -> Sentry -> Editor");
return;
}
diff --git a/src/Sentry.Unity.Editor/SentryCli.cs b/src/Sentry.Unity.Editor/SentryCli.cs
index 1905057e0..8cf8034d8 100644
--- a/src/Sentry.Unity.Editor/SentryCli.cs
+++ b/src/Sentry.Unity.Editor/SentryCli.cs
@@ -8,6 +8,10 @@ namespace Sentry.Unity.Editor
{
internal static class SentryCli
{
+ internal const string SentryCliWindows = "sentry-cli-Windows-x86_64.exe";
+ internal const string SentryCliMacOS = "sentry-cli-Darwin-universal";
+ internal const string SentryCliLinux = "sentry-cli-Linux-x86_64";
+
[DllImport("libc", SetLastError = true)]
private static extern int chmod(string pathname, int mode);
@@ -34,9 +38,9 @@ internal static string GetSentryCliPlatformName(IApplication? application = null
return application.Platform switch
{
- RuntimePlatform.WindowsEditor => "sentry-cli-Windows-x86_64.exe ",
- RuntimePlatform.OSXEditor => "sentry-cli-Darwin-universal",
- RuntimePlatform.LinuxEditor => "sentry-cli-Linux-x86_64 ",
+ RuntimePlatform.WindowsEditor => SentryCliWindows,
+ RuntimePlatform.OSXEditor => SentryCliMacOS,
+ RuntimePlatform.LinuxEditor => SentryCliLinux,
_ => throw new InvalidOperationException(
$"Cannot get sentry-cli for the current platform: {Application.platform}")
};
@@ -69,5 +73,19 @@ internal static void SetExecutePermission(string? filePath = null, IApplication?
throw new UnauthorizedAccessException($"Failed to set permission to {filePath}");
}
}
+
+ internal static void AddExecutableToXcodeProject(string projectPath)
+ {
+ var executableSource = GetSentryCliPath(SentryCliMacOS);
+ var executableDestination = Path.Combine(projectPath, SentryCliMacOS);
+
+ if (!Directory.Exists(projectPath))
+ {
+ throw new DirectoryNotFoundException($"Xcode project directory not found at {executableDestination}");
+ }
+
+ File.Copy(executableSource, executableDestination);
+ SetExecutePermission(executableDestination);
+ }
}
}
diff --git a/src/Sentry.Unity/Properties/AssemblyInfo.cs b/src/Sentry.Unity/Properties/AssemblyInfo.cs
index 22e4895d5..256d67944 100644
--- a/src/Sentry.Unity/Properties/AssemblyInfo.cs
+++ b/src/Sentry.Unity/Properties/AssemblyInfo.cs
@@ -2,8 +2,9 @@
[assembly: InternalsVisibleTo("Sentry.Unity.Tests")]
[assembly: InternalsVisibleTo("Sentry.Unity.Editor")]
-[assembly: InternalsVisibleTo("Sentry.Unity.Editor.iOS")]
[assembly: InternalsVisibleTo("Sentry.Unity.Editor.Tests")]
+[assembly: InternalsVisibleTo("Sentry.Unity.Editor.iOS")]
+[assembly: InternalsVisibleTo("Sentry.Unity.Editor.iOS.Tests")]
[assembly: InternalsVisibleTo("Sentry.Unity.iOS")]
[assembly: InternalsVisibleTo("Sentry.Unity.iOS.Tests")]
[assembly: InternalsVisibleTo("Sentry.Unity.Android")]
diff --git a/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs b/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs
index f027593ed..cc4209382 100644
--- a/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs
+++ b/test/Sentry.Unity.Editor.Tests/SentryCliTests.cs
@@ -17,9 +17,9 @@ public void GetSentryCliPlatformName_UnrecognizedPlatform_ThrowsInvalidOperation
}
[Test]
- [TestCase(RuntimePlatform.WindowsEditor, "sentry-cli-Windows-x86_64.exe ")]
- [TestCase(RuntimePlatform.OSXEditor, "sentry-cli-Darwin-universal")]
- [TestCase(RuntimePlatform.LinuxEditor, "sentry-cli-Linux-x86_64 ")]
+ [TestCase(RuntimePlatform.WindowsEditor, SentryCli.SentryCliWindows)]
+ [TestCase(RuntimePlatform.OSXEditor, SentryCli.SentryCliMacOS)]
+ [TestCase(RuntimePlatform.LinuxEditor, SentryCli.SentryCliLinux)]
public void GetSentryPlatformName_RecognizedPlatform_SetsSentryCliName(RuntimePlatform platform, string expectedName)
{
var application = new TestApplication(platform: platform);
@@ -80,5 +80,24 @@ public void CreateSentryProperties_PropertyFileCreatedAndContainsSentryCliOption
Directory.Delete(propertiesDirectory, true);
}
+
+ [Test]
+ public void AddExecutableToXcodeProject_ProjectPathDoesNotExist_ThrowsDirectoryNotFoundException()
+ {
+ Assert.Throws(() => SentryCli.AddExecutableToXcodeProject("non-existent-path"));
+ }
+
+ [Test]
+ public void AddExecutableToXcodeProject_ProjectPathExists_CopiesSentryCliForMacOS()
+ {
+ var fakeXcodeProjectDirectory = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString());
+ Directory.CreateDirectory(fakeXcodeProjectDirectory);
+
+ SentryCli.AddExecutableToXcodeProject(fakeXcodeProjectDirectory);
+
+ Assert.IsTrue(File.Exists(Path.Combine(fakeXcodeProjectDirectory, SentryCli.SentryCliMacOS)));
+
+ Directory.Delete(fakeXcodeProjectDirectory, true);
+ }
}
}
diff --git a/test/Sentry.Unity.Editor.iOS.Tests/Sentry.Unity.Editor.iOS.Tests.csproj b/test/Sentry.Unity.Editor.iOS.Tests/Sentry.Unity.Editor.iOS.Tests.csproj
index 6731f98d0..8d917b393 100644
--- a/test/Sentry.Unity.Editor.iOS.Tests/Sentry.Unity.Editor.iOS.Tests.csproj
+++ b/test/Sentry.Unity.Editor.iOS.Tests/Sentry.Unity.Editor.iOS.Tests.csproj
@@ -15,4 +15,11 @@
+
+
+
+ %(RecursiveDir)/%(Filename)%(Extension)
+
+
+
diff --git a/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs b/test/Sentry.Unity.Editor.iOS.Tests/SentryXcodeProjectTests.cs
index d70cbbae0..00cdf9d13 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.Reflection;
+using System.Text.RegularExpressions;
using NUnit.Framework;
using Sentry.Extensibility;
+using Sentry.Unity.Tests.SharedClasses;
namespace Sentry.Unity.Editor.iOS.Tests
{
@@ -21,10 +23,22 @@ private class Fixture
{
public string ProjectRoot { get; set; } =
Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "TestFiles", "2019_4");
- public SentryUnityOptions Options { get; set; } = new();
+ public SentryUnityOptions Options { get; set; }
+ public TestLogger TestLogger { get; set; }
public INativeMain NativeMain { get; set; } = new NativeMainTest();
public INativeOptions NativeOptions { get; set; } = new NativeOptionsTest();
+ public Fixture()
+ {
+ TestLogger = new TestLogger();
+ Options = new SentryUnityOptions
+ {
+ Debug = true,
+ DiagnosticLevel = SentryLevel.Debug,
+ DiagnosticLogger = TestLogger
+ };
+ }
+
public SentryXcodeProject GetSut() => new(ProjectRoot, NativeMain, NativeOptions);
}
@@ -83,5 +97,36 @@ public void CreateNativeOptions_CleanXcodeProject_NativeOptionsAdded()
StringAssert.Contains("SentryOptions.m", xcodeProject.ProjectToString());
}
+
+ [Test]
+ public void AddBuildPhaseSymbolUpload_CleanXcodeProject_BuildPhaseSymbolUploadAdded()
+ {
+ var xcodeProject = _fixture.GetSut();
+ xcodeProject.ReadFromProjectFile();
+
+ var didContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase();
+ xcodeProject.AddBuildPhaseSymbolUpload(_fixture.Options.DiagnosticLogger);
+ var doesContainUploadPhase = xcodeProject.MainTargetContainsSymbolUploadBuildPhase();
+
+ Assert.IsFalse(didContainUploadPhase);
+ Assert.IsTrue(doesContainUploadPhase);
+ }
+
+ [Test]
+ public void AddBuildPhaseSymbolUpload_PhaseAlreadyAdded_LogsAndDoesNotAddAgain()
+ {
+ const int expectedBuildPhaseOccurence = 1;
+ var xcodeProject = _fixture.GetSut();
+ xcodeProject.ReadFromProjectFile();
+
+ xcodeProject.AddBuildPhaseSymbolUpload(_fixture.Options.DiagnosticLogger);
+ xcodeProject.AddBuildPhaseSymbolUpload(_fixture.Options.DiagnosticLogger);
+
+ var actualBuildPhaseOccurence = Regex.Matches(xcodeProject.ProjectToString(),
+ Regex.Escape(SentryXcodeProject.SymbolUploadPhaseName)).Count;
+
+ Assert.AreEqual(1, _fixture.TestLogger.Logs.Count);
+ Assert.AreEqual(expectedBuildPhaseOccurence, actualBuildPhaseOccurence);
+ }
}
}
diff --git a/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs b/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs
index 4fc8073f7..d01650b52 100644
--- a/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs
+++ b/test/Sentry.Unity.Tests/Json/SafeSerializerTests.cs
@@ -2,6 +2,7 @@
using System.Linq;
using NUnit.Framework;
using Sentry.Unity.Json;
+using Sentry.Unity.Tests.SharedClasses;
namespace Sentry.Unity.Tests.Json
{
diff --git a/test/Sentry.Unity.Tests/Sentry.Unity.Tests.csproj b/test/Sentry.Unity.Tests/Sentry.Unity.Tests.csproj
index f5f703e74..fa1f81c72 100644
--- a/test/Sentry.Unity.Tests/Sentry.Unity.Tests.csproj
+++ b/test/Sentry.Unity.Tests/Sentry.Unity.Tests.csproj
@@ -11,4 +11,9 @@
+
+
+ %(RecursiveDir)/%(Filename)%(Extension)
+
+
diff --git a/test/Sentry.Unity.Tests/UnityEventProcessorTests.cs b/test/Sentry.Unity.Tests/UnityEventProcessorTests.cs
index 9a6c90aab..f2c476c7f 100644
--- a/test/Sentry.Unity.Tests/UnityEventProcessorTests.cs
+++ b/test/Sentry.Unity.Tests/UnityEventProcessorTests.cs
@@ -1,13 +1,11 @@
using System;
using System.Collections;
-using System.Collections.Concurrent;
-using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NUnit.Framework;
-using Sentry.Extensibility;
+using Sentry.Unity.Tests.SharedClasses;
using Sentry.Unity.Tests.Stubs;
using UnityEngine;
using UnityEngine.TestTools;
@@ -545,19 +543,6 @@ public IEnumerator Process_GpuProtocolGraphicsShaderLevelMinusOne_Ignored()
}
}
- internal sealed class TestLogger : IDiagnosticLogger
- {
- internal readonly ConcurrentBag<(SentryLevel logLevel, string message, Exception? exception)> Logs = new();
-
- public bool IsEnabled(SentryLevel level) => true;
-
- public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args)
- {
- var log = (logLevel, string.Format(message, args), exception);
- Logs.Add(log);
- }
- }
-
internal sealed class TestSentrySystemInfo : ISentrySystemInfo
{
public int? MainThreadId { get; set; } = 1;
diff --git a/test/SharedClasses/README.md b/test/SharedClasses/README.md
new file mode 100644
index 000000000..885be6749
--- /dev/null
+++ b/test/SharedClasses/README.md
@@ -0,0 +1,2 @@
+Here we place classes that are used in multiple projects. Unity re-imports all DLLs and complains about duplications so
+the regular approach of adding a "Helpers" project does not work.
diff --git a/test/SharedClasses/TestLogger.cs b/test/SharedClasses/TestLogger.cs
new file mode 100644
index 000000000..3ec40b6b2
--- /dev/null
+++ b/test/SharedClasses/TestLogger.cs
@@ -0,0 +1,19 @@
+using System;
+using System.Collections.Concurrent;
+using Sentry.Extensibility;
+
+namespace Sentry.Unity.Tests.SharedClasses
+{
+ internal sealed class TestLogger : IDiagnosticLogger
+ {
+ internal readonly ConcurrentBag<(SentryLevel logLevel, string message, Exception? exception)> Logs = new();
+
+ public bool IsEnabled(SentryLevel level) => true;
+
+ public void Log(SentryLevel logLevel, string message, Exception? exception = null, params object?[] args)
+ {
+ var log = (logLevel, string.Format(message, args), exception);
+ Logs.Add(log);
+ }
+ }
+}