From c2ffceb82640f55b434ec5dc5d764da003a8d1d6 Mon Sep 17 00:00:00 2001 From: Jan Jones Date: Fri, 18 Jul 2025 13:33:15 +0200 Subject: [PATCH] Place file-based app artifacts into repo's artifacts dir if used --- documentation/general/dotnet-run-file.md | 4 +- .../Run/VirtualProjectBuildingCommand.cs | 3 +- .../sdk/UseArtifactsOutputPath.props | 2 +- .../Microsoft.NET.DefaultArtifactsPath.props | 8 + .../CommandTests/Run/RunFileTests.cs | 143 ++++++++++++++---- 5 files changed, 126 insertions(+), 34 deletions(-) diff --git a/documentation/general/dotnet-run-file.md b/documentation/general/dotnet-run-file.md index 0c75aa0910d7..c8e8d5600067 100644 --- a/documentation/general/dotnet-run-file.md +++ b/documentation/general/dotnet-run-file.md @@ -178,7 +178,9 @@ have the shared `.cs` files source-included via ` + Condition="'$(UseArtifactsOutput)' == 'true' Or '$(ArtifactsPath)' != '' Or '$(FileBasedAppArtifactsPath)' != ''"/> true diff --git a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DefaultArtifactsPath.props b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DefaultArtifactsPath.props index b5dd5f111c1b..d08a2de949b0 100644 --- a/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DefaultArtifactsPath.props +++ b/src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.DefaultArtifactsPath.props @@ -40,4 +40,12 @@ Copyright (c) .NET Foundation. All rights reserved. $(MSBuildProjectDirectory)\artifacts <_ArtifactsPathLocationType>ProjectFolder + + + + true + $(FileBasedAppArtifactsPath) + false + <_ArtifactsPathLocationType>FileBasedApp + diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs index 98b62c75dd06..214898ebb4bf 100644 --- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs +++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs @@ -13,7 +13,7 @@ namespace Microsoft.DotNet.Cli.Run.Tests; public sealed class RunFileTests(ITestOutputHelper log) : SdkTest(log) { - private static readonly string s_program = """ + private static readonly string s_program = /* lang=C#-Test */ """ if (args.Length > 0) { Console.WriteLine("echo args:" + string.Join(";", args)); @@ -27,7 +27,7 @@ public sealed class RunFileTests(ITestOutputHelper log) : SdkTest(log) #endif """; - private static readonly string s_programDependingOnUtil = """ + private static readonly string s_programDependingOnUtil = /* lang=C#-Test */ """ if (args.Length > 0) { Console.WriteLine("echo args:" + string.Join(";", args)); @@ -35,7 +35,7 @@ public sealed class RunFileTests(ITestOutputHelper log) : SdkTest(log) Console.WriteLine("Hello, " + Util.GetMessage()); """; - private static readonly string s_util = """ + private static readonly string s_util = /* lang=C#-Test */ """ static class Util { public static string GetMessage() @@ -45,6 +45,29 @@ public static string GetMessage() } """; + private static readonly string s_programReadingEmbeddedResource = /* lang=C#-Test */ """ + var assembly = System.Reflection.Assembly.GetExecutingAssembly(); + var resourceName = assembly.GetManifestResourceNames().SingleOrDefault(); + + if (resourceName is null) + { + Console.WriteLine("Resource not found"); + return; + } + + using var stream = assembly.GetManifestResourceStream(resourceName)!; + using var reader = new System.Resources.ResourceReader(stream); + Console.WriteLine(reader.Cast().Single()); + """; + + private static readonly string s_resx = """ + + + TestValue + + + """; + private static readonly string s_consoleProject = $""" @@ -950,26 +973,8 @@ public void BinaryLog_EvaluationData() public void EmbeddedResource() { var testInstance = _testAssetsManager.CreateTestDirectory(); - string code = """ - using var stream = System.Reflection.Assembly.GetExecutingAssembly().GetManifestResourceStream("Program.Resources.resources"); - - if (stream is null) - { - Console.WriteLine("Resource not found"); - return; - } - - using var reader = new System.Resources.ResourceReader(stream); - Console.WriteLine(reader.Cast().Single()); - """; - File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), code); - File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), """ - - - TestValue - - - """); + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), s_programReadingEmbeddedResource); + File.WriteAllText(Path.Join(testInstance.Path, "Resources.resx"), s_resx); new DotnetCommand(Log, "run", "Program.cs") .WithWorkingDirectory(testInstance.Path) @@ -982,7 +987,7 @@ public void EmbeddedResource() // This behavior can be overridden. File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), $""" #:property EnableDefaultEmbeddedResourceItems=false - {code} + {s_programReadingEmbeddedResource} """); new DotnetCommand(Log, "run", "Program.cs") @@ -994,6 +999,35 @@ Resource not found """); } + /// + /// .resx files in ./artifacts/ should not be included. + /// Part of . + /// + [Fact] + public void EmbeddedResource_InRepoArtifacts() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), s_programReadingEmbeddedResource); + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + true + + + """); + var dir = Path.Join(testInstance.Path, "artifacts", "obj", "AnotherApp"); + Directory.CreateDirectory(dir); + File.WriteAllText(Path.Join(dir, "Resources.resx"), s_resx); + + new DotnetCommand(Log, "run", "Program.cs", "-bl") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass() + .And.HaveStdOut(""" + Resource not found + """); + } + [Fact] public void NoRestore_01() { @@ -1408,6 +1442,58 @@ public void ArtifactsDirectory_Permissions() .Should().Be(actualMode, artifactsDir); } + [Fact] + public void ArtifactsPath() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programPath = Path.Join(testInstance.Path, "Program.cs"); + File.WriteAllText(programPath, s_program); + + var globalArtifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath); + if (Directory.Exists(globalArtifactsDir)) Directory.Delete(globalArtifactsDir, recursive: true); + + new DirectoryInfo(Path.Join(testInstance.Path, "artifacts")).Should().NotExist(); + new DirectoryInfo(Path.Join(testInstance.Path, "bin")).Should().NotExist(); + + new DotnetCommand(Log, "build", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + new DirectoryInfo(globalArtifactsDir).EnumerateDirectories().Should().NotBeEmpty(); + new DirectoryInfo(Path.Join(testInstance.Path, "artifacts")).Should().NotExist(); + new DirectoryInfo(Path.Join(testInstance.Path, "bin")).Should().NotExist(); + } + + /// + /// When the surrounding repo uses artifacts layout, file-based apps place their artifacts there. + /// + [Fact] + public void ArtifactsPath_ReusedFromRepo() + { + var testInstance = _testAssetsManager.CreateTestDirectory(); + var programPath = Path.Join(testInstance.Path, "Program.cs"); + File.WriteAllText(programPath, s_program); + File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), """ + + + true + + + """); + + new DirectoryInfo(Path.Join(testInstance.Path, "artifacts")).Should().NotExist(); + + new DotnetCommand(Log, "build", "Program.cs") + .WithWorkingDirectory(testInstance.Path) + .Execute() + .Should().Pass(); + + // We still put our marker files into the global artifacts directory, but it should not contain any subdirectories. + new DirectoryInfo(VirtualProjectBuildingCommand.GetArtifactsPath(programPath)).EnumerateDirectories().Should().BeEmpty(); + new FileInfo(Path.Join(testInstance.Path, "artifacts", "bin", "Program", "debug", "Program.dll")).Should().Exist(); + } + [Fact] public void LaunchProfile() { @@ -1843,8 +1929,7 @@ public void Api() - false - /artifacts + /artifacts artifacts/$(MSBuildProjectName) @@ -1922,8 +2007,7 @@ public void Api_Diagnostic_01() - false - /artifacts + /artifacts artifacts/$(MSBuildProjectName) @@ -1994,8 +2078,7 @@ public void Api_Diagnostic_02() - false - /artifacts + /artifacts artifacts/$(MSBuildProjectName)