diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets index 62c61d29b1d4..02374c8a1388 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.GenerateAssemblyInfo.targets @@ -55,6 +55,7 @@ Copyright (c) .NET Foundation. All rights reserved. BeforeCompile targets (common targets convention). --> diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs index f85e303ae280..2889afc798bc 100644 --- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs +++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToControlGeneratedAssemblyInfo.cs @@ -43,6 +43,7 @@ public void It_respects_opt_outs(string attributeToOptOut) "/p:AssemblyTitle=TestTitle", "/p:Trademark=TestTrademark", "/p:NeutralLanguage=fr", + "/p:EnableSourceControlManagerQueries=false", attributeToOptOut == "All" ? "/p:GenerateAssemblyInfo=false" : $"/p:Generate{attributeToOptOut}=false" @@ -90,6 +91,8 @@ public void It_does_not_include_source_revision_id_if_initialize_source_control_ Name = "ProjectWithSourceRevisionId", TargetFrameworks = ToolsetInfo.CurrentTargetFramework, }; + // Disable the built-in source control integration + testProject.AdditionalProperties["EnableSourceControlManagerQueries"] = "false"; var testAsset = _testAssetsManager.CreateTestProject(testProject); @@ -107,6 +110,8 @@ public void It_does_not_include_source_revision_id_if_source_revision_id_not_set Name = "ProjectWithSourceRevisionId", TargetFrameworks = ToolsetInfo.CurrentTargetFramework, }; + // Disable the built-in source control integration + testProject.AdditionalProperties["EnableSourceControlManagerQueries"] = "false"; var testAsset = _testAssetsManager.CreateTestProject(testProject) .WithProjectChanges((path, project) => @@ -251,7 +256,7 @@ public void It_respects_version_prefix(string targetFramework) var buildCommand = new BuildCommand(testAsset); buildCommand - .Execute($"/p:OutputType=Library", $"/p:TargetFramework={targetFramework}", $"/p:VersionPrefix=1.2.3") + .Execute($"/p:OutputType=Library", $"/p:TargetFramework={targetFramework}", $"/p:VersionPrefix=1.2.3", "/p:EnableSourceControlManagerQueries=false") .Should() .Pass(); @@ -528,6 +533,85 @@ public void TestDisableRuntimeMarshalling(bool disableRuntimeMarshalling, bool? } } + [Fact] + public void TestDisableRuntimeMarshallingWithXamlPreCompileLikeTarget() + { + // This test simulates the WinUI3 scenario where XamlPreCompile runs before CoreCompile + // and needs to see the DisableRuntimeMarshallingAttribute from the generated AssemblyInfo + var testProject = new TestProject() + { + Name = "HelloWorld", + TargetFrameworks = ToolsetInfo.CurrentTargetFramework, + IsExe = true, + }; + testProject.AdditionalProperties["DisableRuntimeMarshalling"] = "true"; + testProject.AdditionalProperties["AllowUnsafeBlocks"] = "true"; + testProject.AdditionalProperties["PublishAot"] = "true"; + + // Add source code that uses GeneratedComInterface which requires DisableRuntimeMarshalling + testProject.SourceFiles["Program.cs"] = @" +using System; + +public class Program +{ + public static void Main() + { + Console.WriteLine(""Hello World""); + } +} +"; + testProject.SourceFiles["ComInterface.cs"] = @" +using System; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.Marshalling; + +[Guid(""00000000-0000-0000-0000-000000000003"")] +[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] +[GeneratedComInterface] +public partial interface IRepro +{ + void Test(System.Drawing.Point pt); +} +"; + + var testAsset = _testAssetsManager.CreateTestProject(testProject) + .WithProjectChanges((path, project) => + { + var ns = project.Root.Name.Namespace; + + // Add a target that simulates XamlPreCompile by running before CoreCompile + // and checking that the DisableRuntimeMarshallingAttribute exists + project.Root.Add( + new XElement(ns + "Target", + new XAttribute("Name", "SimulateXamlPreCompile"), + new XAttribute("BeforeTargets", "CoreCompile"), + new XAttribute("DependsOnTargets", "GenerateAssemblyInfo"), + new XElement(ns + "Message", + new XAttribute("Text", "Simulating XamlPreCompile - AssemblyInfo file should exist"), + new XAttribute("Importance", "High")), + new XElement(ns + "Error", + new XAttribute("Condition", "!Exists('$(GeneratedAssemblyInfoFile)')"), + new XAttribute("Text", "GeneratedAssemblyInfoFile does not exist at '$(GeneratedAssemblyInfoFile)'")) + ) + ); + }); + + var buildCommand = new BuildCommand(testAsset); + + // The build should pass, meaning the GeneratedAssemblyInfo file exists before CoreCompile + buildCommand.Execute() + .Should() + .Pass(); + + var assemblyPath = Path.Combine(buildCommand.GetOutputDirectory(testProject.TargetFrameworks).FullName, "HelloWorld.dll"); + + // Verify that the DisableRuntimeMarshallingAttribute is present in the assembly + var parameterlessAttributes = AssemblyInfo.GetParameterlessAttributes(assemblyPath); + bool contains = parameterlessAttributes.Any(attr => attr.Equals("DisableRuntimeMarshallingAttribute", StringComparison.Ordinal)); + + Assert.True(contains, "DisableRuntimeMarshallingAttribute should be present in the assembly"); + } + [Fact] public void It_respects_out_out_of_internals_visible_to() {