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
40 changes: 31 additions & 9 deletions msbuild/Xamarin.Shared/Xamarin.Shared.targets
Original file line number Diff line number Diff line change
Expand Up @@ -2882,9 +2882,18 @@ Copyright (C) 2018 Microsoft. All rights reserved.
This target runs always, because container projects might want to create debug symbols or strip even if this project doesn't want to, and
in that case the container project would still need to know what to do for contained projects.
-->
<PropertyGroup>
<_CollectItemsForPostProcessingDependsOn>
_CompileToNative;
_ParseBundlerArguments;
_ExpandNativeReferences;
_PrepareForPostProcessing;
$(_CollectItemsForPostProcessingDependsOn);
</_CollectItemsForPostProcessingDependsOn>
</PropertyGroup>
<Target
Name="_CollectItemsForPostProcessing"
DependsOnTargets="_CompileToNative;_ParseBundlerArguments;_ExpandNativeReferences;_PrepareForPostProcessing"
DependsOnTargets="$(_CollectItemsForPostProcessingDependsOn)"
>

<!-- read any data from app extensions -->
Expand All @@ -2908,24 +2917,28 @@ Copyright (C) 2018 Microsoft. All rights reserved.
<Output TaskParameter="FullPath" PropertyName="_SymbolsListFullPath" />
</GetFullPath>

<PropertyGroup>
<_AppContentsRelativePathForPostProcessing Condition="'$(_AppContentsRelativePath)' != ''">$(_AppContentsRelativePath)/</_AppContentsRelativePathForPostProcessing>
Comment thread
rolfbjarne marked this conversation as resolved.
</PropertyGroup>

