Skip to content

Commit cfd1308

Browse files
committed
Make it so the artifacts folders register files for cleanup
1 parent 836489f commit cfd1308

File tree

2 files changed

+94
-3
lines changed

2 files changed

+94
-3
lines changed

src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DefaultOutputPaths.targets

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ Copyright (c) .NET Foundation. All rights reserved.
5151
<ArtifactsBinOutputName Condition="'$(ArtifactsBinOutputName)' == ''">bin</ArtifactsBinOutputName>
5252
<ArtifactsPublishOutputName Condition="'$(ArtifactsPublishOutputName)' == ''">publish</ArtifactsPublishOutputName>
5353
<ArtifactsPackageOutputName Condition="'$(ArtifactsPackageOutputName)' == ''">package</ArtifactsPackageOutputName>
54+
<!-- When using artifacts output, we need to track file writes with a bit more knowledge than the MSBuild Common targets have-->
55+
<IncrementalCleanDependsOn>$(IncrementalCleanDependsOn);_TrackFileWritesShareableUnderArtifactsPath</IncrementalCleanDependsOn>
5456
</PropertyGroup>
5557

5658
<PropertyGroup Condition="'$(UseArtifactsOutput)' == 'true' And '$(ArtifactsPivots)' == ''">
@@ -90,9 +92,9 @@ Copyright (c) .NET Foundation. All rights reserved.
9092

9193
<!-- The package output path does not include the project name, and only includes the Configuration as a pivot -->
9294
<PackageOutputPath Condition="'$(PackageOutputPath)' == ''">$(ArtifactsPath)\$(ArtifactsPackageOutputName)\$(Configuration.ToLowerInvariant())\</PackageOutputPath>
93-
95+
9496
</PropertyGroup>
95-
97+
9698
<PropertyGroup Condition="'$(UseArtifactsOutput)' != 'true'">
9799
<BaseOutputPath Condition="'$(BaseOutputPath)' == ''">bin\</BaseOutputPath>
98100
<BaseOutputPath Condition="!HasTrailingSlash('$(BaseOutputPath)')">$(BaseOutputPath)\</BaseOutputPath>
@@ -155,10 +157,31 @@ Copyright (c) .NET Foundation. All rights reserved.
155157

156158
<NetSdkError Condition="'$(UseArtifactsOutput)' == 'true' and '$(_ArtifactsPathSetEarly)' != 'true'"
157159
ResourceName="ArtifactsPathCannotBeSetInProject" />
158-
160+
159161
<NetSdkError Condition="'$(_ArtifactsPathLocationType)' == 'ProjectFolder'"
160162
ResourceName="UseArtifactsOutputRequiresDirectoryBuildProps" />
161163

162164
</Target>
163165

166+
167+
<!-- Addresses a gap in the _CleanGetCurrentAndPriorFileWrites:
168+
https://github.com/dotnet/msbuild/blob/25d1e4c409f9efd81c345fb41fbee6d2af83bed6/src/Tasks/Microsoft.Common.CurrentVersion.targets#L5765-L5777
169+
170+
This Target won't remove FileWritesShareable if they aren't under the Project's own directory,
171+
but in Artifacts layout that will never be the case. So we allow finding the FileWritesShareable under our output
172+
paths as well -->
173+
<Target Name="_TrackFileWritesShareableUnderArtifactsPath">
174+
<FindUnderPath Path="$(OutputPath)" Files="@(FileWritesShareable)" UpdateToAbsolutePaths="true">
175+
<Output TaskParameter="InPath" ItemName="_LocatedFileWritesShareable"/>
176+
</FindUnderPath>
177+
<FindUnderPath Path="$(IntermediateOutputPath)" Files="@(FileWritesShareable)" UpdateToAbsolutePaths="true">
178+
<Output TaskParameter="InPath" ItemName="_LocatedFileWritesShareable"/>
179+
</FindUnderPath>
180+
181+
<!-- Remove duplicates from files produced in this build. -->
182+
<RemoveDuplicates Inputs="@(_LocatedFileWritesShareable)" >
183+
<Output TaskParameter="Filtered" ItemName="_CleanCurrentFileWrites"/>
184+
</RemoveDuplicates>
185+
</Target>
186+
164187
</Project>

test/Microsoft.NET.Build.Tests/ArtifactsOutputPathTests.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,74 @@ public void ItCanBuildWithMicrosoftBuildArtifactsSdk()
531531
new FileInfo(Path.Combine(testAsset.Path, "MSBuildSdk", "obj", "Debug", ToolsetInfo.CurrentTargetFramework, "MSBuildSdk.dll")).Should().Exist();
532532

533533
}
534+
535+
[Fact]
536+
public void PublishingRegistersWrittenFilesForProperCleanup()
537+
{
538+
var testProject = new TestProject()
539+
{
540+
IsExe = true,
541+
UseArtifactsOutput = true,
542+
};
543+
544+
var testAsset = _testAssetsManager.CreateTestProject(testProject);
545+
546+
// Now add a Directory.Build.props file setting UseArtifactsOutput to true
547+
File.WriteAllText(Path.Combine(testAsset.Path, "Directory.Build.props"), """
548+
<Project>
549+
<PropertyGroup>
550+
<UseArtifactsOutput>true</UseArtifactsOutput>
551+
</PropertyGroup>
552+
</Project>
553+
""");
554+
555+
var projectDir = Path.Combine(testAsset.Path, testAsset.TestProject.Name);
556+
557+
// publish the app
558+
// we publish self-contained so that we include hostfxr.dll.
559+
// if we don't clean up this file, when we build in Release,
560+
// the generated exe will pick up the hostfxr and fail to run.
561+
// so the only way to successfully run the exe is to clean up
562+
// the hostfxr.dll and other self-contained files.
563+
new DotnetPublishCommand(Log)
564+
.WithWorkingDirectory(projectDir)
565+
.Execute("--self-contained")
566+
.Should()
567+
.Pass();
568+
569+
var outputDir = new DirectoryInfo(OutputPathCalculator.FromProject(testAsset.Path, testProject).GetOutputDirectory(configuration: "release"));
570+
outputDir.Should().Exist().And.HaveFile("hostfxr.dll");
571+
LocateAndRunApp(outputDir);
572+
573+
var publishDir = new DirectoryInfo(OutputPathCalculator.FromProject(testAsset.Path, testProject).GetPublishDirectory(configuration: "release"));
574+
publishDir.Should().Exist().And.HaveFile("hostfxr.dll");
575+
LocateAndRunApp(publishDir);
576+
577+
// now build the app in Release configuration.
578+
// now self-contained, so that we are forced to clean up the runtime
579+
// files that were published.
580+
new DotnetBuildCommand(Log)
581+
.WithWorkingDirectory(projectDir)
582+
.Execute("-c", "Release")
583+
.Should()
584+
.Pass();
585+
outputDir.Should().Exist();
586+
outputDir.Should().NotHaveFiles(["hostfxr.dll"]);
587+
LocateAndRunApp(outputDir);
588+
589+
void LocateAndRunApp(DirectoryInfo root)
590+
{
591+
var appBinaryName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
592+
? $"{testProject.Name}.exe"
593+
: testProject.Name;
594+
root.Should().HaveFiles([appBinaryName]);
595+
var binary = root.GetFiles(appBinaryName).First();
596+
new RunExeCommand(Log, binary.FullName)
597+
.Execute()
598+
.Should()
599+
.Pass();
600+
}
601+
}
534602
}
535603

536604
namespace ArtifactsTestExtensions

0 commit comments

Comments
 (0)