Skip to content

Commit 08e9f08

Browse files
authored
Improve support of nuget commands with file-based apps (#53535)
1 parent 27547d7 commit 08e9f08

37 files changed

Lines changed: 529 additions & 457 deletions

src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Hidden/List/ListCommandDefinition.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ public static Argument<string> CreateSlnOrProjectArgument(string name, string de
2020
Arity = ArgumentArity.ZeroOrOne
2121
}.DefaultToCurrentDirectory();
2222

23-
public readonly Argument<string> SlnOrProjectArgument = CreateSlnOrProjectArgument(CommandDefinitionStrings.SolutionOrProjectArgumentName, CommandDefinitionStrings.SolutionOrProjectArgumentDescription);
23+
public readonly Argument<string> SlnOrProjectOrFileArgument = CreateSlnOrProjectArgument(CommandDefinitionStrings.SolutionOrProjectOrFileArgumentName, CommandDefinitionStrings.SolutionOrProjectOrFileArgumentDescription);
2424

2525
public readonly ListPackageCommandDefinition PackageCommand = new();
2626
public readonly ListReferenceCommandDefinition ReferenceCommand = new();
@@ -31,7 +31,7 @@ public ListCommandDefinition()
3131
Hidden = true;
3232
this.DocsLink = Link;
3333

34-
Arguments.Add(SlnOrProjectArgument);
34+
Arguments.Add(SlnOrProjectOrFileArgument);
3535
Subcommands.Add(PackageCommand);
3636
Subcommands.Add(ReferenceCommand);
3737
}

src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Hidden/List/ListPackageCommandDefinition.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,6 @@ internal sealed class ListPackageCommandDefinition() : PackageListCommandDefinit
1212

1313
public ListCommandDefinition Parent => (ListCommandDefinition)Parents.Single();
1414

15-
public override string? GetFileOrDirectory(ParseResult parseResult)
16-
=> parseResult.GetValue(Parent.SlnOrProjectArgument);
15+
public override Argument<string>? GetProjectOrFileArgument()
16+
=> Parent.SlnOrProjectOrFileArgument;
1717
}

src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Hidden/List/ListReferenceCommandDefinition.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ public ListReferenceCommandDefinition() : base(Name)
1919
public ListCommandDefinition Parent => (ListCommandDefinition)Parents.Single();
2020

2121
internal override string? GetFileOrDirectory(ParseResult parseResult)
22-
=> parseResult.GetValue(Parent.SlnOrProjectArgument);
22+
=> parseResult.GetValue(Parent.SlnOrProjectOrFileArgument);
2323
}
2424

2525
internal abstract class ListReferenceCommandDefinitionBase : Command

src/Cli/Microsoft.DotNet.Cli.Definitions/Commands/Package/PackageListCommandDefinition.cs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,12 @@
66

77
namespace Microsoft.DotNet.Cli.Commands.Package.List;
88

9-
internal sealed class PackageListCommandDefinition : PackageListCommandDefinitionBase
9+
internal sealed class PackageListCommandDefinition() : PackageListCommandDefinitionBase(Name)
1010
{
1111
public new const string Name = "list";
1212

13-
public readonly Option<string?> ProjectOption = PackageCommandDefinition.CreateProjectOption();
14-
15-
public PackageListCommandDefinition()
16-
: base(Name)
17-
{
18-
Options.Add(ProjectOption);
19-
}
20-
21-
public override string? GetFileOrDirectory(ParseResult parseResult)
22-
=> parseResult.GetValue(ProjectOption);
13+
public override Argument<string>? GetProjectOrFileArgument()
14+
=> null;
2315
}
2416

2517
internal abstract class PackageListCommandDefinitionBase : Command
@@ -110,6 +102,9 @@ internal abstract class PackageListCommandDefinitionBase : Command
110102
Description = CommandDefinitionStrings.CmdOutputVersionDescription
111103
}.ForwardAsSingle(o => $"--output-version:{o}");
112104

105+
public readonly Option<string?> ProjectOption = PackageCommandDefinition.CreateProjectOption();
106+
public readonly Option<string?> FileOption = PackageCommandDefinition.CreateFileOption();
107+
113108
public PackageListCommandDefinitionBase(string name)
114109
: base(name, CommandDefinitionStrings.PackageListAppFullName)
115110
{
@@ -128,9 +123,11 @@ public PackageListCommandDefinitionBase(string name)
128123
Options.Add(FormatOption);
129124
Options.Add(OutputVersionOption);
130125
Options.Add(NoRestore);
126+
Options.Add(ProjectOption);
127+
Options.Add(FileOption);
131128
}
132129

133-
public abstract string? GetFileOrDirectory(ParseResult parseResult);
130+
public abstract Argument<string>? GetProjectOrFileArgument();
134131

