-
Notifications
You must be signed in to change notification settings - Fork 0
Brthor/cli nuget tools #9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: rel/1.0.0
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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( | ||
| PlatformServices.Default.Application.ApplicationBasePath, | ||
| commandResolverArguments.CommandName); | ||
|
|
||
| return commandPath == null | ||
| ? null | ||
| : CommandResolverCommon.CreateCommandSpecPreferringExe( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wonder if CommandResolverCommon should be injected as well.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had this thought today. It's really more of an There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
|---|---|---|
| @@ -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; } | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 && | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method has a super weird name.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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) | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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.
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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? There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Owner
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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( | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💥 😄