Thanks to visit codestin.com
Credit goes to github.com

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
@@ -0,0 +1,36 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;
namespace Microsoft.DotNet.Cli.Utils
{
internal class AppBaseCommandResolver : ICommandResolver
{
public CommandSpec Resolve(CommandResolverArguments commandResolverArguments)
{
if (commandResolverArguments.CommandName == null)
{
return null;
}

var commandPath = commandResolverArguments.Environment.GetCommandPathFromRootPath(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if Environment is null?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💥 😄

PlatformServices.Default.Application.ApplicationBasePath,
commandResolverArguments.CommandName);

return commandPath == null
? null
: CommandResolverCommon.CreateCommandSpecPreferringExe(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if CommandResolverCommon should be injected as well.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had this thought today. It's really more of an ExePreferredCommandSpecCreator what do you think of that name?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not ICommandSpecFactory then? And then have an implementation for it called ExePrefferedCommandSpecFactory.

commandResolverArguments.CommandName,
commandResolverArguments.CommandArguments,
commandPath,
CommandResolutionStrategy.BaseDirectory,
commandResolverArguments.Environment);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ public enum CommandResolutionStrategy
//command loaded from the same directory as a project.json file
ProjectLocal,

//command loaded from path
//command loaded from PATH environment variable
Path,

//command loaded from rooted path
RootedPath,

//command not found
None
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using NuGet.Frameworks;

namespace Microsoft.DotNet.Cli.Utils
{
public class CommandResolverArguments
{
public string CommandName { get; set; }

public IEnumerable<string> CommandArguments { get; set; }

public NuGetFramework Framework { get; set; }

public string ProjectDirectory { get; set; }

public string Configuration { get; set; }

public IEnumerable<string> InferredExtensions { get; set; }

public IEnvironmentProvider Environment { get; set; }

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not seem like an Argument. Should this be passed in to the implementations of ICommandResolver?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had it the way you are suggesting at first, but went back to this because it made it a little easier to test.

I agree though that is a cleaner approach, will make that change back.

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;

namespace Microsoft.DotNet.Cli.Utils
{
internal class CommandResolverCommon
{
public static CommandSpec CreateCommandSpecPreferringExe(
string commandName,
IEnumerable<string> args,
string commandPath,
CommandResolutionStrategy resolutionStrategy,
IEnvironmentProvider environment)
{
var useComSpec = false;

if (PlatformServices.Default.Runtime.OperatingSystemPlatform == Platform.Windows &&

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This if here is a indicative that you should have different implementations for this. Then you won't need this. You will have a WindowsCommandResolverCommon that would have this in there.

Path.GetExtension(commandPath).Equals(".cmd", StringComparison.OrdinalIgnoreCase))
{
var preferredCommandPath = environment.GetCommandPath(commandName, ".exe");

// Use cmd if we can't find an exe
if (preferredCommandPath == null)
{
useComSpec = true;
}
else
{
commandPath = preferredCommandPath;
}
}

if (useComSpec)
{
return CreateCmdCommandSpec(commandPath, args, resolutionStrategy);
}
else
{
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(args);
return new CommandSpec(commandPath, escapedArgs, resolutionStrategy);
}
}

private static CommandSpec CreateCmdCommandSpec(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method has a super weird name.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would say this file is the ugliest. Copy/Pasted haven't quite figured out where it fits in the design yet.

I'm liking the above suggestions though.

string command,
IEnumerable<string> args,
CommandResolutionStrategy resolutionStrategy)
{
var comSpec = Environment.GetEnvironmentVariable("ComSpec");

// Handle the case where ComSpec is already the command
if (command.Equals(comSpec, StringComparison.OrdinalIgnoreCase))
{
command = args.FirstOrDefault();
args = args.Skip(1);
}
var cmdEscapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForCmdProcessStart(args);

if (ArgumentEscaper.ShouldSurroundWithQuotes(command))
{
command = $"\"{command}\"";
}

var escapedArgString = $"/s /c \"{command} {cmdEscapedArgs}\"";

return new CommandSpec(comSpec, escapedArgString, resolutionStrategy);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;

namespace Microsoft.DotNet.Cli.Utils
{
public class DefaultCommandResolver : ICommandResolver
{
private IList<ICommandResolver> _orderedCommandResolvers;

public DefaultCommandResolver() : this(new PackagedCommandSpecCreator()) { }

public DefaultCommandResolver(IPackagedCommandSpecCreator packagedCommandSpecCreator)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe move this to an extension method. The idea is, if this changes, we don't need to change this class. I am not sure where this is used, like the resolvers and all, otherwise, I would suggest that the "Program.cs" that use this should be the ones setting it up.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure I follow. Move the constructor to an extension method or move the default set of injected classes to an extension method?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My thought was to add a AddResolver method and then have an Extension to DefaultCommandResolver or maybe ICompositeCommandResolver called AddDefaultCommandResolvers that would add all of these. Likewise for ScriptsCommandResolver. If you use the extension, you can get rid of one resolver and just keep the CompositeCommandResolver. You can also unit test the list of resolvers adding by passing a mock of ICompositeCommandResolver and checking what was added to it.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why an extension method? It feels like a strange place for what is essentially a policy to live. Is this a common practice?

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not really. Just what I thought right now. How about you create a CommandResolverPolicy then?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm, pass that in to a CompositeCommandResolver and it uses it to populate the list?

{
_orderedCommandResolvers = new List<ICommandResolver>
{
new RootedCommandResolver(),
new ProjectDependenciesPackageCommandResolver(packagedCommandSpecCreator),
new ProjectToolsPackageCommandResolver(packagedCommandSpecCreator),
new AppBaseCommandResolver(),
new PathCommandResolver()
};
}

public CommandSpec Resolve(CommandResolverArguments commandResolverArguments)
{
foreach (var commandResolver in _orderedCommandResolvers)
{
var commandSpec = commandResolver.Resolve(commandResolverArguments);

if (commandSpec != null)
{
return commandSpec;
}
}

return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Microsoft.DotNet.Cli.Utils
{
public interface ICommandResolver
{
CommandSpec Resolve(CommandResolverArguments arguments);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.DotNet.ProjectModel.Graph;

namespace Microsoft.DotNet.Cli.Utils
{
public interface IPackagedCommandSpecCreator
{
CommandSpec CreateCommandSpecFromLibrary(
LockFilePackageLibrary library,
string commandName,
IEnumerable<string> commandArguments,
IEnumerable<string> allowedExtensions,
string nugetPackagesRoot,
string depsFilePath = null);

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;

namespace Microsoft.DotNet.Cli.Utils
{
internal class PackagedCommandSpecCreator : IPackagedCommandSpecCreator
{
public CommandSpec CreateCommandSpecFromLibrary(
LockFilePackageLibrary library,
string commandName,
IEnumerable<string> commandArguments,
IEnumerable<string> allowedExtensions,
string nugetPackagesRoot,
string depsFilePath)
{
var packageDirectory = GetPackageDirectoryFullPath(library, nugetPackagesRoot);

if (!Directory.Exists(packageDirectory))
{
return null;
}

var commandFile = GetCommandFileRelativePath(library, commandName, allowedExtensions);

if (commandFile == null)
{
return null;
}

var commandPath = Path.Combine(packageDirectory, commandFile);

return CreateCommandSpecUsingCoreHostIfDll(commandPath, commandArguments, depsFilePath);
}

private string GetPackageDirectoryFullPath(LockFilePackageLibrary library, string nugetPackagesRoot)
{
var packageDirectory = new VersionFolderPathResolver(nugetPackagesRoot)
.GetInstallPath(library.Name, library.Version);

return packageDirectory;
}

private string GetCommandFileRelativePath(
LockFilePackageLibrary library,
string commandName,
IEnumerable<string> allowedExtensions)
{
// TODO: Should command names be case sensitive?
return library.Files
.Where(f => Path.GetFileNameWithoutExtension(f) == commandName)
.Where(e => allowedExtensions.Contains(Path.GetExtension(e)))
.FirstOrDefault();
}

private CommandSpec CreateCommandSpecUsingCoreHostIfDll(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The name on this method implies that there is no alternative and it will only create if it is a dll. Which is not really the case.

string commandPath,
IEnumerable<string> commandArguments,
string depsFilePath)
{
var commandExtension = Path.GetExtension(commandPath);

if (commandExtension == FileNameSuffixes.DotNet.DynamicLib)
{
return CreatePackageCommandSpecUsingDotnetExec(commandPath, commandArguments, depsFilePath);
}

return CreateCommandSpec(commandPath, commandArguments);
}

private CommandSpec CreatePackageCommandSpecUsingDotnetExec(
string commandPath,
IEnumerable<string> commandArguments,
string depsFilePath)
{
var corehost = DotnetExec.HostExePath;

var arguments = new List<string>();
arguments.Add("exec");
arguments.Add(commandPath);

if (depsFilePath != null)
{
arguments.Add($"--depsfile:{depsFilePath}");
}

arguments.AddRange(commandArguments);

return CreateCommandSpec(corehost, arguments);
}

private CommandSpec CreateCommandSpec(
string commandPath,
IEnumerable<string> commandArguments)
{
var escapedArgs = ArgumentEscaper.EscapeAndConcatenateArgArrayForProcessStart(commandArguments);

return new CommandSpec(commandPath, escapedArgs, CommandResolutionStrategy.NugetPackage);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using Microsoft.DotNet.ProjectModel;
using Microsoft.DotNet.ProjectModel.Graph;
using Microsoft.Extensions.PlatformAbstractions;
using NuGet.Frameworks;
using NuGet.Packaging;

namespace Microsoft.DotNet.Cli.Utils
{
internal class PathCommandResolver : ICommandResolver
{
public CommandSpec Resolve(CommandResolverArguments commandResolverArguments)
{
if (commandResolverArguments.CommandName == null
|| commandResolverArguments.Environment == null)
{
return null;
}

var commandPath = commandResolverArguments.Environment
.GetCommandPath(commandResolverArguments.CommandName);

return commandPath == null
? null
: CommandResolverCommon.CreateCommandSpecPreferringExe(
commandResolverArguments.CommandName,
commandResolverArguments.CommandArguments,
commandPath,
CommandResolutionStrategy.Path,
commandResolverArguments.Environment);
}
}
}
Loading