Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
c8a5942
framework
JL03-Yue Jan 19, 2024
31ccfc2
add function
JL03-Yue Jan 19, 2024
1c5bf35
call inside functions
JL03-Yue Jan 19, 2024
3126fd0
update the hostfx_initialize_runtime_config function
JL03-Yue Jan 22, 2024
4bb3a3c
update the wrapper for memory initialization
JL03-Yue Jan 22, 2024
0459205
free memory after
JL03-Yue Jan 22, 2024
b7cf735
able to run on unix
JL03-Yue Jan 22, 2024
bed712f
partial logic
JL03-Yue Jan 23, 2024
357069f
output specific target framework
JL03-Yue Jan 23, 2024
943a94e
adding --force to ToolInstallCommand
JL03-Yue Jan 23, 2024
af2dbe4
add force install option to global and local tools
JL03-Yue Jan 23, 2024
97dbdd5
pass force into ToolPackageDownloader
JL03-Yue Jan 23, 2024
8e2641f
add --force condition
JL03-Yue Jan 23, 2024
a1e1584
update the message ui
JL03-Yue Jan 23, 2024
43c115c
adding force option
JL03-Yue Jan 23, 2024
89daa6f
have -g based on command
JL03-Yue Jan 30, 2024
96b01a2
prompt the user to install new net versions
JL03-Yue Jan 30, 2024
0fdfd91
update comment
JL03-Yue Jan 30, 2024
e57862c
resolve merge errors
JL03-Yue Feb 1, 2024
9d2d543
fix tests
JL03-Yue Feb 1, 2024
42e5642
add test for --force
JL03-Yue Feb 1, 2024
ed062c9
fix tests
JL03-Yue Feb 2, 2024
56e36af
clean up the code
JL03-Yue Feb 2, 2024
b443db7
fix verbosity
Markliniubility Jan 25, 2024
f1403f1
update printing minimal verbosity
Markliniubility Jan 25, 2024
f91507e
locally change default option in `tool install`
Markliniubility Feb 2, 2024
d4f55b2
change naming
JL03-Yue Feb 8, 2024
1caa48a
add explanation
JL03-Yue Feb 8, 2024
bc7877c
Change indent
JL03-Yue Feb 8, 2024
a4f59a9
remove hostfxr_initialize_for_runtime_config declaration
JL03-Yue Feb 8, 2024
fe5c737
update the scope of tfmValue
JL03-Yue Feb 13, 2024
8d8902f
return enum result from the runtime API
JL03-Yue Feb 13, 2024
ff12a1a
combine the functions for runtime compatibility error message
JL03-Yue Feb 13, 2024
52ca72b
using existing isQuiet/isMinimal method
JL03-Yue Feb 13, 2024
f1f55d6
validate stderr for roll forward error test
JL03-Yue Mar 19, 2024
f03033c
Tweak string
Forgind Aug 20, 2024
40acd74
Fix build errors
Forgind Aug 21, 2024
68acb5a
Merge remote-tracking branch 'upstream/release/9.0.2xx' into roll-for…
nagilson Oct 17, 2024
eb84eba
Make it so it doesn't look like the runtime is not compatible in an e…
nagilson Oct 17, 2024
8258229
update xlf
nagilson Oct 17, 2024
09ef33e
Merge branch 'release/9.0.2xx' into roll-forward-error
nagilson Oct 18, 2024
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
20 changes: 19 additions & 1 deletion src/Cli/dotnet/CommonLocalizableStrings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -735,5 +735,23 @@ The default is 'false.' However, when targeting .NET 7 or lower, the default is
</data>
<data name="ToolSettingsNotFound" xml:space="preserve">
<value>Tool settings file does not exist for the tool {0}.</value>
</data>
<data name="ToolPackageRuntimeConfigIncompatible" xml:space="preserve">
<value>Installation failed.

This app wasn't installed because it requires .NET {1} to run, which is not installed. This can be resolved by one of the following:

1. Install .NET {1} with the following link

https://aka.ms/dotnet/download/sdk

2. Install with the `--allow-roll-forward` flag, using with the following command.

dotnet tool install {0}{2} --allow-roll-forward

3. Install with `--force` and manually configure the tool to run, using with the following command.

dotnet tool install {0}{2} --force
</value>
</data>
</root>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
using NuGet.Protocol;
using NuGet.Protocol.Core.Types;
using NuGet.Versioning;
using Microsoft.DotNet.Cli;

