diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs
index 7fedc17f3878..cb8befe5fab2 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs
+++ b/src/Cli/Microsoft.DotNet.Cli.Utils/MSBuildArgs.cs
@@ -382,4 +382,12 @@ public void ApplyPropertiesToRestore()
RestoreGlobalProperties = new(newdict);
}
}
+
+ internal string[]? GetResolvedTargets()
+ => this switch
+ {
+ { RequestedTargets: null or { Length: 0 } } => GetTargetResult,
+ { GetTargetResult: null or { Length: 0 } } => RequestedTargets,
+ _ => [.. RequestedTargets.Union(GetTargetResult, StringComparer.OrdinalIgnoreCase)]
+ };
}
diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj b/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj
index 6504a731f36f..cdfa92ae8ce1 100644
--- a/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj
+++ b/src/Cli/Microsoft.TemplateEngine.Cli/Microsoft.TemplateEngine.Cli.csproj
@@ -20,9 +20,10 @@
-
-
-
+
+
+
+
diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/TelemetryHelper.cs b/src/Cli/Microsoft.TemplateEngine.Cli/TelemetryHelper.cs
index f52d1fc17e14..edf67f5b4802 100644
--- a/src/Cli/Microsoft.TemplateEngine.Cli/TelemetryHelper.cs
+++ b/src/Cli/Microsoft.TemplateEngine.Cli/TelemetryHelper.cs
@@ -1,7 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Utils;
diff --git a/src/Cli/Microsoft.TemplateEngine.Cli/TemplateInvoker.cs b/src/Cli/Microsoft.TemplateEngine.Cli/TemplateInvoker.cs
index a34f201e095c..f3e29f93befe 100644
--- a/src/Cli/Microsoft.TemplateEngine.Cli/TemplateInvoker.cs
+++ b/src/Cli/Microsoft.TemplateEngine.Cli/TemplateInvoker.cs
@@ -4,6 +4,7 @@
using System.Text.RegularExpressions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
+using Microsoft.DotNet.Utilities;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Abstractions.TemplatePackage;
using Microsoft.TemplateEngine.Cli.Commands;
diff --git a/src/Cli/dotnet/CommandFactory/CommandResolution/CompositeCommandResolver.cs b/src/Cli/dotnet/CommandFactory/CommandResolution/CompositeCommandResolver.cs
index 0ab407749a36..a0e54bcb7c80 100644
--- a/src/Cli/dotnet/CommandFactory/CommandResolution/CompositeCommandResolver.cs
+++ b/src/Cli/dotnet/CommandFactory/CommandResolution/CompositeCommandResolver.cs
@@ -4,6 +4,7 @@
#nullable disable
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.DotNet.Cli.CommandFactory.CommandResolution;
diff --git a/src/Cli/dotnet/Commands/Build/BuildCommand.cs b/src/Cli/dotnet/Commands/Build/BuildCommand.cs
index 9b6dabdb020a..8c67ed030f36 100644
--- a/src/Cli/dotnet/Commands/Build/BuildCommand.cs
+++ b/src/Cli/dotnet/Commands/Build/BuildCommand.cs
@@ -30,15 +30,14 @@ public static CommandBase FromParseResult(ParseResult parseResult, string? msbui
return CommandFactory.CreateVirtualOrPhysicalCommand(
BuildCommandParser.GetCommand(),
BuildCommandDefinition.SlnOrProjectOrFileArgument,
- (msbuildArgs, appFilePath) => new VirtualProjectBuildingCommand(
+ configureVirtualCommand: (msbuildArgs, appFilePath) => new VirtualProjectBuildingCommand(
entryPointFileFullPath: Path.GetFullPath(appFilePath),
- msbuildArgs: msbuildArgs
- )
+ msbuildArgs: msbuildArgs)
{
NoRestore = noRestore,
NoCache = true,
},
- (msbuildArgs, msbuildPath) => new RestoringCommand(
+ createPhysicalCommand: (msbuildArgs, msbuildPath) => new RestoringCommand(
msbuildArgs: msbuildArgs.CloneWithAdditionalArgs("-consoleloggerparameters:Summary"),
noRestore: noRestore,
msbuildPath: msbuildPath
diff --git a/src/Cli/dotnet/Commands/Clean/CleanCommand.cs b/src/Cli/dotnet/Commands/Clean/CleanCommand.cs
index 15a183a4da6d..e2074b7a7d58 100644
--- a/src/Cli/dotnet/Commands/Clean/CleanCommand.cs
+++ b/src/Cli/dotnet/Commands/Clean/CleanCommand.cs
@@ -24,8 +24,8 @@ public static CommandBase FromParseResult(ParseResult result, string? msbuildPat
CleanCommandParser.GetCommand(),
CleanCommandDefinition.SlnOrProjectOrFileArgument,
static (msbuildArgs, appFilePath) => new VirtualProjectBuildingCommand(
- entryPointFileFullPath: appFilePath,
- msbuildArgs: msbuildArgs)
+ entryPointFileFullPath: appFilePath,
+ msbuildArgs: msbuildArgs)
{
NoBuild = false,
NoRestore = true,
diff --git a/src/Cli/dotnet/Commands/Clean/FileBasedAppArtifacts/CleanFileBasedAppArtifactsCommand.cs b/src/Cli/dotnet/Commands/Clean/FileBasedAppArtifacts/CleanFileBasedAppArtifactsCommand.cs
index 6654ab3697b9..f449417b0340 100644
--- a/src/Cli/dotnet/Commands/Clean/FileBasedAppArtifacts/CleanFileBasedAppArtifactsCommand.cs
+++ b/src/Cli/dotnet/Commands/Clean/FileBasedAppArtifacts/CleanFileBasedAppArtifactsCommand.cs
@@ -7,6 +7,7 @@
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Commands.Clean.FileBasedAppArtifacts;
@@ -60,7 +61,7 @@ public override int Execute()
private IEnumerable GetFoldersToRemove()
{
- var directory = new DirectoryInfo(VirtualProjectBuildingCommand.GetTempSubdirectory());
+ var directory = new DirectoryInfo(VirtualProjectBuilder.GetTempSubdirectory());
if (!directory.Exists)
{
@@ -84,7 +85,7 @@ private IEnumerable GetFoldersToRemove()
private static FileInfo GetMetadataFile()
{
- return new FileInfo(VirtualProjectBuildingCommand.GetTempSubpath(RunFileArtifactsMetadata.FilePath));
+ return new FileInfo(VirtualProjectBuilder.GetTempSubpath(RunFileArtifactsMetadata.FilePath));
}
private FileStream? OpenMetadataFile()
diff --git a/src/Cli/dotnet/Commands/CommandFactory.cs b/src/Cli/dotnet/Commands/CommandFactory.cs
index 40bc500082b3..8d857d8383e8 100644
--- a/src/Cli/dotnet/Commands/CommandFactory.cs
+++ b/src/Cli/dotnet/Commands/CommandFactory.cs
@@ -5,6 +5,7 @@
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Commands;
@@ -23,7 +24,7 @@ internal static CommandBase CreateVirtualOrPhysicalCommand(
var args = parseResult.GetValue(catchAllUserInputArgument) ?? [];
LoggerUtility.SeparateBinLogArguments(args, out var binLogArgs, out var nonBinLogArgs);
var forwardedArgs = parseResult.OptionValuesToBeForwarded(command);
- if (nonBinLogArgs is [{ } arg] && VirtualProjectBuildingCommand.IsValidEntryPointPath(arg))
+ if (nonBinLogArgs is [{ } arg] && VirtualProjectBuilder.IsValidEntryPointPath(arg))
{
var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([.. forwardedArgs, .. binLogArgs],
[
diff --git a/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs b/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs
index af7e3df0c98e..057e00d4aa2d 100644
--- a/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs
+++ b/src/Cli/dotnet/Commands/Hidden/InternalReportInstallSuccess/InternalReportInstallSuccessCommand.cs
@@ -8,6 +8,7 @@
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.DotNet.Cli.Commands.Hidden.InternalReportInstallSuccess;
diff --git a/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs b/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs
index 265b1eafbb76..d50dfd57a071 100644
--- a/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs
+++ b/src/Cli/dotnet/Commands/MSBuild/MSBuildLogger.cs
@@ -6,6 +6,7 @@
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.DotNet.Cli.Commands.MSBuild;
diff --git a/src/Cli/dotnet/Commands/New/MSBuildEvaluation/MSBuildEvaluator.cs b/src/Cli/dotnet/Commands/New/MSBuildEvaluation/MSBuildEvaluator.cs
index a79c2ec2af7c..318f1ec64418 100644
--- a/src/Cli/dotnet/Commands/New/MSBuildEvaluation/MSBuildEvaluator.cs
+++ b/src/Cli/dotnet/Commands/New/MSBuildEvaluation/MSBuildEvaluator.cs
@@ -4,6 +4,7 @@
using System.Diagnostics;
using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
using Microsoft.Extensions.Logging;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Utils;
diff --git a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs
index c88d6cba80e7..97d1df80e369 100644
--- a/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs
+++ b/src/Cli/dotnet/Commands/Package/Add/PackageAddCommand.cs
@@ -6,13 +6,13 @@
using Microsoft.Build.Construction;
using Microsoft.Build.Evaluation;
using Microsoft.CodeAnalysis;
+using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Commands.MSBuild;
using Microsoft.DotNet.Cli.Commands.NuGet;
using Microsoft.DotNet.Cli.Commands.Run;
-using Microsoft.DotNet.Cli.CommandLine;
-using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
using NuGet.ProjectModel;
namespace Microsoft.DotNet.Cli.Commands.Package.Add;
@@ -25,7 +25,7 @@ public override int Execute()
{
var (fileOrDirectory, allowedAppKinds) = PackageCommandDefinition.ProcessPathOptions(_parseResult);
- if (allowedAppKinds.HasFlag(AppKinds.FileBased) && VirtualProjectBuildingCommand.IsValidEntryPointPath(fileOrDirectory))
+ if (allowedAppKinds.HasFlag(AppKinds.FileBased) && VirtualProjectBuilder.IsValidEntryPointPath(fileOrDirectory))
{
return ExecuteForFileBasedApp(fileOrDirectory);
}
@@ -192,6 +192,7 @@ private int ExecuteForFileBasedApp(string path)
NoCache = true,
NoBuild = true,
};
+
var projectCollection = new ProjectCollection();
var projectInstance = command.CreateProjectInstance(projectCollection);
diff --git a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs
index c38cdfc91a11..8abbb1cc8f4b 100644
--- a/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs
+++ b/src/Cli/dotnet/Commands/Package/Remove/PackageRemoveCommand.cs
@@ -8,6 +8,7 @@
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Commands.Package.Remove;
@@ -24,7 +25,7 @@ public override int Execute()
var (fileOrDirectory, allowedAppKinds) = PackageCommandDefinition.ProcessPathOptions(_parseResult);
- if (allowedAppKinds.HasFlag(AppKinds.FileBased) && VirtualProjectBuildingCommand.IsValidEntryPointPath(fileOrDirectory))
+ if (allowedAppKinds.HasFlag(AppKinds.FileBased) && VirtualProjectBuilder.IsValidEntryPointPath(fileOrDirectory))
{
return ExecuteForFileBasedApp(path: fileOrDirectory, packageId: packageToRemove);
}
diff --git a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
index 46c52d650bd7..ee9a5e1d77a5 100644
--- a/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
+++ b/src/Cli/dotnet/Commands/Project/Convert/ProjectConvertCommand.cs
@@ -9,6 +9,7 @@
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
using Microsoft.TemplateEngine.Cli.Commands;
namespace Microsoft.DotNet.Cli.Commands.Project.Convert;
@@ -23,31 +24,24 @@ public override int Execute()
{
// Check the entry point file path.
string file = Path.GetFullPath(_file);
- if (!VirtualProjectBuildingCommand.IsValidEntryPointPath(file))
+ if (!VirtualProjectBuilder.IsValidEntryPointPath(file))
{
throw new GracefulException(CliCommandStrings.InvalidFilePath, file);
}
string targetDirectory = DetermineOutputDirectory(file);
- // Find directives (this can fail, so do this before creating the target directory).
- var sourceFile = SourceFile.Load(file);
- var directives = FileLevelDirectiveHelpers.FindDirectives(sourceFile, reportAllErrors: !_force, VirtualProjectBuildingCommand.ThrowingReporter);
-
// Create a project instance for evaluation.
var projectCollection = new ProjectCollection();
- var command = new VirtualProjectBuildingCommand(
- entryPointFileFullPath: file,
- msbuildArgs: MSBuildArgs.FromOtherArgs([]))
- {
- Directives = directives,
- };
- var projectInstance = command.CreateProjectInstance(projectCollection);
- // Evaluate directives.
- directives = VirtualProjectBuildingCommand.EvaluateDirectives(projectInstance, directives, sourceFile, VirtualProjectBuildingCommand.ThrowingReporter);
- command.Directives = directives;
- projectInstance = command.CreateProjectInstance(projectCollection);
+ var builder = new VirtualProjectBuilder(file, VirtualProjectBuildingCommand.TargetFrameworkVersion);
+
+ builder.CreateProjectInstance(
+ projectCollection,
+ VirtualProjectBuildingCommand.ThrowingReporter,
+ out var projectInstance,
+ out var evaluatedDirectives,
+ validateAllDirectives: !_force);
// Find other items to copy over, e.g., default Content items like JSON files in Web apps.
var includeItems = FindIncludedItems().ToList();
@@ -66,7 +60,7 @@ public override int Execute()
}
else
{
- VirtualProjectBuildingCommand.RemoveDirectivesFromFile(directives, sourceFile.Text, targetFile);
+ VirtualProjectBuilder.RemoveDirectivesFromFile(evaluatedDirectives, builder.EntryPointSourceFile.Text, targetFile);
}
// Create project file.
@@ -79,9 +73,9 @@ public override int Execute()
{
using var stream = File.Open(projectFile, FileMode.Create, FileAccess.Write);
using var writer = new StreamWriter(stream, Encoding.UTF8);
- VirtualProjectBuildingCommand.WriteProjectFile(writer, UpdateDirectives(directives), isVirtualProject: false,
+ VirtualProjectBuilder.WriteProjectFile(writer, UpdateDirectives(evaluatedDirectives), isVirtualProject: false,
userSecretsId: DetermineUserSecretsId(),
- excludeDefaultProperties: FindDefaultPropertiesToExclude());
+ defaultProperties: GetDefaultProperties());
}
// Copy or move over included items.
@@ -230,14 +224,14 @@ ImmutableArray UpdateDirectives(ImmutableArray
return result.DrainToImmutable();
}
- IEnumerable FindDefaultPropertiesToExclude()
+ IEnumerable<(string name, string value)> GetDefaultProperties()
{
- foreach (var (name, defaultValue) in VirtualProjectBuildingCommand.DefaultProperties)
+ foreach (var (name, defaultValue) in VirtualProjectBuilder.GetDefaultProperties(VirtualProjectBuildingCommand.TargetFrameworkVersion))
{
string projectValue = projectInstance.GetPropertyValue(name);
- if (!string.Equals(projectValue, defaultValue, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(projectValue, defaultValue, StringComparison.OrdinalIgnoreCase))
{
- yield return name;
+ yield return (name, defaultValue);
}
}
}
diff --git a/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs b/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs
index 68396f2951b6..1a3cb3645bfa 100644
--- a/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs
+++ b/src/Cli/dotnet/Commands/Run/Api/RunApiCommand.cs
@@ -9,6 +9,7 @@
using System.Text.Json.Serialization;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Commands.Run.Api;
@@ -66,10 +67,16 @@ public override RunApiOutput Execute()
{
var sourceFile = SourceFile.Load(EntryPointFileFullPath);
var directives = FileLevelDirectiveHelpers.FindDirectives(sourceFile, reportAllErrors: true, ErrorReporters.CreateCollectingReporter(out var diagnostics));
- string artifactsPath = ArtifactsPath ?? VirtualProjectBuildingCommand.GetArtifactsPath(EntryPointFileFullPath);
+ string artifactsPath = ArtifactsPath ?? VirtualProjectBuilder.GetArtifactsPath(EntryPointFileFullPath);
var csprojWriter = new StringWriter();
- VirtualProjectBuildingCommand.WriteProjectFile(csprojWriter, directives, isVirtualProject: true, targetFilePath: EntryPointFileFullPath, artifactsPath: artifactsPath);
+ VirtualProjectBuilder.WriteProjectFile(
+ csprojWriter,
+ directives,
+ VirtualProjectBuilder.GetDefaultProperties(VirtualProjectBuildingCommand.TargetFrameworkVersion),
+ isVirtualProject: true,
+ targetFilePath: EntryPointFileFullPath,
+ artifactsPath: artifactsPath);
return new RunApiOutput.Project
{
@@ -88,11 +95,8 @@ public override RunApiOutput Execute()
{
var msbuildArgs = MSBuildArgs.FromVerbosity(VerbosityOptions.quiet);
var buildCommand = new VirtualProjectBuildingCommand(
- entryPointFileFullPath: EntryPointFileFullPath,
- msbuildArgs: msbuildArgs)
- {
- CustomArtifactsPath = ArtifactsPath,
- };
+ EntryPointFileFullPath, msbuildArgs, artifactsPath: ArtifactsPath);
+
buildCommand.MarkArtifactsFolderUsed();
var runCommand = new RunCommand(
diff --git a/src/Cli/dotnet/Commands/Run/RunCommand.cs b/src/Cli/dotnet/Commands/Run/RunCommand.cs
index 9351e2e7538c..4a0b48ddb7df 100644
--- a/src/Cli/dotnet/Commands/Run/RunCommand.cs
+++ b/src/Cli/dotnet/Commands/Run/RunCommand.cs
@@ -19,6 +19,7 @@
using Microsoft.DotNet.Cli.Utils.Extensions;
using Microsoft.DotNet.FileBasedPrograms;
using Microsoft.DotNet.ProjectTools;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.DotNet.Cli.Commands.Run;
@@ -545,7 +546,7 @@ static void SetRootVariableName(ICommand command, string runtimeIdentifier, stri
static ICommand CreateCommandForCscBuiltProgram(string entryPointFileFullPath, string[] args)
{
- var artifactsPath = VirtualProjectBuildingCommand.GetArtifactsPath(entryPointFileFullPath);
+ var artifactsPath = VirtualProjectBuilder.GetArtifactsPath(entryPointFileFullPath);
var exePath = Path.Join(artifactsPath, "bin", "debug", Path.GetFileNameWithoutExtension(entryPointFileFullPath) + FileNameSuffixes.CurrentPlatform.Exe);
var commandSpec = new CommandSpec(path: exePath, args: ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args));
var command = CommandFactoryUsingResolver.Create(commandSpec);
@@ -664,7 +665,7 @@ internal static void ThrowUnableToRunError(ProjectInstance project)
if (!readCodeFromStdin)
{
- if (VirtualProjectBuildingCommand.IsValidEntryPointPath(arg))
+ if (VirtualProjectBuilder.IsValidEntryPointPath(arg))
{
arg = Path.GetFullPath(arg);
}
@@ -749,7 +750,7 @@ public static RunCommand FromParseResult(ParseResult parseResult)
// If '-' is specified as the input file, read all text from stdin into a temporary file and use that as the entry point.
// We create a new directory for each file so other files are not included in the compilation.
// We fail if the file already exists to avoid reusing the same file for multiple stdin runs (in case the random name is duplicate).
- string directory = VirtualProjectBuildingCommand.GetTempSubpath(Path.GetRandomFileName());
+ string directory = VirtualProjectBuilder.GetTempSubpath(Path.GetRandomFileName());
VirtualProjectBuildingCommand.CreateTempSubdirectory(directory);
entryPointFilePath = Path.Join(directory, "app.cs");
using (var stdinStream = Console.OpenStandardInput())
diff --git a/src/Cli/dotnet/Commands/Run/RunTelemetry.cs b/src/Cli/dotnet/Commands/Run/RunTelemetry.cs
index fd45de6a6a63..0d3ab62507b8 100644
--- a/src/Cli/dotnet/Commands/Run/RunTelemetry.cs
+++ b/src/Cli/dotnet/Commands/Run/RunTelemetry.cs
@@ -6,6 +6,7 @@
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
using Microsoft.DotNet.ProjectTools;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.DotNet.Cli.Commands.Run;
diff --git a/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs b/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs
index 68a2665b070e..a2e434a6927a 100644
--- a/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs
+++ b/src/Cli/dotnet/Commands/Run/VirtualProjectBuildingCommand.cs
@@ -1,28 +1,23 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-using System.Collections.Frozen;
using System.Collections.Immutable;
using System.Collections.ObjectModel;
using System.Diagnostics;
-using System.Security;
using System.Text.Json;
using System.Text.Json.Serialization;
-using System.Xml;
-using Microsoft.Build.Construction;
-using Microsoft.Build.Definition;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Framework;
using Microsoft.Build.Logging;
using Microsoft.Build.Logging.SimpleErrorLogger;
using Microsoft.CodeAnalysis;
-using Microsoft.CodeAnalysis.Text;
using Microsoft.DotNet.Cli.Commands.Clean.FileBasedAppArtifacts;
using Microsoft.DotNet.Cli.Commands.Restore;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Commands.Run;
@@ -69,19 +64,6 @@ internal sealed class VirtualProjectBuildingCommand : CommandBase
("MSBuild.rsp", true),
];
- ///
- /// Kept in sync with the default dotnet new console project file (enforced by DotnetProjectAddTests.SameAsTemplate).
- ///
- public static readonly FrozenDictionary DefaultProperties = FrozenDictionary.Create(StringComparer.OrdinalIgnoreCase,
- [
- new("OutputType", "Exe"),
- new("TargetFramework", $"net{TargetFrameworkVersion}"),
- new("ImplicitUsings", "enable"),
- new("Nullable", "enable"),
- new("PublishAot", "true"),
- new("PackAsTool", "true"),
- ]);
-
///
/// For purposes of determining whether CSC is enough to build as opposed to full MSBuild,
/// we can ignore properties that do not affect the build on their own.
@@ -101,43 +83,6 @@ internal sealed class VirtualProjectBuildingCommand : CommandBase
public static string TargetFrameworkVersion => Product.TargetFrameworkVersion;
- public VirtualProjectBuildingCommand(
- string entryPointFileFullPath,
- MSBuildArgs msbuildArgs)
- {
- Debug.Assert(Path.IsPathFullyQualified(entryPointFileFullPath));
-
- EntryPointFileFullPath = entryPointFileFullPath;
- MSBuildArgs = msbuildArgs.CloneWithAdditionalProperties(new Dictionary(StringComparer.OrdinalIgnoreCase)
- {
- // See https://github.com/dotnet/msbuild/blob/main/documentation/specs/build-nonexistent-projects-by-default.md.
- { "_BuildNonexistentProjectsByDefault", bool.TrueString },
- { "RestoreUseSkipNonexistentTargets", bool.FalseString },
- { "ProvideCommandLineArgs", bool.TrueString },
- }
- .AsReadOnly());
-
- if (MSBuildArgs.RequestedTargets is null or [])
- {
- RequestedTargets = MSBuildArgs.GetTargetResult;
- }
- else if (MSBuildArgs.GetTargetResult is null or [])
- {
- RequestedTargets = MSBuildArgs.RequestedTargets;
- }
- else
- {
- RequestedTargets = MSBuildArgs.RequestedTargets
- .Union(MSBuildArgs.GetTargetResult, StringComparer.OrdinalIgnoreCase)
- .ToArray();
- }
- }
-
- public string EntryPointFileFullPath { get; }
- public MSBuildArgs MSBuildArgs { get; }
- private string[]? RequestedTargets { get; }
- public string? CustomArtifactsPath { get; init; }
- public string ArtifactsPath => field ??= CustomArtifactsPath ?? GetArtifactsPath(EntryPointFileFullPath);
public bool NoRestore { get; init; }
///
@@ -161,18 +106,8 @@ public VirtualProjectBuildingCommand(
///
public bool NoWriteBuildMarkers { get; init; }
- private SourceFile EntryPointSourceFile
- {
- get
- {
- if (field == default)
- {
- field = SourceFile.Load(EntryPointFileFullPath);
- }
-
- return field;
- }
- }
+ public VirtualProjectBuilder Builder { get; }
+ public MSBuildArgs MSBuildArgs { get; }
public ImmutableArray Directives
{
@@ -180,7 +115,7 @@ public ImmutableArray Directives
{
if (field.IsDefault)
{
- field = FileLevelDirectiveHelpers.FindDirectives(EntryPointSourceFile, reportAllErrors: false, VirtualProjectBuildingCommand.ThrowingReporter);
+ field = FileLevelDirectiveHelpers.FindDirectives(Builder.EntryPointSourceFile, reportAllErrors: false, ThrowingReporter);
Debug.Assert(!field.IsDefault);
}
@@ -190,10 +125,27 @@ public ImmutableArray Directives
set;
}
+ public VirtualProjectBuildingCommand(
+ string entryPointFileFullPath,
+ MSBuildArgs msbuildArgs,
+ string? artifactsPath = null)
+ {
+ MSBuildArgs = msbuildArgs.CloneWithAdditionalProperties(new Dictionary(StringComparer.OrdinalIgnoreCase)
+ {
+ // See https://github.com/dotnet/msbuild/blob/main/documentation/specs/build-nonexistent-projects-by-default.md.
+ { "_BuildNonexistentProjectsByDefault", bool.TrueString },
+ { "RestoreUseSkipNonexistentTargets", bool.FalseString },
+ { "ProvideCommandLineArgs", bool.TrueString },
+ }
+ .AsReadOnly());
+
+ Builder = new VirtualProjectBuilder(entryPointFileFullPath, TargetFrameworkVersion, MSBuildArgs.GetResolvedTargets(), artifactsPath);
+ }
+
public override int Execute()
{
bool msbuildGet = MSBuildArgs.GetProperty is [_, ..] || MSBuildArgs.GetItem is [_, ..] || MSBuildArgs.GetTargetResult is [_, ..];
- bool evalOnly = msbuildGet && RequestedTargets is null or [];
+ bool evalOnly = msbuildGet && Builder.RequestedTargets is null or [];
bool minimizeStdOut = msbuildGet && MSBuildArgs.GetResultOutputFile is null or [];
var verbosity = MSBuildArgs.Verbosity ?? MSBuildForwardingAppWithoutLogging.DefaultVerbosity;
@@ -218,7 +170,7 @@ public override int Execute()
if (!NoWriteBuildMarkers)
{
- CreateTempSubdirectory(ArtifactsPath);
+ CreateTempSubdirectory(Builder.ArtifactsPath);
MarkArtifactsFolderUsed();
}
}
@@ -262,8 +214,8 @@ public override int Execute()
// Execute CSC.
int result = new CSharpCompilerCommand
{
- EntryPointFileFullPath = EntryPointFileFullPath,
- ArtifactsPath = ArtifactsPath,
+ EntryPointFileFullPath = Builder.EntryPointFileFullPath,
+ ArtifactsPath = Builder.ArtifactsPath,
CanReuseAuxiliaryFiles = cache.DetermineFinalCanReuseAuxiliaryFiles(),
CscArguments = cache.PreviousEntry?.CscArguments ?? [],
BuildResultFile = cache.PreviousEntry?.BuildResultFile,
@@ -349,7 +301,7 @@ public override int Execute()
{
var buildRequest = new BuildRequestData(
CreateProjectInstance(projectCollection),
- targetsToBuild: RequestedTargets ?? [Constants.Build, Constants.CoreCompile]);
+ targetsToBuild: Builder.RequestedTargets ?? [Constants.Build, Constants.CoreCompile]);
var buildResult = BuildManager.DefaultBuildManager.BuildRequest(buildRequest);
if (buildResult.OverallResult != BuildResultCode.Success)
@@ -716,7 +668,7 @@ private CacheInfo ComputeCacheEntry()
RuntimeVersion = CSharpCompilerCommand.RuntimeVersion,
};
- var entryPointFile = new FileInfo(EntryPointFileFullPath);
+ var entryPointFile = new FileInfo(Builder.EntryPointFileFullPath);
// Collect current implicit build files.
CollectImplicitBuildFiles(entryPointFile.Directory, cacheEntry.ImplicitBuildFiles, out var exampleMSBuildFile);
@@ -764,7 +716,7 @@ private bool NeedsToBuild(out CacheInfo cache)
// Check cache files.
- string artifactsDirectory = ArtifactsPath;
+ string artifactsDirectory = Builder.ArtifactsPath;
var successCacheFile = new FileInfo(Path.Join(artifactsDirectory, BuildSuccessCacheFileName));
if (!successCacheFile.Exists)
@@ -911,7 +863,7 @@ static FileSystemInfo ResolveLinkTargetOrSelf(FileSystemInfo fileSystemInfo)
public RunFileBuildCacheEntry? GetPreviousCacheEntry()
{
- return DeserializeCacheEntry(Path.Join(ArtifactsPath, BuildSuccessCacheFileName));
+ return DeserializeCacheEntry(Path.Join(Builder.ArtifactsPath, BuildSuccessCacheFileName));
}
private void EnsurePreviousCacheEntry(CacheInfo cache)
@@ -927,7 +879,7 @@ private BuildLevel GetBuildLevel(out CacheInfo cache)
{
if (!NeedsToBuild(out cache))
{
- Reporter.Verbose.WriteLine("No need to build, the output is up to date. Cache: " + ArtifactsPath);
+ Reporter.Verbose.WriteLine("No need to build, the output is up to date. Cache: " + Builder.ArtifactsPath);
return BuildLevel.None;
}
@@ -1027,7 +979,7 @@ public void MarkArtifactsFolderUsed()
return;
}
- string directory = ArtifactsPath;
+ string directory = Builder.ArtifactsPath;
try
{
@@ -1046,13 +998,13 @@ private void MarkBuildStart()
return;
}
- string directory = ArtifactsPath;
+ string directory = Builder.ArtifactsPath;
CreateTempSubdirectory(directory);
MarkArtifactsFolderUsed();
- File.WriteAllText(Path.Join(directory, BuildStartCacheFileName), EntryPointFileFullPath);
+ File.WriteAllText(Path.Join(directory, BuildStartCacheFileName), Builder.EntryPointFileFullPath);
}
private void MarkBuildSuccess(CacheInfo cache)
@@ -1062,133 +1014,31 @@ private void MarkBuildSuccess(CacheInfo cache)
return;
}
- string successCacheFile = Path.Join(ArtifactsPath, BuildSuccessCacheFileName);
+ string successCacheFile = Path.Join(Builder.ArtifactsPath, BuildSuccessCacheFileName);
using var stream = File.Open(successCacheFile, FileMode.Create, FileAccess.Write, FileShare.None);
JsonSerializer.Serialize(stream, cache.CurrentEntry, RunFileJsonSerializerContext.Default.RunFileBuildCacheEntry);
}
- ///
- /// If there are any #:project , expands $() in them and ensures they point to project files (not directories).
- ///
- public static ImmutableArray EvaluateDirectives(
- ProjectInstance? project,
- ImmutableArray directives,
- SourceFile sourceFile,
- ErrorReporter errorReporter)
- {
- if (directives.OfType().Any())
- {
- return directives
- .Select(d => d is CSharpDirective.Project p
- ? (project is null
- ? p
- : p.WithName(project.ExpandString(p.Name), CSharpDirective.Project.NameKind.Expanded))
- .EnsureProjectFilePath(sourceFile, errorReporter)
- : d)
- .ToImmutableArray();
- }
-
- return directives;
- }
-
public ProjectInstance CreateProjectInstance(ProjectCollection projectCollection)
{
return CreateProjectInstance(projectCollection, addGlobalProperties: null);
}
- private ProjectInstance CreateProjectInstance(
- ProjectCollection projectCollection,
- Action>? addGlobalProperties)
+ public ProjectInstance CreateProjectInstance(ProjectCollection projectCollection, Action>? addGlobalProperties)
{
- var project = CreateProjectInstance(projectCollection, Directives, addGlobalProperties);
+ Builder.CreateProjectInstance(
+ projectCollection,
+ ThrowingReporter,
+ out var project,
+ out var evaluatedDirectives,
+ Directives,
+ addGlobalProperties);
- var directives = EvaluateDirectives(project, Directives, EntryPointSourceFile, VirtualProjectBuildingCommand.ThrowingReporter);
- if (directives != Directives)
- {
- Directives = directives;
- project = CreateProjectInstance(projectCollection, directives, addGlobalProperties);
- }
+ Directives = evaluatedDirectives;
return project;
}
- private ProjectInstance CreateProjectInstance(
- ProjectCollection projectCollection,
- ImmutableArray directives,
- Action>? addGlobalProperties)
- {
- var projectRoot = CreateProjectRootElement(projectCollection);
-
- var globalProperties = projectCollection.GlobalProperties;
- if (addGlobalProperties is not null)
- {
- globalProperties = new Dictionary(projectCollection.GlobalProperties, StringComparer.OrdinalIgnoreCase);
- addGlobalProperties(globalProperties);
- }
-
- return ProjectInstance.FromProjectRootElement(projectRoot, new ProjectOptions
- {
- ProjectCollection = projectCollection,
- GlobalProperties = globalProperties,
- });
-
- ProjectRootElement CreateProjectRootElement(ProjectCollection projectCollection)
- {
- var projectFileFullPath = Path.ChangeExtension(EntryPointFileFullPath, ".csproj");
- var projectFileWriter = new StringWriter();
- WriteProjectFile(
- projectFileWriter,
- directives,
- isVirtualProject: true,
- targetFilePath: EntryPointFileFullPath,
- artifactsPath: ArtifactsPath,
- includeRuntimeConfigInformation: RequestedTargets?.ContainsAny("Publish", "Pack") != true);
- var projectFileText = projectFileWriter.ToString();
-
- using var reader = new StringReader(projectFileText);
- using var xmlReader = XmlReader.Create(reader);
- var projectRoot = ProjectRootElement.Create(xmlReader, projectCollection);
- projectRoot.FullPath = projectFileFullPath;
- return projectRoot;
- }
- }
-
- public static string GetArtifactsPath(string entryPointFileFullPath)
- {
- // Include entry point file name so the directory name is not completely opaque.
- string fileName = Path.GetFileNameWithoutExtension(entryPointFileFullPath);
- string hash = Sha256Hasher.HashWithNormalizedCasing(entryPointFileFullPath);
- string directoryName = $"{fileName}-{hash}";
-
- return GetTempSubpath(directoryName);
- }
-
- ///
- /// Obtains a temporary subdirectory for file-based app artifacts, e.g., /tmp/dotnet/runfile/.
- ///
- public static string GetTempSubdirectory()
- {
- // We want a location where permissions are expected to be restricted to the current user.
- string directory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
- ? Path.GetTempPath()
- : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
-
- if (string.IsNullOrEmpty(directory))
- {
- throw new InvalidOperationException(FileBasedProgramsResources.EmptyTempPath);
- }
-
- return Path.Join(directory, "dotnet", "runfile");
- }
-
- ///
- /// Obtains a specific temporary path in a subdirectory for file-based app artifacts, e.g., /tmp/dotnet/runfile/{name}.
- ///
- public static string GetTempSubpath(string name)
- {
- return Path.Join(GetTempSubdirectory(), name);
- }
-
///
/// Creates a temporary subdirectory for file-based apps.
/// Use to obtain the path.
@@ -1208,369 +1058,6 @@ public static void CreateTempSubdirectory(string path)
}
}
- public static void WriteProjectFile(
- TextWriter writer,
- ImmutableArray directives,
- bool isVirtualProject,
- string? targetFilePath = null,
- string? artifactsPath = null,
- bool includeRuntimeConfigInformation = true,
- string? userSecretsId = null,
- IEnumerable? excludeDefaultProperties = null)
- {
- Debug.Assert(userSecretsId == null || !isVirtualProject);
- Debug.Assert(excludeDefaultProperties == null || !isVirtualProject);
-
- int processedDirectives = 0;
-
- var sdkDirectives = directives.OfType();
- var propertyDirectives = directives.OfType();
- var packageDirectives = directives.OfType();
- var projectDirectives = directives.OfType();
-
- const string defaultSdkName = "Microsoft.NET.Sdk";
- string firstSdkName;
- string? firstSdkVersion;
-
- if (sdkDirectives.FirstOrDefault() is { } firstSdk)
- {
- firstSdkName = firstSdk.Name;
- firstSdkVersion = firstSdk.Version;
- processedDirectives++;
- }
- else
- {
- firstSdkName = defaultSdkName;
- firstSdkVersion = null;
- }
-
- if (isVirtualProject)
- {
- Debug.Assert(!string.IsNullOrWhiteSpace(artifactsPath));
-
- // Note that ArtifactsPath needs to be specified before Sdk.props
- // (usually it's recommended to specify it in Directory.Build.props
- // but importing Sdk.props manually afterwards also works).
- writer.WriteLine($"""
-
-
-
- false
- {EscapeValue(artifactsPath)}
- artifacts/$(MSBuildProjectName)
- artifacts/$(MSBuildProjectName)
- true
- false
- true
- """);
-
- // Only set these to false when using the default SDK with no additional SDKs
- // to avoid including .resx and other files that are typically not expected in simple file-based apps.
- // When other SDKs are used (e.g., Microsoft.NET.Sdk.Web), keep the default behavior.
- bool usingOnlyDefaultSdk = firstSdkName == defaultSdkName && sdkDirectives.Count() <= 1;
- if (usingOnlyDefaultSdk)
- {
- writer.WriteLine($"""
- false
- false
- """);
- }
-
- // Write default properties before importing SDKs so they can be overridden by SDKs
- // (and implicit build files which are imported by the default .NET SDK).
- foreach (var (name, value) in DefaultProperties)
- {
- writer.WriteLine($"""
- <{name}>{EscapeValue(value)}{name}>
- """);
- }
-
- writer.WriteLine($"""
-
-
-
-
-
-
- """);
-
- if (firstSdkVersion is null)
- {
- writer.WriteLine($"""
-
- """);
- }
- else
- {
- writer.WriteLine($"""
-
- """);
- }
- }
- else
- {
- string slashDelimited = firstSdkVersion is null
- ? firstSdkName
- : $"{firstSdkName}/{firstSdkVersion}";
- writer.WriteLine($"""
-
-
- """);
- }
-
- foreach (var sdk in sdkDirectives.Skip(1))
- {
- if (isVirtualProject)
- {
- WriteImport(writer, "Sdk.props", sdk);
- }
- else if (sdk.Version is null)
- {
- writer.WriteLine($"""
-
- """);
- }
- else
- {
- writer.WriteLine($"""
-
- """);
- }
-
- processedDirectives++;
- }
-
- if (isVirtualProject || processedDirectives > 1)
- {
- writer.WriteLine();
- }
-
- // Write default and custom properties.
- {
- writer.WriteLine("""
-
- """);
-
- // First write the default properties except those specified by the user.
- if (!isVirtualProject)
- {
- var customPropertyNames = propertyDirectives
- .Select(static d => d.Name)
- .Concat(excludeDefaultProperties ?? [])
- .ToHashSet(StringComparer.OrdinalIgnoreCase);
- foreach (var (name, value) in DefaultProperties)
- {
- if (!customPropertyNames.Contains(name))
- {
- writer.WriteLine($"""
- <{name}>{EscapeValue(value)}{name}>
- """);
- }
- }
-
- if (userSecretsId != null && !customPropertyNames.Contains("UserSecretsId"))
- {
- writer.WriteLine($"""
- {EscapeValue(userSecretsId)}
- """);
- }
- }
-
- // Write custom properties.
- foreach (var property in propertyDirectives)
- {
- writer.WriteLine($"""
- <{property.Name}>{EscapeValue(property.Value)}{property.Name}>
- """);
-
- processedDirectives++;
- }
-
- // Write virtual-only properties which cannot be overridden.
- if (isVirtualProject)
- {
- writer.WriteLine("""
- false
- $(Features);FileBasedProgram
- """);
- }
-
- writer.WriteLine("""
-
-
- """);
- }
-
- if (packageDirectives.Any())
- {
- writer.WriteLine("""
-
- """);
-
- foreach (var package in packageDirectives)
- {
- if (package.Version is null)
- {
- writer.WriteLine($"""
-
- """);
- }
- else
- {
- writer.WriteLine($"""
-
- """);
- }
-
- processedDirectives++;
- }
-
- writer.WriteLine("""
-
-
- """);
- }
-
- if (projectDirectives.Any())
- {
- writer.WriteLine("""
-
- """);
-
- foreach (var projectReference in projectDirectives)
- {
- writer.WriteLine($"""
-
- """);
-
- processedDirectives++;
- }
-
- writer.WriteLine("""
-
-
- """);
- }
-
- Debug.Assert(processedDirectives + directives.OfType().Count() == directives.Length);
-
- if (isVirtualProject)
- {
- Debug.Assert(targetFilePath is not null);
-
- // Only add explicit Compile item when EnableDefaultCompileItems is not true.
- // When EnableDefaultCompileItems=true, the file is included via default MSBuild globbing.
- // See https://github.com/dotnet/sdk/issues/51785
- writer.WriteLine($"""
-
-
-
-
- """);
-
- if (includeRuntimeConfigInformation)
- {
- var targetDirectory = Path.GetDirectoryName(targetFilePath) ?? "";
- writer.WriteLine($"""
-
-
-
-
-
- """);
- }
-
- foreach (var sdk in sdkDirectives)
- {
- WriteImport(writer, "Sdk.targets", sdk);
- }
-
- if (!sdkDirectives.Any())
- {
- Debug.Assert(firstSdkName == defaultSdkName && firstSdkVersion == null);
- writer.WriteLine($"""
-
- """);
- }
-
- writer.WriteLine();
- }
-
- writer.WriteLine("""
-
- """);
-
- static string EscapeValue(string value) => SecurityElement.Escape(value);
-
- static void WriteImport(TextWriter writer, string project, CSharpDirective.Sdk sdk)
- {
- if (sdk.Version is null)
- {
- writer.WriteLine($"""
-
- """);
- }
- else
- {
- writer.WriteLine($"""
-
- """);
- }
- }
- }
-
- public static SourceText? RemoveDirectivesFromFile(ImmutableArray directives, SourceText text)
- {
- if (directives.Length == 0)
- {
- return null;
- }
-
- Debug.Assert(directives.OrderBy(d => d.Info.Span.Start).SequenceEqual(directives), "Directives should be ordered by source location.");
-
- for (int i = directives.Length - 1; i >= 0; i--)
- {
- var directive = directives[i];
- text = text.Replace(directive.Info.Span, string.Empty);
- }
-
- return text;
- }
-
- public static void RemoveDirectivesFromFile(ImmutableArray directives, SourceText text, string filePath)
- {
- if (RemoveDirectivesFromFile(directives, text) is { } modifiedText)
- {
- new SourceFile(filePath, modifiedText).Save();
- }
- }
-
- public static bool IsValidEntryPointPath(string entryPointFilePath)
- {
- if (!File.Exists(entryPointFilePath))
- {
- return false;
- }
-
- if (entryPointFilePath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
- {
- return true;
- }
-
- // Check if the first two characters are #!
- try
- {
- using var stream = File.OpenRead(entryPointFilePath);
- int first = stream.ReadByte();
- int second = stream.ReadByte();
- return first == '#' && second == '!';
- }
- catch
- {
- return false;
- }
- }
-
public static readonly ErrorReporter ThrowingReporter =
static (sourceFile, textSpan, message) => throw new GracefulException($"{sourceFile.GetLocationString(textSpan)}: {FileBasedProgramsResources.DirectiveError}: {message}");
}
diff --git a/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs b/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs
index 906305d373cf..5a471cc8b1ef 100644
--- a/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs
+++ b/src/Cli/dotnet/Installer/Windows/WindowsUtils.cs
@@ -7,6 +7,7 @@
using System.Security.Principal;
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
using Microsoft.Win32;
namespace Microsoft.DotNet.Cli.Installer.Windows;
diff --git a/src/Cli/dotnet/Program.cs b/src/Cli/dotnet/Program.cs
index 077d46aa615a..4f5e8e663e2f 100644
--- a/src/Cli/dotnet/Program.cs
+++ b/src/Cli/dotnet/Program.cs
@@ -18,6 +18,8 @@
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Cli.Utils.Extensions;
using Microsoft.DotNet.Configurer;
+using Microsoft.DotNet.ProjectTools;
+using Microsoft.DotNet.Utilities;
using Microsoft.Extensions.EnvironmentAbstractions;
using NuGet.Frameworks;
using CommandResult = System.CommandLine.Parsing.CommandResult;
@@ -307,7 +309,7 @@ internal static int ProcessArgs(string[] args, TimeSpan startupTime)
// If we didn't match any built-in commands, and a C# file path is the first argument,
// parse as `dotnet run --file file.cs ..rest_of_args` instead.
if (parseResult.GetResult(Parser.DotnetSubCommand) is { Tokens: [{ Type: TokenType.Argument, Value: { } } unmatchedCommandOrFile] }
- && VirtualProjectBuildingCommand.IsValidEntryPointPath(unmatchedCommandOrFile.Value))
+ && VirtualProjectBuilder.IsValidEntryPointPath(unmatchedCommandOrFile.Value))
{
List otherTokens = new(parseResult.Tokens.Count - 1);
foreach (var token in parseResult.Tokens)
diff --git a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs
index 2f39135d0cf2..fba8ee42a7c3 100644
--- a/src/Cli/dotnet/ReleasePropertyProjectLocator.cs
+++ b/src/Cli/dotnet/ReleasePropertyProjectLocator.cs
@@ -8,6 +8,7 @@
using Microsoft.Build.Execution;
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.ProjectTools;
using Microsoft.NET.Build.Tasks;
using Microsoft.VisualStudio.SolutionPersistence.Model;
@@ -110,7 +111,7 @@ DependentCommandOptions commandOptions
{
foreach (string arg in _slnOrProjectArgs.Append(Directory.GetCurrentDirectory()))
{
- if (VirtualProjectBuildingCommand.IsValidEntryPointPath(arg))
+ if (VirtualProjectBuilder.IsValidEntryPointPath(arg))
{
return new VirtualProjectBuildingCommand(Path.GetFullPath(arg), MSBuildArgs.FromProperties(globalProps))
.CreateProjectInstance(ProjectCollection.GlobalProjectCollection);
diff --git a/src/Cli/dotnet/Telemetry/TelemetryCommonProperties.cs b/src/Cli/dotnet/Telemetry/TelemetryCommonProperties.cs
index 625e343d7589..8c71bff14e7d 100644
--- a/src/Cli/dotnet/Telemetry/TelemetryCommonProperties.cs
+++ b/src/Cli/dotnet/Telemetry/TelemetryCommonProperties.cs
@@ -6,6 +6,7 @@
using System.Collections.Frozen;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Configurer;
+using Microsoft.DotNet.Utilities;
using RuntimeEnvironment = Microsoft.DotNet.Cli.Utils.RuntimeEnvironment;
using RuntimeInformation = System.Runtime.InteropServices.RuntimeInformation;
diff --git a/src/Cli/dotnet/dotnet.csproj b/src/Cli/dotnet/dotnet.csproj
index 390d2c7a1e7b..a6be5fb2a5e6 100644
--- a/src/Cli/dotnet/dotnet.csproj
+++ b/src/Cli/dotnet/dotnet.csproj
@@ -45,7 +45,6 @@
-
diff --git a/src/Microsoft.DotNet.ProjectTools/Microsoft.DotNet.ProjectTools.csproj b/src/Microsoft.DotNet.ProjectTools/Microsoft.DotNet.ProjectTools.csproj
index 398ea7eecdab..9de97043c331 100644
--- a/src/Microsoft.DotNet.ProjectTools/Microsoft.DotNet.ProjectTools.csproj
+++ b/src/Microsoft.DotNet.ProjectTools/Microsoft.DotNet.ProjectTools.csproj
@@ -12,6 +12,7 @@
+
diff --git a/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs b/src/Microsoft.DotNet.ProjectTools/Utilities/Sha256Hasher.cs
similarity index 57%
rename from src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs
rename to src/Microsoft.DotNet.ProjectTools/Utilities/Sha256Hasher.cs
index 33fe02237329..176173e776c5 100644
--- a/src/Cli/Microsoft.DotNet.Cli.Utils/Sha256Hasher.cs
+++ b/src/Microsoft.DotNet.ProjectTools/Utilities/Sha256Hasher.cs
@@ -1,11 +1,9 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
-#if NET
-
using System.Security.Cryptography;
-namespace Microsoft.DotNet.Cli.Utils;
+namespace Microsoft.DotNet.Utilities;
public static class Sha256Hasher
{
@@ -13,20 +11,8 @@ public static class Sha256Hasher
/// The hashed mac address needs to be the same hashed value as produced by the other distinct sources given the same input. (e.g. VsCode)
///
public static string Hash(string text)
- {
- byte[] bytes = Encoding.UTF8.GetBytes(text);
- byte[] hash = SHA256.HashData(bytes);
-#if NET9_0_OR_GREATER
- return Convert.ToHexStringLower(hash);
-#else
- return Convert.ToHexString(hash).ToLowerInvariant();
-#endif
- }
+ => Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(text)));
public static string HashWithNormalizedCasing(string text)
- {
- return Hash(text.ToUpperInvariant());
- }
+ => Hash(text.ToUpperInvariant());
}
-
-#endif
diff --git a/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs
new file mode 100644
index 000000000000..ace65c8c4726
--- /dev/null
+++ b/src/Microsoft.DotNet.ProjectTools/VirtualProjectBuilder.cs
@@ -0,0 +1,559 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Collections.Immutable;
+using System.Diagnostics;
+using System.Security;
+using System.Xml;
+using Microsoft.Build.Construction;
+using Microsoft.Build.Definition;
+using Microsoft.Build.Evaluation;
+using Microsoft.Build.Execution;
+using Microsoft.CodeAnalysis.Text;
+using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.Utilities;
+
+namespace Microsoft.DotNet.ProjectTools;
+
+internal sealed class VirtualProjectBuilder
+{
+ private readonly IEnumerable<(string name, string value)> _defaultProperties;
+
+ public string EntryPointFileFullPath { get; }
+
+ public SourceFile EntryPointSourceFile
+ {
+ get
+ {
+ if (field == default)
+ {
+ field = SourceFile.Load(EntryPointFileFullPath);
+ }
+
+ return field;
+ }
+ }
+
+ public string ArtifactsPath
+ => field ??= GetArtifactsPath(EntryPointFileFullPath);
+
+ public string[]? RequestedTargets { get; }
+
+ public VirtualProjectBuilder(
+ string entryPointFileFullPath,
+ string targetFrameworkVersion,
+ string[]? requestedTargets = null,
+ string? artifactsPath = null)
+ {
+ Debug.Assert(Path.IsPathFullyQualified(entryPointFileFullPath));
+
+ EntryPointFileFullPath = entryPointFileFullPath;
+ RequestedTargets = requestedTargets;
+ ArtifactsPath = artifactsPath;
+ _defaultProperties = GetDefaultProperties(targetFrameworkVersion);
+ }
+
+ ///
+ /// Kept in sync with the default dotnet new console project file (enforced by DotnetProjectConvertTests.SameAsTemplate).
+ ///
+ public static IEnumerable<(string name, string value)> GetDefaultProperties(string targetFrameworkVersion) =>
+ [
+ ("OutputType", "Exe"),
+ ("TargetFramework", $"net{targetFrameworkVersion}"),
+ ("ImplicitUsings", "enable"),
+ ("Nullable", "enable"),
+ ("PublishAot", "true"),
+ ("PackAsTool", "true"),
+ ];
+
+ public static string GetArtifactsPath(string entryPointFileFullPath)
+ {
+ // Include entry point file name so the directory name is not completely opaque.
+ string fileName = Path.GetFileNameWithoutExtension(entryPointFileFullPath);
+ string hash = Sha256Hasher.HashWithNormalizedCasing(entryPointFileFullPath);
+ string directoryName = $"{fileName}-{hash}";
+
+ return GetTempSubpath(directoryName);
+ }
+
+ ///
+ /// Obtains a temporary subdirectory for file-based app artifacts, e.g., /tmp/dotnet/runfile/.
+ ///
+ public static string GetTempSubdirectory()
+ {
+ // We want a location where permissions are expected to be restricted to the current user.
+ string directory = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)
+ ? Path.GetTempPath()
+ : Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
+
+ if (string.IsNullOrEmpty(directory))
+ {
+ throw new InvalidOperationException(FileBasedProgramsResources.EmptyTempPath);
+ }
+
+ return Path.Join(directory, "dotnet", "runfile");
+ }
+
+ ///
+ /// Obtains a specific temporary path in a subdirectory for file-based app artifacts, e.g., /tmp/dotnet/runfile/{name}.
+ ///
+ public static string GetTempSubpath(string name)
+ {
+ return Path.Join(GetTempSubdirectory(), name);
+ }
+
+ public static bool IsValidEntryPointPath(string entryPointFilePath)
+ {
+ if (!File.Exists(entryPointFilePath))
+ {
+ return false;
+ }
+
+ if (entryPointFilePath.EndsWith(".cs", StringComparison.OrdinalIgnoreCase))
+ {
+ return true;
+ }
+
+ // Check if the first two characters are #!
+ try
+ {
+ using var stream = File.OpenRead(entryPointFilePath);
+ int first = stream.ReadByte();
+ int second = stream.ReadByte();
+ return first == '#' && second == '!';
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
+ ///
+ /// If there are any #:project ,
+ /// evaluates their values as MSBuild expressions (i.e. substitutes $() and @() with property and item values, etc.) and
+ /// resolves the evaluated values to full project file paths (e.g. if the evaluted value is a directory finds a project in that directory).
+ ///
+ internal static ImmutableArray EvaluateDirectives(
+ ProjectInstance? project,
+ ImmutableArray directives,
+ SourceFile sourceFile,
+ ErrorReporter errorReporter)
+ {
+ if (directives.OfType().Any())
+ {
+ return directives
+ .Select(d => d is CSharpDirective.Project p
+ ? (project is null
+ ? p
+ : p.WithName(project.ExpandString(p.Name), CSharpDirective.Project.NameKind.Expanded))
+ .EnsureProjectFilePath(sourceFile, errorReporter)
+ : d)
+ .ToImmutableArray();
+ }
+
+ return directives;
+ }
+
+ public void CreateProjectInstance(
+ ProjectCollection projectCollection,
+ ErrorReporter errorReporter,
+ out ProjectInstance project,
+ out ImmutableArray evaluatedDirectives,
+ ImmutableArray directives = default,
+ Action>? addGlobalProperties = null,
+ bool validateAllDirectives = false)
+ {
+ if (directives.IsDefault)
+ {
+ directives = FileLevelDirectiveHelpers.FindDirectives(EntryPointSourceFile, validateAllDirectives, errorReporter);
+ }
+
+ project = CreateProjectInstance(projectCollection, directives, addGlobalProperties);
+
+ evaluatedDirectives = EvaluateDirectives(project, directives, EntryPointSourceFile, errorReporter);
+ if (evaluatedDirectives != directives)
+ {
+ project = CreateProjectInstance(projectCollection, evaluatedDirectives, addGlobalProperties);
+ }
+ }
+
+ private ProjectInstance CreateProjectInstance(
+ ProjectCollection projectCollection,
+ ImmutableArray directives,
+ Action>? addGlobalProperties = null)
+ {
+ var projectRoot = CreateProjectRootElement(projectCollection);
+
+ var globalProperties = projectCollection.GlobalProperties;
+ if (addGlobalProperties is not null)
+ {
+ globalProperties = new Dictionary(projectCollection.GlobalProperties, StringComparer.OrdinalIgnoreCase);
+ addGlobalProperties(globalProperties);
+ }
+
+ return ProjectInstance.FromProjectRootElement(projectRoot, new ProjectOptions
+ {
+ ProjectCollection = projectCollection,
+ GlobalProperties = globalProperties,
+ });
+
+ ProjectRootElement CreateProjectRootElement(ProjectCollection projectCollection)
+ {
+ var projectFileFullPath = Path.ChangeExtension(EntryPointFileFullPath, ".csproj");
+ var projectFileWriter = new StringWriter();
+
+ WriteProjectFile(
+ projectFileWriter,
+ directives,
+ _defaultProperties,
+ isVirtualProject: true,
+ targetFilePath: EntryPointFileFullPath,
+ artifactsPath: ArtifactsPath,
+ includeRuntimeConfigInformation: RequestedTargets?.ContainsAny("Publish", "Pack") != true);
+
+ var projectFileText = projectFileWriter.ToString();
+
+ using var reader = new StringReader(projectFileText);
+ using var xmlReader = XmlReader.Create(reader);
+ var projectRoot = ProjectRootElement.Create(xmlReader, projectCollection);
+ projectRoot.FullPath = projectFileFullPath;
+ return projectRoot;
+ }
+ }
+
+ public static void WriteProjectFile(
+ TextWriter writer,
+ ImmutableArray directives,
+ IEnumerable<(string name, string value)> defaultProperties,
+ bool isVirtualProject,
+ string? targetFilePath = null,
+ string? artifactsPath = null,
+ bool includeRuntimeConfigInformation = true,
+ string? userSecretsId = null)
+ {
+ Debug.Assert(userSecretsId == null || !isVirtualProject);
+
+ int processedDirectives = 0;
+
+ var sdkDirectives = directives.OfType();
+ var propertyDirectives = directives.OfType();
+ var packageDirectives = directives.OfType();
+ var projectDirectives = directives.OfType();
+
+ const string defaultSdkName = "Microsoft.NET.Sdk";
+ string firstSdkName;
+ string? firstSdkVersion;
+
+ if (sdkDirectives.FirstOrDefault() is { } firstSdk)
+ {
+ firstSdkName = firstSdk.Name;
+ firstSdkVersion = firstSdk.Version;
+ processedDirectives++;
+ }
+ else
+ {
+ firstSdkName = defaultSdkName;
+ firstSdkVersion = null;
+ }
+
+ if (isVirtualProject)
+ {
+ Debug.Assert(!string.IsNullOrWhiteSpace(artifactsPath));
+
+ // Note that ArtifactsPath needs to be specified before Sdk.props
+ // (usually it's recommended to specify it in Directory.Build.props
+ // but importing Sdk.props manually afterwards also works).
+ writer.WriteLine($"""
+
+
+
+ false
+ {EscapeValue(artifactsPath)}
+ artifacts/$(MSBuildProjectName)
+ artifacts/$(MSBuildProjectName)
+ true
+ false
+ true
+ """);
+
+ // Only set these to false when using the default SDK with no additional SDKs
+ // to avoid including .resx and other files that are typically not expected in simple file-based apps.
+ // When other SDKs are used (e.g., Microsoft.NET.Sdk.Web), keep the default behavior.
+ bool usingOnlyDefaultSdk = firstSdkName == defaultSdkName && sdkDirectives.Count() <= 1;
+ if (usingOnlyDefaultSdk)
+ {
+ writer.WriteLine($"""
+ false
+ false
+ """);
+ }
+
+ // Write default properties before importing SDKs so they can be overridden by SDKs
+ // (and implicit build files which are imported by the default .NET SDK).
+ foreach (var (name, value) in defaultProperties)
+ {
+ writer.WriteLine($"""
+ <{name}>{EscapeValue(value)}{name}>
+ """);
+ }
+
+ writer.WriteLine($"""
+
+
+
+
+
+
+ """);
+
+ if (firstSdkVersion is null)
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+ else
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+ }
+ else
+ {
+ string slashDelimited = firstSdkVersion is null
+ ? firstSdkName
+ : $"{firstSdkName}/{firstSdkVersion}";
+ writer.WriteLine($"""
+
+
+ """);
+ }
+
+ foreach (var sdk in sdkDirectives.Skip(1))
+ {
+ if (isVirtualProject)
+ {
+ WriteImport(writer, "Sdk.props", sdk);
+ }
+ else if (sdk.Version is null)
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+ else
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+
+ processedDirectives++;
+ }
+
+ if (isVirtualProject || processedDirectives > 1)
+ {
+ writer.WriteLine();
+ }
+
+ // Write default and custom properties.
+ {
+ writer.WriteLine("""
+
+ """);
+
+ // First write the default properties except those specified by the user.
+ if (!isVirtualProject)
+ {
+ var customPropertyNames = propertyDirectives
+ .Select(static d => d.Name)
+ .ToHashSet(StringComparer.OrdinalIgnoreCase);
+
+ foreach (var (name, value) in defaultProperties)
+ {
+ if (!customPropertyNames.Contains(name))
+ {
+ writer.WriteLine($"""
+ <{name}>{EscapeValue(value)}{name}>
+ """);
+ }
+ }
+
+ if (userSecretsId != null && !customPropertyNames.Contains("UserSecretsId"))
+ {
+ writer.WriteLine($"""
+ {EscapeValue(userSecretsId)}
+ """);
+ }
+ }
+
+ // Write custom properties.
+ foreach (var property in propertyDirectives)
+ {
+ writer.WriteLine($"""
+ <{property.Name}>{EscapeValue(property.Value)}{property.Name}>
+ """);
+
+ processedDirectives++;
+ }
+
+ // Write virtual-only properties which cannot be overridden.
+ if (isVirtualProject)
+ {
+ writer.WriteLine("""
+ false
+ $(Features);FileBasedProgram
+ """);
+ }
+
+ writer.WriteLine("""
+
+
+ """);
+ }
+
+ if (packageDirectives.Any())
+ {
+ writer.WriteLine("""
+
+ """);
+
+ foreach (var package in packageDirectives)
+ {
+ if (package.Version is null)
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+ else
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+
+ processedDirectives++;
+ }
+
+ writer.WriteLine("""
+
+
+ """);
+ }
+
+ if (projectDirectives.Any())
+ {
+ writer.WriteLine("""
+
+ """);
+
+ foreach (var projectReference in projectDirectives)
+ {
+ writer.WriteLine($"""
+
+ """);
+
+ processedDirectives++;
+ }
+
+ writer.WriteLine("""
+
+
+ """);
+ }
+
+ Debug.Assert(processedDirectives + directives.OfType().Count() == directives.Length);
+
+ if (isVirtualProject)
+ {
+ Debug.Assert(targetFilePath is not null);
+
+ // Only add explicit Compile item when EnableDefaultCompileItems is not true.
+ // When EnableDefaultCompileItems=true, the file is included via default MSBuild globbing.
+ // See https://github.com/dotnet/sdk/issues/51785
+ writer.WriteLine($"""
+
+
+
+
+ """);
+
+ if (includeRuntimeConfigInformation)
+ {
+ var targetDirectory = Path.GetDirectoryName(targetFilePath) ?? "";
+ writer.WriteLine($"""
+
+
+
+
+
+ """);
+ }
+
+ foreach (var sdk in sdkDirectives)
+ {
+ WriteImport(writer, "Sdk.targets", sdk);
+ }
+
+ if (!sdkDirectives.Any())
+ {
+ Debug.Assert(firstSdkName == defaultSdkName && firstSdkVersion == null);
+ writer.WriteLine($"""
+
+ """);
+ }
+
+ writer.WriteLine();
+ }
+
+ writer.WriteLine("""
+
+ """);
+
+ static string EscapeValue(string value) => SecurityElement.Escape(value);
+
+ static void WriteImport(TextWriter writer, string project, CSharpDirective.Sdk sdk)
+ {
+ if (sdk.Version is null)
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+ else
+ {
+ writer.WriteLine($"""
+
+ """);
+ }
+ }
+ }
+
+ public static SourceText? RemoveDirectivesFromFile(ImmutableArray directives, SourceText text)
+ {
+ if (directives.Length == 0)
+ {
+ return null;
+ }
+
+ Debug.Assert(directives.OrderBy(d => d.Info.Span.Start).SequenceEqual(directives), "Directives should be ordered by source location.");
+
+ for (int i = directives.Length - 1; i >= 0; i--)
+ {
+ var directive = directives[i];
+ text = text.Replace(directive.Info.Span, string.Empty);
+ }
+
+ return text;
+ }
+
+ public static void RemoveDirectivesFromFile(ImmutableArray directives, SourceText text, string filePath)
+ {
+ if (RemoveDirectivesFromFile(directives, text) is { } modifiedText)
+ {
+ new SourceFile(filePath, modifiedText).Save();
+ }
+ }
+}
diff --git a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs
index 2dfa4c6fda50..7e1b1e6f1896 100644
--- a/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs
+++ b/test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildANetCoreAppForTelemetry.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using System.Reflection;
-using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.NET.Build.Tests
{
diff --git a/test/Microsoft.NET.Build.Tests/Microsoft.NET.Build.Tests.csproj b/test/Microsoft.NET.Build.Tests/Microsoft.NET.Build.Tests.csproj
index cda2e95e19e4..1bc85b007091 100644
--- a/test/Microsoft.NET.Build.Tests/Microsoft.NET.Build.Tests.csproj
+++ b/test/Microsoft.NET.Build.Tests/Microsoft.NET.Build.Tests.csproj
@@ -37,6 +37,7 @@
+
diff --git a/test/Microsoft.TemplateEngine.Cli.UnitTests/TelemetryHelperTests.cs b/test/Microsoft.TemplateEngine.Cli.UnitTests/TelemetryHelperTests.cs
index 842143be0acc..f7f61ea14770 100644
--- a/test/Microsoft.TemplateEngine.Cli.UnitTests/TelemetryHelperTests.cs
+++ b/test/Microsoft.TemplateEngine.Cli.UnitTests/TelemetryHelperTests.cs
@@ -2,7 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.
using FakeItEasy;
-using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
using Microsoft.TemplateEngine.Abstractions;
using Microsoft.TemplateEngine.Abstractions.Parameters;
using Microsoft.TemplateEngine.Utils;
diff --git a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs
index 3dd157806387..1b800a103c8b 100644
--- a/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs
+++ b/test/dotnet.Tests/CommandTests/Project/Convert/DotnetProjectConvertTests.cs
@@ -10,6 +10,7 @@
using Microsoft.DotNet.Cli.Run.Tests;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Project.Convert.Tests;
@@ -1003,6 +1004,51 @@ public void UserSecretsId_Overridden_SameAsImplicit(bool hasDirective, bool hasD
""");
}
+ [Fact]
+ public void ForceOption_Off()
+ {
+ var testInstance = _testAssetsManager.CreateTestDirectory();
+ var filePath = Path.Join(testInstance.Path, "Program.cs");
+ File.WriteAllText(filePath, """
+ #:property Prop1=1
+ #define X
+ #:property Prop2=2
+ Console.WriteLine();
+ #:property Prop1=3
+ """);
+
+ new DotnetCommand(Log, "project", "convert", "Program.cs")
+ .WithWorkingDirectory(testInstance.Path)
+ .Execute()
+ .Should().Fail()
+ .And.HaveStdErrContaining(FileBasedProgramsResources.CannotConvertDirective);
+
+ new DirectoryInfo(Path.Join(testInstance.Path))
+ .EnumerateDirectories().Should().BeEmpty();
+ }
+
+ [Fact]
+ public void ForceOption_On()
+ {
+ var testInstance = _testAssetsManager.CreateTestDirectory();
+ var filePath = Path.Join(testInstance.Path, "Program.cs");
+ File.WriteAllText(filePath, """
+ #:property Prop1=1
+ #define X
+ #:property Prop2=2
+ Console.WriteLine();
+ #:property Prop1=3
+ """);
+
+ new DotnetCommand(Log, "project", "convert", "--force", "Program.cs")
+ .WithWorkingDirectory(testInstance.Path)
+ .Execute()
+ .Should().Pass();
+
+ new DirectoryInfo(Path.Join(testInstance.Path))
+ .EnumerateDirectories().Should().NotBeEmpty();
+ }
+
[Fact]
public void Directives()
{
@@ -1698,11 +1744,11 @@ private static void Convert(string inputCSharp, out string actualProject, out st
actualDiagnostics = null;
var diagnosticBag = collectDiagnostics ? ErrorReporters.CreateCollectingReporter(out actualDiagnostics) : VirtualProjectBuildingCommand.ThrowingReporter;
var directives = FileLevelDirectiveHelpers.FindDirectives(sourceFile, reportAllErrors: !force, diagnosticBag);
- directives = VirtualProjectBuildingCommand.EvaluateDirectives(project: null, directives, sourceFile, diagnosticBag);
+ directives = VirtualProjectBuilder.EvaluateDirectives(project: null, directives, sourceFile, diagnosticBag);
var projectWriter = new StringWriter();
- VirtualProjectBuildingCommand.WriteProjectFile(projectWriter, directives, isVirtualProject: false);
+ VirtualProjectBuilder.WriteProjectFile(projectWriter, directives, VirtualProjectBuilder.GetDefaultProperties(VirtualProjectBuildingCommand.TargetFrameworkVersion), isVirtualProject: false);
actualProject = projectWriter.ToString();
- actualCSharp = VirtualProjectBuildingCommand.RemoveDirectivesFromFile(directives, sourceFile.Text)?.ToString();
+ actualCSharp = VirtualProjectBuilder.RemoveDirectivesFromFile(directives, sourceFile.Text)?.ToString();
}
///
diff --git a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
index ac3e989cfa21..42a012d1a03d 100644
--- a/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
+++ b/test/dotnet.Tests/CommandTests/Run/RunFileTests.cs
@@ -11,6 +11,7 @@
using Microsoft.DotNet.Cli.Commands.Run;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.FileBasedPrograms;
+using Microsoft.DotNet.ProjectTools;
namespace Microsoft.DotNet.Cli.Run.Tests;
@@ -912,7 +913,7 @@ public void WorkingDirectory(bool cscOnly)
var workDir = TestPathUtility.ResolveTempPrefixLink(Path.GetTempPath()).TrimEnd(Path.DirectorySeparatorChar);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance,
@@ -961,7 +962,7 @@ public void WorkingDirectory_CscOnly_AfterMSBuild()
var workDir = TestPathUtility.ResolveTempPrefixLink(Path.GetTempPath()).TrimEnd(Path.DirectorySeparatorChar);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance,
@@ -1668,7 +1669,7 @@ public void NoRestore_01()
File.WriteAllText(programFile, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
// It is an error when never restored before.
@@ -1700,7 +1701,7 @@ public void NoRestore_02()
File.WriteAllText(programFile, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
// It is an error when never restored before.
@@ -1744,7 +1745,7 @@ public void Restore_StaticGraph_Implicit()
File.WriteAllText(programFile, "Console.WriteLine();");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "restore", "Program.cs")
@@ -1764,7 +1765,7 @@ public void Restore_StaticGraph_Explicit()
""");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "restore", "Program.cs")
@@ -1782,7 +1783,7 @@ public void NoBuild_01()
File.WriteAllText(programFile, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
// It is an error when never built before.
@@ -1822,7 +1823,7 @@ public void NoBuild_02()
File.WriteAllText(programFile, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
// It is an error when never built before.
@@ -1865,7 +1866,7 @@ public void Build_Library()
class C;
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "lib.cs")
@@ -1905,7 +1906,7 @@ class C;
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "lib.cs")
@@ -1940,7 +1941,7 @@ public void Build_Module()
class C;
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "module.cs")
@@ -1968,7 +1969,7 @@ public void Build_WinExe()
Console.WriteLine("Hello WinExe");
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "winexe.cs")
@@ -1993,7 +1994,7 @@ public void Build_Exe()
Console.WriteLine("Hello Exe");
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "exe.cs")
@@ -2030,7 +2031,7 @@ public void Build_Exe_MultiTarget()
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "exe.cs")
@@ -2061,7 +2062,7 @@ public void Build_AppContainerExe()
Console.WriteLine("Hello AppContainerExe");
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "appcontainerexe.cs")
@@ -2086,7 +2087,7 @@ public void Publish()
var programFile = Path.Join(testInstance.Path, "Program.cs");
File.WriteAllText(programFile, s_program);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var publishDir = Path.Join(testInstance.Path, "artifacts");
@@ -2117,7 +2118,7 @@ public void PublishWithCustomTarget()
var programFile = Path.Join(testInstance.Path, "Program.cs");
File.WriteAllText(programFile, s_program);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var publishDir = Path.Join(testInstance.Path, "artifacts");
@@ -2152,7 +2153,7 @@ public void Publish_WithJson()
{ "MyKey": "MyValue" }
""");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var publishDir = Path.Join(testInstance.Path, "artifacts");
@@ -2176,7 +2177,7 @@ public void Publish_Options()
var programFile = Path.Join(testInstance.Path, "Program.cs");
File.WriteAllText(programFile, s_program);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var publishDir = Path.Join(testInstance.Path, "artifacts");
@@ -2201,7 +2202,7 @@ public void Publish_PublishDir_IncludesFileName()
var programFile = Path.Join(testInstance.Path, "MyCustomProgram.cs");
File.WriteAllText(programFile, s_program);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var publishDir = Path.Join(testInstance.Path, "artifacts");
@@ -2269,7 +2270,7 @@ public void Publish_In_SubDir()
var programFile = Path.Join(subDir.FullName, "Program.cs");
File.WriteAllText(programFile, s_program);
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var publishDir = Path.Join(subDir.FullName, "artifacts");
@@ -2304,7 +2305,7 @@ public void Pack()
.Should().Pass()
.And.HaveStdOut("Hello; EntryPointFilePath set? True");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var outputDir = Path.Join(testInstance.Path, "artifacts");
@@ -2348,7 +2349,7 @@ public void Pack_CustomPath()
.Should().Pass()
.And.HaveStdOut("Hello; EntryPointFilePath set? True");
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var outputDir = Path.Join(testInstance.Path, "custom");
@@ -2384,7 +2385,7 @@ public void Clean()
.Should().Pass()
.And.HaveStdOut("Hello from Program");
- var artifactsDir = new DirectoryInfo(VirtualProjectBuildingCommand.GetArtifactsPath(programFile));
+ var artifactsDir = new DirectoryInfo(VirtualProjectBuilder.GetArtifactsPath(programFile));
artifactsDir.Should().HaveFiles(["build-start.cache", "build-success.cache"]);
var dllFile = artifactsDir.File("bin/debug/Program.dll");
@@ -2409,7 +2410,7 @@ public void ArtifactsDirectory_Permissions()
File.WriteAllText(programFile, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programFile);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programFile);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "build", "Program.cs")
@@ -2898,7 +2899,7 @@ public void UserSecrets(bool useIdArg, string? userSecretsId)
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
if (useIdArg)
@@ -2953,7 +2954,7 @@ public void CscArguments()
File.WriteAllText(entryPointPath, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(entryPointPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(entryPointPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
// Build using MSBuild.
@@ -3235,7 +3236,7 @@ public void CscVsMSBuild(string fileName)
string programName = Path.GetFileNameWithoutExtension(fileName);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(entryPointPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(entryPointPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var artifactsBackupDir = Path.ChangeExtension(artifactsDir, ".bak");
if (Directory.Exists(artifactsBackupDir)) Directory.Delete(artifactsBackupDir, recursive: true);
@@ -3310,7 +3311,7 @@ public void UpToDate()
""");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.Csc, expectedOutput: "Hello v1");
@@ -3493,7 +3494,7 @@ public void UpToDate_SymbolicLink()
File.CreateSymbolicLink(path: programPath, pathToTarget: originalPath);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1", programFileName: programFileName);
@@ -3533,7 +3534,7 @@ public void UpToDate_SymbolicLink2()
File.CreateSymbolicLink(path: programPath, pathToTarget: intermediatePath);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1", programFileName: programFileName);
@@ -3588,7 +3589,7 @@ public class LibClass
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var programFileName = "App/Program.cs";
@@ -3683,7 +3684,7 @@ public void CscOnly()
""");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.Csc, expectedOutput: "v1");
@@ -3801,7 +3802,7 @@ public void CscOnly_IntermediateFiles()
""");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
File.WriteAllText(Path.Join(testInstance.Path, "Directory.Build.props"), "");
@@ -3844,7 +3845,7 @@ public void CscOnly_NotRestored()
File.WriteAllText(Path.Join(testInstance.Path, "Program.cs"), s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(Path.Join(testInstance.Path, "Program.cs"));
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
new DotnetCommand(Log, "run", "Program.cs", "-bl", "--no-restore")
@@ -3890,7 +3891,7 @@ public void CscOnly_SpacesInPath()
""");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.Csc, expectedOutput: "v1", programFileName: programFileName);
@@ -3904,7 +3905,7 @@ public void CscOnly_Args()
File.WriteAllText(programPath, s_program);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.Csc, args: ["test", "args"], expectedOutput: """
@@ -3935,7 +3936,7 @@ public void CscOnly_SymbolicLink()
File.CreateSymbolicLink(path: programPath, pathToTarget: originalPath);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.Csc, expectedOutput: "v1", programFileName: programFileName);
@@ -3970,7 +3971,7 @@ public void CscOnly_AfterMSBuild()
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1 Release");
@@ -4029,7 +4030,7 @@ public void CscOnly_AfterMSBuild_SpacesInPath()
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1 Release", programFileName: programFileName);
@@ -4057,7 +4058,7 @@ public void CscOnly_AfterMSBuild_Args()
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, args: ["test", "args"], expectedOutput: """
@@ -4096,7 +4097,7 @@ public void CscOnly_AfterMSBuild_HardLinks()
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All);
@@ -4130,7 +4131,7 @@ public void CscOnly_AfterMSBuild_SymbolicLink()
File.CreateSymbolicLink(path: programPath, pathToTarget: originalPath);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1", programFileName: programFileName);
@@ -4183,7 +4184,7 @@ public class LibClass
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var programFileName = "App/Program.cs";
@@ -4235,7 +4236,7 @@ public void CscOnly_AfterMSBuild_OptOut(bool canSkipMSBuild, bool inDirectoryBui
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1 Release");
@@ -4266,7 +4267,7 @@ public void CscOnly_AfterMSBuild_AuxiliaryFilesNotReused()
File.WriteAllText(programPath, code);
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(programPath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(programPath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
Build(testInstance, BuildLevel.All, expectedOutput: "v1 Release");
@@ -4565,7 +4566,7 @@ public void EntryPointFilePath(bool cscOnly)
"""");
// Remove artifacts from possible previous runs of this test.
- var artifactsDir = VirtualProjectBuildingCommand.GetArtifactsPath(filePath);
+ var artifactsDir = VirtualProjectBuilder.GetArtifactsPath(filePath);
if (Directory.Exists(artifactsDir)) Directory.Delete(artifactsDir, recursive: true);
var prefix = cscOnly
diff --git a/test/dotnet.Tests/TelemetryCommandTest.cs b/test/dotnet.Tests/TelemetryCommandTest.cs
index 8d31a917db13..aa180fe14930 100644
--- a/test/dotnet.Tests/TelemetryCommandTest.cs
+++ b/test/dotnet.Tests/TelemetryCommandTest.cs
@@ -6,6 +6,7 @@
using Microsoft.DotNet.Cli.Commands.Hidden.InternalReportInstallSuccess;
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
namespace Microsoft.DotNet.Tests
{
diff --git a/test/dotnet.Tests/TelemetryFilterTest.cs b/test/dotnet.Tests/TelemetryFilterTest.cs
index 5b067a3f5cca..db5f835e791d 100644
--- a/test/dotnet.Tests/TelemetryFilterTest.cs
+++ b/test/dotnet.Tests/TelemetryFilterTest.cs
@@ -5,6 +5,7 @@
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
+using Microsoft.DotNet.Utilities;
using Parser = Microsoft.DotNet.Cli.Parser;
namespace Microsoft.DotNet.Tests