Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -385,7 +385,7 @@ internal void GenerateDepsJsonFile(
string? stdOut;
string? stdErr;

var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([..args], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, BuildCommandParser.TargetOption, BuildCommandParser.VerbosityOption);
var msbuildArgs = MSBuildArgs.AnalyzeMSBuildArguments([.. args], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, BuildCommandParser.TargetOption, BuildCommandParser.VerbosityOption);
var forwardingAppWithoutLogging = new MSBuildForwardingAppWithoutLogging(msbuildArgs, msBuildExePath);
if (forwardingAppWithoutLogging.ExecuteMSBuildOutOfProc)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Build/BuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ public static class BuildCommand
{
public static CommandBase FromArgs(string[] args, string? msbuildPath = null)
{
var parseResult = Parser.Parse(["dotnet", "build", ..args]);
var parseResult = Parser.Parse(["dotnet", "build", .. args]);
return FromParseResult(parseResult, msbuildPath);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Cli/dotnet/Commands/Clean/CleanCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class CleanCommand(MSBuildArgs msbuildArgs, string? msbuildPath = null) :
{
public static CommandBase FromArgs(string[] args, string? msbuildPath = null)
{
var result = Parser.Parse(["dotnet", "clean", ..args]);
var result = Parser.Parse(["dotnet", "clean", .. args]);
return FromParseResult(result, msbuildPath);
}

Expand All @@ -33,7 +33,7 @@ public static CommandBase FromParseResult(ParseResult result, string? msbuildPat
NoWriteBuildMarkers = true,
},
static (msbuildArgs, msbuildPath) => new CleanCommand(msbuildArgs, msbuildPath),
[ CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, CleanCommandParser.TargetOption, CleanCommandParser.VerbosityOption ],
[CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, CleanCommandParser.TargetOption, CleanCommandParser.VerbosityOption],
result,
msbuildPath
);
Expand Down
3 changes: 3 additions & 0 deletions src/Cli/dotnet/Commands/CliCommandStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -2698,4 +2698,7 @@ Proceed?</value>
<value>Received 'ExecutionId' of value '{0}' for message '{1}' while the 'ExecutionId' received of the handshake message was '{2}'.</value>
<comment>{Locked="ExecutionId"}</comment>
</data>
<data name="SolutionFilterDoesNotSupportFolderOptions" xml:space="preserve">
<value>Solution filter files (.slnf) do not support the --in-root or --solution-folder options.</value>
</data>
</root>
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Format/FormatCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public class FormatCommand(IEnumerable<string> argsToForward) : FormatForwarding
{
public static FormatCommand FromArgs(string[] args)
{
var result = Parser.Parse(["dotnet", "format", ..args]);
var result = Parser.Parse(["dotnet", "format", .. args]);
return FromParseResult(result);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Hidden/Complete/CompleteCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ public static int Run(ParseResult parseResult)

public static int RunWithReporter(string[] args, IReporter reporter)
{
var result = Parser.Parse(["dotnet", "complete", ..args]);
var result = Parser.Parse(["dotnet", "complete", .. args]);
return RunWithReporter(result, reporter);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public static int Run(ParseResult parseResult)

public static void ProcessInputAndSendTelemetry(string[] args, ITelemetry telemetry)
{
var result = Parser.Parse(["dotnet", "internal-reportinstallsuccess", ..args]);
var result = Parser.Parse(["dotnet", "internal-reportinstallsuccess", .. args]);
ProcessInputAndSendTelemetry(result, telemetry);
}

Expand Down
4 changes: 2 additions & 2 deletions src/Cli/dotnet/Commands/MSBuild/MSBuildCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ namespace Microsoft.DotNet.Cli.Commands.MSBuild;
public class MSBuildCommand(
IEnumerable<string> msbuildArgs,
string? msbuildPath = null
) : MSBuildForwardingApp(MSBuildArgs.AnalyzeMSBuildArguments([..msbuildArgs], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, MSBuildCommandParser.TargetOption, CommonOptions.VerbosityOption()), msbuildPath, includeLogo: true)
) : MSBuildForwardingApp(MSBuildArgs.AnalyzeMSBuildArguments([.. msbuildArgs], CommonOptions.PropertiesOption, CommonOptions.RestorePropertiesOption, MSBuildCommandParser.TargetOption, CommonOptions.VerbosityOption()), msbuildPath, includeLogo: true)
{
public static MSBuildCommand FromArgs(string[] args, string? msbuildPath = null)
{
var result = Parser.Parse(["dotnet", "msbuild", ..args]);
var result = Parser.Parse(["dotnet", "msbuild", .. args]);
return FromParseResult(result, msbuildPath);
}

Expand Down
6 changes: 3 additions & 3 deletions src/Cli/dotnet/Commands/Pack/PackCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public class PackCommand(
{
public static CommandBase FromArgs(string[] args, string? msbuildPath = null)
{
var parseResult = Parser.Parse(["dotnet", "pack", ..args]);
var parseResult = Parser.Parse(["dotnet", "pack", .. args]);
return FromParseResult(parseResult, msbuildPath);
}

Expand Down Expand Up @@ -92,14 +92,14 @@ public static int RunPackCommand(ParseResult parseResult)

if (args.Count != 1)
{
Console.Error.WriteLine(CliStrings.PackCmd_OneNuspecAllowed);
Console.Error.WriteLine(CliStrings.PackCmd_OneNuspecAllowed);
return 1;
}

var nuspecPath = args[0];

var packArgs = new PackArgs()
{
{
Logger = new NuGetConsoleLogger(),
Exclude = new List<string>(),
OutputDirectory = parseResult.GetValue(PackCommandParser.OutputOption),
Expand Down
4 changes: 2 additions & 2 deletions src/Cli/dotnet/Commands/Package/List/PackageListCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@
#nullable disable

using System.CommandLine;
using System.Globalization;
using Microsoft.DotNet.Cli.Commands.Hidden.List;
using Microsoft.DotNet.Cli.Commands.MSBuild;
using Microsoft.DotNet.Cli.Commands.NuGet;
using Microsoft.DotNet.Cli.Extensions;
using Microsoft.DotNet.Cli.Utils;
using System.Globalization;
using Microsoft.DotNet.Cli.Commands.MSBuild;

namespace Microsoft.DotNet.Cli.Commands.Package.List;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@

#nullable disable

using System.CommandLine;
using Microsoft.DotNet.Cli.Commands.NuGet;
using Microsoft.DotNet.Cli.Extensions;
using System.CommandLine;

namespace Microsoft.DotNet.Cli.Commands.Package.Search;

Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Publish/PublishCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ private PublishCommand(

public static CommandBase FromArgs(string[] args, string? msbuildPath = null)
{
var parseResult = Parser.Parse(["dotnet", "publish", ..args]);
var parseResult = Parser.Parse(["dotnet", "publish", .. args]);
return FromParseResult(parseResult);
}

Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Restore/RestoreCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ public static class RestoreCommand
{
public static CommandBase FromArgs(string[] args, string? msbuildPath = null)
{
var result = Parser.Parse(["dotnet", "restore", ..args]);
var result = Parser.Parse(["dotnet", "restore", .. args]);
return FromParseResult(result, msbuildPath);
}

Expand Down
8 changes: 4 additions & 4 deletions src/Cli/dotnet/Commands/Restore/RestoringCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public RestoringCommand(
string? msbuildPath = null,
string? userProfileDir = null,
bool? advertiseWorkloadUpdates = null)
: base(GetCommandArguments(msbuildArgs, noRestore), msbuildPath)
: base(GetCommandArguments(msbuildArgs, noRestore), msbuildPath)
{
userProfileDir = CliFolderPathCalculator.DotnetUserProfileFolderPath;
Task.Run(() => WorkloadManifestUpdater.BackgroundUpdateAdvertisingManifestsAsync(userProfileDir));
Expand Down Expand Up @@ -122,13 +122,13 @@ private static MSBuildArgs GetCommandArguments(
ReadOnlyDictionary<string, string> restoreProperties =
msbuildArgs.GlobalProperties?
.Where(kvp => !IsPropertyExcludedFromRestore(kvp.Key))?
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase) is { } filteredList ? new(filteredList): ReadOnlyDictionary<string, string>.Empty;
.ToDictionary(kvp => kvp.Key, kvp => kvp.Value, StringComparer.OrdinalIgnoreCase) is { } filteredList ? new(filteredList) : ReadOnlyDictionary<string, string>.Empty;
var restoreMSBuildArgs =
MSBuildArgs.FromProperties(RestoreOptimizationProperties)
.CloneWithAdditionalTargets("Restore")
.CloneWithExplicitArgs([.. newArgumentsToAdd, .. existingArgumentsToForward])
.CloneWithAdditionalProperties(restoreProperties);
if (msbuildArgs.Verbosity is {} verbosity)
if (msbuildArgs.Verbosity is { } verbosity)
{
restoreMSBuildArgs = restoreMSBuildArgs.CloneWithVerbosity(verbosity);
}
Expand Down Expand Up @@ -175,7 +175,7 @@ private static bool HasPropertyToExcludeFromRestore(MSBuildArgs msbuildArgs)

private static readonly List<string> FlagsThatTriggerSilentSeparateRestore = [.. ComputeFlags(FlagsThatTriggerSilentRestore)];

private static readonly List<string> PropertiesToExcludeFromSeparateRestore = [ .. PropertiesToExcludeFromRestore ];
private static readonly List<string> PropertiesToExcludeFromSeparateRestore = [.. PropertiesToExcludeFromRestore];

/// <summary>
/// We investigate the arguments we're about to send to a separate restore call and filter out
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ public static LaunchSettingsApplyResult TryApplyLaunchSettings(string launchSett
{
if (prop.Value.TryGetProperty(CommandNameKey, out var commandNameElement) && commandNameElement.ValueKind == JsonValueKind.String)
{
if (commandNameElement.GetString() is { } commandNameElementKey && _providers.ContainsKey(commandNameElementKey))
if (commandNameElement.GetString() is { } commandNameElementKey && _providers.ContainsKey(commandNameElementKey))
{
profileObject = prop.Value;
break;
Expand Down Expand Up @@ -120,7 +120,7 @@ public static LaunchSettingsApplyResult TryApplyLaunchSettings(string launchSett
}
}

private static bool TryLocateHandler(string? commandName, [NotNullWhen(true)]out ILaunchSettingsProvider? provider)
private static bool TryLocateHandler(string? commandName, [NotNullWhen(true)] out ILaunchSettingsProvider? provider)
{
if (commandName == null)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Run/RunTelemetry.cs
Original file line number Diff line number Diff line change
Expand Up @@ -234,4 +234,4 @@ private static bool IsDefaultProfile(string? profileName)
// The default profile name at this point is "(Default)"
return profileName.Equals("(Default)", StringComparison.OrdinalIgnoreCase);
}
}
}
72 changes: 67 additions & 5 deletions src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public SolutionAddCommand(ParseResult parseResult) : base(parseResult)
_solutionFolderPath = parseResult.GetValue(SolutionAddCommandParser.SolutionFolderOption);
_includeReferences = parseResult.GetValue(SolutionAddCommandParser.IncludeReferencesOption);
SolutionArgumentValidator.ParseAndValidateArguments(_fileOrDirectory, _projects, SolutionArgumentValidator.CommandType.Add, _inRoot, _solutionFolderPath);
_solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory);
_solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory, includeSolutionFilterFiles: true);
}

public override int Execute()
Expand All @@ -58,14 +58,22 @@ public override int Execute()
// Get project paths from the command line arguments
PathUtility.EnsureAllPathsExist(_projects, CliStrings.CouldNotFindProjectOrDirectory, true);

IEnumerable<string> fullProjectPaths = _projects.Select(project =>
List<string> fullProjectPaths = _projects.Select(project =>
{
var fullPath = Path.GetFullPath(project);
return Directory.Exists(fullPath) ? MsbuildProject.GetProjectFileFromDirectory(fullPath).FullName : fullPath;
});
}).ToList();

// Add projects to the solution
AddProjectsToSolutionAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
// Check if we're working with a solution filter file
if (_solutionFileFullPath.HasExtension(".slnf"))
{
AddProjectsToSolutionFilterAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
}
else
{
// Add projects to the solution
AddProjectsToSolutionAsync(fullProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
}
return 0;
}

Expand Down Expand Up @@ -224,4 +232,58 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio
}
}
}

private async Task AddProjectsToSolutionFilterAsync(IEnumerable<string> projectPaths, CancellationToken cancellationToken)
{
// Solution filter files don't support --in-root or --solution-folder options
if (_inRoot || !string.IsNullOrEmpty(_solutionFolderPath))
{
throw new GracefulException(CliCommandStrings.SolutionFilterDoesNotSupportFolderOptions);
}

// Load the filtered solution to get the parent solution path and existing projects
SolutionModel filteredSolution = SlnFileFactory.CreateFromFilteredSolutionFile(_solutionFileFullPath);
string parentSolutionPath = filteredSolution.Description!; // The parent solution path is stored in Description

// Load the parent solution to validate projects exist in it
SolutionModel parentSolution = SlnFileFactory.CreateFromFileOrDirectory(parentSolutionPath);

// Get existing projects in the filter (already normalized to OS separator by CreateFromFilteredSolutionFile)
var existingProjects = filteredSolution.SolutionProjects.Select(p => p.FilePath).ToHashSet();

// Get solution-relative paths for new projects
var newProjects = new List<string>();
string parentSolutionDirectory = Path.GetDirectoryName(parentSolutionPath) ?? string.Empty;
foreach (var projectPath in projectPaths)
{
string parentSolutionRelativePath = Path.GetRelativePath(parentSolutionDirectory, projectPath);

// Normalize to OS separator for consistent comparison
parentSolutionRelativePath = parentSolutionRelativePath.Replace('\\', Path.DirectorySeparatorChar).Replace('/', Path.DirectorySeparatorChar);

// Check if project exists in parent solution
var projectInParent = parentSolution.FindProject(parentSolutionRelativePath);
if (projectInParent is null)
{
Reporter.Error.WriteLine(CliStrings.ProjectNotFoundInTheSolution, parentSolutionRelativePath, parentSolutionPath);
continue;
}

// Check if project is already in the filter
if (existingProjects.Contains(parentSolutionRelativePath))
{
Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, parentSolutionRelativePath);
continue;
}

newProjects.Add(parentSolutionRelativePath);
Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, parentSolutionRelativePath);
}

// Add new projects to the existing list and save
var allProjects = existingProjects.Concat(newProjects).OrderBy(p => p);
SlnfFileHelper.SaveSolutionFilter(_solutionFileFullPath, parentSolutionPath, allProjects);

await Task.CompletedTask;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,9 @@ public override int Execute()
{
ConvertToSlnxAsync(slnFileFullPath, slnxFileFullPath, CancellationToken.None).Wait();
return 0;
} catch (Exception ex) {
}
catch (Exception ex)
{
throw new GracefulException(ex.Message, ex);
}
}
Expand Down
46 changes: 43 additions & 3 deletions src/Cli/dotnet/Commands/Solution/Remove/SolutionRemoveCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public SolutionRemoveCommand(ParseResult parseResult) : base(parseResult)

public override int Execute()
{
string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory);
string solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory, includeSolutionFilterFiles: true);
if (_projects.Count == 0)
{
throw new GracefulException(CliStrings.SpecifyAtLeastOneProjectToRemove);
Expand All @@ -43,7 +43,15 @@ public override int Execute()
? MsbuildProject.GetProjectFileFromDirectory(p).FullName
: p));

RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
// Check if we're working with a solution filter file
if (solutionFileFullPath.HasExtension(".slnf"))
{
RemoveProjectsFromSolutionFilterAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
}
else
{
RemoveProjectsAsync(solutionFileFullPath, relativeProjectPaths, CancellationToken.None).GetAwaiter().GetResult();
}
return 0;
}
catch (Exception ex) when (ex is not GracefulException)
Expand Down Expand Up @@ -124,10 +132,42 @@ private static async Task RemoveProjectsAsync(string solutionFileFullPath, IEnum
{
solution.RemoveFolder(folder);
// After removal, adjust index and continue to avoid skipping folders after removal
i--;
i--;
}
}

await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken);
}

