Merge branch 'rel/1.0.0' into dev/jgoshi/removeProj

This commit is contained in:
Justin Goshi 2016-12-16 08:44:38 -08:00
commit 82f5278d8a
22 changed files with 480 additions and 56 deletions

View file

@ -0,0 +1,18 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26006.2
MinimumVisualStudioVersion = 10.0.40219.1
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View file

@ -189,6 +189,14 @@ namespace Microsoft.DotNet.Cli.CommandLine
}
}
if (Commands.Count > 0 && command == this)
{
throw new CommandParsingException(
command,
"Required command missing",
isRequireSubCommandMissing: true);
}
return command.Invoke();
}

View file

@ -7,12 +7,17 @@ namespace Microsoft.DotNet.Cli.CommandLine
{
internal class CommandParsingException : Exception
{
public CommandParsingException(CommandLineApplication command, string message)
public CommandParsingException(
CommandLineApplication command,
string message,
bool isRequireSubCommandMissing = false)
: base(message)
{
Command = command;
IsRequireSubCommandMissing = isRequireSubCommandMissing;
}
public CommandLineApplication Command { get; }
public bool IsRequireSubCommandMissing { get; }
}
}

View file

@ -1,4 +1,7 @@
namespace Microsoft.DotNet.Tools
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools
{
internal class CommonLocalizableStrings
{
@ -88,6 +91,7 @@
public const string OptionIsInvalid = "Option {0} is invalid.";
public const string ArgumentIsInvalid = "Argument {0} is invalid.";
public const string RequiredArgumentNotPassed = "Required argument {0} was not provided.";
public const string RequiredCommandNotPassed = "Required command was not provided.";
// dotnet <verb>
/// Project

View file

@ -76,7 +76,11 @@ namespace Microsoft.DotNet.Cli
}
catch (CommandParsingException e)
{
Reporter.Error.WriteLine(e.Message.Red());
string errorMessage = e.IsRequireSubCommandMissing
? CommonLocalizableStrings.RequiredCommandNotPassed
: e.Message;
Reporter.Error.WriteLine(errorMessage.Red());
return 1;
}
}

