Skip to content

Commit

Permalink
Enable building app extensions with NativeAOT
Browse files Browse the repository at this point in the history
  • Loading branch information
ivanpovazan committed Jul 12, 2024
1 parent 896b26b commit 8866177
Show file tree
Hide file tree
Showing 5 changed files with 56 additions and 19 deletions.
9 changes: 7 additions & 2 deletions dotnet/targets/Xamarin.Shared.Sdk.props
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,12 @@
<!-- Disable our own assembly IL stripping logic, because ILC does that already -->
<EnableAssemblyILStripping>false</EnableAssemblyILStripping>

<!-- We're using our own native main function when using NativeAOT -->
<CustomNativeMain>true</CustomNativeMain>
<!--
We're using our own native main function when using NativeAOT.
This is true for both: managed executables and app extensions (which are libraries).
Since ILC expects to find a managed main function whenever NativeLib=static and CustomNativeMain=true,
we are only setting this flag when we are building executables. (Class libraries do not have a managed Main)
-->
<CustomNativeMain Condition="'$(OutputType)' == 'Exe'">true</CustomNativeMain>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion tests/dotnet/ExtensionConsumer/iOS/ExtensionConsumer.sln
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MySimpleApp", "MySimpleApp.csproj", "{23664512-6B06-4135-9A94-C012BDA93CB1}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionConsumer", "ExtensionConsumer.csproj", "{23664512-6B06-4135-9A94-C012BDA93CB1}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtensionProject", "..\..\ExtensionProject\iOS\ExtensionProject.csproj", "{8A72DB8F-4C30-4462-9F7A-6095E41D5D46}"
EndProject
Expand Down
57 changes: 41 additions & 16 deletions tests/dotnet/UnitTests/ProjectTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1063,46 +1063,71 @@ public void KillEverything ()
}


[TestCase (ApplePlatform.iOS)]
[TestCase (ApplePlatform.TVOS)]
[TestCase (ApplePlatform.MacOSX)]
[TestCase (ApplePlatform.iOS, "ios-arm64", false)]
[TestCase (ApplePlatform.iOS, "ios-arm64", true)]
[TestCase (ApplePlatform.iOS, "iossimulator-x64", false)]
[TestCase (ApplePlatform.iOS, "iossimulator-x64", true)]
[TestCase (ApplePlatform.TVOS, "tvossimulator-x64", false)]
[TestCase (ApplePlatform.TVOS, "tvossimulator-x64", true)]
[TestCase (ApplePlatform.MacOSX, "osx-x64", false)]
[TestCase (ApplePlatform.MacOSX, "osx-x64", true)]
[TestCase (ApplePlatform.MacOSX, "osx-x64;osx-arm64", false)]
[TestCase (ApplePlatform.MacOSX, "osx-x64;osx-arm64", true)]
// [TestCase ("MacCatalyst", "")] - No extension support yet
public void BuildProjectsWithExtensions (ApplePlatform platform)
public void BuildProjectsWithExtensions (ApplePlatform platform, string runtimeIdentifier, bool isNativeAot)
{
Configuration.IgnoreIfIgnoredPlatform (platform);
var consumingProjectDir = GetProjectPath ("ExtensionConsumer", platform: platform);
var consumingProjectDir = GetProjectPath ("ExtensionConsumer", runtimeIdentifier, platform, out var appPath);
var extensionProjectDir = GetProjectPath ("ExtensionProject", platform: platform);

Clean (extensionProjectDir);
Clean (consumingProjectDir);

DotNet.AssertBuild (consumingProjectDir, verbosity);
var properties = GetDefaultProperties (runtimeIdentifier);

if (isNativeAot) {
properties ["PublishAot"] = "true";
properties ["_IsPublishing"] = "true";
}

DotNet.AssertBuild (consumingProjectDir, properties);

var extensionPath = Path.Combine (Path.GetDirectoryName (consumingProjectDir)!, "bin", "Debug", platform.ToFramework (), GetDefaultRuntimeIdentifier (platform), "MySimpleApp.app", GetPlugInsRelativePath (platform), "ExtensionProject.appex");
var extensionPath = Path.Combine (Path.GetDirectoryName (consumingProjectDir)!, "bin", "Debug", platform.ToFramework (), runtimeIdentifier, "ExtensionConsumer.app", GetPlugInsRelativePath (platform), "ExtensionProject.appex");
Assert.That (Directory.Exists (extensionPath), $"App extension directory does not exist: {extensionPath}");

var pathToSearch = Path.Combine (Path.GetDirectoryName (consumingProjectDir)!, "bin", "Debug");
string [] configFiles = Directory.GetFiles (pathToSearch, "*.runtimeconfig.*", SearchOption.AllDirectories);
Assert.AreNotEqual (0, configFiles.Length, "runtimeconfig.json file does not exist");
}

