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
4 changes: 3 additions & 1 deletion dotnet/targets/Microsoft.Sdk.R2R.targets
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,8 @@
<_ComputeFrameworksToCreateAppManifestInputs Include="$(SupportedOSPlatformVersion)" />
<_ComputeFrameworksToCreateAppManifestInputs Include="$(TargetArchitectures)" />
<_ComputeFrameworksToCreateAppManifestInputs Include="$(_ComputedTargetFrameworkMoniker)" />
<!-- The bundle identifier is used to compute the framework's CFBundleIdentifier -->
<_ComputeFrameworksToCreateAppManifestInputs Include="$(_BundleIdentifier)" />
</ItemGroup>

<Hash
Expand All @@ -485,7 +487,7 @@

<Target
Name="_CreateInfoPlistForFrameworks"
Inputs="@(_CreatedFrameworksFromDylibs->'%(OriginatingDylib)');$(_ComputeFrameworksToCreateAppManifestHashedInputs)"
Inputs="@(_CreatedFrameworksFromDylibs->'%(OriginatingDylib)');$(_ComputeFrameworksToCreateAppManifestHashedInputsPath)"
Outputs="@(_CreatedFrameworksFromDylibs->'%(AppManifestPath)')"
>

Expand Down
5 changes: 4 additions & 1 deletion dotnet/targets/Xamarin.Shared.Sdk.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1022,6 +1022,9 @@
<SourceDirectory>%(_DirectoriesToPublish.SourceDirectory)</SourceDirectory>
<TargetDirectory>%(_DirectoriesToPublish.TargetDirectory)</TargetDirectory>
</_StampFilesToDelete>

<!-- Also collect Info.plist files from framework directories as additional inputs for the copy target -->
<_DirectoriesToPublishInfoPlist Include="@(_DirectoriesToPublish->'%(SourceDirectory)/Info.plist')" Condition="Exists('%(_DirectoriesToPublish.SourceDirectory)/Info.plist')" />
</ItemGroup>

<Delete
Expand All @@ -1032,7 +1035,7 @@

<Target Name="_CopyDirectoriesToBundle"
DependsOnTargets="_CollectDecompressedPlugins;_ComputeFrameworkFilesToPublish;_CollectDecompressedXpcServices;_CreateStampLocationForCopyDirectoriesToBundle"
Inputs="@(_DirectoriesToPublish)"
Inputs="@(_DirectoriesToPublish);@(_DirectoriesToPublishInfoPlist)"
Outputs="@(_DirectoriesToPublish -> '%(StampLocation)')"
>

Expand Down
70 changes: 70 additions & 0 deletions tests/dotnet/UnitTests/PostBuildTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,76 @@ public void BuildIpaTest_CoreCLR (ApplePlatform platform, string runtimeIdentifi
BuildIpaTestImpl (platform, runtimeIdentifiers, useMonoRuntime: false);
}

[Test]
[TestCase (ApplePlatform.iOS, "ios-arm64")]
[TestCase (ApplePlatform.TVOS, "tvos-arm64")]
public void CoreCLR_ConvertedFrameworks_HaveInfoPlist (ApplePlatform platform, string runtimeIdentifiers)
{
// Ref: https://github.com/dotnet/macios/issues/25248
// Verify that converted CoreCLR dylib frameworks have a valid Info.plist
// with the correct CFBundleIdentifier, so that codesign signs them as
// framework bundles and uses the plist bundle identifier.
var project = "MySimpleApp";
var configuration = "Release";
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 ["BuildIpa"] = "true";
properties ["Configuration"] = configuration;
properties ["UseMonoRuntime"] = "false";

DotNet.AssertBuild (project_path, properties);

var frameworksDir = Path.Combine (appPath, "Frameworks");
Assert.That (frameworksDir, Does.Exist, "Frameworks directory should exist for CoreCLR device builds");

var frameworkDirs = Directory.GetDirectories (frameworksDir, "*.framework");
Assert.That (frameworkDirs.Length, Is.GreaterThan (0), "Expected at least one .framework directory");

// Read the main app's bundle identifier
var appInfoPlistPath = Path.Combine (appPath, "Info.plist");
Assert.That (appInfoPlistPath, Does.Exist, "App Info.plist should exist");
var appPlist = PDictionary.FromFile (appInfoPlistPath);
Assert.That (appPlist, Is.Not.Null, $"Failed to parse Info.plist at '{appInfoPlistPath}'");
var bundleIdentifierValue = appPlist!.GetString ("CFBundleIdentifier");
Assert.That (bundleIdentifierValue, Is.Not.Null, $"CFBundleIdentifier should exist in '{appInfoPlistPath}'");
var bundleIdentifier = bundleIdentifierValue!.Value;

foreach (var fwDir in frameworkDirs) {
var fwName = Path.GetFileNameWithoutExtension (fwDir);
var infoPlistPath = Path.Combine (fwDir, "Info.plist");
Assert.That (infoPlistPath, Does.Exist, $"Info.plist should exist in {fwName}.framework");

var plist = PDictionary.FromFile (infoPlistPath);
Assert.That (plist, Is.Not.Null, $"Failed to parse Info.plist at '{infoPlistPath}'");
var fwBundleId = plist!.GetString ("CFBundleIdentifier")?.Value;
Assert.That (fwBundleId, Does.StartWith (bundleIdentifier + "."), $"CFBundleIdentifier for {fwName}.framework should start with the app bundle identifier");

var bundleExe = plist.GetString ("CFBundleExecutable")?.Value;
Assert.That (bundleExe, Is.EqualTo (fwName), $"CFBundleExecutable for {fwName}.framework");

var packageType = plist.GetString ("CFBundlePackageType")?.Value;
Assert.That (packageType, Is.EqualTo ("FMWK"), $"CFBundlePackageType for {fwName}.framework");

// Verify the executable binary actually exists
Assert.That (Path.Combine (fwDir, fwName), Does.Exist, $"Executable should exist in {fwName}.framework");

// If the framework was signed, verify it was signed as a bundle (not a bare Mach-O)
// and that the codesign identifier matches the Info.plist CFBundleIdentifier
var codeSignatureDir = Path.Combine (fwDir, "_CodeSignature");
if (Directory.Exists (codeSignatureDir)) {
var exitCode = ExecutionHelper.Execute ("/usr/bin/codesign", new string [] { "-dvvv", fwDir }, out var codesignOutput);
var output = codesignOutput.ToString ();
Comment thread
rolfbjarne marked this conversation as resolved.
Assert.That (exitCode, Is.EqualTo (0), $"codesign failed for framework {fwName}. Codesign output:\n{output}");
Assert.That (output, Does.Contain ("Format=bundle with Mach-O"), $"Framework {fwName} should be signed as a bundle, not a bare Mach-O. Codesign output:\n{output}");
Assert.That (output, Does.Contain ($"Identifier={fwBundleId}"), $"Framework {fwName} codesign identifier should match its Info.plist CFBundleIdentifier. Codesign output:\n{output}");
}
}
}

void BuildIpaTestImpl (ApplePlatform platform, string runtimeIdentifiers, bool useMonoRuntime)
{
var project = "MySimpleApp";
Expand Down
Loading