135132
public void EnforceOptionRules(ParseResult parseResult)
136133
{

src/Cli/dotnet/Commands/CliCommandStrings.resx

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -651,11 +651,6 @@ Tool '{1}' (version '{2}') was successfully installed. Entry is added to the man
651651
<data name="PackageRemoveSpecifyExactlyOnePackageReference" xml:space="preserve">
652652
<value>Specify only one package reference to remove.</value>
653653
</data>
654-
<data name="DirectivesRemoved" xml:space="preserve">
655-
<value>Removed '{0}' directives ({1}) for '{2}' from: {3}</value>
656-
<comment>{0} is a directive kind (like '#:package'). {1} is number of removed directives.
657-
{2} is directive key (e.g., package name). {3} is file path from which directives were removed.</comment>
658-
</data>
659654
<data name="PackagesCommandNameCollisionConclusion" xml:space="preserve">
660655
<value>Command names conflict. Command names are case insensitive.
661656
{0}</value>

src/Cli/dotnet/Commands/NuGet/NuGetCommand.cs

Lines changed: 39 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,19 +6,36 @@
66
using System.CommandLine;
77
using Microsoft.DotNet.Cli.Extensions;
88
using Microsoft.DotNet.Cli.Utils;
9+
using Microsoft.DotNet.ProjectTools;
910

1011
namespace Microsoft.DotNet.Cli.Commands.NuGet;
1112

12-
public class NuGetCommand
13+
internal class NuGetCommand
1314
{
14-
public static int Run(string[] args)
15+
public static int Run(string[] args, bool isFileBasedApp = false)
1516
{
16-
return Run(args, new NuGetCommandRunner());
17+
return Run(args, isFileBasedApp
18+
? new InProcessNuGetCommandRunner(NuGetVirtualProjectBuilder.Instance)
19+
: new NuGetCommandRunner());
1720
}
1821

1922
public static int Run(ParseResult parseResult)
2023
{
21-
return Run(parseResult.GetArguments(), new NuGetCommandRunner());
24+
ICommandRunner runner;
25+
26+
if (parseResult.CommandResult.Command.Name == "why"
27+
&& parseResult.CommandResult.Command.Arguments.FirstOrDefault() is Argument<string> pathArg
28+
&& parseResult.GetValue(pathArg) is { } path
29+
&& VirtualProjectBuilder.IsValidEntryPointPath(path))
30+
{
31+
runner = new InProcessNuGetCommandRunner(NuGetVirtualProjectBuilder.Instance);
32+
}
33+
else
34+
{
35+
runner = new NuGetCommandRunner();
36+
}
37+
38+
return Run(parseResult.GetArguments(), runner);
2239
}
2340

2441
public static int Run(string[] args, ICommandRunner nugetCommandRunner)
@@ -43,11 +60,28 @@ private class NuGetCommandRunner : ICommandRunner
4360
public int Run(string[] args)
4461
{
4562
var nugetApp = new NuGetForwardingApp(args);
46-
nugetApp.WithEnvironmentVariable("DOTNET_HOST_PATH", GetDotnetPath());
63+
nugetApp.WithEnvironmentVariable(EnvironmentVariableNames.DOTNET_HOST_PATH, GetDotnetPath());
4764
return nugetApp.Execute();
4865
}
4966
}
5067

68+
private class InProcessNuGetCommandRunner(NuGetVirtualProjectBuilder virtualProjectBuilder) : ICommandRunner
69+
{
70+
public int Run(string[] args)
71+
{
72+
var originalDotNetHostPath = Environment.GetEnvironmentVariable(EnvironmentVariableNames.DOTNET_HOST_PATH);
73+
Environment.SetEnvironmentVariable(EnvironmentVariableNames.DOTNET_HOST_PATH, GetDotnetPath());
74+
try
75+
{
76+
return global::NuGet.CommandLine.XPlat.Program.Run(args, virtualProjectBuilder);
77+
}
78+
finally
79+
{
80+
Environment.SetEnvironmentVariable(EnvironmentVariableNames.DOTNET_HOST_PATH, originalDotNetHostPath);
81+
}
82+
}
83+
}
84+
5185
private static string GetDotnetPath()
5286
{
5387
return new Muxer().MuxerPath;
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using Microsoft.Build.Construction;
5+
using Microsoft.Build.Evaluation;
6+
using Microsoft.DotNet.Cli.Commands.Package;
7+
using Microsoft.DotNet.Cli.Commands.Run;
8+
using Microsoft.DotNet.FileBasedPrograms;
9+
using Microsoft.DotNet.ProjectTools;
10+
using NuGet.CommandLine.XPlat;
11+
12+
namespace Microsoft.DotNet.Cli.Commands.NuGet;
13+
14+
internal sealed class NuGetVirtualProjectBuilder : IVirtualProjectBuilder
15+
{
16+
public static NuGetVirtualProjectBuilder Instance => field ??= new();
17+
18+
private NuGetVirtualProjectBuilder() { }
19+
20+
public bool IsValidEntryPointPath(string entryPointFilePath) => VirtualProjectBuilder.IsValidEntryPointPath(entryPointFilePath);
21+
22+
public string GetVirtualProjectPath(string entryPointFilePath) => VirtualProjectBuilder.GetVirtualProjectPath(entryPointFilePath);
23+
24+
public ProjectRootElement CreateProjectRootElement(string entryPointFilePath, ProjectCollection projectCollection)
25+
{
26+
if (!Path.IsPathFullyQualified(entryPointFilePath))
27+
{
28+
throw new ArgumentException($"'{entryPointFilePath}' is not a fully qualified path.", paramName: nameof(entryPointFilePath));
29+
}
30+
31+
var builder = new VirtualProjectBuilder(entryPointFilePath, VirtualProjectBuildingCommand.TargetFramework);
32+
33+
builder.CreateProjectInstance(
34+
projectCollection,
35+
ErrorReporters.IgnoringReporter,
36+
project: out _,
37+
out var projectRootElement,
38+
evaluatedDirectives: out _);
39+
40+
return projectRootElement;
41+
}
42+
43+
public void SaveProject(string entryPointFilePath, ProjectRootElement projectRootElement)
44+
{
45+
VirtualProjectPackageReflector.ReflectChangesToDirectives(projectRootElement, entryPointFilePath);
46+
}
47+
}

0 commit comments

Comments
 (0)