<ItemGroup>
<!-- add frameworks, with the path relative to the app bundle -->
<_PostProcessingItem Include="@(_ResolvedNativeReference->'$(_AppBundleName)$(AppBundleExtension/$(_AppFrameworksRelativePath)%(Filename).framework/%(Filename)')" Condition="'%(_ResolvedNativeReference.Kind' == 'Framework'">
<_PostProcessingItem Include="@(_ResolvedNativeReference->'$(_AppBundleName)$(AppBundleExtension)/$(_AppFrameworksRelativePath)%(Filename)%(Extension).framework/%(Filename)%(Extension)')" Condition="'%(Kind)' == 'Framework'">
<!-- Store where this item came from -->
<ItemSourcePath>%(_ResolvedNativeReference.Identity)</ItemSourcePath>
<ItemSourcePath>%(Identity)</ItemSourcePath>
<!-- If the item in question came with a .dSYM directory, this would be the path to it: -->
<dSYMSourcePath>$([System.IO.Path]::GetDirectoryName('%(_ResolvedNativeReference.Identity)')).dSYM</dSYMSourcePath>
<dSYMSourcePath>$([System.IO.Path]::GetDirectoryName('%(Identity)')).dSYM</dSYMSourcePath>
<!-- This is the name of the dSYM that will be created (if that's the case) -->
<DSymName>%(_ResolvedNativeReference.Filename)%(_ResolvedNativeReference.Extension).dSYM</DSymName>
<DSymName>%(Filename)%(Extension).framework.dSYM</DSymName>
</_PostProcessingItem>
<!-- add dylibs, with the path relative to the app bundle -->
<_PostProcessingItem Include="@(_ResolvedNativeReference->'$(_AppBundleName)$(AppBundleExtension/$(_AppContentsRelativePath)%(Filename).framework/%(Filename)')" Condition="'%(_ResolvedNativeReference.Kind' == 'Dynamic'">
<_PostProcessingItem Include="@(_FileNativeReference->'$(_AppBundleName)$(AppBundleExtension)/$(_AppContentsRelativePathForPostProcessing)%(Filename)%(Extension)')" Condition="'%(Kind)' == 'Dynamic'">
<!-- Store where this item came from -->
<ItemSourcePath>%(_ResolvedNativeReference.Identity)</ItemSourcePath>
<ItemSourcePath>%(Identity)</ItemSourcePath>
<!-- If the item in question came with a .dSYM directory, this would be the path to it: -->
<dSYMSourcePath>$([System.IO.Path]::GetDirectoryName('%(_ResolvedNativeReference.Identity)')).dSYM</dSYMSourcePath>
<dSYMSourcePath>%(Identity).dSYM</dSYMSourcePath>
<!-- This is the name of the dSYM that will be created (if that's the case) -->
<DSymName>%(_ResolvedNativeReference.Filename)%(_ResolvedNativeReference.Extension).dSYM</DSymName>
<DSymName>%(Filename).dSYM</DSymName>
</_PostProcessingItem>
<_PostProcessingItem Include="$([System.IO.Path]::GetFileName('$(AppBundleDir)'))/$(_NativeExecutableRelativePath)" Condition="'$(IsWatchApp)' != 'true'">
<SymbolFile>$(_SymbolsListFullPath)</SymbolFile>
Expand All @@ -2934,7 +2947,11 @@ Copyright (C) 2018 Microsoft. All rights reserved.
<IsAppExtension>$(IsAppExtension)</IsAppExtension>
</_PostProcessingItem>

<!-- Compute a few default values that are applicable to all _PostProcessingItem -->
<_PostProcessingItem>
<!-- This is the name of the dSYM that will be created (if that's the case) -->
<DSymName Condition="'%(_PostProcessingItem.DSymName)' == ''">%(Filename).dSYM</DSymName>
<!-- Set the default values for NoSymbolStrip and NoDSymUtil from the global values -->
<NoSymbolStrip Condition="'%(_PostProcessingItem.NoSymbolStrip)' == ''">$(NoSymbolStrip)</NoSymbolStrip>
<NoDSymUtil Condition="'%(_PostProcessingItem.NoDSymUtil)' == ''">$(NoDSymUtil)</NoDSymUtil>
<!-- Did the item in question come with a .dSYM directory? -->
Expand All @@ -2945,6 +2962,11 @@ Copyright (C) 2018 Microsoft. All rights reserved.
<BCSymbolMapName>%(Filename)%(Extension).bcsymbolmap</BCSymbolMapName>
</_PostProcessingItem>

<!-- Don't strip dylibs from the .NET runtime - they may contain symbols that can't be stripped (https://github.com/dotnet/runtime/issues/124570) -->
<_PostProcessingItem Condition="$([MSBuild]::ValueOrDefault('%(_PostProcessingItem.NuGetPackageId)', '').StartsWith('Microsoft.NETCore.App.Runtime'))">
<NoSymbolStrip>true</NoSymbolStrip>
</_PostProcessingItem>

<!-- Add any items from app extensions -->
<_PostProcessingAppExtensions Include="@(_AppExtensionPostProcessingItems)" Condition="'%(_AppExtensionPostProcessingItems.IsAppExtension)' == 'true' And '%(_AppExtensionPostProcessingItems.IsXPCService)' != 'true'" />
<_PostProcessingXpcServices Include="@(_AppExtensionPostProcessingItems)" Condition="'%(_AppExtensionPostProcessingItems.IsAppExtension)' == 'true' And '%(_AppExtensionPostProcessingItems.IsXPCService)' == 'true'" />
Expand Down
1 change: 1 addition & 0 deletions tests/dotnet/UnitTests/AppSizeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ void Run (ApplePlatform platform, string runtimeIdentifiers, string configuratio
if (supportsAssemblyInspection)
AssertAssemblyReport (platform, name, appPath, update, expectedDirectory);

AssertExpectedDSyms (platform, appPath);
});
}

Expand Down
121 changes: 121 additions & 0 deletions tests/dotnet/UnitTests/PostBuildTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -233,5 +233,126 @@ public void PublishFailureTest (ApplePlatform platform, string runtimeIdentifier

Assert.That (pkgPath, Does.Not.Exist, "ipa/pkg creation");
}

[Test]
[TestCase (ApplePlatform.iOS, "iossimulator-arm64")]
[TestCase (ApplePlatform.MacOSX, "osx-arm64")]
public void DylibPostProcessingItems (ApplePlatform platform, string runtimeIdentifiers)
{
var project = "NativeDynamicLibraryReferencesApp";
Configuration.IgnoreIfIgnoredPlatform (platform);
Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);

var project_path = GetProjectPath (project, runtimeIdentifiers, platform, out var appPath);
Clean (project_path);
var properties = GetDefaultProperties (runtimeIdentifiers);

var result = DotNet.AssertBuild (project_path, properties);
var postProcessingItems = GetPostProcessingItems (result.BinLogPath);

// Find the user's dylib item (not SDK runtime dylibs)
var dylibItems = postProcessingItems.Where (i => i.ItemSpec.Contains ("libframework.dylib")).ToList ();
Assert.That (dylibItems.Count, Is.EqualTo (1), $"Expected 1 libframework.dylib post-processing item, got {dylibItems.Count}. All items:\n\t{string.Join ("\n\t", postProcessingItems.Select (i => i.ItemSpec))}");
var dylibItem = dylibItems [0];

// Verify the path does NOT contain ".framework/" (the bug was that dylibs were treated as frameworks)
Assert.That (dylibItem.ItemSpec, Does.Not.Contain (".framework/"), "Dylib path should not contain .framework/");

// Verify the path contains the full dylib filename
Assert.That (dylibItem.ItemSpec, Does.Contain ("libframework.dylib"), "Dylib path should contain the full dylib filename");

// Verify the DSymName is correct for a dylib (should be "libframework.dSYM", not "libframework.dylib.dSYM")
var dSymName = dylibItem.GetMetadata ("DSymName");
Assert.That (dSymName, Is.EqualTo ("libframework.dSYM"), "DSymName for dylib");

// Verify dSYMSourcePath points to where a pre-existing dSYM would be for the dylib.
// For a dylib at /path/to/libfoo.dylib, the dSYMSourcePath should be /path/to/libfoo.dylib.dSYM
var dSYMSourcePath = dylibItem.GetMetadata ("dSYMSourcePath");
var itemSourcePath = dylibItem.GetMetadata ("ItemSourcePath");
Assert.That (dSYMSourcePath, Is.EqualTo (itemSourcePath + ".dSYM"), "dSYMSourcePath for dylib");

// Debug builds don't generate dSYMs, verify none exist
var appContainerDir = Path.GetDirectoryName (appPath)!;
var dSymDirs = Directory.GetDirectories (appContainerDir, "*.dSYM");
Assert.That (dSymDirs, Is.Empty, "No dSYMs should exist for Debug builds");
}

[Test]
[TestCase (ApplePlatform.iOS, "iossimulator-x64")]
[TestCase (ApplePlatform.MacOSX, "osx-arm64")]
public void FrameworkPostProcessingItems (ApplePlatform platform, string runtimeIdentifiers)
{
var project = "NativeFrameworkReferencesApp";
Configuration.IgnoreIfIgnoredPlatform (platform);
Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);

var project_path = GetProjectPath (project, runtimeIdentifiers, platform, out var appPath);
Clean (project_path);
var properties = GetDefaultProperties (runtimeIdentifiers);

var result = DotNet.AssertBuild (project_path, properties);
var postProcessingItems = GetPostProcessingItems (result.BinLogPath);

// Find the framework item (XTest.framework is the dynamic framework)
var frameworkItems = postProcessingItems.Where (i => i.ItemSpec.Contains ("XTest.framework/XTest")).ToList ();
Assert.That (frameworkItems.Count, Is.EqualTo (1), $"Expected 1 XTest framework post-processing item, got {frameworkItems.Count}. All items:\n\t{string.Join ("\n\t", postProcessingItems.Select (i => i.ItemSpec))}");
var frameworkItem = frameworkItems [0];

// Verify the DSymName is correct for a framework (should be "XTest.framework.dSYM")
var dSymName = frameworkItem.GetMetadata ("DSymName");
Assert.That (dSymName, Is.EqualTo ("XTest.framework.dSYM"), "DSymName for framework");

// Verify dSYMSourcePath points to where a pre-existing dSYM would be for the framework.
// For a framework at /path/to/XTest.framework/XTest, the dSYMSourcePath should be /path/to/XTest.framework.dSYM
var dSYMSourcePath = frameworkItem.GetMetadata ("dSYMSourcePath");
var itemSourcePath = frameworkItem.GetMetadata ("ItemSourcePath");
Assert.That (dSYMSourcePath, Is.EqualTo (Path.GetDirectoryName (itemSourcePath) + ".dSYM"), "dSYMSourcePath for framework");

// Debug builds don't generate dSYMs, verify none exist
var appContainerDir = Path.GetDirectoryName (appPath)!;
var dSymDirs = Directory.GetDirectories (appContainerDir, "*.dSYM");
Assert.That (dSymDirs, Is.Empty, "No dSYMs should exist for Debug builds");
}