[TestCase (ApplePlatform.iOS)]
[TestCase (ApplePlatform.TVOS)]
[TestCase (ApplePlatform.MacOSX)]
[TestCase (ApplePlatform.iOS, "iossimulator-x64", false)]
[TestCase (ApplePlatform.iOS, "iossimulator-x64", true)]
[TestCase (ApplePlatform.TVOS, "tvossimulator-x64", false)]
[TestCase (ApplePlatform.TVOS, "tvossimulator-x64", true)]
[TestCase (ApplePlatform.MacOSX, "osx-x64", false)]
[TestCase (ApplePlatform.MacOSX, "osx-x64", true)]
[TestCase (ApplePlatform.MacOSX, "osx-x64;osx-arm64", false)]
[TestCase (ApplePlatform.MacOSX, "osx-x64;osx-arm64", true)]
// [TestCase ("MacCatalyst", "")] - No extension support yet
public void BuildProjectsWithExtensionsAndFrameworks (ApplePlatform platform)
public void BuildProjectsWithExtensionsAndFrameworks (ApplePlatform platform, string runtimeIdentifier, bool isNativeAot)
{
Configuration.IgnoreIfIgnoredPlatform (platform);
var runtimeIdentifiers = GetDefaultRuntimeIdentifier (platform);
var consumingProjectDir = GetProjectPath ("ExtensionConsumerWithFrameworks", runtimeIdentifiers: runtimeIdentifiers, platform: platform, out var appPath);
var consumingProjectDir = GetProjectPath ("ExtensionConsumerWithFrameworks", runtimeIdentifiers: runtimeIdentifier, platform: platform, out var appPath);
var extensionProjectDir = GetProjectPath ("ExtensionProjectWithFrameworks", platform: platform);

Clean (extensionProjectDir);
Clean (consumingProjectDir);

DotNet.AssertBuild (consumingProjectDir, verbosity);
var properties = GetDefaultProperties (runtimeIdentifier);

var extensionPath = Path.Combine (Path.GetDirectoryName (consumingProjectDir)!, "bin", "Debug", platform.ToFramework (), GetDefaultRuntimeIdentifier (platform), "ExtensionConsumerWithFrameworks.app", GetPlugInsRelativePath (platform), "ExtensionProjectWithFrameworks.appex");
if (isNativeAot) {
properties ["PublishAot"] = "true";
properties ["_IsPublishing"] = "true";
}

DotNet.AssertBuild (consumingProjectDir, properties);

var extensionPath = Path.Combine (Path.GetDirectoryName (consumingProjectDir)!, "bin", "Debug", platform.ToFramework (), runtimeIdentifier, "ExtensionConsumerWithFrameworks.app", GetPlugInsRelativePath (platform), "ExtensionProjectWithFrameworks.appex");
Assert.That (Directory.Exists (extensionPath), $"App extension directory does not exist: {extensionPath}");
var extensionFrameworksPath = Path.Combine (extensionPath, GetFrameworksRelativePath (platform));
Assert.IsFalse (Directory.Exists (extensionFrameworksPath), $"App extension framework directory exists when it shouldn't: {extensionFrameworksPath}");
Expand All @@ -1119,7 +1144,7 @@ public void BuildProjectsWithExtensionsAndFrameworks (ApplePlatform platform)
Assert.That (File.Exists (Path.Combine (appFrameworksPath, "UnknownE.framework", "UnknownE")), "UnknownE");

var appExecutable = GetNativeExecutable (platform, appPath);
ExecuteWithMagicWordAndAssert (platform, runtimeIdentifiers, appExecutable);
ExecuteWithMagicWordAndAssert (platform, runtimeIdentifier, appExecutable);
}


Expand Down
7 changes: 7 additions & 0 deletions tools/common/Target.cs
Original file line number Diff line number Diff line change
Expand Up @@ -897,6 +897,13 @@ void GenerateIOSMain (StringWriter sw, Abi abi)
sw.WriteLine ("\treturn rv;");
sw.WriteLine ("}");

// Add an empty __managed__Main function when building class lib app extensions with NativeAOT to workaround static reference to this symbol from nativeaot-bridge.m
if (app.IsExtension && app.XamarinRuntime == XamarinRuntime.NativeAOT) {
sw.WriteLine ();
sw.Write ("extern \"C\" int __managed__Main (int argc, const char** argv) { return 0; } ");
sw.WriteLine ();
}

string extension_main = null;
if (app.Platform == ApplePlatform.WatchOS && app.IsWatchExtension) {
// We're building a watch extension, and we have multiple scenarios, depending on the watchOS version we're executing on:
Expand Down

0 comments on commit 8866177

Please sign in to comment.