From 823dcc10472191444740e30bf894cceeb7794b90 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 10 Jul 2026 14:58:35 +0200 Subject: [PATCH 1/3] [msbuild] Copy the .dSYM to the publish directory when publishing When running `dotnet publish`, the generated `*.dSYM` directories (which are created next to the app bundle in the output directory) are now copied into the publish directory, so the debug symbols end up alongside the published `.ipa`/`.pkg`. This makes it much easier to locate the dSYMs for symbolication without knowing the exact build output layout. This can be disabled by setting `CopyDSYMToPublishDirectory=false`. Fixes https://github.com/dotnet/macios/issues/15384 Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- docs/building-apps/build-properties.md | 11 +++++ .../Xamarin.Shared.Sdk.Publish.targets | 29 +++++++++++++- tests/dotnet/UnitTests/PostBuildTest.cs | 40 +++++++++++++++++++ 3 files changed, 79 insertions(+), 1 deletion(-) diff --git a/docs/building-apps/build-properties.md b/docs/building-apps/build-properties.md index 97525d1af61e..ae4cf7fb95b6 100644 --- a/docs/building-apps/build-properties.md +++ b/docs/building-apps/build-properties.md @@ -300,6 +300,17 @@ This also applies to how native references are stored inside NuGets. > [!NOTE] > In some cases it can be beneficial to force a zip file on iOS as well, especially when there's a framework with files that have long names, because the zip file can sometimes work around MAX_PATH issues on Windows. +## CopyDSYMToPublishDirectory + +A boolean property that specifies whether any generated `*.dSYM` directories should be +copied to the publish directory when publishing (`dotnet publish`). + +The `*.dSYM` directories are generated next to the app bundle (see [NoDSymUtil](#nodsymutil)), +and when this property is `true` they'll also be copied to the publish directory (next to the +generated `.ipa`/`.pkg`). + +The default value is `true`. + ## CopySceneKitAssetsPath The full path to the `copySceneKitAssets` tool. diff --git a/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets b/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets index 8f6dca62905e..db4217024d79 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets @@ -20,8 +20,35 @@ Condition="$(RuntimeIdentifiers.Contains('iossimulator-')) Or $(RuntimeIdentifiers.Contains('tvossimulator-'))" /> - + + + + + + + + + <_DSymFileToPublish Include="%(_DSymDirToPublish.Identity)/**/*"> + %(_DSymDirToPublish.Filename)%(_DSymDirToPublish.Extension) + + + + + + diff --git a/tests/dotnet/UnitTests/PostBuildTest.cs b/tests/dotnet/UnitTests/PostBuildTest.cs index 91c459d8e75e..983fc2ecf744 100644 --- a/tests/dotnet/UnitTests/PostBuildTest.cs +++ b/tests/dotnet/UnitTests/PostBuildTest.cs @@ -541,6 +541,46 @@ public void StaticFrameworksNotInPostProcessing (ApplePlatform platform, string Assert.That (staticFrameworkItems, Is.Empty, $"Static framework XStaticArTest should not be in post-processing items. All items:\n\t{string.Join ("\n\t", postProcessingItems.Select (i => i.ItemSpec))}"); } + [Test] + [TestCase (ApplePlatform.iOS, "ios-arm64", true)] + [TestCase (ApplePlatform.iOS, "ios-arm64", false)] + public void PublishDSymToPublishDirectory (ApplePlatform platform, string runtimeIdentifiers, bool copyDSym) + { + // https://github.com/dotnet/macios/issues/15384 + // When publishing, the generated *.dSYM directories should be copied to the publish + // directory (unless CopyDSYMToPublishDirectory=false). + var project = "MySimpleApp"; + var configuration = "Release"; + Configuration.IgnoreIfIgnoredPlatform (platform); + Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers); + + var project_path = GetProjectPath (project, runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration); + Clean (project_path); + + var properties = GetDefaultProperties (runtimeIdentifiers); + properties ["Configuration"] = configuration; + if (!copyDSym) + properties ["CopyDSYMToPublishDirectory"] = "false"; + + DotNet.AssertPublish (project_path, properties); + + var appContainerDir = Path.GetDirectoryName (appPath)!; + var appBundleName = Path.GetFileName (appPath); + var publishDir = Path.Combine (appContainerDir, "publish"); + + // The dSYM must have been generated next to the app bundle in the first place. + var sourceDSym = Path.Combine (appContainerDir, appBundleName + ".dSYM"); + Assert.That (sourceDSym, Does.Exist, "Source dSYM"); + + var publishedDSym = Path.Combine (publishDir, appBundleName + ".dSYM"); + if (copyDSym) { + Assert.That (publishedDSym, Does.Exist, "Published dSYM"); + Assert.That (Path.Combine (publishedDSym, "Contents", "Info.plist"), Does.Exist, "Published dSYM Info.plist"); + } else { + Assert.That (publishedDSym, Does.Not.Exist, "Published dSYM (disabled)"); + } + } + static ITaskItem AssertApplicationArtifact (string binLogPath, string path, ApplePlatform platform, string packageFormat, bool isDirectory) { var outputs = GetItems (binLogPath, "ApplicationArtifact"); From 7fc8531578e4e3d29bdc11ac616fc9e498af1638 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Fri, 10 Jul 2026 16:10:38 +0200 Subject: [PATCH 2/3] [msbuild] Use ->Count() for the dSYM copy empty check Avoid a potentially huge string-join in the log message when there are many files inside the .dSYM directories. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- dotnet/targets/Xamarin.Shared.Sdk.Publish.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets b/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets index db4217024d79..d382a261b389 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets @@ -44,7 +44,7 @@ From 199899dc5497400cb661377f6f3075af6156c616 Mon Sep 17 00:00:00 2001 From: Rolf Bjarne Kvinge Date: Mon, 13 Jul 2026 14:40:05 +0200 Subject: [PATCH 3/3] [msbuild] Guard the dSYM ItemGroup to avoid a drive-root wildcard (MSB5029) When no *.dSYM directories exist (the default on desktop platforms, where NoDSymUtil is true unless archiving), the batched metadata %(_DSymDirToPublish.Identity) expanded to an empty string, turning the ItemGroup Include into "/**/*" - a wildcard rooted at the drive root. MSBuild rejects this with error MSB5029, which broke every publish test that doesn't produce a dSYM. Add a Condition on the ItemGroup so it only runs when at least one dSYM directory was found. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> --- dotnet/targets/Xamarin.Shared.Sdk.Publish.targets | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets b/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets index d382a261b389..e2d66c9fa52c 100644 --- a/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets +++ b/dotnet/targets/Xamarin.Shared.Sdk.Publish.targets @@ -37,7 +37,7 @@ - + <_DSymFileToPublish Include="%(_DSymDirToPublish.Identity)/**/*"> %(_DSymDirToPublish.Filename)%(_DSymDirToPublish.Extension)