[Test]
[TestCase (ApplePlatform.iOS, "ios-arm64", "Release")]
[TestCase (ApplePlatform.MacOSX, "osx-arm64", "Release")]
public void BundleStructureDSyms (ApplePlatform platform, string runtimeIdentifiers, string configuration)
{
var project = "BundleStructure";
Configuration.IgnoreIfIgnoredPlatform (platform);
Configuration.AssertRuntimeIdentifiersAvailable (platform, runtimeIdentifiers);

var project_path = GetProjectPath (project, runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath, configuration: configuration);
Clean (project_path);
var properties = GetDefaultProperties (runtimeIdentifiers);
properties ["Configuration"] = configuration;
properties ["_IsAppSigned"] = "true";
// macOS and Mac Catalyst default to NoDSymUtil=true (dSYMs only generated when archiving),
// so explicitly disable it to test dSYM generation.
properties ["NoDSymUtil"] = "false";

DotNet.AssertBuild (project_path, properties);

AssertExpectedDSyms (platform, appPath);
}

static List<ITaskItem> GetPostProcessingItems (string binLogPath)
{
var items = new Dictionary<string, ITaskItem> ();
foreach (var args in BinLog.ReadBuildEvents (binLogPath)) {
if (args is not TaskParameterEventArgs tpea)
continue;
if (tpea.Kind != TaskParameterMessageKind.AddItem)
continue;
if (tpea.ItemType != "_PostProcessingItem")
continue;
foreach (var item in tpea.Items) {
if (item is ITaskItem taskItem)
items [taskItem.ItemSpec] = taskItem;
}
}
return items.Values.ToList ();
}
}
}
50 changes: 50 additions & 0 deletions tests/dotnet/UnitTests/TestBaseClass.cs
Original file line number Diff line number Diff line change
Expand Up @@ -307,6 +307,56 @@ protected void AssertDSymDirectory (string appPath)
Assert.That (dSYMDirectory, Does.Exist, "dsym directory");
}