private static async Task RemoveProjectsFromSolutionFilterAsync(string slnfFileFullPath, IEnumerable<string> projectPaths, CancellationToken cancellationToken)
{
// Load the filtered solution to get the parent solution path and existing projects
SolutionModel filteredSolution = SlnFileFactory.CreateFromFilteredSolutionFile(slnfFileFullPath);
string parentSolutionPath = filteredSolution.Description!; // The parent solution path is stored in Description

// Get existing projects in the filter
var existingProjects = filteredSolution.SolutionProjects.Select(p => p.FilePath).ToHashSet();

// Remove specified projects
foreach (var projectPath in projectPaths)
{
// Normalize the path to be relative to parent solution
string normalizedPath = projectPath;

// Try to find and remove the project
if (existingProjects.Remove(normalizedPath))
{
Reporter.Output.WriteLine(CliStrings.ProjectRemovedFromTheSolution, normalizedPath);
}
else
{
Reporter.Output.WriteLine(CliStrings.ProjectNotFoundInTheSolution, normalizedPath);
}
}

// Save updated filter
SlnfFileHelper.SaveSolutionFilter(slnfFileFullPath, parentSolutionPath, existingProjects.OrderBy(p => p));

await Task.CompletedTask;
}
}
2 changes: 1 addition & 1 deletion src/Cli/dotnet/Commands/Store/StoreCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private StoreCommand(IEnumerable<string> msbuildArgs, string msbuildPath = null)

public static StoreCommand FromArgs(string[] args, string msbuildPath = null)
{
var result = Parser.Parse(["dotnet", "store", ..args]);
var result = Parser.Parse(["dotnet", "store", .. args]);
return FromParseResult(result, msbuildPath);
}

Expand Down
Loading
Loading