namespace Microsoft.DotNet.Cli.NuGetPackageDownloader
{
Expand Down Expand Up @@ -132,17 +133,11 @@ public async Task<string> DownloadPackageAsync(PackageId packageId,
return nupkgPath;
}

private bool verbosityGreaterThanMinimal()
{
return _verbosityOptions != VerbosityOptions.quiet && _verbosityOptions != VerbosityOptions.q
&& _verbosityOptions != VerbosityOptions.minimal && _verbosityOptions != VerbosityOptions.m;
}

private void VerifySigning(string nupkgPath)
{
if (!_verifySignatures && !_validationMessagesDisplayed)
{
if (verbosityGreaterThanMinimal())
if (!(_verbosityOptions.IsQuiet() || _verbosityOptions.IsMinimal()))
{
_reporter.WriteLine(LocalizableStrings.NuGetPackageSignatureVerificationSkipped);
}
Expand Down
4 changes: 2 additions & 2 deletions src/Cli/dotnet/ToolPackage/IToolPackageDownloader.cs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Threading.Tasks;
using Microsoft.DotNet.ToolPackage;
using NuGet.Versioning;

Expand All @@ -15,7 +14,8 @@ IToolPackage InstallPackage(PackageLocation packageLocation,
VersionRange versionRange = null,
string targetFramework = null,
bool isGlobalTool = false,
bool isGlobalToolRollForward = false
bool isGlobalToolRollForward = false,
bool forceInstall = false
);

NuGetVersion GetNuGetVersion(
Expand Down
68 changes: 52 additions & 16 deletions src/Cli/dotnet/ToolPackage/ToolPackageDownloader.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,7 @@
// 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;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.CommandLine;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.DotNet.Cli.NuGetPackageDownloader;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.DotNet.ToolPackage;
Expand All @@ -27,10 +20,9 @@
using NuGet.Versioning;
using NuGet.Configuration;
using Microsoft.TemplateEngine.Utils;
using System.Text.Json;
using System.Xml;
using System.Text.Json.Nodes;
using Newtonsoft.Json.Linq;
using System.Text.Json;
using static Microsoft.DotNet.NativeWrapper.NETCoreSdkResolverNativeWrapper;

namespace Microsoft.DotNet.Cli.ToolPackage
{
Expand Down Expand Up @@ -77,7 +69,8 @@ public IToolPackage InstallPackage(PackageLocation packageLocation, PackageId pa
VersionRange versionRange = null,
string targetFramework = null,
bool isGlobalTool = false,
bool isGlobalToolRollForward = false
bool isGlobalToolRollForward = false,
bool forceInstall = false
)
{
var packageRootDirectory = _toolPackageStore.GetRootPackageDirectory(packageId);
Expand Down Expand Up @@ -111,7 +104,7 @@ public IToolPackage InstallPackage(PackageLocation packageLocation, PackageId pa
}
NuGetVersion packageVersion = nugetPackageDownloader.GetBestPackageVersionAsync(packageId, versionRange, packageSourceLocation).GetAwaiter().GetResult();

rollbackDirectory = isGlobalTool ? toolDownloadDir.Value: Path.Combine(toolDownloadDir.Value, packageId.ToString(), packageVersion.ToString());
rollbackDirectory = isGlobalTool ? toolDownloadDir.Value : Path.Combine(toolDownloadDir.Value, packageId.ToString(), packageVersion.ToString());

if (isGlobalTool)
{
Expand All @@ -134,17 +127,17 @@ public IToolPackage InstallPackage(PackageLocation packageLocation, PackageId pa
{
DownloadAndExtractPackage(packageId, nugetPackageDownloader, toolDownloadDir.Value, packageVersion, packageSourceLocation, includeUnlisted: givenSpecificVersion).GetAwaiter().GetResult();
}
else if(isGlobalTool)
else if (isGlobalTool)
{
throw new ToolPackageException(
string.Format(
CommonLocalizableStrings.ToolPackageConflictPackageId,
packageId,
packageVersion.ToNormalizedString()));
}

CreateAssetFile(packageId, packageVersion, toolDownloadDir, assetFileDirectory, _runtimeJsonPath, targetFramework);

DirectoryPath toolReturnPackageDirectory;
DirectoryPath toolReturnJsonParentDirectory;

Expand All @@ -168,12 +161,17 @@ public IToolPackage InstallPackage(PackageLocation packageLocation, PackageId pa
packageDirectory: toolReturnPackageDirectory,
assetsJsonParentDirectory: toolReturnJsonParentDirectory);

if (!forceInstall && !isGlobalToolRollForward)
{
CheckIfRequiredRuntimeIsInstalled(toolPackageInstance, packageId, isGlobalTool);
}

if (isGlobalToolRollForward)
{
UpdateRuntimeConfig(toolPackageInstance);
}

return toolPackageInstance;
return toolPackageInstance;
},
rollback: () =>
{
Expand All @@ -190,6 +188,44 @@ public IToolPackage InstallPackage(PackageLocation packageLocation, PackageId pa
});
}

private static void CheckIfRequiredRuntimeIsInstalled(
ToolPackageInstance toolPackageInstance,
PackageId packageId,
bool isGlobalTool
)
{
var executableFilePath = toolPackageInstance.Commands[0].Executable;
var runtimeConfigFilePath = Path.ChangeExtension(executableFilePath.ToString(), ".runtimeconfig.json");

// Check if the runtimeconfig.json file is compatible with the current runtime
Copy link
Member

Choose a reason for hiding this comment

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

compatible with the current runtime

Just want to call out exactly what this means. The hostfxr_initialize_for_runtime_config is being run in-proc here, so it will check for compatibility with the already loaded runtime.

  • The current/loaded runtime in this context would be what the SDK command is runnning on.
  • The SDK chosen is affected by global.json settings and then the runtime is chosen based on the SDK's dotnet.runtimeconfig.json, which specifies the runtime matching the SDK version.
  • This means current runtime may not be the latest installed runtime.

Copy link
Member

@elinor-fung elinor-fung Feb 9, 2024

Choose a reason for hiding this comment

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

Actually, I think this is a larger problem. Checking for compatibility with the loaded runtime means checking for frameworks - not just the core runtime. Since dotnet.dll only depends on the core runtime, it will only have that loaded.

Tools could depend on other frameworks like AspNetCore or WindowsDesktop, right? I think that is a test case that should be covered. I expect any tools like that would fail the compatibility check here.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, tool could be depend on other frameworks.

Copy link
Member

Choose a reason for hiding this comment

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

The new hostfxr_resolve_frameworks_for_runtime_config API considers other types of frameworks. We should call that and see if it returns 0.

if (File.Exists(runtimeConfigFilePath))
{
string tfmValue = "";
JsonElement rootElement = JsonDocument.Parse(File.ReadAllText(runtimeConfigFilePath)).RootElement;

if (rootElement.TryGetProperty("runtimeOptions", out JsonElement runtimeOptionsElement) &&
runtimeOptionsElement.TryGetProperty("tfm", out JsonElement tfmElement))
{
tfmValue = new string(tfmElement.GetString().Where(c => char.IsDigit(c) || c == '.').ToArray());
Copy link
Member

Choose a reason for hiding this comment

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

It seems like you should be able to use the value directly instead of filtering it to digits and periods. Did you run into cases where there were invalid values or characters in it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the comment. This value contains net inside, such as net5.0 instead of 5.0, and I'm seeking a way to extract the numbers.

Copy link
Member

Choose a reason for hiding this comment

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

Depending on the API this may or may not be necessary. Not sure yet.

}
var result = InitializeForRuntimeConfig(runtimeConfigFilePath);

// Error if tool is incompatible
var global = isGlobalTool ? " -g" : "";
switch (result)
{
case InitializationRuntimeConfigResult.Success:
break;

default:
throw new GracefulException(
string.Format(
CommonLocalizableStrings.ToolPackageRuntimeConfigIncompatible,
packageId, tfmValue, global));
}
}
}

// The following methods are copied from the LockFileUtils class in Nuget.Client
private static void AddToolsAssets(
ManagedCodeConventions managedCodeConventions,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,4 +247,7 @@ If you would like to create a manifest, use the `--create-manifest-if-needed` fl
<data name="AllowPackageDowngradeOptionDescription" xml:space="preserve">
<value>Allow package downgrade when installing a .NET tool package.</value>
</data>
<data name="ForceInstallOptionName" xml:space="preserve">
<value>Forces a tool to be installed even if a compatible .NET runtime is not installed.</value>
</data>
</root>
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,12 @@ internal static class ToolInstallCommandParser
public static readonly CliOption<bool> AllowPackageDowngradeOption = new("--allow-downgrade")
{
Description = LocalizableStrings.AllowPackageDowngradeOptionDescription
};
};

public static readonly CliOption<bool> ForceInstallOption = new("--force")
{
Description = LocalizableStrings.ForceInstallOptionName
};

public static readonly CliOption<VerbosityOptions> VerbosityOption = CommonOptions.VerbosityOption;

Expand Down Expand Up @@ -98,6 +103,7 @@ private static CliCommand ConstructCommand()
command.Options.Add(CreateManifestIfNeededOption);
command.Options.Add(AllowPackageDowngradeOption);
command.Options.Add(RollForwardOption);
command.Options.Add(ForceInstallOption);

command.SetAction((parseResult) => new ToolInstallCommand(parseResult).Execute());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ internal class ToolInstallGlobalOrToolPathCommand : CommandBase
private readonly bool _allowRollForward;
private readonly bool _allowPackageDowngrade;
private readonly bool _updateAll;

private readonly bool _forceInstall;

public ToolInstallGlobalOrToolPathCommand(
ParseResult parseResult,
Expand Down Expand Up @@ -95,6 +95,7 @@ public ToolInstallGlobalOrToolPathCommand(
_createToolPackageStoreDownloaderUninstaller = createToolPackageStoreDownloaderUninstaller ??
ToolPackageFactory.CreateToolPackageStoresAndDownloaderAndUninstaller;
_updateAll = parseResult.GetValue(ToolUpdateCommandParser.UpdateAllOption);
_forceInstall = parseResult.GetValue(ToolInstallCommandParser.ForceInstallOption);

_reporter = (reporter ?? Reporter.Output);
}
Expand Down Expand Up @@ -190,7 +191,8 @@ private int ExecuteInstallCommand(PackageId packageId)
targetFramework: _framework,
verbosity: _verbosity,
isGlobalTool: true,
isGlobalToolRollForward: _allowRollForward
isGlobalToolRollForward: _allowRollForward,
forceInstall: _forceInstall
);

EnsureVersionIsHigher(oldPackageNullable, newInstalledPackage, _allowPackageDowngrade);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ internal class ToolInstallLocalCommand : CommandBase
private readonly IReporter _reporter;
private readonly PackageId? _packageId;
private readonly bool _allowPackageDowngrade;
private readonly bool _forceInstall;

private readonly string _explicitManifestFile;
private readonly bool _createManifestIfNeeded;
Expand Down Expand Up @@ -56,6 +57,7 @@ public ToolInstallLocalCommand(
_toolLocalPackageInstaller = new ToolInstallLocalInstaller(parseResult, toolPackageDownloader, runtimeJsonPathForTests);
_allowRollForward = parseResult.GetValue(ToolInstallCommandParser.RollForwardOption);
_allowPackageDowngrade = parseResult.GetValue(ToolInstallCommandParser.AllowPackageDowngradeOption);
_forceInstall = parseResult.GetValue(ToolInstallCommandParser.ForceInstallOption);
}

public override int Execute()
Expand Down Expand Up @@ -93,11 +95,11 @@ private int ExecuteInstallCommand(PackageId packageId)

if (!existingPackageWithPackageId.Any())
{
return InstallNewTool(manifestFile, packageId);
return InstallNewTool(manifestFile, packageId, _forceInstall);
}

var existingPackage = existingPackageWithPackageId.Single();
var toolDownloadedPackage = _toolLocalPackageInstaller.Install(manifestFile, packageId);
var toolDownloadedPackage = _toolLocalPackageInstaller.Install(manifestFile, packageId, _forceInstall);

InstallToolUpdate(existingPackage, toolDownloadedPackage, manifestFile, packageId);

Expand Down Expand Up @@ -154,10 +156,10 @@ public int InstallToolUpdate(ToolManifestPackage existingPackage, IToolPackage t
return 0;
}

public int InstallNewTool(FilePath manifestFile, PackageId packageId)
public int InstallNewTool(FilePath manifestFile, PackageId packageId, bool forceInstall)
{
IToolPackage toolDownloadedPackage =
_toolLocalPackageInstaller.Install(manifestFile, packageId);
_toolLocalPackageInstaller.Install(manifestFile, packageId, forceInstall);

_toolManifestEditor.Add(
manifestFile,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public ToolInstallLocalInstaller(
TargetFrameworkToInstall = BundledTargetFramework.GetTargetFrameworkMoniker();
}

public IToolPackage Install(FilePath manifestFile, PackageId packageId)
public IToolPackage Install(FilePath manifestFile, PackageId packageId, bool forceInstall)
{
if (!string.IsNullOrEmpty(_configFilePath) && !File.Exists(_configFilePath))
{
Expand Down Expand Up @@ -70,7 +70,8 @@ public IToolPackage Install(FilePath manifestFile, PackageId packageId)
packageId,
verbosity: _verbosity,
versionRange,
TargetFrameworkToInstall
TargetFrameworkToInstall,
forceInstall: forceInstall
);

return toolDownloadedPackage;
Expand Down

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading