From 471d3f22dc65d703e0f3bfaeb81f08c5e95d0252 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Thu, 1 May 2025 20:18:07 -0700 Subject: [PATCH 01/11] sln-add: Add --include-references option Update option name --- src/Cli/dotnet/Commands/CliCommandStrings.resx | 5 ++++- .../Commands/Solution/Add/SolutionAddCommand.cs | 17 +++++++++++++++++ .../Solution/Add/SolutionAddCommandParser.cs | 8 ++++++++ 3 files changed, 29 insertions(+), 1 deletion(-) diff --git a/src/Cli/dotnet/Commands/CliCommandStrings.resx b/src/Cli/dotnet/Commands/CliCommandStrings.resx index 5fb8a21e1ca5..82b2f559d0f4 100644 --- a/src/Cli/dotnet/Commands/CliCommandStrings.resx +++ b/src/Cli/dotnet/Commands/CliCommandStrings.resx @@ -2483,4 +2483,7 @@ To display a value, specify the corresponding command-line option without provid Zero tests ran - + + Recursively add projects' ReferencedProjects to solution + + \ No newline at end of file diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index 137ec586119d..6b83b7b1bce1 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -20,6 +20,7 @@ internal class SolutionAddCommand : CommandBase private readonly IReadOnlyCollection _projects; private readonly string? _solutionFolderPath; private string _solutionFileFullPath = string.Empty; + private bool _addReferencedProjects; private static string GetSolutionFolderPathWithForwardSlashes(string path) { @@ -41,6 +42,7 @@ public SolutionAddCommand(ParseResult parseResult) : base(parseResult) _projects = (IReadOnlyCollection)(parseResult.GetValue(SolutionAddCommandParser.ProjectPathArgument) ?? []); _inRoot = parseResult.GetValue(SolutionAddCommandParser.InRootOption); _solutionFolderPath = parseResult.GetValue(SolutionAddCommandParser.SolutionFolderOption); + _addReferencedProjects = parseResult.GetValue(SolutionAddCommandParser.AddReferencedProjectsOption); SolutionArgumentValidator.ParseAndValidateArguments(_fileOrDirectory, _projects, SolutionArgumentValidator.CommandType.Add, _inRoot, _solutionFolderPath); _solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory); } @@ -202,6 +204,21 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio project.AddProjectConfigurationRule(new ConfigurationRule(BuildDimension.BuildType, solutionBuildType, "*", projectBuildType)); } + // Get referencedprojects from the project instance + var referencedProjectsFullPaths = projectInstance.EvaluatedItemElements + .Where(item => item.ItemType == "ProjectReference") + .Select(item => item.Include) + .Select(item => Path.GetFullPath(item, Path.GetDirectoryName(fullProjectPath))) + .ToList(); + Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, solutionRelativeProjectPath); + + if (_addReferencedProjects) + { + foreach (var referencedProjectFullPath in referencedProjectsFullPaths) + { + AddProject(solution, referencedProjectFullPath, serializer); + } + } } } diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs index e8eeb5670d56..4b4c71e0c3a3 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.CommandLine; +using System.CommandLine.Parsing; namespace Microsoft.DotNet.Cli.Commands.Solution.Add; @@ -24,6 +25,12 @@ public static class SolutionAddCommandParser Description = CliCommandStrings.AddProjectSolutionFolderArgumentDescription }; + public static readonly Option AddReferencedProjectsOption = new("--include-references") + { + Description = CliCommandStrings.SolutionAddReferencedProjectsOptionDescription, + DefaultValueFactory = (_) => true, + }; + private static readonly Command Command = ConstructCommand(); public static Command GetCommand() @@ -38,6 +45,7 @@ private static Command ConstructCommand() command.Arguments.Add(ProjectPathArgument); command.Options.Add(InRootOption); command.Options.Add(SolutionFolderOption); + command.Options.Add(AddReferencedProjectsOption); command.SetAction((parseResult) => new SolutionAddCommand(parseResult).Execute()); From 69a479aa6f6872f80494cd491f2df3b9cf2745ef Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 5 May 2025 14:00:51 -0700 Subject: [PATCH 02/11] Add tests --- .../Solution/Add/SolutionAddCommand.cs | 6 ++--- .../Solution/Add/SolutionAddCommandParser.cs | 4 +-- .../SlnFileWithReferencedProjects/A/A.csproj | 12 +++++++++ .../SlnFileWithReferencedProjects/App.sln | 14 +++++++++++ .../SlnFileWithReferencedProjects/App.slnx | 1 + .../SlnFileWithReferencedProjects/B/B.csproj | 10 ++++++++ .../Solution/Add/GivenDotnetSlnAdd.cs | 25 +++++++++++++++++++ 7 files changed, 67 insertions(+), 5 deletions(-) create mode 100644 test/TestAssets/TestProjects/SlnFileWithReferencedProjects/A/A.csproj create mode 100644 test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.sln create mode 100644 test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx create mode 100644 test/TestAssets/TestProjects/SlnFileWithReferencedProjects/B/B.csproj diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index 6b83b7b1bce1..96aa27226354 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -20,7 +20,7 @@ internal class SolutionAddCommand : CommandBase private readonly IReadOnlyCollection _projects; private readonly string? _solutionFolderPath; private string _solutionFileFullPath = string.Empty; - private bool _addReferencedProjects; + private bool _includeReferences; private static string GetSolutionFolderPathWithForwardSlashes(string path) { @@ -42,7 +42,7 @@ public SolutionAddCommand(ParseResult parseResult) : base(parseResult) _projects = (IReadOnlyCollection)(parseResult.GetValue(SolutionAddCommandParser.ProjectPathArgument) ?? []); _inRoot = parseResult.GetValue(SolutionAddCommandParser.InRootOption); _solutionFolderPath = parseResult.GetValue(SolutionAddCommandParser.SolutionFolderOption); - _addReferencedProjects = parseResult.GetValue(SolutionAddCommandParser.AddReferencedProjectsOption); + _includeReferences = parseResult.GetValue(SolutionAddCommandParser.IncludeReferencesOption); SolutionArgumentValidator.ParseAndValidateArguments(_fileOrDirectory, _projects, SolutionArgumentValidator.CommandType.Add, _inRoot, _solutionFolderPath); _solutionFileFullPath = SlnFileFactory.GetSolutionFileFullPath(_fileOrDirectory); } @@ -213,7 +213,7 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, solutionRelativeProjectPath); - if (_addReferencedProjects) + if (_includeReferences) { foreach (var referencedProjectFullPath in referencedProjectsFullPaths) { diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs index 4b4c71e0c3a3..f5bfda7da35a 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommandParser.cs @@ -25,7 +25,7 @@ public static class SolutionAddCommandParser Description = CliCommandStrings.AddProjectSolutionFolderArgumentDescription }; - public static readonly Option AddReferencedProjectsOption = new("--include-references") + public static readonly Option IncludeReferencesOption = new("--include-references") { Description = CliCommandStrings.SolutionAddReferencedProjectsOptionDescription, DefaultValueFactory = (_) => true, @@ -45,7 +45,7 @@ private static Command ConstructCommand() command.Arguments.Add(ProjectPathArgument); command.Options.Add(InRootOption); command.Options.Add(SolutionFolderOption); - command.Options.Add(AddReferencedProjectsOption); + command.Options.Add(IncludeReferencesOption); command.SetAction((parseResult) => new SolutionAddCommand(parseResult).Execute()); diff --git a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/A/A.csproj b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/A/A.csproj new file mode 100644 index 000000000000..6b3ec4712e96 --- /dev/null +++ b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/A/A.csproj @@ -0,0 +1,12 @@ + + + + Exe + net10.0 + enable + enable + + + + + diff --git a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.sln b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.sln new file mode 100644 index 000000000000..58ea56664421 --- /dev/null +++ b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.sln @@ -0,0 +1,14 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx new file mode 100644 index 000000000000..4e2253ddceed --- /dev/null +++ b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx @@ -0,0 +1 @@ + diff --git a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/B/B.csproj b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/B/B.csproj new file mode 100644 index 000000000000..ed9781c223ab --- /dev/null +++ b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/B/B.csproj @@ -0,0 +1,10 @@ + + + + Exe + net10.0 + enable + enable + + + diff --git a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs index 9f9c69a6a51b..72e9cddb16c0 100644 --- a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs +++ b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs @@ -8,6 +8,7 @@ using Microsoft.VisualStudio.SolutionPersistence; using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.DotNet.Cli.Commands; +using System.Threading.Tasks; namespace Microsoft.DotNet.Cli.Sln.Add.Tests { @@ -1154,6 +1155,28 @@ public async Task WhenAddingProjectOutsideDirectoryItShouldNotAddSolutionFolders solution.SolutionFolders.Count.Should().Be(0); } + [Theory] + [InlineData("sln", ".sln")] + [InlineData("solution", ".sln")] + [InlineData("sln", ".slnx")] + [InlineData("solution", ".slnx")] + public async Task WhenSolutionIsPassedAProjectWithReferenceItAddsOtherProject(string solutionCommand, string solutionExtension) + { + var projectDirectory = _testAssetsManager + .CopyTestAsset("SlnFileWithReferencedProjects", identifier: $"GivenDotnetSlnAdd-{solutionCommand}") + .WithSource() + .Path; + var projectToAdd = Path.Combine("A", "A.csproj"); + var cmd = new DotnetCommand(Log) + .WithWorkingDirectory(Path.Join(projectDirectory)) + .Execute(solutionCommand, $"App{solutionExtension}", "add", projectToAdd); + cmd.Should().Pass(); + // Should have two projects + ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(Path.Join(projectDirectory, $"App{solutionExtension}")); + SolutionModel solution = await serializer.OpenAsync(Path.Join(projectDirectory, $"App{solutionExtension}"), CancellationToken.None); + solution.SolutionProjects.Count.Should().Be(2); + } + private string GetExpectedSlnContents( string slnPath, string slnTemplateName, @@ -1237,6 +1260,8 @@ public void WhenSolutionIsPassedAsProjectWithSolutionFolderItPrintsSuggestionAnd { VerifySuggestionAndUsage(solutionCommand, "--solution-folder", solutionExtension); } + + private void VerifySuggestionAndUsage(string solutionCommand, string arguments, string solutionExtension) { var projectDirectory = _testAssetsManager From 38c7840a871586ca90c7637033e1fe597bf465e5 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 5 May 2025 14:04:22 -0700 Subject: [PATCH 03/11] Check for flag too --- .../Solution/Add/GivenDotnetSlnAdd.cs | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs index 72e9cddb16c0..a275c327d3f0 100644 --- a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs +++ b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs @@ -1156,11 +1156,15 @@ public async Task WhenAddingProjectOutsideDirectoryItShouldNotAddSolutionFolders } [Theory] - [InlineData("sln", ".sln")] - [InlineData("solution", ".sln")] - [InlineData("sln", ".slnx")] - [InlineData("solution", ".slnx")] - public async Task WhenSolutionIsPassedAProjectWithReferenceItAddsOtherProject(string solutionCommand, string solutionExtension) + [InlineData("sln", ".sln", "")] + [InlineData("solution", ".sln", "")] + [InlineData("sln", ".slnx", "")] + [InlineData("solution", ".slnx", "")] + [InlineData("sln", ".sln", "--include-references=false")] + [InlineData("solution", ".sln", "--include-references=false")] + [InlineData("sln", ".slnx", "--include-references=false")] + [InlineData("solution", ".slnx", "--include-references=false")] + public async Task WhenSolutionIsPassedAProjectWithReferenceItAddsOtherProjectUnlessSpecified(string solutionCommand, string solutionExtension, string option) { var projectDirectory = _testAssetsManager .CopyTestAsset("SlnFileWithReferencedProjects", identifier: $"GivenDotnetSlnAdd-{solutionCommand}") @@ -1169,12 +1173,20 @@ public async Task WhenSolutionIsPassedAProjectWithReferenceItAddsOtherProject(st var projectToAdd = Path.Combine("A", "A.csproj"); var cmd = new DotnetCommand(Log) .WithWorkingDirectory(Path.Join(projectDirectory)) - .Execute(solutionCommand, $"App{solutionExtension}", "add", projectToAdd); + .Execute(solutionCommand, $"App{solutionExtension}", "add", projectToAdd, option); cmd.Should().Pass(); // Should have two projects ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(Path.Join(projectDirectory, $"App{solutionExtension}")); SolutionModel solution = await serializer.OpenAsync(Path.Join(projectDirectory, $"App{solutionExtension}"), CancellationToken.None); - solution.SolutionProjects.Count.Should().Be(2); + + if (option.Equals("--include-references=false")) // Option is true by default + { + solution.SolutionProjects.Count.Should().Be(1); + } + else + { + solution.SolutionProjects.Count.Should().Be(2); + } } private string GetExpectedSlnContents( From 993c28cb95b88a496751b9bf1bdd5462a54f86e4 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 5 May 2025 14:40:48 -0700 Subject: [PATCH 04/11] Fix tests --- src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf | 5 +++++ src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf | 5 +++++ .../CommandTests/Solution/Add/GivenDotnetSlnAdd.cs | 8 ++++---- 14 files changed, 69 insertions(+), 4 deletions(-) diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf index c30a1ee4f568..905e767f24ab 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.cs.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf index 8fefe9ecb9f9..3896c64b9e61 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.de.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf index d58d43020211..20afd4520f23 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.es.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf index 59225c3273f9..71ff36a7e759 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.fr.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf index 7a64459070aa..3a29e67f41db 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.it.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf index c3bb747280ee..5c1579261b1a 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ja.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf index 07fbe8c1f4c1..2b3218faac44 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ko.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf index 9f983fdf516e..9a75d810dd44 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pl.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf index a3a94cf6a8b0..72a2415040df 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.pt-BR.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf index 0f760888b545..04df2e5813ca 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.ru.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf index 732e31dbf8cf..892aecbdce6b 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.tr.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf index 862bfeed422e..08f128a0eeab 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hans.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf index d0dd5815d7d2..61319bc77d40 100644 --- a/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/Commands/xlf/CliCommandStrings.zh-Hant.xlf @@ -2721,6 +2721,11 @@ Your project targets multiple frameworks. Specify which framework to run using ' .slnx file {0} generated. + + Recursively add projects' ReferencedProjects to solution + Recursively add projects' ReferencedProjects to solution + + .NET modify solution file command .NET modify solution file command diff --git a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs index a275c327d3f0..2ecd5228fb8a 100644 --- a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs +++ b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs @@ -1156,10 +1156,10 @@ public async Task WhenAddingProjectOutsideDirectoryItShouldNotAddSolutionFolders } [Theory] - [InlineData("sln", ".sln", "")] - [InlineData("solution", ".sln", "")] - [InlineData("sln", ".slnx", "")] - [InlineData("solution", ".slnx", "")] + [InlineData("sln", ".sln", "--include-references=true")] + [InlineData("solution", ".sln", "--include-references=true")] + [InlineData("sln", ".slnx", "--include-references=true")] + [InlineData("solution", ".slnx", "--include-references=true")] [InlineData("sln", ".sln", "--include-references=false")] [InlineData("solution", ".sln", "--include-references=false")] [InlineData("sln", ".slnx", "--include-references=false")] From bd001ce1207aae30b1e90d88e5edfca0f7f8e4d2 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Mon, 5 May 2025 14:55:51 -0700 Subject: [PATCH 05/11] Address pr comments --- .../dotnet/Commands/Solution/Add/SolutionAddCommand.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index 96aa27226354..0681f7ec5d75 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -204,14 +204,12 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio project.AddProjectConfigurationRule(new ConfigurationRule(BuildDimension.BuildType, solutionBuildType, "*", projectBuildType)); } + Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, solutionRelativeProjectPath); + // Get referencedprojects from the project instance var referencedProjectsFullPaths = projectInstance.EvaluatedItemElements - .Where(item => item.ItemType == "ProjectReference") - .Select(item => item.Include) - .Select(item => Path.GetFullPath(item, Path.GetDirectoryName(fullProjectPath))) - .ToList(); - - Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, solutionRelativeProjectPath); + .Where(item => item.ItemType.Equals("ProjectReference")) + .Select(item => Path.GetFullPath(item.Include, Path.GetDirectoryName(fullProjectPath))); if (_includeReferences) { From 09abcb99956aab3f781fd2b0c610d6bc4755bd0b Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 6 May 2025 10:27:29 -0700 Subject: [PATCH 06/11] Udpate cli completions --- .../DotnetCliSnapshotTests.VerifyCompletions.verified.sh | 6 +++++- .../DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 | 1 + .../DotnetCliSnapshotTests.VerifyCompletions.verified.zsh | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh b/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh index f3989e9b1bc8..6e76bbce5854 100644 --- a/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh +++ b/test/dotnet.Tests/CompletionTests/snapshots/bash/DotnetCliSnapshotTests.VerifyCompletions.verified.sh @@ -1421,7 +1421,7 @@ _testhost_solution_add() { prev="${COMP_WORDS[COMP_CWORD-1]}" COMPREPLY=() - opts="--in-root --solution-folder --help" + opts="--in-root --solution-folder --include-references --help" if [[ $COMP_CWORD == "$1" ]]; then COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) @@ -1433,6 +1433,10 @@ _testhost_solution_add() { COMPREPLY=( $(compgen -W "False True" -- "$cur") ) return ;; + --include-references) + COMPREPLY=( $(compgen -W "False True" -- "$cur") ) + return + ;; esac COMPREPLY=( $(compgen -W "$opts" -- "$cur") ) diff --git a/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 b/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 index 41d7d01919e3..cbac51733cdf 100644 --- a/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 +++ b/test/dotnet.Tests/CompletionTests/snapshots/pwsh/DotnetCliSnapshotTests.VerifyCompletions.verified.ps1 @@ -840,6 +840,7 @@ Register-ArgumentCompleter -Native -CommandName 'testhost' -ScriptBlock { [CompletionResult]::new('--in-root', '--in-root', [CompletionResultType]::ParameterName, "Place project in root of the solution, rather than creating a solution folder.") [CompletionResult]::new('--solution-folder', '--solution-folder', [CompletionResultType]::ParameterName, "The destination solution folder path to add the projects to.") [CompletionResult]::new('--solution-folder', '-s', [CompletionResultType]::ParameterName, "The destination solution folder path to add the projects to.") + [CompletionResult]::new('--include-references', '--include-references', [CompletionResultType]::ParameterName, "Recursively add projects`' ReferencedProjects to solution") [CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, "Show command line help.") [CompletionResult]::new('--help', '-h', [CompletionResultType]::ParameterName, "Show command line help.") ) diff --git a/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh b/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh index 758af444236c..109551b4e1a4 100644 --- a/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh +++ b/test/dotnet.Tests/CompletionTests/snapshots/zsh/DotnetCliSnapshotTests.VerifyCompletions.verified.zsh @@ -888,6 +888,7 @@ _testhost() { '--in-root=[Place project in root of the solution, rather than creating a solution folder.]: :((False\:"False" True\:"True" ))' \ '--solution-folder=[The destination solution folder path to add the projects to.]: : ' \ '-s=[The destination solution folder path to add the projects to.]: : ' \ + '--include-references=[Recursively add projects'\'' ReferencedProjects to solution]: :((False\:"False" True\:"True" ))' \ '--help[Show command line help.]' \ '-h[Show command line help.]' \ '*::PROJECT_PATH -- The paths to the projects to add to the solution.: ' \ From 1576ff52ea7611b469a139a342f586cd8d45e3ae Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Tue, 6 May 2025 11:25:01 -0700 Subject: [PATCH 07/11] Update tests messages --- test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs index 2ecd5228fb8a..97d8f36769da 100644 --- a/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs +++ b/test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs @@ -37,6 +37,7 @@ dotnet solution add [...] [options] Options: --in-root Place project in root of the solution, rather than creating a solution folder. -s, --solution-folder The destination solution folder path to add the projects to. + --include-references Recursively add projects' ReferencedProjects to solution [default: True] -?, -h, --help Show command line help"; public GivenDotnetSlnAdd(ITestOutputHelper log) : base(log) From 34c0d3789c0a2bfd0340e4813011faa53b1dee6e Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Wed, 7 May 2025 09:38:17 -0700 Subject: [PATCH 08/11] Correctly get referencedprojects --- .../Commands/Solution/Add/SolutionAddCommand.cs | 11 +++++------ .../SlnFileWithReferencedProjects/App.slnx | 4 +++- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index 0681f7ec5d75..aee5b319fa7c 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -138,7 +138,7 @@ private async Task AddProjectsToSolutionAsync(IEnumerable projectPaths, await serializer.SaveAsync(_solutionFileFullPath, solution, cancellationToken); } - private void AddProject(SolutionModel solution, string fullProjectPath, ISolutionSerializer serializer = null) + private void AddProject(SolutionModel solution, string fullProjectPath, ISolutionSerializer serializer = null, bool showMessageOnDuplicate = true) { string solutionRelativeProjectPath = Path.GetRelativePath(Path.GetDirectoryName(_solutionFileFullPath), fullProjectPath); @@ -173,7 +173,7 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio Reporter.Error.WriteLine(CliStrings.UnsupportedProjectType, fullProjectPath); return; } - catch (SolutionArgumentException ex) when (ex.Type == SolutionErrorType.DuplicateProjectName || solution.FindProject(solutionRelativeProjectPath) is not null) + catch (SolutionArgumentException ex) when (showMessageOnDuplicate && ex.Type == SolutionErrorType.DuplicateProjectName || solution.FindProject(solutionRelativeProjectPath) is not null) { Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, solutionRelativeProjectPath); return; @@ -207,15 +207,14 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio Reporter.Output.WriteLine(CliStrings.ProjectAddedToTheSolution, solutionRelativeProjectPath); // Get referencedprojects from the project instance - var referencedProjectsFullPaths = projectInstance.EvaluatedItemElements - .Where(item => item.ItemType.Equals("ProjectReference")) - .Select(item => Path.GetFullPath(item.Include, Path.GetDirectoryName(fullProjectPath))); + var referencedProjectsFullPaths = projectInstance.GetItems("ProjectReference") + .Select(item => Path.GetFullPath(item.EvaluatedInclude, Path.GetDirectoryName(fullProjectPath))); if (_includeReferences) { foreach (var referencedProjectFullPath in referencedProjectsFullPaths) { - AddProject(solution, referencedProjectFullPath, serializer); + AddProject(solution, referencedProjectFullPath, serializer, showMessageOnDuplicate: false); } } } diff --git a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx index 4e2253ddceed..386e90e3e732 100644 --- a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx +++ b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx @@ -1 +1,3 @@ - + + + From 266ef2e33450a1d8543eeb82937c125083d0bf0b Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Thu, 8 May 2025 10:41:08 -0700 Subject: [PATCH 09/11] Fix tests --- .../TestProjects/SlnFileWithReferencedProjects/App.slnx | 1 - 1 file changed, 1 deletion(-) diff --git a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx index 386e90e3e732..ba788ff0d17d 100644 --- a/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx +++ b/test/TestAssets/TestProjects/SlnFileWithReferencedProjects/App.slnx @@ -1,3 +1,2 @@ - From f8e64feaf15ba0e48292baf1686b20362a94f2a0 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Thu, 8 May 2025 14:15:30 -0700 Subject: [PATCH 10/11] Add parentheses to condition --- src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index aee5b319fa7c..e30a2739ac36 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -173,7 +173,7 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio Reporter.Error.WriteLine(CliStrings.UnsupportedProjectType, fullProjectPath); return; } - catch (SolutionArgumentException ex) when (showMessageOnDuplicate && ex.Type == SolutionErrorType.DuplicateProjectName || solution.FindProject(solutionRelativeProjectPath) is not null) + catch (SolutionArgumentException ex) when (showMessageOnDuplicate && (ex.Type == SolutionErrorType.DuplicateProjectName || solution.FindProject(solutionRelativeProjectPath) is not null)) { Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, solutionRelativeProjectPath); return; From 4b810a59d0376c120c5970deee092cd3ef804514 Mon Sep 17 00:00:00 2001 From: Eduardo Villalpando Mello Date: Thu, 8 May 2025 21:09:47 -0700 Subject: [PATCH 11/11] Address pr comments --- src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs index e0457a9fd9ad..5cf0b3817fde 100644 --- a/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs +++ b/src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs @@ -175,9 +175,12 @@ private void AddProject(SolutionModel solution, string fullProjectPath, ISolutio Reporter.Error.WriteLine(CliStrings.UnsupportedProjectType, fullProjectPath); return; } - catch (SolutionArgumentException ex) when (showMessageOnDuplicate && (ex.Type == SolutionErrorType.DuplicateProjectName || solution.FindProject(solutionRelativeProjectPath) is not null)) + catch (SolutionArgumentException ex) when (ex.Type == SolutionErrorType.DuplicateProjectName || solution.FindProject(solutionRelativeProjectPath) is not null) { - Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, solutionRelativeProjectPath); + if (showMessageOnDuplicate) + { + Reporter.Output.WriteLine(CliStrings.SolutionAlreadyContainsProject, _solutionFileFullPath, solutionRelativeProjectPath); + } return; }