diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs b/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs index d0bb6235f7a5..fe037a6302e2 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs +++ b/src/Tasks/Microsoft.NET.Build.Tasks/PrepareForReadyToRunCompilation.cs @@ -243,10 +243,20 @@ private void ProcessInputFileList( else if (eligibility.CompileUnrootedIntoCompositeImage) { r2rCompositeUnrootedInput.Add(file); + + // crossgen2 emits component assemblies flat into the composite output directory using only their + // file names, so the publish source path must use just the file name. The RelativePath metadata + // (already set on r2rFileToPublish) continues to drive the final publish destination. + r2rFileToPublish.ItemSpec = Path.Combine(OutputPath, Path.GetFileName(outputR2RImageRelativePath)); } else if (eligibility.CompileIntoCompositeImage) { r2rCompositeInputList.Add(file); + + // crossgen2 emits component assemblies flat into the composite output directory using only their + // file names, so the publish source path must use just the file name. The RelativePath metadata + // (already set on r2rFileToPublish) continues to drive the final publish destination. + r2rFileToPublish.ItemSpec = Path.Combine(OutputPath, Path.GetFileName(outputR2RImageRelativePath)); } r2rFileToPublish.RemoveMetadata(MetadataKeys.OriginalItemSpec); diff --git a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs index 46bfd97d48a0..71112dce8517 100644 --- a/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs +++ b/test/Microsoft.NET.Publish.Tests/GivenThatWeWantToPublishReadyToRun.cs @@ -239,6 +239,59 @@ public void It_can_publish_readytorun_using_crossgen2_composite_mode(string targ TestProjectPublishing_Internal("Crossgen2TestApp", targetFramework, isSelfContained: true, emitNativeSymbols: false, useCrossgen2: true, composite: true, identifier: targetFramework); } + [TestMethod] + [RequiresMSBuildVersion("17.0.0.32901")] + [DataRow(ToolsetInfo.CurrentTargetFramework)] + public void It_can_publish_readytorun_composite_with_subdirectory_relative_path(string targetFramework) + { + // When a target rewrites the RelativePath of the published assemblies to include a subdirectory, + // composite ReadyToRun publishing used to fail because the SDK looked for the crossgen2 component + // output under that subdirectory even though crossgen2 emits the components flat into the composite + // output directory. + var projectName = "Crossgen2SubdirApp"; + + var testProject = CreateTestProjectForR2RTesting( + targetFramework, + projectName, + "ClassLib"); + + testProject.AdditionalProperties["PublishReadyToRun"] = "True"; + testProject.AdditionalProperties["PublishReadyToRunUseCrossgen2"] = "True"; + testProject.AdditionalProperties["PublishReadyToRunComposite"] = "True"; + testProject.SelfContained = "True"; + + var testProjectInstance = TestAssetsManager.CreateTestProject(testProject, identifier: targetFramework) + .WithProjectChanges(project => + { + var ns = project.Root.Name.Namespace; + var target = new XElement(ns + "Target", + new XAttribute("Name", "PublishAssembliesToSubdirectory"), + new XAttribute("BeforeTargets", "CreateReadyToRunImages"), + new XAttribute("AfterTargets", "ComputeResolvedFilesToPublishList"), + new XElement(ns + "ItemGroup", + new XElement(ns + "ResolvedFileToPublish", + new XAttribute("Update", "@(ResolvedFileToPublish)"), + new XAttribute("Condition", "'%(Extension)' == '.dll'"), + new XAttribute("RelativePath", "subdir/%(RelativePath)")))); + project.Root.Add(target); + }); + + var publishCommand = new PublishCommand(testProjectInstance); + publishCommand.Execute().Should().Pass(); + + DirectoryInfo publishDirectory = publishCommand.GetOutputDirectory( + targetFramework, + "Debug", + testProject.RuntimeIdentifier); + + var subdirectory = new DirectoryInfo(Path.Combine(publishDirectory.FullName, "subdir")); + subdirectory.Should().HaveFile($"{projectName}.dll"); + subdirectory.Should().HaveFile("ClassLib.dll"); + + DoesImageHaveR2RInfo(Path.Combine(subdirectory.FullName, $"{projectName}.dll")).Should().BeTrue(); + DoesImageHaveR2RInfo(Path.Combine(subdirectory.FullName, "ClassLib.dll")).Should().BeTrue(); + } + [TestMethod] [RequiresMSBuildVersion("17.0.0.32901")] [DataRow("net6.0")]