Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions msbuild/Xamarin.MacDev.Tasks/Tasks/SymbolStrip.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,10 +27,16 @@ public class SymbolStrip : XamarinParallelTask, ITaskCallback {
public string Kind { get; set; } = string.Empty;
#endregion

bool GetIsFramework (ITaskItem item)
bool GetIsFrameworkOrDynamicLibrary (ITaskItem item)
{
var value = GetNonEmptyStringOrFallback (item, "Kind", Kind);
return string.Equals (value, "Framework", StringComparison.OrdinalIgnoreCase);
if (string.Equals (value, "Framework", StringComparison.OrdinalIgnoreCase))
return true;

if (string.Equals (value, "Dynamic", StringComparison.OrdinalIgnoreCase) || item.ItemSpec.EndsWith (".dylib", StringComparison.OrdinalIgnoreCase))
return true;

return false;
}

void ExecuteStrip (ITaskItem item)
Expand All @@ -45,7 +51,7 @@ void ExecuteStrip (ITaskItem item)
args.Add (symbolFile);
}

if (GetIsFramework (item)) {
if (GetIsFrameworkOrDynamicLibrary (item)) {
// Only remove debug symbols from frameworks.
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment says “Only remove debug symbols from frameworks”, but this branch now applies the same strip flags to dynamic libraries too. Please update the comment to match the new behavior (frameworks and dylibs) so future readers don’t misinterpret why -S/-x are used here.

Suggested change
// Only remove debug symbols from frameworks.
// Only remove debug symbols from frameworks and dynamic libraries.

Copilot uses AI. Check for mistakes.
args.Add ("-S");
args.Add ("-x");
Expand Down
9 changes: 9 additions & 0 deletions msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2962,6 +2962,15 @@ Copyright (C) 2018 Microsoft. All rights reserved.
<!-- This is the name of the dSYM that will be created (if that's the case) -->
<DSymName>%(Filename).dSYM</DSymName>
</_PostProcessingItem>
<!-- In outer multi-RID builds the merged app bundle already contains the universal dylibs,
but the RID-specific native references are no longer available as items. Scan the merged
app bundle so that dsymutil/strip still runs for those dylibs. -->
<_MergedAppBundleDylib Include="$(AppBundleDir)/$(_AppContentsRelativePathForPostProcessing)*.dylib" Condition="'$(RuntimeIdentifiers)' != ''" />
<_PostProcessingItem Include="@(_MergedAppBundleDylib -> '$(_AppBundleName)$(AppBundleExtension)/$(_AppContentsRelativePathForPostProcessing)%(Filename)%(Extension)')" Condition="'$(RuntimeIdentifiers)' != ''">
<ItemSourcePath>$(_AppContainerDir)%(_PostProcessingItem.Identity)</ItemSourcePath>
<dSYMSourcePath>$(_AppContainerDir)%(_PostProcessingItem.Identity).dSYM</dSYMSourcePath>
<DSymName>%(Filename).dSYM</DSymName>
Comment on lines +2968 to +2972
Copy link

Copilot AI Apr 7, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The new multi-RID fallback that scans the merged app bundle for *.dylib items doesn't carry over item metadata like NuGetPackageId. That metadata is later used to set NoSymbolStrip=true for Microsoft.NETCore.App.Runtime dylibs; without it, runtime dylibs found via the glob will be stripped in multi-RID builds, which contradicts the existing exemption and can reintroduce strip failures. Consider propagating NuGetPackageId (or directly setting NoSymbolStrip) for these globbed dylibs, e.g., by mapping back to ResolvedFileToPublish or by applying the same exemption by filename for known runtime dylibs.

Suggested change
<_MergedAppBundleDylib Include="$(AppBundleDir)/$(_AppContentsRelativePathForPostProcessing)*.dylib" Condition="'$(RuntimeIdentifiers)' != ''" />
<_PostProcessingItem Include="@(_MergedAppBundleDylib -> '$(_AppBundleName)$(AppBundleExtension)/$(_AppContentsRelativePathForPostProcessing)%(Filename)%(Extension)')" Condition="'$(RuntimeIdentifiers)' != ''">
<ItemSourcePath>$(_AppContainerDir)%(_PostProcessingItem.Identity)</ItemSourcePath>
<dSYMSourcePath>$(_AppContainerDir)%(_PostProcessingItem.Identity).dSYM</dSYMSourcePath>
<DSymName>%(Filename).dSYM</DSymName>
<_MergedAppBundleDylib Include="$(AppBundleDir)/$(_AppContentsRelativePathForPostProcessing)*.dylib" Condition="'$(RuntimeIdentifiers)' != ''">
<NuGetPackageId>@(ResolvedFileToPublish->WithMetadataValue('RelativePath', '$(_AppContentsRelativePathForPostProcessing)%(Filename)%(Extension)')->'%(NuGetPackageId)')</NuGetPackageId>
</_MergedAppBundleDylib>
<_PostProcessingItem Include="@(_MergedAppBundleDylib -> '$(_AppBundleName)$(AppBundleExtension)/$(_AppContentsRelativePathForPostProcessing)%(Filename)%(Extension)')" Condition="'$(RuntimeIdentifiers)' != ''">
<ItemSourcePath>$(_AppContainerDir)%(_PostProcessingItem.Identity)</ItemSourcePath>
<dSYMSourcePath>$(_AppContainerDir)%(_PostProcessingItem.Identity).dSYM</dSYMSourcePath>
<DSymName>%(Filename).dSYM</DSymName>
<NuGetPackageId>%(NuGetPackageId)</NuGetPackageId>
<NoSymbolStrip Condition="'%(NuGetPackageId)' != '' And $([System.String]::Copy('%(NuGetPackageId)').StartsWith('Microsoft.NETCore.App.Runtime'))">true</NoSymbolStrip>

Copilot uses AI. Check for mistakes.
</_PostProcessingItem>
<_PostProcessingItem Include="$([System.IO.Path]::GetFileName('$(AppBundleDir)'))/$(_NativeExecutableRelativePath)" Condition="'$(IsWatchApp)' != 'true'">
<SymbolFile Condition="'$(_ExportSymbolsExplicitly)' == 'true'">$(_SymbolsListFullPath)</SymbolFile>
<DSymName>$(_AppBundleName)$(AppBundleExtension).dSYM</DSymName>
Expand Down
14 changes: 11 additions & 3 deletions tests/dotnet/SizeTestApp/AppDelegate.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,21 @@
#if HAS_UIKIT
using UIKit;
#endif
#if HAS_APPKIT
using AppKit;
#endif

namespace MySimpleApp {
public class Program {
static int Main (string [] args)
{
#if HAS_UIKIT

UIApplication.Main (args, null, typeof (AppDelegate));
#else
#elif HAS_APPKIT
NSApplication.Init ();
NSApplication.Main (args);
#else
#error This test app has not been implemented for this platform.
#endif
return 0;
}
Expand All @@ -40,7 +44,11 @@ public override bool FinishedLaunching (UIApplication app, NSDictionary options)
return true;
}
}
#elif HAS_APPKIT
[Register ("AppDelegate")]
public class AppDelegate : NSApplicationDelegate {
}
#else
#error This test app has not been implemented for AppKit yet.
#error This test app has not been implemented for this platform.
#endif
}
48 changes: 40 additions & 8 deletions tests/dotnet/UnitTests/AppSizeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,53 @@ namespace Xamarin.Tests {
public class AppSizeTest : TestBaseClass {

[TestCase (ApplePlatform.iOS, "ios-arm64")]
[TestCase (ApplePlatform.TVOS, "tvos-arm64")]
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-arm64")]
public void MonoVM (ApplePlatform platform, string runtimeIdentifiers)
{
Run (platform, runtimeIdentifiers, "Release", $"{platform}-MonoVM", true, new Dictionary<string, string> () { { "UseMonoRuntime", "true" } });
var dict = new Dictionary<string, string> () {
{ "UseMonoRuntime", "true" },
{ "NoDSymUtil", "false" },
};
Run (platform, runtimeIdentifiers, "Release", $"{platform}-MonoVM", true, dict);
}

[TestCase (ApplePlatform.iOS, "ios-arm64")]
[TestCase (ApplePlatform.TVOS, "tvos-arm64")]
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-arm64")]
public void MonoVM_Interpreter (ApplePlatform platform, string runtimeIdentifiers)
{
Run (platform, runtimeIdentifiers, "Release", $"{platform}-MonoVM-interpreter", true, new Dictionary<string, string> () { { "UseInterpreter", "true" }, { "UseMonoRuntime", "true" } });
var dict = new Dictionary<string, string> () {
{ "UseInterpreter", "true" },
{ "UseMonoRuntime", "true" },
{ "NoDSymUtil", "false" },
};
Run (platform, runtimeIdentifiers, "Release", $"{platform}-MonoVM-interpreter", true, dict);
}

[TestCase (ApplePlatform.iOS, "ios-arm64")]
[TestCase (ApplePlatform.TVOS, "tvos-arm64")]
[TestCase (ApplePlatform.MacCatalyst, "maccatalyst-arm64")]
[TestCase (ApplePlatform.MacOSX, "osx-arm64;osx-x64")]
public void NativeAOT (ApplePlatform platform, string runtimeIdentifiers)
{
Run (platform, runtimeIdentifiers, "Release", $"{platform}-NativeAOT", false, new Dictionary<string, string> () { { "PublishAot", "true" }, { "_IsPublishing", "true" } });
var dict = new Dictionary<string, string> () {
{ "PublishAot", "true" },
{ "_IsPublishing", "true" },
{ "NoDSymUtil", "false" }, // off by default for macOS, but we want to test it, so enable it
};
Run (platform, runtimeIdentifiers, "Release", $"{platform}-NativeAOT", false, dict);
}

[TestCase (ApplePlatform.MacOSX, "osx-arm64;osx-x64", false)]
public void CoreCLR_Interpreter (ApplePlatform platform, string runtimeIdentifiers, bool isTrimmed)
{
var dict = new Dictionary<string, string> () {
{ "UseMonoRuntime", "false" },
{ "PublishReadyToRun", "false" },
{ "NoDSymUtil", "false" }, // off by default for macOS, but we want to test it, so enable it
};
Run (platform, runtimeIdentifiers, "Release", $"{platform}-CoreCLR-Interpreter", isTrimmed, dict);
}

// This test will build the SizeTestApp, and capture the resulting app size.
Expand Down Expand Up @@ -122,11 +154,11 @@ static void AssertAppSize (ApplePlatform platform, string name, string appPath,
var allKeys = expectedLines.Keys.Union (actualLines.Keys).OrderBy (v => v);
foreach (var key in allKeys) {
if (!expectedLines.TryGetValue (key, out var expectedLine)) {
Console.WriteLine ($" File '{key}' was removed from app bundle: {actualLines [key]}");
Assert.Fail ($"The file '{key}' was removed from the app bundle.");
} else if (!actualLines.TryGetValue (key, out var actualLine)) {
Console.WriteLine ($" File '{key}' was added to app bundle: {expectedLine}");
Console.WriteLine ($" File '{key}' was added to app bundle: {actualLines [key]}");
Assert.Fail ($"The file '{key}' was added to the app bundle.");
} else if (!actualLines.TryGetValue (key, out var actualLine)) {
Console.WriteLine ($" File '{key}' was removed from app bundle: {expectedLine}");
Assert.Fail ($"The file '{key}' was removed from the app bundle.");
} else if (expectedLine != actualLine) {
Console.WriteLine ($" File '{key}' changed in app bundle:");
Console.WriteLine ($" -{expectedLine}");
Expand Down Expand Up @@ -154,7 +186,7 @@ void AssertAssemblyReport (ApplePlatform platform, string name, string appPath,
}
preservedAPIs.Sort ();
var expectedFile = Path.Combine (expectedDirectory, $"{name}-preservedapis.txt");
var expectedAPIs = File.ReadAllLines (expectedFile);
var expectedAPIs = File.Exists (expectedFile) ? File.ReadAllLines (expectedFile) : [];
var addedAPIs = preservedAPIs.Except (expectedAPIs).ToList ();
var removedAPIs = expectedAPIs.Except (preservedAPIs).ToList ();

Expand Down
Loading
Loading