diff --git a/src/Cli/dotnet/commands/dotnet-sln/LocalizableStrings.resx b/src/Cli/dotnet/commands/dotnet-sln/LocalizableStrings.resx index 954f3b45f1e9..4748d3d4504b 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/LocalizableStrings.resx +++ b/src/Cli/dotnet/commands/dotnet-sln/LocalizableStrings.resx @@ -183,4 +183,10 @@ .slnx file {0} generated. + + Only .sln files can be migrated to .slnx format. + + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + \ No newline at end of file diff --git a/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs b/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs index 47169569fc96..b2a2f685b752 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/SlnCommandParser.cs @@ -4,6 +4,8 @@ using System.CommandLine; using Microsoft.DotNet.Cli.Utils; using Microsoft.DotNet.Tools; +using Microsoft.VisualStudio.SolutionPersistence; +using Microsoft.VisualStudio.SolutionPersistence.Serializer; using NuGet.Packaging; using LocalizableStrings = Microsoft.DotNet.Tools.Sln.LocalizableStrings; @@ -50,11 +52,13 @@ internal static string GetSlnFileFullPath(string slnFileOrDirectory) { if (File.Exists(slnFileOrDirectory)) { - return slnFileOrDirectory; + return Path.GetFullPath(slnFileOrDirectory); } if (Directory.Exists(slnFileOrDirectory)) { - var files = Directory.GetFiles(slnFileOrDirectory, "*.sln", SearchOption.TopDirectoryOnly); + string[] files = [ + ..Directory.GetFiles(slnFileOrDirectory, "*.sln", SearchOption.TopDirectoryOnly), + ..Directory.GetFiles(slnFileOrDirectory, "*.slnx", SearchOption.TopDirectoryOnly)]; if (files.Length == 0) { throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionIn, slnFileOrDirectory); @@ -63,9 +67,19 @@ internal static string GetSlnFileFullPath(string slnFileOrDirectory) { throw new GracefulException(CommonLocalizableStrings.MoreThanOneSolutionInDirectory, slnFileOrDirectory); } - return files.Single().ToString(); + return Path.GetFullPath(files.Single()); } throw new GracefulException(CommonLocalizableStrings.CouldNotFindSolutionOrDirectory, slnFileOrDirectory); } + + internal static ISolutionSerializer GetSolutionSerializer(string solutionFilePath) + { + ISolutionSerializer? serializer = SolutionSerializers.GetSerializerByMoniker(solutionFilePath); + if (serializer is null) + { + throw new GracefulException(LocalizableStrings.SerializerNotFound, solutionFilePath); + } + return serializer; + } } } diff --git a/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs index a46642c421b4..d2921f243767 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/list/Program.cs @@ -5,7 +5,9 @@ using Microsoft.DotNet.Cli; using Microsoft.DotNet.Cli.Sln.Internal; using Microsoft.DotNet.Cli.Utils; -using Microsoft.DotNet.Tools.Common; +using Microsoft.VisualStudio.SolutionPersistence; +using Microsoft.VisualStudio.SolutionPersistence.Model; +using CommandLocalizableStrings = Microsoft.DotNet.Tools.CommonLocalizableStrings; namespace Microsoft.DotNet.Tools.Sln.List { @@ -23,25 +25,36 @@ public ListProjectsInSolutionCommand( public override int Execute() { - var slnFile = SlnFileFactory.CreateFromFileOrDirectory(_fileOrDirectory); + string solutionFileFullPath = SlnCommandParser.GetSlnFileFullPath(_fileOrDirectory); + try + { + ListAllProjectsAsync(solutionFileFullPath, CancellationToken.None).Wait(); + return 0; + } + catch (Exception ex) + { + throw new GracefulException(CommandLocalizableStrings.InvalidSolutionFormatString, solutionFileFullPath, ex.Message); + } + } + private async Task ListAllProjectsAsync(string solutionFileFullPath, CancellationToken cancellationToken) + { + ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath); + SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken); string[] paths; - if (_displaySolutionFolders) { - paths = slnFile.Projects - .GetProjectsByType(ProjectTypeGuids.SolutionFolderGuid) - .Select(folder => folder.GetFullSolutionFolderPath()) + paths = solution.SolutionFolders + // VS-SolutionPersistence does not return a path object, so there might be issues with forward/backward slashes on different platforms + .Select(folder => Path.GetDirectoryName(folder.Path.TrimStart("/"))) .ToArray(); } else { - paths = slnFile.Projects - .GetProjectsNotOfType(ProjectTypeGuids.SolutionFolderGuid) + paths = solution.SolutionProjects .Select(project => project.FilePath) .ToArray(); } - if (paths.Length == 0) { Reporter.Output.WriteLine(CommonLocalizableStrings.NoProjectsFound); @@ -51,14 +64,14 @@ public override int Execute() Array.Sort(paths); string header = _displaySolutionFolders ? LocalizableStrings.SolutionFolderHeader : LocalizableStrings.ProjectsHeader; - Reporter.Output.WriteLine($"{header}"); + Reporter.Output.WriteLine(header); Reporter.Output.WriteLine(new string('-', header.Length)); foreach (string slnProject in paths) { Reporter.Output.WriteLine(slnProject); } } - return 0; + } } } diff --git a/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs b/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs index f7be83272e40..1ac8dde1468e 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/migrate/SlnMigrateCommand.cs @@ -14,6 +14,7 @@ using Microsoft.VisualStudio.SolutionPersistence.Model; using Microsoft.VisualStudio.SolutionPersistence.Serializer; using LocalizableStrings = Microsoft.DotNet.Tools.Sln.LocalizableStrings; +using Microsoft.DotNet.Tools.Common; namespace Microsoft.DotNet.Cli { @@ -26,13 +27,17 @@ public SlnMigrateCommand( IReporter reporter = null) : base(parseResult) { - _slnFileOrDirectory = Path.GetFullPath(parseResult.GetValue(SlnCommandParser.SlnArgument)); + _slnFileOrDirectory = parseResult.GetValue(SlnCommandParser.SlnArgument); _reporter = reporter ?? Reporter.Output; } public override int Execute() { string slnFileFullPath = SlnCommandParser.GetSlnFileFullPath(_slnFileOrDirectory); + if (slnFileFullPath.HasExtension(".slnx")) + { + throw new GracefulException(LocalizableStrings.CannotMigrateSlnx); + } string slnxFileFullPath = Path.ChangeExtension(slnFileFullPath, "slnx"); try { @@ -45,12 +50,7 @@ public override int Execute() private async Task ConvertToSlnxAsync(string filePath, string slnxFilePath, CancellationToken cancellationToken) { - // See if the file is a known solution file. - ISolutionSerializer? serializer = SolutionSerializers.GetSerializerByMoniker(filePath); - if (serializer is null) - { - throw new GracefulException("Could not find serializer for file {0}", filePath); - } + ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(filePath); SolutionModel solution = await serializer.OpenAsync(filePath, cancellationToken); await SolutionSerializers.SlnXml.SaveAsync(slnxFilePath, solution, cancellationToken); _reporter.WriteLine(LocalizableStrings.SlnxGenerated, slnxFilePath); diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf index 6c53575ea9eb..f5b32233e7d0 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.cs.xlf @@ -27,6 +27,11 @@ Přidá do souboru řešení jeden nebo více projektů. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Umístěte projekt do kořene řešení, není potřeba vytvářet složku řešení. @@ -62,6 +67,11 @@ Vypíše seznam všech projektů v řešení. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf index 4e3b2955d5b6..f8eb231f7950 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.de.xlf @@ -27,6 +27,11 @@ Fügt einer Projektmappendatei ein oder mehrere Projekte hinzu. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Platzieren Sie das Projekt im Stamm der Projektmappe, statt einen Projektmappenordner zu erstellen. @@ -62,6 +67,11 @@ Listet alle Projekte in der Projektmappe auf. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf index aa87dd21de2b..6ed23a5e39fe 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.es.xlf @@ -27,6 +27,11 @@ Agrega uno o varios proyectos a un archivo de solución. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Coloque el proyecto en la raíz de la solución, en lugar de crear una carpeta de soluciones. @@ -62,6 +67,11 @@ Enumere todos los proyectos de la solución. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf index 243195f00fff..b1d8c5880aff 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.fr.xlf @@ -27,6 +27,11 @@ Ajoutez un ou plusieurs projets à un fichier solution. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Place le projet à la racine de la solution, au lieu de créer un dossier solution. @@ -62,6 +67,11 @@ Répertoriez tous les projets de la solution. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf index d9684bca6f50..989a4f33844e 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.it.xlf @@ -27,6 +27,11 @@ Consente di aggiungere uno o più progetti a un file di soluzione. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Inserisce il progetto nella radice della soluzione invece di creare una cartella soluzione. @@ -62,6 +67,11 @@ Elenca tutti i progetti presenti nella soluzione. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf index 15ab2149217c..2683028f11aa 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ja.xlf @@ -27,6 +27,11 @@ 1 つ以上のプロジェクトをソリューション ファイルに追加します。 + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. ソリューション フォルダーを作成するのではなく、プロジェクトをソリューションのルートに配置します。 @@ -62,6 +67,11 @@ ソリューション内のすべてのプロジェクトを一覧表示します。 + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf index c1004aaae4a6..895400338f29 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ko.xlf @@ -27,6 +27,11 @@ 솔루션 파일에 하나 이상의 프로젝트를 추가합니다. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. 솔루션 폴더를 만드는 대신, 솔루션의 루트에 프로젝트를 배치하세요. @@ -62,6 +67,11 @@ 솔루션의 프로젝트를 모두 나열합니다. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf index 8ccc8c2101b7..e384d5d00475 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pl.xlf @@ -27,6 +27,11 @@ Dodaj co najmniej jeden projekt do pliku rozwiązania. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Umieść projekt w katalogu głównym rozwiązania zamiast tworzyć folder rozwiązania. @@ -62,6 +67,11 @@ Wyświetl listę wszystkich projektów w rozwiązaniu. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf index 033a36200e57..ba4d879b8340 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.pt-BR.xlf @@ -27,6 +27,11 @@ Adicionar um ou mais projetos em um arquivo de solução. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Coloque o projeto na raiz da solução, em vez de criar uma pasta da solução. @@ -62,6 +67,11 @@ Listar todos os projetos na solução. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf index 793e580c833b..c5dfbd52a60c 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.ru.xlf @@ -27,6 +27,11 @@ Добавление проектов в файл решения. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Поместите проект в корень решения вместо создания папки решения. @@ -62,6 +67,11 @@ Перечисляет все проекты в решении. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf index 708dd2cf1377..fea7bcc4ea34 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.tr.xlf @@ -27,6 +27,11 @@ Bir çözüm dosyasına bir veya daha fazla proje ekler. + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. Bir çözüm klasörü oluşturmak yerine projeyi çözümün köküne yerleştirin. @@ -62,6 +67,11 @@ Çözümdeki tüm projeleri listeleyin. + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf index fd02b1f09400..82241d2d4a6a 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hans.xlf @@ -27,6 +27,11 @@ 将一个或多个项目添加到解决方案文件。 + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. 将项目放在解决方案的根目录下,而不是创建解决方案文件夹。 @@ -62,6 +67,11 @@ 列出解决方案中的所有项目。 + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf index 634a6210b0e9..5b061ec4f5f2 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf +++ b/src/Cli/dotnet/commands/dotnet-sln/xlf/LocalizableStrings.zh-Hant.xlf @@ -27,6 +27,11 @@ 為解決方案檔新增一或多個專案。 + + Only .sln files can be migrated to .slnx format. + Only .sln files can be migrated to .slnx format. + + Place project in root of the solution, rather than creating a solution folder. 請將專案放置在解決方案的根目錄中,而非放置於建立解決方案的資料夾中。 @@ -62,6 +67,11 @@ 列出解決方案中的所有專案。 + + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + Could not read solution file {0}. Supported files are .sln and .slnx valid solutions. + + .slnx file {0} generated. .slnx file {0} generated. diff --git a/test/dotnet-sln.Tests/GivenDotnetSlnList.cs b/test/dotnet-sln.Tests/GivenDotnetSlnList.cs index 77670963b1ad..f8144602ad25 100644 --- a/test/dotnet-sln.Tests/GivenDotnetSlnList.cs +++ b/test/dotnet-sln.Tests/GivenDotnetSlnList.cs @@ -100,7 +100,8 @@ public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage(string solutionComm .WithWorkingDirectory(projectDirectory) .Execute(solutionCommand, "InvalidSolution.sln", "list"); cmd.Should().Fail(); - cmd.StdErr.Should().Be(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, "InvalidSolution.sln", LocalizableStrings.FileHeaderMissingError)); + cmd.StdErr.Should().Contain( + string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, Path.Combine(projectDirectory, "InvalidSolution.sln"), "").TrimEnd(".")); cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized(""); } @@ -119,7 +120,8 @@ public void WhenInvalidSolutionIsFoundListPrintsErrorAndUsage(string solutionCom .WithWorkingDirectory(projectDirectory) .Execute(solutionCommand, "list"); cmd.Should().Fail(); - cmd.StdErr.Should().Be(string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFullPath, LocalizableStrings.FileHeaderMissingError)); + cmd.StdErr.Should().Contain( + string.Format(CommonLocalizableStrings.InvalidSolutionFormatString, solutionFullPath, "").TrimEnd(".")); cmd.StdOut.Should().BeVisuallyEquivalentToIfNotLocalized(""); }