diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs index 9ec56c965a8b..9447c16cbd02 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/ProcessFrameworkReferences.cs @@ -99,7 +99,10 @@ public class ProcessFrameworkReferences : TaskBase public ITaskItem[] Crossgen2Packs { get; set; } [Output] - public ITaskItem[] ILCompilerPacks { get; set; } + public ITaskItem[] HostILCompilerPacks { get; set; } + + [Output] + public ITaskItem[] TargetILCompilerPacks { get; set; } // Runtime packs which aren't available for the specified RuntimeIdentifier [Output] @@ -617,7 +620,29 @@ private bool AddAotOrR2RRuntimePackage(AotPackageType packageType, Version norma } else { - ILCompilerPacks = new[] { newItem }; + HostILCompilerPacks = new[] { newItem }; + // ILCompiler supports cross target compilation. If there is a cross-target request, we need to download that package as well + // We expect RuntimeIdentifier to be defined during publish but can allow during build + if (RuntimeIdentifier != null) + { + var targetRuntimeIdentifier = NuGetUtils.GetBestMatchingRid(runtimeGraph, RuntimeIdentifier, packSupportedRuntimeIdentifiers, out bool wasInGraph2); + if (targetRuntimeIdentifier == null) + { + return false; + } + if (!hostRuntimeIdentifier.Equals(targetRuntimeIdentifier)) + { + var runtimeIlcPackName = packPattern.Replace("**RID**", targetRuntimeIdentifier); + TaskItem targetIlcPackToDownload = new TaskItem(runtimeIlcPackName); + targetIlcPackToDownload.SetMetadata(MetadataKeys.Version, packVersion); + packagesToDownload.Add(targetIlcPackToDownload); + + var newItem2 = new TaskItem(runtimeIlcPackName); + newItem2.SetMetadata(MetadataKeys.NuGetPackageId, runtimeIlcPackName); + newItem2.SetMetadata(MetadataKeys.NuGetPackageVersion, packVersion); + TargetILCompilerPacks = new[] { newItem2 }; + } + } } return true; diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets index fbfadfb41f81..df564baca1a4 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.FrameworkReferenceResolution.targets @@ -120,7 +120,8 @@ Copyright (c) .NET Foundation. All rights reserved. - + + @@ -252,12 +253,19 @@ Copyright (c) .NET Foundation. All rights reserved. - + + + + + + diff --git a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs index 07b7d06f841a..c20ecbf015e8 100644 --- a/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs +++ b/src/Tests/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishAnAotApp.cs @@ -78,6 +78,40 @@ public void NativeAot_hw_runs_with_no_warnings_when_PublishAot_is_enabled(string } } + [RequiresMSBuildVersionTheory("17.0.0.32901")] + [InlineData(ToolsetInfo.CurrentTargetFramework)] + public void NativeAot_hw_runs_with_no_warnings_when_PublishAot_is_false(string targetFramework) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + var projectName = "HellowWorldNativeAotApp"; + var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); + + var testProject = CreateHelloWorldTestProject(targetFramework, projectName, true); + testProject.AdditionalProperties["PublishAot"] = "false"; + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var publishCommand = new PublishCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + publishCommand + .Execute($"/p:RuntimeIdentifier={rid}") + .Should().Pass() + .And.NotHaveStdOutContaining("IL2026") + .And.NotHaveStdErrContaining("NETSDK1179") + .And.NotHaveStdErrContaining("warning"); + + var publishDirectory = publishCommand.GetOutputDirectory(targetFramework: targetFramework, runtimeIdentifier: rid).FullName; + var publishedDll = Path.Combine(publishDirectory, $"{projectName}.dll"); + var publishedExe = Path.Combine(publishDirectory, $"{testProject.Name}{Constants.ExeSuffix}"); + + // PublishAot=false will be a normal publish + File.Exists(publishedDll).Should().BeTrue(); + + var command = new RunExeCommand(Log, publishedExe) + .Execute().Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + } + [RequiresMSBuildVersionTheory("17.0.0.32901")] [InlineData(ToolsetInfo.CurrentTargetFramework)] public void NativeAot_app_runs_in_debug_with_no_config_when_PublishAot_is_enabled(string targetFramework) @@ -245,7 +279,6 @@ public void NativeAot_hw_runs_with_PackageReference_PublishAot_is_enabled(string if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { testProject.AdditionalProperties["StripSymbols"] = "true"; - testProject.AdditionalProperties["ObjCopyName"] = "objcopy"; } var testAsset = _testAssetsManager.CreateTestProject(testProject); @@ -274,6 +307,102 @@ public void NativeAot_hw_runs_with_PackageReference_PublishAot_is_enabled(string } } + [RequiresMSBuildVersionTheory("17.0.0.32901")] + [InlineData(ToolsetInfo.CurrentTargetFramework)] + public void NativeAot_hw_runs_with_PackageReference_PublishAot_is_empty(string targetFramework) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + var projectName = "HellowWorldNativeAotApp"; + var rid = EnvironmentInfo.GetCompatibleRid(targetFramework); + + var testProject = CreateHelloWorldTestProject(targetFramework, projectName, true); + + // This will add a reference to a package that will also be automatically imported by the SDK + testProject.PackageReferences.Add(new TestPackageReference("Microsoft.DotNet.ILCompiler", "7.0.0-*")); + + // Linux symbol files are embedded and require additional steps to be stripped to a separate file + // assumes /bin (or /usr/bin) are in the PATH + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + testProject.AdditionalProperties["StripSymbols"] = "true"; + } + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var publishCommand = new PublishCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + publishCommand + .Execute($"/p:RuntimeIdentifier={rid}") + .Should().Pass(); + + var publishDirectory = publishCommand.GetOutputDirectory(targetFramework: targetFramework, runtimeIdentifier: rid).FullName; + var sharedLibSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".dll" : ".so"; + var publishedDll = Path.Combine(publishDirectory, $"{projectName}{sharedLibSuffix}"); + var publishedExe = Path.Combine(publishDirectory, $"{testProject.Name}{Constants.ExeSuffix}"); + var symbolSuffix = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".pdb" : ".dbg"; + var publishedDebugFile = Path.Combine(publishDirectory, $"{testProject.Name}{symbolSuffix}"); + + // NativeAOT published dir should not contain a non-host stand alone package + File.Exists(publishedDll).Should().BeFalse(); + // The exe exist and should be native + File.Exists(publishedExe).Should().BeTrue(); + File.Exists(publishedDebugFile).Should().BeTrue(); + IsNativeImage(publishedExe).Should().BeTrue(); + + var command = new RunExeCommand(Log, publishedExe) + .Execute().Should().Pass() + .And.HaveStdOutContaining("Hello World"); + } + } + + [RequiresMSBuildVersionTheory("17.0.0.32901")] + [InlineData(ToolsetInfo.CurrentTargetFramework)] + public void NativeAot_hw_runs_with_cross_PackageReference_PublishAot_is_enabled(string targetFramework) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (RuntimeInformation.OSArchitecture == System.Runtime.InteropServices.Architecture.X64)) + { + var projectName = "HellowWorldNativeAotApp"; + var rid = "win-arm64"; + + var testProject = CreateHelloWorldTestProject(targetFramework, projectName, true); + testProject.AdditionalProperties["PublishAot"] = "true"; + + // This will add a reference to a package that will also be automatically imported by the SDK + testProject.PackageReferences.Add(new TestPackageReference("Microsoft.DotNet.ILCompiler", "7.0.0-*")); + testProject.PackageReferences.Add(new TestPackageReference("runtime.win-x64.Microsoft.DotNet.ILCompiler", "7.0.0-*")); + + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var publishCommand = new PublishCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + publishCommand + .Execute($"/p:RuntimeIdentifier={rid}") + .Should().Pass(); + } + } + + [RequiresMSBuildVersionTheory("17.0.0.32901")] + [InlineData(ToolsetInfo.CurrentTargetFramework)] + public void NativeAot_hw_runs_with_cross_PackageReference_PublishAot_is_empty(string targetFramework) + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && (RuntimeInformation.OSArchitecture == System.Runtime.InteropServices.Architecture.X64)) + { + var projectName = "HellowWorldNativeAotApp"; + var rid = "win-arm64"; + + var testProject = CreateHelloWorldTestProject(targetFramework, projectName, true); + + // This will add a reference to a package that will also be automatically imported by the SDK + testProject.PackageReferences.Add(new TestPackageReference("Microsoft.DotNet.ILCompiler", "7.0.0-*")); + testProject.PackageReferences.Add(new TestPackageReference("runtime.win-x64.Microsoft.DotNet.ILCompiler", "7.0.0-*")); + + var testAsset = _testAssetsManager.CreateTestProject(testProject); + + var publishCommand = new PublishCommand(Log, Path.Combine(testAsset.TestRoot, testProject.Name)); + publishCommand + .Execute($"/p:RuntimeIdentifier={rid}") + .Should().Pass(); + } + } + [RequiresMSBuildVersionTheory("17.0.0.32901")] [InlineData(ToolsetInfo.CurrentTargetFramework)] public void Only_Aot_warnings_are_produced_if_EnableAotAnalyzer_is_set(string targetFramework)