// Assert that the expected dSYMs exist for all binaries in the app bundle, and that no unexpected dSYMs exist.
protected void AssertExpectedDSyms (ApplePlatform platform, string appPath)
{
var appContainerDir = Path.GetDirectoryName (appPath)!;
var appBundleName = Path.GetFileName (appPath);

// Collect expected dSYM names based on the binaries in the app bundle
var expectedDSyms = new HashSet<string> ();

// The app bundle itself should have a dSYM
expectedDSyms.Add (appBundleName + ".dSYM");

// Find frameworks in the app bundle
var frameworksDir = Path.Combine (appPath, GetFrameworksRelativePath (platform));
if (Directory.Exists (frameworksDir)) {
foreach (var frameworkDir in Directory.GetDirectories (frameworksDir, "*.framework")) {
var frameworkName = Path.GetFileNameWithoutExtension (frameworkDir);
var frameworkBinary = Path.Combine (frameworkDir, frameworkName);
if (File.Exists (frameworkBinary))
expectedDSyms.Add (frameworkName + ".framework.dSYM");
}
}

// Find dylibs in the app bundle
var contentsRelativeDir = GetRelativeDylibDirectory (platform);
var contentsDir = string.IsNullOrEmpty (contentsRelativeDir) ? appPath : Path.Combine (appPath, contentsRelativeDir);
if (Directory.Exists (contentsDir)) {
foreach (var dylib in Directory.GetFiles (contentsDir, "*.dylib")) {
var fileName = Path.GetFileNameWithoutExtension (dylib);
expectedDSyms.Add (fileName + ".dSYM");
}
}

// Find actual dSYM directories
var actualDSyms = Directory.GetDirectories (appContainerDir, "*.dSYM")
.Select (d => Path.GetFileName (d))
.ToHashSet ();

var missingDSyms = expectedDSyms.Except (actualDSyms).OrderBy (v => v).ToList ();
var unexpectedDSyms = actualDSyms.Except (expectedDSyms).OrderBy (v => v).ToList ();

if (missingDSyms.Count > 0)
Console.WriteLine ($" Missing dSYMs:\n {string.Join ("\n ", missingDSyms)}");
if (unexpectedDSyms.Count > 0)
Console.WriteLine ($" Unexpected dSYMs:\n {string.Join ("\n ", unexpectedDSyms)}");

Assert.That (missingDSyms, Is.Empty, "Missing dSYMs");
Assert.That (unexpectedDSyms, Is.Empty, "Unexpected dSYMs");
}

protected static string GetNativeExecutable (ApplePlatform platform, string app_directory)
{
var executableName = Path.GetFileNameWithoutExtension (app_directory);
Expand Down
2 changes: 1 addition & 1 deletion tests/dotnet/UnitTests/WindowsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ class FileData {
public required string RelativePath;
}

void AssertMaxFileLengthInBinAndObjDirectories (ApplePlatform platform, string project_path, string runtimeIdentifiers, string configuration, int maxLength = 110)
void AssertMaxFileLengthInBinAndObjDirectories (ApplePlatform platform, string project_path, string runtimeIdentifiers, string configuration, int maxLength = 118)
{
var binDir = GetBinDir (project_path, platform, runtimeIdentifiers, configuration);
var objDir = GetObjDir (project_path, platform, runtimeIdentifiers, configuration);
Expand Down
Loading