View file

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.Add
{
internal class LocalizableStrings

View file

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.Add.ProjectToProjectReference
{
internal class LocalizableStrings

View file

@ -0,0 +1,13 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Collections.Generic;
namespace Microsoft.DotNet.Tools.List
{
public interface IListSubCommand
{
string LocalizedErrorMessageNoItemsFound { get; }
IList<string> Items { get; }
}
}

View file

@ -0,0 +1,71 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.Common;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace Microsoft.DotNet.Tools.List
{
public abstract class ListSubCommandBase
{
protected abstract string CommandName { get; }
protected abstract string LocalizedDisplayName { get; }
protected abstract string LocalizedDescription { get; }
protected abstract IListSubCommand CreateIListSubCommand(string fileOrDirectory);
internal CommandLineApplication Create(CommandLineApplication parentApp)
{
CommandLineApplication app = parentApp.Command(CommandName, throwOnUnexpectedArg: false);
app.FullName = LocalizedDisplayName;
app.Description = LocalizedDescription;
app.HelpOption("-h|--help");
app.OnExecute(() => {
try
{
if (!parentApp.Arguments.Any())
{
throw new GracefulException(
CommonLocalizableStrings.RequiredArgumentNotPassed,
Constants.ProjectOrSolutionArgumentName);
}
var projectOrDirectory = parentApp.Arguments.First().Value;
if (string.IsNullOrEmpty(projectOrDirectory))
{
projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory());
}
var listCommand = CreateIListSubCommand(projectOrDirectory);
if (listCommand.Items.Count == 0)
{
Reporter.Output.WriteLine(listCommand.LocalizedErrorMessageNoItemsFound);
return 0;
}
Reporter.Output.WriteLine($"{CommonLocalizableStrings.ProjectReferenceOneOrMore}");
Reporter.Output.WriteLine(new string('-', CommonLocalizableStrings.ProjectReferenceOneOrMore.Length));
foreach (var item in listCommand.Items)
{
Reporter.Output.WriteLine(item);
}
return 0;
}
catch (GracefulException e)
{
Reporter.Error.WriteLine(e.Message.Red());
app.ShowHelp();
return 1;
}
});
return app;
}
}
}

View file

@ -1,4 +1,7 @@
namespace Microsoft.DotNet.Tools.List
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.List
{
internal class LocalizableStrings
{

View file

@ -6,6 +6,7 @@ using System.Collections.Generic;
using Microsoft.DotNet.Cli;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Tools.List.ProjectToProjectReferences;
using Microsoft.DotNet.Tools.List.ProjectsInSolution;
namespace Microsoft.DotNet.Tools.List
{
@ -16,6 +17,7 @@ namespace Microsoft.DotNet.Tools.List
internal override List<Func<CommandLineApplication, CommandLineApplication>> SubCommands =>
new List<Func<CommandLineApplication, CommandLineApplication>>
{
ListProjectsInSolutionCommand.CreateApplication,
ListProjectToProjectReferencesCommand.CreateApplication,
};

View file

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.List.ProjectToProjectReferences
{
internal class LocalizableStrings

View file

@ -3,65 +3,51 @@
using Microsoft.Build.Evaluation;
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.Tools.Common;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.DotNet.Tools.List;
namespace Microsoft.DotNet.Tools.List.ProjectToProjectReferences
{
public class ListProjectToProjectReferencesCommand
public class ListProjectToProjectReferences : IListSubCommand
{
private string _fileOrDirectory = null;
private IList<string> _items = new List<string>();
public ListProjectToProjectReferences(string fileOrDirectory)
{
_fileOrDirectory = fileOrDirectory;
var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), fileOrDirectory);
var p2ps = msbuildProj.GetProjectToProjectReferences();
foreach (var p2p in p2ps)
{
_items.Add(p2p.Include);
}
}
public string LocalizedErrorMessageNoItemsFound => string.Format(
LocalizableStrings.NoReferencesFound,
CommonLocalizableStrings.P2P,
_fileOrDirectory);
public IList<string> Items => _items;
}
public class ListProjectToProjectReferencesCommand : ListSubCommandBase
{
protected override string CommandName => "p2ps";
protected override string LocalizedDisplayName => LocalizableStrings.AppFullName;
protected override string LocalizedDescription => LocalizableStrings.AppDescription;
protected override IListSubCommand CreateIListSubCommand(string fileOrDirectory)
{
return new ListProjectToProjectReferences(fileOrDirectory);
}
internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp)
{
CommandLineApplication app = parentApp.Command("p2ps", throwOnUnexpectedArg: false);
app.FullName = LocalizableStrings.AppFullName;
app.Description = LocalizableStrings.AppDescription;
app.HelpOption("-h|--help");
app.OnExecute(() => {
try
{
if (!parentApp.Arguments.Any())
{
throw new GracefulException(CommonLocalizableStrings.RequiredArgumentNotPassed, Constants.ProjectOrSolutionArgumentName);
}
var projectOrDirectory = parentApp.Arguments.First().Value;
if (string.IsNullOrEmpty(projectOrDirectory))
{
projectOrDirectory = PathUtility.EnsureTrailingSlash(Directory.GetCurrentDirectory());
}
var msbuildProj = MsbuildProject.FromFileOrDirectory(new ProjectCollection(), projectOrDirectory);
var p2ps = msbuildProj.GetProjectToProjectReferences();
if (p2ps.Count() == 0)
{
Reporter.Output.WriteLine(string.Format(LocalizableStrings.NoReferencesFound, CommonLocalizableStrings.P2P, projectOrDirectory));
return 0;
}
Reporter.Output.WriteLine($"{CommonLocalizableStrings.ProjectReferenceOneOrMore}");
Reporter.Output.WriteLine(new string('-', CommonLocalizableStrings.ProjectReferenceOneOrMore.Length));
foreach (var p2p in p2ps)
{
Reporter.Output.WriteLine(p2p.Include);
}
return 0;
}
catch (GracefulException e)
{
Reporter.Error.WriteLine(e.Message.Red());
app.ShowHelp();
return 1;
}
});
return app;
var command = new ListProjectToProjectReferencesCommand();
return command.Create(parentApp);
}
}
}

View file

@ -0,0 +1,12 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.List.ProjectsInSolution
{
internal class LocalizableStrings
{
public const string AppFullName = ".NET Projects in Solution viewer";
public const string AppDescription = "Command to list projects in a solution";
}
}

View file

@ -0,0 +1,46 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using Microsoft.DotNet.Cli.CommandLine;
using Microsoft.DotNet.Cli.Sln.Internal;
using Microsoft.DotNet.Tools.Common;
using System.Collections.Generic;
using Microsoft.DotNet.Tools.List;
namespace Microsoft.DotNet.Tools.List.ProjectsInSolution
{
public class ListProjectsInSolution : IListSubCommand
{
private IList<string> _items = new List<string>();
public ListProjectsInSolution(string fileOrDirectory)
{
SlnFile slnFile = SlnFileFactory.CreateFromFileOrDirectory(fileOrDirectory);
foreach (var slnProject in slnFile.Projects)
{
_items.Add(slnProject.FilePath);
}
}
public string LocalizedErrorMessageNoItemsFound => CommonLocalizableStrings.NoProjectsFound;
public IList<string> Items => _items;
}
public class ListProjectsInSolutionCommand : ListSubCommandBase
{
protected override string CommandName => "projects";
protected override string LocalizedDisplayName => LocalizableStrings.AppFullName;
protected override string LocalizedDescription => LocalizableStrings.AppDescription;
protected override IListSubCommand CreateIListSubCommand(string fileOrDirectory)
{
return new ListProjectsInSolution(fileOrDirectory);
}
internal static CommandLineApplication CreateApplication(CommandLineApplication parentApp)
{
var command = new ListProjectsInSolutionCommand();
return command.Create(parentApp);
}
}
}

View file

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.Remove
{
internal class LocalizableStrings

View file

@ -1,3 +1,6 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace Microsoft.DotNet.Tools.Remove.ProjectToProjectReference
{
internal class LocalizableStrings

View file

@ -49,6 +49,15 @@ Args:
cmd.StdOut.Should().Be("Specify --help for a list of available options and commands.");
}
[Fact]
public void WhenTooManyArgumentsArePassedItPrintsError()
{
var cmd = new DotnetCommand()
.ExecuteWithCapturedOutput("add one.sln two.sln three.sln project");
cmd.Should().Fail();
cmd.StdErr.Should().Contain("Unrecognized command or argument");
}
[Theory]
[InlineData("idontexist.sln")]
[InlineData("ihave?invalidcharacters")]

View file

@ -0,0 +1,182 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using FluentAssertions;
using Microsoft.DotNet.Cli.Sln.Internal;
using Microsoft.DotNet.Tools.Test.Utilities;
using System;
using System.IO;
using System.Linq;
using Xunit;
namespace Microsoft.DotNet.Cli.List.Proj.Tests
{
public class GivenDotnetListProj : TestBase
{
private const string HelpText = @".NET Projects in Solution viewer
Usage: dotnet list <PROJECT_OR_SOLUTION> projects [options]
Arguments:
<PROJECT_OR_SOLUTION> The project or solution to operation on. If a file is not specified, the current directory is searched.
Options:
-h|--help Show help information";
[Theory]
[InlineData("--help")]
[InlineData("-h")]
public void WhenHelpOptionIsPassedItPrintsUsage(string helpArg)
{
var cmd = new DotnetCommand()
.ExecuteWithCapturedOutput($"list projects {helpArg}");
cmd.Should().Pass();
cmd.StdOut.Should().Contain(HelpText);
}
[Theory]
[InlineData("")]
[InlineData("unknownCommandName")]
public void WhenNoCommandIsPassedItPrintsError(string commandName)
{
var cmd = new DotnetCommand()
.ExecuteWithCapturedOutput($"list {commandName}");
cmd.Should().Fail();
cmd.StdErr.Should().Contain("Required command was not provided.");
}
[Fact]
public void WhenTooManyArgumentsArePassedItPrintsError()
{
var cmd = new DotnetCommand()
.ExecuteWithCapturedOutput("list one.sln two.sln three.sln projects");
cmd.Should().Fail();
cmd.StdErr.Should().Contain("Unrecognized command or argument 'two.sln'");
}
[Theory]
[InlineData("idontexist.sln")]
[InlineData("ihave?invalidcharacters.sln")]
[InlineData("ihaveinv@lidcharacters.sln")]
[InlineData("ihaveinvalid/characters")]
[InlineData("ihaveinvalidchar\\acters")]
public void WhenNonExistingSolutionIsPassedItPrintsErrorAndUsage(string solutionName)
{
var cmd = new DotnetCommand()
.ExecuteWithCapturedOutput($"list {solutionName} projects");
cmd.Should().Fail();
cmd.StdErr.Should().Contain($"Could not find solution or directory `{solutionName}`");
cmd.StdOut.Should().Contain(HelpText);
}
[Fact]
public void WhenInvalidSolutionIsPassedItPrintsErrorAndUsage()
{
var projectDirectory = TestAssets
.Get("InvalidSolution")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var cmd = new DotnetCommand()
.WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput("list InvalidSolution.sln projects");
cmd.Should().Fail();
cmd.StdErr.Should().Contain("Invalid solution `InvalidSolution.sln`");
cmd.StdOut.Should().Contain(HelpText);
}
[Fact]
public void WhenInvalidSolutionIsFoundItPrintsErrorAndUsage()
{
var projectDirectory = TestAssets
.Get("InvalidSolution")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var solutionFullPath = Path.Combine(projectDirectory, "InvalidSolution.sln");
var cmd = new DotnetCommand()
.WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput("list projects");
cmd.Should().Fail();
cmd.StdErr.Should().Contain($"Invalid solution `{solutionFullPath}`");
cmd.StdOut.Should().Contain(HelpText);
}
[Fact]
public void WhenNoSolutionExistsInTheDirectoryItPrintsErrorAndUsage()
{
var projectDirectory = TestAssets
.Get("TestAppWithSlnAndCsprojFiles")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var solutionDir = Path.Combine(projectDirectory, "App");
var cmd = new DotnetCommand()
.WithWorkingDirectory(solutionDir)
.ExecuteWithCapturedOutput("list projects");
cmd.Should().Fail();
cmd.StdErr.Should().Contain($"Specified solution file {solutionDir + Path.DirectorySeparatorChar} does not exist, or there is no solution file in the directory");
cmd.StdOut.Should().Contain(HelpText);
}
[Fact]
public void WhenMoreThanOneSolutionExistsInTheDirectoryItPrintsErrorAndUsage()
{
var projectDirectory = TestAssets
.Get("TestAppWithMultipleSlnFiles")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var cmd = new DotnetCommand()
.WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput("list projects");
cmd.Should().Fail();
cmd.StdErr.Should().Contain($"Found more than one solution file in {projectDirectory + Path.DirectorySeparatorChar}. Please specify which one to use.");
cmd.StdOut.Should().Contain(HelpText);
}
[Fact]
public void WhenNoProjectReferencesArePresentInTheSolutionItPrintsANoProjectMessage()
{
var projectDirectory = TestAssets
.Get("SlnFileWithNoProjectReferences")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var cmd = new DotnetCommand()
.WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput("list projects");
cmd.Should().Pass();
cmd.StdOut.Should().Contain("No projects found in the solution.");
}
[Fact]
public void WhenProjectReferencesArePresentInTheSolutionItListsThem()
{
var projectDirectory = TestAssets
.Get("TestAppWithSlnAndExistingCsprojReferences")
.CreateInstance()
.WithSourceFiles()
.Root
.FullName;
var cmd = new DotnetCommand()
.WithWorkingDirectory(projectDirectory)
.ExecuteWithCapturedOutput("list projects");
cmd.Should().Pass();
cmd.StdOut.Should().Contain("Project reference(s)");
cmd.StdOut.Should().Contain(@"App\App.csproj");
cmd.StdOut.Should().Contain(@"Lib\Lib.csproj");
}
}
}

View file

@ -0,0 +1 @@
https://github.com/Microsoft/msbuild/issues/927

View file

@ -0,0 +1 @@
https://github.com/Microsoft/msbuild/issues/927

View file

@ -0,0 +1,44 @@
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" />
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.props))\dir.props" />
<PropertyGroup>
<TargetFramework>netcoreapp1.0</TargetFramework>
<GenerateRuntimeConfigurationFiles>true</GenerateRuntimeConfigurationFiles>
<AssemblyName>dotnet-list-proj.Tests</AssemblyName>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netcoreapp1.0' ">$(PackageTargetFallback);dotnet5.4;portable-net451+win8</PackageTargetFallback>
</PropertyGroup>
<ItemGroup>
<Compile Include="**\*.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Microsoft.DotNet.Tools.Tests.Utilities\Microsoft.DotNet.Tools.Tests.Utilities.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.Cli.Sln.Internal\Microsoft.DotNet.Cli.Sln.Internal.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.Cli.Utils\Microsoft.DotNet.Cli.Utils.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.InternalAbstractions\Microsoft.DotNet.InternalAbstractions.csproj" />
<ProjectReference Include="..\..\src\Microsoft.DotNet.TestFramework\Microsoft.DotNet.TestFramework.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Sdk">
<Version>$(CLI_NETSDK_Version)</Version>
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk">
<Version>15.0.0-preview-20161024-02</Version>
</PackageReference>
<PackageReference Include="xunit.runner.visualstudio">
<Version>2.2.0-beta4-build1194</Version>
</PackageReference>
<PackageReference Include="Microsoft.NETCore.App">
<Version>1.0.1</Version>
</PackageReference>
<PackageReference Include="xunit">
<Version>2.2.0-beta4-build3444</Version>
</PackageReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>