未验证 提交 5098d45c 编写于 作者: E Elinor Fung 提交者: GitHub

Disable multi-level lookup by default (#67022)

上级 32320b11
# Framework version resolution
This document describes .NET Core 3.0 version resolution behavior when the host resolves framework references for framework dependent apps.
It's just a part of the overall framework resolution scenario described in [multilevel-sharedfx-lookup](multilevel-sharedfx-lookup.md).
It's just a part of the overall framework resolution scenario described in [sharedfx-lookup](sharedfx-lookup.md).
## Framework references
Application defines its framework dependencies in its `.runtimeconfig.json` file. Each framework then defines its dependencies in its copy of `.runtimeconfig.json`. Each dependency is expressed as a framework reference. Together these form a graph. The host must resolve the references by finding the actual frameworks which are available on the machine. It must also unify references if there are multi references to the same framework.
......
......@@ -20,7 +20,7 @@ The entry-point typically does just one thing: it finds the `hostfxr` library an
## Host FXR
This library finds and resolves the runtime and all the frameworks the app needs. Then it loads the `hostpolicy` library and transfers control to it.
The host FXR library reads the `.runtimeconfig.json` of the app (and all it's dependent frameworks) and resolves the frameworks. It implements the algorithm for framework resolution as described in [SharedFX Lookup](multilevel-sharedfx-lookup.md) and in [Framework version resolution](framework-version-resolution.md).
The host FXR library reads the `.runtimeconfig.json` of the app (and all it's dependent frameworks) and resolves the frameworks. It implements the algorithm for framework resolution as described in [SharedFX Lookup](sharedfx-lookup.md) and in [Framework version resolution](framework-version-resolution.md).
In most cases the latest available version of `hostfxr` is used. Self-contained apps use `hostfxr` from the app folder.
......
......@@ -48,7 +48,7 @@ The list of probing paths ordered according to their priority. First path in the
If the app (or framework) has dependencies on frameworks, these frameworks are used as probing paths.
The order is from the higher level framework to lower level framework. The app is considered the highest level, it direct dependencies are next and so on.
For assets from frameworks, only that framework and lower level frameworks are considered.
Note: These directories come directly out of the framework resolution process. Special note on Windows where global locations are always considered even if the app is not executed via the shared `dotnet.exe`. More details can be found in [Multi-level Shared FX Lookup](multilevel-sharedfx-lookup.md).
Note: These directories come directly out of the framework resolution process. Special note on Windows where global locations are always considered even if the app is not executed via the shared `dotnet.exe`. More details can be found in [Shared FX Lookup](sharedfx-lookup.md).
* Shared store paths
* `$DOTNET_SHARED_STORE/|arch|/|tfm|` - The environment variable `DOTNET_SHARED_STORE` can contain multiple paths, in which case each is appended with `|arch|/|tfm|` and used as a probing path.
* If the app is executed through `dotnet.exe` then path relative to the directory with the `dotnet.exe` is used
......
# Multi-level SharedFX Lookup
# SharedFX Lookup
## Introduction
There are two possible ways of running .NET Core Applications: through dotnet.exe or through a custom executable appname.exe. The first one is used when the user wants to run a framework-dependent app or a .NET Core command while the second one is used for self-contained applications. Both executables share exactly the same source code.
There are two main ways of running .NET Applications: through `dotnet` or through the `apphost` executables. The executable is in charge of finding and loading `hostfxr`. `hostfxr`, in turn, must find and load `hostpolicy`. It is also responsible for searching for the SDK when running .NET SDK commands. Finally, `hostpolicy` must find and load the runtime (`coreclr`). See [host components](host-components.md) for details.
The executable is in charge of finding and loading the hostfxr.dll file. The hostfxr, in turn, must find and load the hostpolicy.dll file (it’s also responsible for searching for the SDK when running .NET commands). At last the coreclr.dll file must be found and loaded by the hostpolicy. Self-contained apps are supposed to keep all its dependencies in the same location as the executable. Framework-dependent apps must have the runtime files inside predefined folders.
An application can either be [framework-dependent](https://docs.microsoft.com/dotnet/core/deploying/#publish-framework-dependent) or [self-contained](https://docs.microsoft.com/dotnet/core/deploying/#publish-self-contained). Framework-dependent apps must have the runtime files inside predefined folders. Self-contained apps are expected to have their dependencies in the same location as the executable.
## Semantic Versioning
......@@ -219,6 +219,16 @@ In order to compare versions of an assembly, the assemblyVersion and fileVersion
## Global locations
Global install locations are described in the [install locations design](https://github.com/dotnet/designs/blob/main/accepted/2020/install-locations.md) document.
**.NET 7.0 and above**
When running `dotnet`, only the executable directory will be searched and global locations are not searched. For all other [entry-point hosts](host-components.md#entry-point-hosts), if the `DOTNET_ROOT` environment variable is set, that path is searched. If the environment variable is not set, the global location as described in [install locations](https://github.com/dotnet/designs/blob/main/accepted/2020/install-locations.md) is searched.
See [disable multi-level lookup](https://github.com/dotnet/designs/blob/main/accepted/2022/disable-multi-level-lookup-by-default.md) for more details.
**Before .NET 7.0**
In addition to searching the executable directory, the global .NET location is also searched. The global folders may vary depending on the running operational system. They are defined as follows:
Global .NET location:
......@@ -271,11 +281,19 @@ To make sure that the changes are working correctly, the following behavior cond
### SDK search
Like the Framework search, the SDK is searched for a compatible version. Instead of looking for it only in relation to the executable directory, it is also searched in the folders specified above by following the same priority rank.
Like the Framework search, the SDK is searched for a compatible version.
Unlike the Framework search, the SDK search does a roll-forward for pre-release versions when the patch version changes. For example, if you install v2.0.1-pre, it will be used over v2.0.0.
**.NET 7.0 and above**
Only the executable directory will be searched. See [disable multi-level lookup](https://github.com/dotnet/designs/blob/main/accepted/2022/disable-multi-level-lookup-by-default.md) for more details.
**Before .NET 7.0**
Aside from looking for it in relation to the executable directory, it is also searched in the folders specified above by following the same priority rank.
The search is conducted as follows:
1. In relation to the executable directory: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, proceed to the next step.
2. In relation to the global location: search for the specified version. If it cannot be found, choose the most appropriate available version. If there’s no available version, then we were not able to find any version folder and an error message is returned.
Unlike the Framework search, the SDK search does a roll-forward for pre-release versions when the patch version changes. For example, if you install v2.0.1-pre, it will be used over v2.0.0.
......@@ -79,7 +79,7 @@ public void InvalidFileOrCommand_NoSDK_ListsPossibleIssues()
.Execute(fExpectedToFail: true)
.Should().Fail()
.And.HaveStdErrContaining($"The application '{fileName}' does not exist")
.And.HaveStdErrContaining($"It was not possible to find any installed .NET SDKs");
.And.FindAnySdk(false);
}
// Return a non-exisitent path that contains a mix of / and \
......
......@@ -35,10 +35,10 @@ public static AndConstraint<CommandResultAssertions> ShouldHaveResolvedFramework
/// <returns>Constraint</returns>
public static AndConstraint<CommandResultAssertions> ShouldHaveResolvedFrameworkOrFailToFind(this CommandResult result, string resolvedFrameworkName, string resolvedFrameworkVersion, string resolvedFrameworkBasePath = null)
{
if (resolvedFrameworkName == null || resolvedFrameworkVersion == null ||
if (resolvedFrameworkName == null || resolvedFrameworkVersion == null ||
resolvedFrameworkVersion == FrameworkResolutionBase.ResolvedFramework.NotFound)
{
return result.ShouldFailToFindCompatibleFrameworkVersion();
return result.ShouldFailToFindCompatibleFrameworkVersion(resolvedFrameworkName);
}
else
{
......@@ -46,15 +46,21 @@ public static AndConstraint<CommandResultAssertions> ShouldHaveResolvedFramework
}
}
public static AndConstraint<CommandResultAssertions> DidNotFindCompatibleFrameworkVersion(this CommandResultAssertions assertion)
public static AndConstraint<CommandResultAssertions> DidNotFindCompatibleFrameworkVersion(this CommandResultAssertions assertion, string frameworkName, string requestedVersion)
{
return assertion.HaveStdErrContaining("It was not possible to find any compatible framework version");
var constraint = assertion.HaveStdErrContaining("You must install or update .NET to run this application.");
if (frameworkName is not null)
{
constraint = constraint.And.HaveStdErrContaining($"Framework: '{frameworkName}', {(requestedVersion is null ? "" : $"version '{requestedVersion}'")}");
}
return constraint;
}
public static AndConstraint<CommandResultAssertions> ShouldFailToFindCompatibleFrameworkVersion(this CommandResult result)
public static AndConstraint<CommandResultAssertions> ShouldFailToFindCompatibleFrameworkVersion(this CommandResult result, string frameworkName, string requestedVersion = null)
{
return result.Should().Fail()
.And.DidNotFindCompatibleFrameworkVersion();
.And.DidNotFindCompatibleFrameworkVersion(frameworkName, requestedVersion);
}
public static AndConstraint<CommandResultAssertions> FailedToReconcileFrameworkReference(
......@@ -96,7 +102,7 @@ public static AndConstraint<CommandResultAssertions> ShouldFailToFindCompatibleF
}
else if (resolvedVersion == FrameworkResolutionBase.ResolvedFramework.NotFound)
{
return result.ShouldFailToFindCompatibleFrameworkVersion();
return result.ShouldFailToFindCompatibleFrameworkVersion(frameworkName, null);
}
else
{
......
......@@ -25,7 +25,7 @@ public MultipleHives(SharedTestState sharedState)
[Theory]
// MLL where global hive has a better match
[InlineData("5.0.0", "net5.0", true, "5.1.2")]
[InlineData("5.0.0", "net5.0", null, "5.1.2")] // MLL is on by default, so same as true
[InlineData("5.0.0", "net5.0", null, "5.1.2")] // MLL is on by default before 7.0, so same as true
[InlineData("5.0.0", "net5.0", false, "5.2.0")] // No global hive allowed
// MLL (where global hive has better match) with various TFMs
[InlineData("5.0.0", "netcoreapp3.0", true, "5.1.2")]
......@@ -37,11 +37,12 @@ public MultipleHives(SharedTestState sharedState)
[InlineData("5.0.0", "net6.0", true, "5.1.2")]
[InlineData("5.0.0", "net6.0", null, "5.1.2")]
[InlineData("5.0.0", "net6.0", false, "5.2.0")]
[InlineData("7.0.0", "net7.0", true, "7.0.1")]
[InlineData("7.0.0", "net7.0", null, "7.0.1")]
// MLL is disabled for 7.0+
[InlineData("7.0.0", "net7.0", true, "7.1.2")] // MLL disabled for 7.0+ - setting it doesn't change anything
[InlineData("7.0.0", "net7.0", null, "7.1.2")]
[InlineData("7.0.0", "net7.0", false, "7.1.2")]
[InlineData("7.0.0", "net8.0", true, "7.0.1")]
[InlineData("7.0.0", "net8.0", null, "7.0.1")]
[InlineData("7.0.0", "net8.0", true, "7.1.2")] // MLL disabled for 7.0+ - setting it doesn't change anything
[InlineData("7.0.0", "net8.0", null, "7.1.2")]
[InlineData("7.0.0", "net8.0", false, "7.1.2")]
// MLL where main hive has a better match
[InlineData("6.0.0", "net6.0", true, "6.1.4")] // Global hive with better version (higher patch)
......@@ -82,7 +83,7 @@ public void FrameworkHiveSelection_CurrentDirectoryIsIgnored()
[InlineData("6.1.4", "net6.0", true, "6.1.4", true)]
[InlineData("6.1.4", "net6.0", null, "6.1.4", true)]
[InlineData("6.1.4", "net6.0", false, ResolvedFramework.NotFound, false)]
[InlineData("6.1.4", "net7.0", true, "6.1.4", true)]
[InlineData("6.1.4", "net7.0", true, ResolvedFramework.NotFound, false)] // MLL disabled for 7.0+
[InlineData("7.1.2", "net6.0", true, "7.1.2", false)] // 7.1.2 is in both main and global hives - the main should always win with exact match
[InlineData("7.1.2", "net6.0", null, "7.1.2", false)]
[InlineData("7.1.2", "net6.0", false, "7.1.2", false)]
......@@ -154,7 +155,7 @@ public void ListRuntimes(bool? multiLevelLookup)
string expectedOutput = string.Join(
string.Empty,
GetExpectedFrameworks(multiLevelLookup)
GetExpectedFrameworks(false) // MLL Is always disabled for dotnet --list-runtimes
.Select(t => $"{MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}"));
// !!IMPORTANT!!: This test verifies the exact match of the entire output of the command (not a substring!)
......@@ -179,7 +180,7 @@ public void DotnetInfo(bool? multiLevelLookup)
string expectedOutput =
$".NET runtimes installed:{Environment.NewLine}" +
string.Join(string.Empty,
GetExpectedFrameworks(multiLevelLookup)
GetExpectedFrameworks(false) // MLL is always disabled for dotnet --info
.Select(t => $" {MicrosoftNETCoreApp} {t.Version} [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}"));
RunTest(
......@@ -190,13 +191,14 @@ public void DotnetInfo(bool? multiLevelLookup)
}
[Theory]
[InlineData("net5.0", true)]
[InlineData("net5.0", null)]
[InlineData("net5.0", false)]
[InlineData("net7.0", true)]
[InlineData("net7.0", null)]
[InlineData("net7.0", false)]
public void FrameworkResolutionError(string tfm, bool? multiLevelLookup)
[InlineData("net5.0", true, true)]
[InlineData("net5.0", null, true)]
[InlineData("net5.0", false, false)]
// MLL is disabled for 7.0+
[InlineData("net7.0", true, false)]
[InlineData("net7.0", null, false)]
[InlineData("net7.0", false, false)]
public void FrameworkResolutionError(string tfm, bool? multiLevelLookup, bool effectiveMultiLevelLookup)
{
// Multi-level lookup is only supported on Windows.
if (!OperatingSystem.IsWindows() && multiLevelLookup != false)
......@@ -205,8 +207,8 @@ public void FrameworkResolutionError(string tfm, bool? multiLevelLookup)
string expectedOutput =
$"The following frameworks were found:{Environment.NewLine}" +
string.Join(string.Empty,
GetExpectedFrameworks(multiLevelLookup)
.Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}"));
GetExpectedFrameworks(effectiveMultiLevelLookup)
.Select(t => $" {t.Version} at [{Path.Combine(t.Path, "shared", MicrosoftNETCoreApp)}]{Environment.NewLine}"));
RunTest(
runtimeConfig => runtimeConfig
......@@ -214,7 +216,8 @@ public void FrameworkResolutionError(string tfm, bool? multiLevelLookup)
.WithFramework(MicrosoftNETCoreApp, "9999.9.9"),
multiLevelLookup)
.Should().Fail()
.And.HaveStdErrContaining(expectedOutput);
.And.HaveStdErrContaining(expectedOutput)
.And.HaveStdErrContaining("https://aka.ms/dotnet/app-launch-failed");
}
private CommandResult RunTest(Func<RuntimeConfig, RuntimeConfig> runtimeConfig, bool? multiLevelLookup = true)
......
......@@ -101,10 +101,11 @@ public void CollisionsOnCommandLine_RollForwardOnNoCandidateFx()
SettingLocation rollForwardOnNoCandidateFxLocation,
bool passes)
{
string requestedVersion = "5.0.0";
CommandResult result = RunTest(
new TestSettings()
.WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig
.WithFramework(MicrosoftNETCoreApp, "5.0.0"))
.WithFramework(MicrosoftNETCoreApp, requestedVersion))
.With(RollForwardSetting(rollForwardLocation, Constants.RollForwardSetting.Major))
.With(RollForwardOnNoCandidateFxSetting(rollForwardOnNoCandidateFxLocation, 0)));
......@@ -114,7 +115,7 @@ public void CollisionsOnCommandLine_RollForwardOnNoCandidateFx()
}
else
{
result.Should().Fail().And.DidNotFindCompatibleFrameworkVersion();
result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
}
......
......@@ -8,7 +8,7 @@
namespace Microsoft.DotNet.CoreSetup.Test.HostActivation.FrameworkResolution
{
public class RollForwardOnNoCandidateFx :
public class RollForwardOnNoCandidateFx :
FrameworkResolutionBase,
IClassFixture<RollForwardOnNoCandidateFx.SharedTestState>
{
......@@ -113,18 +113,19 @@ public void RollForwardToLatestPatch_RollForwardOnNoCandidateFx(int? rollForward
[InlineData(2, false, true)]
public void RollForwardOnMinor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCandidateFx, bool? applyPatches, bool passes)
{
string requestedVersion = "5.0.0";
CommandResult result = RunTestWithOneFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx)
.WithApplyPatches(applyPatches)
.WithFramework(MicrosoftNETCoreApp, "5.0.0"));
.WithFramework(MicrosoftNETCoreApp, requestedVersion));
if (passes)
{
result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3");
}
else
{
result.ShouldFailToFindCompatibleFrameworkVersion();
result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
}
......@@ -140,18 +141,19 @@ public void RollForwardOnMinor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCa
[InlineData(2, false, true)]
public void RollForwardOnMajor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCandidateFx, bool? applyPatches, bool passes)
{
string requestedVersion = "4.1.0";
CommandResult result = RunTestWithOneFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx)
.WithApplyPatches(applyPatches)
.WithFramework(MicrosoftNETCoreApp, "4.1.0"));
.WithFramework(MicrosoftNETCoreApp, requestedVersion));
if (passes)
{
result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3");
}
else
{
result.ShouldFailToFindCompatibleFrameworkVersion();
result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
}
......@@ -165,12 +167,13 @@ public void RollForwardOnMajor_RollForwardOnNoCandidateFx(int? rollForwardOnNoCa
[InlineData(2, false)]
public void NeverRollBackOnRelease(int? rollForwardOnNoCandidateFx, bool? applyPatches)
{
string requestedVersion = "5.1.4";
RunTestWithOneFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx)
.WithApplyPatches(applyPatches)
.WithFramework(MicrosoftNETCoreApp, "5.1.4"))
.ShouldFailToFindCompatibleFrameworkVersion();
.WithFramework(MicrosoftNETCoreApp, requestedVersion))
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verifies that if both rollForwardOnNoCandidateFx=0 and applyPatches=0 there will be no rolling forward.
......@@ -203,14 +206,14 @@ public void RollForwardDisabledOnCandidateFxAndDisabledApplyPatches_MatchesExact
[Fact]
public void RollForwardOnMinorDisabledOnNoCandidateFx_FailsToRoll()
{
string requestedVersion = "5.0.0";
RunTestWithOneFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(0)
.WithFramework(MicrosoftNETCoreApp, "5.0.0"))
.WithFramework(MicrosoftNETCoreApp, requestedVersion))
// Will still attempt roll forward to latest patch
.Should().Fail()
.And.HaveStdErrContaining("Attempting FX roll forward")
.And.DidNotFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion)
.And.HaveStdErrContaining("Attempting FX roll forward");
}
// 3.0 change: In 2.* pre-release never rolled to release. In 3.* it will follow normal roll-forward rules.
......@@ -313,22 +316,23 @@ public void RollForwardToPreReleaseLatestPatch_RollForwardOnNoCandidateFx(int? r
[InlineData(2, null, true)]
[InlineData(2, false, true)]
public void RollForwardToPreReleaseOnMinor_RollForwardOnNoCandidateFx(
int? rollForwardOnNoCandidateFx,
bool? applyPatches,
int? rollForwardOnNoCandidateFx,
bool? applyPatches,
bool passes)
{
string requestedVersion = "5.0.0";
CommandResult result = RunTestWithPreReleaseFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx)
.WithApplyPatches(applyPatches)
.WithFramework(MicrosoftNETCoreApp, "5.0.0"));
.WithFramework(MicrosoftNETCoreApp, requestedVersion));
if (passes)
{
result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3-preview.2");
}
else
{
result.ShouldFailToFindCompatibleFrameworkVersion();
result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
}
......@@ -346,18 +350,19 @@ public void RollForwardToPreReleaseLatestPatch_RollForwardOnNoCandidateFx(int? r
bool? applyPatches,
bool passes)
{
string requestedVersion = "4.1.0";
CommandResult result = RunTestWithPreReleaseFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx)
.WithApplyPatches(applyPatches)
.WithFramework(MicrosoftNETCoreApp, "4.1.0"));
.WithFramework(MicrosoftNETCoreApp, requestedVersion));
if (passes)
{
result.ShouldHaveResolvedFramework(MicrosoftNETCoreApp, "5.1.3-preview.2");
}
else
{
result.ShouldFailToFindCompatibleFrameworkVersion();
result.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
}
......@@ -371,12 +376,13 @@ public void RollForwardToPreReleaseLatestPatch_RollForwardOnNoCandidateFx(int? r
[InlineData(2, false)]
public void NeverRollBackOnPreRelease(int? rollForwardOnNoCandidateFx, bool? applyPatches)
{
string requestedVersion = "5.1.3-preview.9";
RunTestWithPreReleaseFramework(
runtimeConfig => runtimeConfig
.WithRollForwardOnNoCandidateFx(rollForwardOnNoCandidateFx)
.WithApplyPatches(applyPatches)
.WithFramework(MicrosoftNETCoreApp, "5.1.3-preview.9"))
.ShouldFailToFindCompatibleFrameworkVersion();
.WithFramework(MicrosoftNETCoreApp, requestedVersion))
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
private CommandResult RunTestWithPreReleaseFramework(Func<RuntimeConfig, RuntimeConfig> runtimeConfig)
......@@ -623,7 +629,7 @@ private CommandResult RunTestWithPreReleaseFramework(Func<RuntimeConfig, Runtime
[InlineData(2, false, "2.3.2")] // Pre-release is ignored, roll forward to closest release available
public void RollForwardToClosestReleaseWithPreReleaseAvailable_FromRelease(
int? rollForwardOnNoCandidateFx,
bool? applyPatches,
bool? applyPatches,
string resolvedFramework)
{
RunTestWithManyVersions(
......
......@@ -24,12 +24,12 @@ public RollForwardOnNoCandidateFxSettings(SharedTestState sharedState)
[Fact]
public void Default()
{
string requestedVersion = "4.0.0";
RunTest(
new TestSettings()
.WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig
.WithFramework(MicrosoftNETCoreApp, "4.0.0")))
.Should().Fail()
.And.DidNotFindCompatibleFrameworkVersion();
.WithFramework(MicrosoftNETCoreApp, requestedVersion)))
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
RunTest(
new TestSettings()
......@@ -122,7 +122,7 @@ public void EnvironmentPriority(SettingLocation settingLocation, bool envVariabl
// Verifies interaction between variour <settingLocation> and inner framework reference setting
[Theory] // settingLocation innerReferenceWins
// Command line overrides everything - even inner framework references
[InlineData(SettingLocation.CommandLine, false)]
[InlineData(SettingLocation.CommandLine, false)]
[InlineData(SettingLocation.RuntimeOptions, true)]
[InlineData(SettingLocation.FrameworkReference, true)]
[InlineData(SettingLocation.Environment, true)]
......@@ -149,7 +149,7 @@ public void InnerFrameworkReference(SettingLocation settingLocation, bool innerR
// RuntimeOptions and FrameworkReference settings are not inherited to inner reference
[InlineData(SettingLocation.FrameworkReference, false)]
// Since none is specified for the inner reference, environment is used
[InlineData(SettingLocation.Environment, true)]
[InlineData(SettingLocation.Environment, true)]
public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appWins)
{
RunTest(
......@@ -172,7 +172,7 @@ public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appW
// RuntimeOptions and FrameworkReference settings are not inherited to inner reference
[InlineData(SettingLocation.FrameworkReference, false)]
// Since none is specified for the inner reference, environment is used
[InlineData(SettingLocation.Environment, true)]
[InlineData(SettingLocation.Environment, true)]
public void NoInheritance_MoreRestrictive(SettingLocation settingLocation, bool appWins)
{
RunTest(
......@@ -186,7 +186,7 @@ public void NoInheritance_MoreRestrictive(SettingLocation settingLocation, bool
.ShouldHaveResolvedFrameworkOrFailToFind(MicrosoftNETCoreApp, appWins ? null : "5.1.3");
}
private CommandResult RunTest(TestSettings testSettings) =>
private CommandResult RunTest(TestSettings testSettings) =>
RunTest(SharedState.DotNetWithFrameworks, SharedState.FrameworkReferenceApp, testSettings);
public class SharedTestState : SharedTestStateBase
......@@ -202,7 +202,7 @@ public SharedTestState()
.AddMicrosoftNETCoreAppFrameworkMockHostPolicy("2.5.5")
.AddMicrosoftNETCoreAppFrameworkMockHostPolicy("5.1.3")
.AddFramework(
MiddleWare, "2.1.2",
MiddleWare, "2.1.2",
runtimeConfig => runtimeConfig.WithFramework(MicrosoftNETCoreApp, "5.1.3"))
.Build();
......
......@@ -122,11 +122,12 @@ public void RollForwardOnMajor_FromReleaseToPreRelease(string rollForward, bool?
[InlineData(Constants.RollForwardSetting.LatestPatch, false)]
public void NeverRollBackOnPreRelease_PreReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "5.1.2-preview.3";
RunTest(
"5.1.2-preview.3",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verifies that rollForward settings won't roll back (on patch).
......@@ -139,11 +140,12 @@ public void NeverRollBackOnPreRelease_PreReleaseOnly(string rollForward, bool? a
[InlineData(Constants.RollForwardSetting.LatestPatch, false)]
public void NeverRollBackOnPatch_PreReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "5.1.3-preview.1";
RunTest(
"5.1.3-preview.1",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verifies that rollForward settings won't roll back (on minor).
......@@ -157,11 +159,12 @@ public void NeverRollBackOnPatch_PreReleaseOnly(string rollForward, bool? applyP
[InlineData(Constants.RollForwardSetting.LatestMinor, false)]
public void NeverRollBackOnMinor_PreReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "5.3.0-preview.1";
RunTest(
"5.3.0-preview.1",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verifies that rollForward settings won't roll back (on major).
......@@ -175,11 +178,12 @@ public void NeverRollBackOnMinor_PreReleaseOnly(string rollForward, bool? applyP
[InlineData(Constants.RollForwardSetting.LatestMinor, false)]
public void NeverRollBackOnMajor_PreReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "7.1.0-preview.1";
RunTest(
"7.1.0-preview.1",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verifies that rollForward settings behave as expected starting with framework reference
......
......@@ -166,11 +166,12 @@ public void RollForwardOnMajor_ReleaseOnly(string rollForward, bool? applyPatche
[InlineData(Constants.RollForwardSetting.LatestPatch, false)]
public void NeverRollBackOnPatch_ReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "2.1.4";
RunTest(
"2.1.4",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verify that rollForward settings will never roll back to lower minor version.
......@@ -183,11 +184,12 @@ public void NeverRollBackOnPatch_ReleaseOnly(string rollForward, bool? applyPatc
[InlineData(Constants.RollForwardSetting.LatestMinor, false)]
public void NeverRollBackOnMinor_ReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "2.5.0";
RunTest(
"2.5.0",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
// Verify that rollForward settings will never roll back to lower major version.
......@@ -200,11 +202,12 @@ public void NeverRollBackOnMinor_ReleaseOnly(string rollForward, bool? applyPatc
[InlineData(Constants.RollForwardSetting.LatestMinor, false)]
public void NeverRollBackOnMajor_ReleaseOnly(string rollForward, bool? applyPatches)
{
string requestedVersion = "4.1.0";
RunTest(
"4.1.0",
requestedVersion,
rollForward,
applyPatches)
.ShouldFailToFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
}
private CommandResult RunTest(
......
......@@ -24,12 +24,12 @@ public RollForwardSettings(SharedTestState sharedState)
[Fact]
public void Default()
{
string requestedVersion = "4.0.0";
RunTest(
new TestSettings()
.WithRuntimeConfigCustomizer(runtimeConfig => runtimeConfig
.WithFramework(MicrosoftNETCoreApp, "4.0.0")))
.Should().Fail()
.And.DidNotFindCompatibleFrameworkVersion();
.ShouldFailToFindCompatibleFrameworkVersion(MicrosoftNETCoreApp, requestedVersion);
RunTest(
new TestSettings()
......@@ -138,7 +138,7 @@ private void ValidateValueIgnoresCase(SettingLocation settingLocation, string ro
// RuntimeOptions and FrameworkReference settings are not inherited to inner reference
[InlineData(SettingLocation.FrameworkReference, false)]
// Since none is specified for the inner reference, environment is used
[InlineData(SettingLocation.Environment, true)]
[InlineData(SettingLocation.Environment, true)]
public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appWins)
{
RunTest(
......@@ -161,7 +161,7 @@ public void NoInheritance_MoreRelaxed(SettingLocation settingLocation, bool appW
// RuntimeOptions and FrameworkReference settings are not inherited to inner reference
[InlineData(SettingLocation.FrameworkReference, false)]
// Since none is specified for the inner reference, environment is used
[InlineData(SettingLocation.Environment, true)]
[InlineData(SettingLocation.Environment, true)]
public void NoInheritance_MoreRestrictive(SettingLocation settingLocation, bool appWins)
{
RunTest(
......
......@@ -81,17 +81,20 @@ public void Dispose()
[PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows.
public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
{
// Multi-level lookup is disabled for 7.0+, so the resolved SDK should never be from the registered directory.
// Set specified SDK version = 9999.3.4-global-dummy
SetGlobalJsonVersion("SingleDigit-global.json");
string globalJsonPath = SetGlobalJsonVersion("SingleDigit-global.json");
string requestedVersion = "9999.3.4-global-dummy";
// Specified SDK version: 9999.3.4-global-dummy
// Cwd: empty
// Exe: empty
// Reg: empty
// Expected: no compatible version and a specific error messages
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.4.1", "9999.3.4-dummy");
......@@ -100,10 +103,10 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
// Cwd: empty
// Exe: 9999.4.1, 9999.3.4-dummy
// Reg: empty
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.3");
......@@ -112,10 +115,10 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
// Cwd: empty
// Exe: 9999.4.1, 9999.3.4-dummy
// Reg: 9999.3.3
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4");
......@@ -136,10 +139,10 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
// Cwd: empty
// Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4
// Reg: 9999.3.3, 9999.3.5-dummy
// Expected: 9999.3.5-dummy from reg dir
// Expected: 9999.3.4 from exe dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
.And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator));
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.600");
......@@ -148,10 +151,10 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
// Cwd: empty
// Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.4, 9999.3.600
// Reg: 9999.3.3, 9999.3.5-dummy
// Expected: 9999.3.5-dummy from reg dir
// Expected: 9999.3.4-dummy from exe dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.5-dummy", _dotnetSdkDllMessageTerminator));
.And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.4", _dotnetSdkDllMessageTerminator));
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.4-global-dummy");
......@@ -171,27 +174,28 @@ public void SdkMultilevelLookup_Global_Json_Single_Digit_Patch_Rollup()
.And.HaveStdOutContaining("9999.3.4-dummy")
.And.HaveStdOutContaining("9999.3.4-global-dummy")
.And.HaveStdOutContaining("9999.4.1")
.And.HaveStdOutContaining("9999.3.3")
.And.HaveStdOutContaining("9999.3.4")
.And.HaveStdOutContaining("9999.3.600")
.And.HaveStdOutContaining("9999.3.5-dummy");
.And.HaveStdOutContaining("9999.3.600");
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows.
public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup()
{
// Multi-level lookup is disabled for 7.0+, so the resolved SDK should never be from the registered directory.
// Set specified SDK version = 9999.3.304-global-dummy
SetGlobalJsonVersion("TwoPart-global.json");
string globalJsonPath = SetGlobalJsonVersion("TwoPart-global.json");
string requestedVersion = "9999.3.304-global-dummy";
// Specified SDK version: 9999.3.304-global-dummy
// Cwd: empty
// Exe: empty
// Reg: empty
// Expected: no compatible version and a specific error messages
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.57", "9999.3.4-dummy");
......@@ -200,10 +204,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup()
// Cwd: empty
// Exe: empty
// Reg: 9999.3.57, 9999.3.4-dummy
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.300", "9999.7.304-global-dummy");
......@@ -212,10 +216,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup()
// Cwd: empty
// Exe: 9999.3.300, 9999.7.304-global-dummy
// Reg: 9999.3.57, 9999.3.4-dummy
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_regSdkBaseDir, "9999.3.304");
......@@ -224,10 +228,10 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup()
// Cwd: empty
// Exe: 9999.3.300, 9999.7.304-global-dummy
// Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304
// Expected: 9999.3.304 from reg dir
// Expected: no compatible version
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304", _dotnetSdkDllMessageTerminator));
.Should().Fail()
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion);
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.3.399", "9999.3.399-dummy", "9999.3.400");
......@@ -261,25 +265,21 @@ public void SdkMultilevelLookup_Global_Json_Two_Part_Patch_Rollup()
// Cwd: empty
// Exe: 9999.3.300, 9999.7.304-global-dummy, 9999.3.399, 9999.3.399-dummy, 9999.3.400, 9999.3.2400, 9999.3.3004
// Reg: 9999.3.57, 9999.3.4-dummy, 9999.3.304, 9999.3.2400, 9999.3.3004, 9999.3.304-global-dummy
// Expected: 9999.3.304-global-dummy from reg dir
// Expected: 9999.3.399 from exe dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.3.304-global-dummy", _dotnetSdkDllMessageTerminator));
.And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.3.399", _dotnetSdkDllMessageTerminator));
// Verify we have the expected SDK versions
RunTest("--list-sdks")
.Should().Pass()
.And.HaveStdOutContaining("9999.3.57")
.And.HaveStdOutContaining("9999.3.4-dummy")
.And.HaveStdOutContaining("9999.3.300")
.And.HaveStdOutContaining("9999.7.304-global-dummy")
.And.HaveStdOutContaining("9999.3.399")
.And.HaveStdOutContaining("9999.3.399-dummy")
.And.HaveStdOutContaining("9999.3.400")
.And.HaveStdOutContaining("9999.3.2400")
.And.HaveStdOutContaining("9999.3.3004")
.And.HaveStdOutContaining("9999.3.304")
.And.HaveStdOutContaining("9999.3.304-global-dummy");
.And.HaveStdOutContaining("9999.3.3004");
}
[Fact]
......@@ -295,10 +295,10 @@ public void SdkMultilevelLookup_Precedential_Order()
// Cwd: empty
// Exe: empty
// Reg: 9999.0.4
// Expected: 9999.0.4 from reg dir
// Expected: no SDKs found
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator));
.Should().Fail()
.And.FindAnySdk(false);
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.4");
......@@ -319,12 +319,12 @@ public void SdkMultilevelLookup_RegistryAccess()
{
// The purpose of this test is to verify that the product uses correct code to access
// the registry to extract the path to search for SDKs.
// Most of our tests rely on a shortcut which is to set _DOTNET_TEST_SDK_SELF_REGISTERED_DIR env variable
// Most of our tests rely on a shortcut which is to set _DOTNET_TEST_GLOBALLY_REGISTERED_PATH env variable
// which will skip the registry reading code in the product and simply use the specified value.
// This test is different since it actually runs the registry reading code.
// Normally the reg key the product uses is in HKEY_LOCAL_MACHINE which is only writable as admin
// so we would require the tests to run as admin to modify that key (and it may introduce races with other code running on the machine).
// So instead the tests use _DOTENT_TEST_SDK_REGISTRY_PATH env variable to point to the produce to use
// So instead the tests use _DOTENT_TEST_REGISTRY_PATH env variable to point to the produce to use
// different registry key, inside the HKEY_CURRENT_USER hive which is writable without admin.
// Note that the test creates a unique key (based on PID) for every run, to avoid collisions between parallel running tests.
......@@ -341,15 +341,15 @@ public void SdkMultilevelLookup_RegistryAccess()
// Cwd: empty
// Exe: empty
// Reg: 9999.0.4
// Expected: 9999.0.4 from reg dir
// Expected: no SDKs found
DotNet.Exec("help")
.WorkingDirectory(_currentWorkingDir)
.MultilevelLookup(true)
.ApplyRegisteredInstallLocationOverride(registeredInstallLocationOverride)
.EnableTracingAndCaptureOutputs()
.Execute()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.4", _dotnetSdkDllMessageTerminator));
.Should().Fail()
.And.FindAnySdk(false);
}
}
......@@ -366,10 +366,10 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version()
// Cwd: empty
// Exe: empty
// Reg: 9999.0.0, 9999.0.3-dummy
// Expected: 9999.0.3-dummy from reg dir
// Expected: no SDKs found
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.3-dummy", _dotnetSdkDllMessageTerminator));
.Should().Fail()
.And.FindAnySdk(false);
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.3");
......@@ -394,7 +394,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version()
// Expected: 9999.0.100 from reg dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
.And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.3", _dotnetSdkDllMessageTerminator));
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.80");
......@@ -406,7 +406,7 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version()
// Expected: 9999.0.100 from reg dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.100", _dotnetSdkDllMessageTerminator));
.And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.80", _dotnetSdkDllMessageTerminator));
// Add SDK versions
AddAvailableSdkVersions(_exeSdkBaseDir, "9999.0.5500000");
......@@ -430,21 +430,17 @@ public void SdkMultilevelLookup_Must_Pick_The_Highest_Semantic_Version()
// Expected: 9999.0.52000000 from reg dir
RunTest()
.Should().Pass()
.And.HaveStdErrContaining(Path.Combine(_regSelectedMessage, "9999.0.52000000", _dotnetSdkDllMessageTerminator));
.And.HaveStdErrContaining(Path.Combine(_exeSelectedMessage, "9999.0.5500000", _dotnetSdkDllMessageTerminator));
// Verify we have the expected SDK versions
RunTest("--list-sdks")
.Should().Pass()
.And.HaveStdOutContaining("9999.0.0")
.And.HaveStdOutContaining("9999.0.3-dummy")
.And.HaveStdOutContaining("9999.0.3")
.And.HaveStdOutContaining("9999.0.100")
.And.HaveStdOutContaining("9999.0.80")
.And.HaveStdOutContaining("9999.0.5500000")
.And.HaveStdOutContaining("9999.0.52000000");
.And.HaveStdOutContaining("9999.0.5500000");
}
private List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList(bool? multiLevelLookup)
private List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList()
{
AddAvailableSdkVersions(_exeSdkBaseDir, "5.0.2");
AddAvailableSdkVersions(_exeSdkBaseDir, "6.1.1");
......@@ -457,11 +453,7 @@ private List<(string version, string rootPath)> AddSdkVersionsAndGetExpectedList
expectedList.Add(("5.0.2", _exeSdkBaseDir));
expectedList.Add(("6.1.1", _exeSdkBaseDir));
expectedList.Add(("7.1.2", _exeSdkBaseDir));
if (multiLevelLookup is null || multiLevelLookup == true)
{
expectedList.Add(("6.2.0", _regSdkBaseDir));
expectedList.Add(("7.0.1", _regSdkBaseDir));
}
// MLL is always disabled for SDK resolution, so only the "exe" SDKs are listed
expectedList.Sort((a, b) =>
{
if (!Version.TryParse(a.version, out var aVersion))
......@@ -485,7 +477,7 @@ public void ListSdks(bool? multiLevelLookup)
if (!OperatingSystem.IsWindows() && multiLevelLookup != false)
return;
var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup);
var expectedList = AddSdkVersionsAndGetExpectedList();
string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}"));
// !!IMPORTANT!!: This test verifies the exact match of the entire output of the command (not a substring!)
......@@ -506,14 +498,16 @@ public void SdkResolutionError(bool? multiLevelLookup)
return;
// Set specified SDK version = 9999.3.4-global-dummy - such SDK doesn't exist
SetGlobalJsonVersion("SingleDigit-global.json");
string globalJsonPath = SetGlobalJsonVersion("SingleDigit-global.json");
string requestedVersion = "9999.3.4-global-dummy";
// When we fail to resolve SDK version, we print out all available SDKs
var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup);
string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}"));
var expectedList = AddSdkVersionsAndGetExpectedList();
string expectedOutput = string.Join(string.Empty, expectedList.Select(t => $"{t.version} [{t.rootPath}]{Environment.NewLine}"));
RunTest("help", multiLevelLookup)
.Should().Fail()
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.HaveStdOutContaining(expectedOutput);
}
......@@ -527,7 +521,7 @@ public void DotnetInfo(bool? multiLevelLookup)
if (!OperatingSystem.IsWindows() && multiLevelLookup != false)
return;
var expectedList = AddSdkVersionsAndGetExpectedList(multiLevelLookup);
var expectedList = AddSdkVersionsAndGetExpectedList();
string expectedOutput =
$".NET SDKs installed:{Environment.NewLine}" +
string.Join(string.Empty, expectedList.Select(t => $" {t.version} [{t.rootPath}]{Environment.NewLine}"));
......@@ -575,13 +569,14 @@ private void AddAvailableSdkVersions(string sdkBaseDir, params string[] availabl
}
// Put a global.json file in the cwd in order to specify a CLI
private void SetGlobalJsonVersion(string globalJsonFileName)
private string SetGlobalJsonVersion(string globalJsonFileName)
{
string destFile = Path.Combine(_currentWorkingDir, "global.json");
string srcFile = Path.Combine(RepoDirectories.TestAssetsFolder, "TestUtils",
"SDKLookup", globalJsonFileName);
File.Copy(srcFile, destFile, true);
return destFile;
}
private void WriteGlobalJson(string contents)
......
......@@ -128,24 +128,18 @@ public SdkResolutionFixture(SharedTestState state)
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows.
[PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway)
public void Hostfxr_get_available_sdks_with_multilevel_lookup()
{
var f = new SdkResolutionFixture(sharedTestState);
// With multi-level lookup (windows only): get local and global sdks sorted by ascending version,
// with global sdk coming before local sdk when versions are equal
// Starting with .NET 7, multi-level lookup is completely disabled for hostfxr API calls.
// This test is still valuable to validate that it is in fact disabled
string expectedList = string.Join(';', new[]
{
Path.Combine(f.LocalSdkDir, "0.1.2"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"),
Path.Combine(f.LocalSdkDir, "1.2.3"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"),
Path.Combine(f.LocalSdkDir, "5.6.7-preview"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"),
});
using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath))
......@@ -317,7 +311,7 @@ public void Hostfxr_get_dotnet_environment_info_dotnet_root_only()
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows.
[PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway)
public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotnet_root()
{
var f = new SdkResolutionFixture(sharedTestState);
......@@ -325,33 +319,18 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn
{
"0.1.2",
"1.2.3",
"1.2.3",
"2.3.4-preview",
"3.0.0",
"4.5.6",
"5.6.7-preview",
"5.6.7",
"15.1.4-preview"
});
string expectedSdkPaths = string.Join(';', new[]
{
Path.Combine(f.LocalSdkDir, "0.1.2"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"),
Path.Combine(f.LocalSdkDir, "1.2.3"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"),
Path.Combine(f.LocalSdkDir, "5.6.7-preview"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"),
});
string expectedFrameworkNames = string.Join(';', new[]
{
"HostFxr.Test.A",
"HostFxr.Test.A",
"HostFxr.Test.B",
"HostFxr.Test.B",
"HostFxr.Test.B",
"HostFxr.Test.C"
......@@ -359,20 +338,14 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn
string expectedFrameworkVersions = string.Join(';', new[]
{
"1.2.3",
"3.0.0",
"4.0.0",
"5.6.7-A",
"5.6.7-A",
"3.0.0"
});
string expectedFrameworkPaths = string.Join(';', new[]
{
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"),
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"),
Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.B"),
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.B"),
Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.B"),
Path.Combine(f.LocalFrameworksDir, "HostFxr.Test.C")
});
......@@ -396,51 +369,13 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_with_dotn
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows.
[PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway)
public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only()
{
var f = new SdkResolutionFixture(sharedTestState);
string expectedSdkVersions = string.Join(';', new[]
{
"1.2.3",
"2.3.4-preview",
"3.0.0",
"4.5.6",
"5.6.7",
"15.1.4-preview"
});
string expectedSdkPaths = string.Join(';', new[]
{
Path.Combine(f.ProgramFilesGlobalSdkDir, "1.2.3"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "2.3.4-preview"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "3.0.0"),
Path.Combine(f.ProgramFilesGlobalSdkDir, "4.5.6"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "5.6.7"),
Path.Combine(f.SelfRegisteredGlobalSdkDir, "15.1.4-preview"),
});
string expectedFrameworkNames = string.Join(';', new[]
{
"HostFxr.Test.A",
"HostFxr.Test.A",
"HostFxr.Test.B",
});
string expectedFrameworkVersions = string.Join(';', new[]
{
"1.2.3",
"3.0.0",
"5.6.7-A",
});
string expectedFrameworkPaths = string.Join(';', new[]
{
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"),
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"),
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.B"),
});
// Multi-level lookup is completely disabled on 7+
// The test runs the API with the dotnet root directory set to a location which doesn't have any SDKs or frameworks
using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath))
{
// We pass f.WorkingDir so that we don't resolve dotnet_dir to the global installation
......@@ -453,41 +388,20 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only()
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[{expectedSdkVersions}]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[{expectedSdkPaths}]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[{expectedFrameworkNames}]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[{expectedFrameworkVersions}]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[{expectedFrameworkPaths}]");
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk versions:[]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info sdk paths:[]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[]");
}
}
[Fact]
[PlatformSpecific(TestPlatforms.Windows)] // Multi-level lookup is only supported on Windows.
[PlatformSpecific(TestPlatforms.Windows)] // The test setup only works on Windows (and MLL was Windows-only anyway)
public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only_self_register_program_files()
{
var f = new SdkResolutionFixture(sharedTestState);
string expectedFrameworkNames = string.Join(';', new[]
{
"HostFxr.Test.A",
"HostFxr.Test.A",
"HostFxr.Test.B",
});
string expectedFrameworkVersions = string.Join(';', new[]
{
"1.2.3",
"3.0.0",
"5.6.7-A",
});
string expectedFrameworkPaths = string.Join(';', new[]
{
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"),
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.A"),
Path.Combine(f.ProgramFilesGlobalFrameworksDir, "HostFxr.Test.B"),
});
using (TestOnlyProductBehavior.Enable(f.Dotnet.GreatestVersionHostFxrFilePath))
{
// We pass f.WorkingDir so that we don't resolve dotnet_dir to the global installation
......@@ -501,9 +415,9 @@ public void Hostfxr_get_dotnet_environment_info_with_multilevel_lookup_only_self
.Execute()
.Should().Pass()
.And.HaveStdOutContaining("hostfxr_get_dotnet_environment_info:Success")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[{expectedFrameworkNames}]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[{expectedFrameworkVersions}]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[{expectedFrameworkPaths}]");
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework names:[]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework versions:[]")
.And.HaveStdOutContaining($"hostfxr_get_dotnet_environment_info framework paths:[]");
}
}
......
......@@ -474,8 +474,8 @@ public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReported
.BinPath;
expectedErrorCode = Constants.ErrorCode.FrameworkMissingFailure;
expectedStdErr = $"The framework '{Constants.MicrosoftNETCoreApp}', " +
$"version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture}) was not found.";
expectedStdErr = $"Framework: '{Constants.MicrosoftNETCoreApp}', " +
$"version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture})";
expectedUrlQuery = $"framework={Constants.MicrosoftNETCoreApp}&framework_version={sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}";
}
......@@ -487,7 +487,7 @@ public void AppHost_CLI_FrameworkDependent_MissingRuntimeFramework_ErrorReported
var result = command.WaitForExit(true);
result.Should().Fail()
.And.HaveStdErrContaining($"- https://aka.ms/dotnet-core-applaunch?{expectedUrlQuery}")
.And.HaveStdErrContaining($"https://aka.ms/dotnet-core-applaunch?{expectedUrlQuery}")
.And.HaveStdErrContaining(expectedStdErr);
// Some Unix systems will have 8 bit exit codes.
......@@ -529,11 +529,13 @@ public void AppHost_GUI_FrameworkDependent_MissingRuntimeFramework_ErrorReported
WindowsUtils.WaitForPopupFromProcess(command.Process);
command.Process.Kill();
string expectedMissingFramework = $"'{Constants.MicrosoftNETCoreApp}', version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture})";
var result = command.WaitForExit(true)
.Should().Fail()
.And.HaveStdErrContaining($"Showing error dialog for application: '{Path.GetFileName(appExe)}' - error code: 0x{expectedErrorCode}")
.And.HaveStdErrContaining($"url: 'https://aka.ms/dotnet-core-applaunch?{expectedUrlQuery}")
.And.HaveStdErrContaining("&gui=true");
.And.HaveStdErrContaining("&gui=true")
.And.HaveStdErrMatching($"dialog message: (?>.|\\s)*{System.Text.RegularExpressions.Regex.Escape(expectedMissingFramework)}");
}
}
......
......@@ -39,15 +39,16 @@ public SDKLookup(SharedTestState sharedState)
public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
{
// Set specified SDK version = 9999.3.4-global-dummy
CopyGlobalJson("SingleDigit-global.json");
string globalJsonPath = CopyGlobalJson("SingleDigit-global.json");
string requestedVersion = "9999.3.4-global-dummy";
// Specified SDK version: 9999.3.4-global-dummy
// Exe: empty
// Expected: no compatible version and a specific error messages
// Expected: no compatible version, no SDKs found
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version")
.And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs")
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.FindAnySdk(false)
.And.HaveStdErrContaining("aka.ms/dotnet-download")
.And.NotHaveStdErrContaining("Checking if resolved SDK dir");
......@@ -56,22 +57,22 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
// Specified SDK version: 9999.3.4-global-dummy
// Exe: 9999.4.1, 9999.3.4-dummy
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version")
.And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.FindAnySdk(true);
// Add SDK versions
AddAvailableSdkVersions("9999.3.3");
// Specified SDK version: 9999.3.4-global-dummy
// Exe: 9999.4.1, 9999.3.4-dummy, 9999.3.3
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version")
.And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.FindAnySdk(true);
// Add SDK versions
AddAvailableSdkVersions("9999.3.4");
......@@ -129,37 +130,38 @@ public void SdkLookup_Global_Json_Single_Digit_Patch_Rollup()
public void SdkLookup_Global_Json_Two_Part_Patch_Rollup()
{
// Set specified SDK version = 9999.3.304-global-dummy
CopyGlobalJson("TwoPart-global.json");
string globalJsonPath = CopyGlobalJson("TwoPart-global.json");
string requestedVersion = "9999.3.304-global-dummy";
// Specified SDK version: 9999.3.304-global-dummy
// Exe: empty
// Expected: no compatible version and a specific error messages
// Expected: no compatible version, no SDKs found
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version")
.And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.FindAnySdk(false);
// Add SDK versions
AddAvailableSdkVersions("9999.3.57", "9999.3.4-dummy");
// Specified SDK version: 9999.3.304-global-dummy
// Exe: 9999.3.57, 9999.3.4-dummy
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version")
.And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.FindAnySdk(true);
// Add SDK versions
AddAvailableSdkVersions("9999.3.300", "9999.7.304-global-dummy");
// Specified SDK version: 9999.3.304-global-dummy
// Exe: 9999.3.57, 9999.3.4-dummy, 9999.3.300, 9999.7.304-global-dummy
// Expected: no compatible version and a specific error message
// Expected: no compatible version
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("A compatible installed .NET SDK for global.json version")
.And.NotHaveStdErrContaining("It was not possible to find any installed .NET SDKs");
.And.NotFindCompatibleSdk(globalJsonPath, requestedVersion)
.And.FindAnySdk(true);
// Add SDK versions
AddAvailableSdkVersions("9999.3.304");
......@@ -227,11 +229,10 @@ public void SdkLookup_Negative_Version()
// Specified SDK version: none
// Exe: -1.-1.-1
// Expected: no compatible version and a specific error messages
// Expected: no compatible version, no SDKs found
RunTest()
.Should().Fail()
.And.HaveStdErrContaining("It was not possible to find any installed .NET SDKs")
.And.HaveStdErrContaining("Install a .NET SDK from");
.And.FindAnySdk(false);
// Add SDK versions
AddAvailableSdkVersions("9999.0.4");
......@@ -391,16 +392,13 @@ public void It_rolls_forward_as_expected(string policy, string requested, bool a
if (expected == null)
{
result
.Should()
.Fail()
.And.HaveStdErrContaining($"A compatible installed .NET SDK for global.json version [{requested}] from [{globalJson}] was not found")
.And.HaveStdErrContaining($"Install the [{requested}] .NET SDK or update [{globalJson}] with an installed .NET SDK:");
.Should().Fail()
.And.NotFindCompatibleSdk(globalJson, requested);
}
else
{
result
.Should()
.Pass()
.Should().Pass()
.And.HaveStdErrContaining($"SDK path resolved to [{Path.Combine(ExecutableDotNet.BinPath, "sdk", expected)}]");
}
}
......@@ -1001,12 +999,13 @@ private void AddAvailableSdkVersions(params string[] availableVersions)
}
// Put a global.json file in the cwd in order to specify a CLI
private void CopyGlobalJson(string globalJsonFileName)
private string CopyGlobalJson(string globalJsonFileName)
{
string destFile = Path.Combine(SharedState.CurrentWorkingDir, "global.json");
string srcFile = Path.Combine(SharedState.TestAssetsPath, globalJsonFileName);
File.Copy(srcFile, destFile, true);
return destFile;
}
private static string FormatGlobalJson(string version = null, string policy = null, bool? allowPrerelease = null)
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using FluentAssertions;
using Microsoft.DotNet.Cli.Build.Framework;
namespace Microsoft.DotNet.CoreSetup.Test.HostActivation
{
internal static class SDKResolutionCommandResultExtensions
{
public static AndConstraint<CommandResultAssertions> FindAnySdk(this CommandResultAssertions assertion, bool shouldFindAnySdk)
{
string noSdkMessage = "No .NET SDKs were found";
return shouldFindAnySdk
? assertion.NotHaveStdErrContaining(noSdkMessage)
: assertion.HaveStdErrContaining(noSdkMessage)
.And.HaveStdErrContaining("Download a .NET SDK:");
}
public static AndConstraint<CommandResultAssertions> NotFindCompatibleSdk(this CommandResultAssertions assertion, string globalJsonPath = null, string requestedVersion = null)
{
var constraint = assertion.HaveStdErrContaining("compatible .NET SDK was not found");
if (globalJsonPath is not null)
{
constraint = constraint.And.HaveStdErrContaining($"global.json file: {globalJsonPath}");
}
if (requestedVersion is not null)
{
constraint = constraint.And.HaveStdErrContaining($"Requested SDK version: {requestedVersion}");
}
if (globalJsonPath is not null && requestedVersion is not null)
{
constraint = constraint.And.HaveStdErrContaining($"Install the [{requestedVersion}] .NET SDK or update [{globalJsonPath}] to match an installed SDK.");
}
return constraint;
}
}
}
// Licensed to the .NET Foundation under one or more agreements.
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using FluentAssertions.Execution;
......
......@@ -50,6 +50,26 @@ namespace
::DeregisterEventSource(eventSource);
}
bool try_get_url_from_line(const pal::string_t& line, pal::string_t& url)
{
const pal::char_t url_prefix[] = DOTNET_CORE_APPLAUNCH_URL _X("?");
if (utils::starts_with(line, url_prefix, true))
{
url.assign(line);
return true;
}
const pal::char_t url_prefix_before_7_0[] = _X(" - ") DOTNET_CORE_APPLAUNCH_URL _X("?");
if (utils::starts_with(line, url_prefix_before_7_0, true))
{
size_t offset = utils::strlen(url_prefix_before_7_0) - utils::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1;
url.assign(line.substr(offset, line.length() - offset));
return true;
}
return false;
}
void show_error_dialog(const pal::char_t *executable_name, int error_code)
{
pal::string_t gui_errors_disabled;
......@@ -58,17 +78,15 @@ namespace
pal::string_t dialogMsg;
pal::string_t url;
const pal::string_t url_prefix = _X(" - ") DOTNET_CORE_APPLAUNCH_URL _X("?");
if (error_code == StatusCode::CoreHostLibMissingFailure)
{
dialogMsg = pal::string_t(_X("To run this application, you must install .NET Desktop Runtime ")) + _STRINGIFY(COMMON_HOST_PKG_VER) + _X(" (") + get_arch() + _X(").\n\n");
pal::string_t line;
pal::stringstream_t ss(g_buffered_errors);
while (std::getline(ss, line, _X('\n'))) {
if (starts_with(line, url_prefix, true))
while (std::getline(ss, line, _X('\n')))
{
if (try_get_url_from_line(line, url))
{
size_t offset = url_prefix.length() - pal::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1;
url = line.substr(offset, line.length() - offset);
break;
}
}
......@@ -77,28 +95,29 @@ namespace
{
// We don't have a great way of passing out different kinds of detailed error info across components, so
// just match the expected error string. See fx_resolver.messages.cpp.
dialogMsg = pal::string_t(_X("To run this application, you must install missing frameworks for .NET.\n\n"));
dialogMsg = pal::string_t(INSTALL_OR_UPDATE_NET_ERROR_MESSAGE _X("\n\n"));
pal::string_t line;
pal::stringstream_t ss(g_buffered_errors);
while (std::getline(ss, line, _X('\n'))){
const pal::string_t prefix = _X("The framework '");
const pal::string_t suffix = _X("' was not found.");
const pal::string_t custom_prefix = _X(" _ ");
if (starts_with(line, prefix, true) && ends_with(line, suffix, true))
while (std::getline(ss, line, _X('\n')))
{
const pal::char_t prefix[] = _X("Framework: '");
const pal::char_t prefix_before_7_0[] = _X("The framework '");
const pal::char_t suffix_before_7_0[] = _X(" was not found.");
const pal::char_t custom_prefix[] = _X(" _ ");
if (utils::starts_with(line, prefix, true)
|| (utils::starts_with(line, prefix_before_7_0, true) && utils::ends_with(line, suffix_before_7_0, true)))
{
dialogMsg.append(line);
dialogMsg.append(_X("\n\n"));
}
else if (starts_with(line, custom_prefix, true))
else if (utils::starts_with(line, custom_prefix, true))
{
dialogMsg.erase();
dialogMsg.append(line.substr(custom_prefix.length()));
dialogMsg.append(line.substr(utils::strlen(custom_prefix)));
dialogMsg.append(_X("\n\n"));
}
else if (starts_with(line, url_prefix, true))
else if (try_get_url_from_line(line, url))
{
size_t offset = url_prefix.length() - pal::strlen(DOTNET_CORE_APPLAUNCH_URL) - 1;
url = line.substr(offset, line.length() - offset);
break;
}
}
......@@ -107,8 +126,9 @@ namespace
{
pal::string_t line;
pal::stringstream_t ss(g_buffered_errors);
while (std::getline(ss, line, _X('\n'))) {
if (starts_with(line, _X("Bundle header version compatibility check failed."), true))
while (std::getline(ss, line, _X('\n')))
{
if (utils::starts_with(line, _X("Bundle header version compatibility check failed."), true))
{
dialogMsg = pal::string_t(_X("To run this application, you must install .NET Desktop Runtime ")) + _STRINGIFY(COMMON_HOST_PKG_VER) + _X(" (") + get_arch() + _X(").\n\n");
url = get_download_url();
......@@ -121,15 +141,20 @@ namespace
return;
}
else
{
return;
}
dialogMsg.append(_X("Would you like to download it now?"));
dialogMsg.append(
_X("Would you like to download it now?\n\n")
_X("Learn about framework resolution:\n")
DOTNET_APP_LAUNCH_FAILED_URL);
assert(url.length() > 0);
assert(is_gui_application());
url.append(_X("&gui=true"));
trace::verbose(_X("Showing error dialog for application: '%s' - error code: 0x%x - url: '%s'"), executable_name, error_code, url.c_str());
trace::verbose(_X("Showing error dialog for application: '%s' - error code: 0x%x - url: '%s' - dialog message: %s"), executable_name, error_code, url.c_str(), dialogMsg.c_str());
if (::MessageBoxW(nullptr, dialogMsg.c_str(), executable_name, MB_ICONERROR | MB_YESNO) == IDYES)
{
// Open the URL in default browser
......
......@@ -282,11 +282,12 @@ int command_line::parse_args_for_sdk_command(
void command_line::print_muxer_info(const pal::string_t &dotnet_root)
{
trace::println();
trace::println(_X("Host (useful for support):"));
trace::println(_X(" Version: %s"), _STRINGIFY(HOST_FXR_PKG_VER));
trace::println(_X("Host:"));
trace::println(_X(" Version: %s"), _STRINGIFY(HOST_FXR_PKG_VER));
trace::println(_X(" Architecture: %s"), get_arch());
pal::string_t commit = _STRINGIFY(REPO_COMMIT_HASH);
trace::println(_X(" Commit: %s"), commit.substr(0, 10).c_str());
trace::println(_X(" Commit: %s"), commit.substr(0, 10).c_str());
trace::println();
trace::println(_X(".NET SDKs installed:"));
......@@ -303,9 +304,12 @@ void command_line::print_muxer_info(const pal::string_t &dotnet_root)
}
trace::println();
trace::println(_X("To install additional .NET runtimes or SDKs:"));
trace::println(_X("Download .NET:"));
trace::println(_X(" %s"), DOTNET_CORE_DOWNLOAD_URL);
}
trace::println();
trace::println(_X("Learn about .NET Runtimes and SDKs:"));
trace::println(_X(" %s"), DOTNET_INFO_URL);}
void command_line::print_muxer_usage(bool is_sdk_present)
{
......
......@@ -35,10 +35,11 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info &
/*static*/ void framework_info::get_all_framework_infos(
const pal::string_t& own_dir,
const pal::string_t& fx_name,
bool disable_multilevel_lookup,
std::vector<framework_info>* framework_infos)
{
std::vector<pal::string_t> hive_dir;
get_framework_and_sdk_locations(own_dir, &hive_dir);
get_framework_and_sdk_locations(own_dir, disable_multilevel_lookup, &hive_dir);
int32_t hive_depth = 0;
......@@ -97,7 +98,7 @@ bool compare_by_name_and_version(const framework_info &a, const framework_info &
/*static*/ bool framework_info::print_all_frameworks(const pal::string_t& own_dir, const pal::string_t& leading_whitespace)
{
std::vector<framework_info> framework_infos;
get_all_framework_infos(own_dir, _X(""), &framework_infos);
get_all_framework_infos(own_dir, _X(""), /*disable_multilevel_lookup*/ true, &framework_infos);
for (framework_info info : framework_infos)
{
trace::println(_X("%s%s %s [%s]"), leading_whitespace.c_str(), info.name.c_str(), info.version.as_str().c_str(), info.path.c_str());
......
......@@ -18,6 +18,7 @@ struct framework_info
static void get_all_framework_infos(
const pal::string_t& own_dir,
const pal::string_t& fx_name,
bool disable_multilevel_lookup,
std::vector<framework_info>* framework_infos);
static bool print_all_frameworks(const pal::string_t& own_dir, const pal::string_t& leading_whitespace);
......
......@@ -466,7 +466,7 @@ namespace
}
else
{
rc = fx_resolver_t::resolve_frameworks_for_app(host_info, override_settings, app_config, fx_definitions);
rc = fx_resolver_t::resolve_frameworks_for_app(host_info, app_config.get_is_multilevel_lookup_disabled(), override_settings, app_config, fx_definitions, mode == host_mode_t::muxer ? app_candidate.c_str() : nullptr);
if (rc != StatusCode::Success)
{
return rc;
......@@ -620,7 +620,7 @@ namespace
return StatusCode::InvalidConfigFile;
}
rc = fx_resolver_t::resolve_frameworks_for_app(host_info, override_settings, app_config, fx_definitions);
rc = fx_resolver_t::resolve_frameworks_for_app(host_info, app_config.get_is_multilevel_lookup_disabled(), override_settings, app_config, fx_definitions);
if (rc != StatusCode::Success)
return rc;
......@@ -1066,15 +1066,17 @@ int fx_muxer_t::handle_cli(
}
else if (pal::strcasecmp(_X("--info"), argv[1]) == 0)
{
resolver.print_global_file_path();
command_line::print_muxer_info(host_info.dotnet_root);
return StatusCode::Success;
}
trace::error(_X("Could not execute because the application was not found or a compatible .NET SDK is not installed."));
trace::error(_X("Possible reasons for this include:"));
trace::error(_X(" * You intended to execute a .NET program:"));
trace::error(_X(" The application '%s' does not exist."), app_candidate.c_str());
trace::error(_X(" * You intended to execute a .NET SDK command:"));
trace::error(
_X("The command could not be loaded, possibly because:\n")
_X(" * You intended to execute a .NET application:\n")
_X(" The application '%s' does not exist.\n")
_X(" * You intended to execute a .NET SDK command:"),
app_candidate.c_str());
resolver.print_resolution_error(host_info.dotnet_root, _X(" "));
return StatusCode::LibHostSdkFindFailure;
......@@ -1122,6 +1124,7 @@ int fx_muxer_t::handle_cli(
if (pal::strcasecmp(_X("--info"), argv[1]) == 0)
{
resolver.print_global_file_path();
command_line::print_muxer_info(host_info.dotnet_root);
}
......
......@@ -190,7 +190,8 @@ namespace
fx_definition_t* resolve_framework_reference(
const fx_reference_t & fx_ref,
const pal::string_t & oldest_requested_version,
const pal::string_t & dotnet_dir)
const pal::string_t & dotnet_dir,
const bool disable_multilevel_lookup)
{
#if defined(DEBUG)
assert(!fx_ref.get_fx_name().empty());
......@@ -205,7 +206,7 @@ namespace
fx_ref.get_fx_name().c_str(), fx_ref.get_fx_version().c_str());
std::vector<pal::string_t> hive_dir;
get_framework_and_sdk_locations(dotnet_dir, &hive_dir);
get_framework_and_sdk_locations(dotnet_dir, disable_multilevel_lookup, &hive_dir);
pal::string_t selected_fx_dir;
pal::string_t selected_fx_version;
......@@ -286,7 +287,7 @@ namespace
if (selected_fx_dir.empty())
{
trace::error(_X("It was not possible to find any compatible framework version"));
trace::verbose(_X("It was not possible to find any compatible framework version"));
return nullptr;
}
......@@ -394,10 +395,12 @@ void fx_resolver_t::update_newest_references(
// InvalidConfigFile - reading of a runtime config for some of the processed frameworks has failed.
StatusCode fx_resolver_t::read_framework(
const host_startup_info_t & host_info,
bool disable_multilevel_lookup,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t & config,
const fx_reference_t * effective_parent_fx_ref,
fx_definition_vector_t & fx_definitions)
fx_definition_vector_t & fx_definitions,
const pal::char_t* app_display_name)
{
// This reconciles duplicate references to minimize the number of resolve retries.
update_newest_references(config);
......@@ -439,10 +442,17 @@ StatusCode fx_resolver_t::read_framework(
m_effective_fx_references[fx_name] = new_effective_fx_ref;
// Resolve the effective framework reference against the the existing physical framework folders
fx_definition_t* fx = resolve_framework_reference(new_effective_fx_ref, m_oldest_fx_references[fx_name].get_fx_version(), host_info.dotnet_root);
fx_definition_t* fx = resolve_framework_reference(new_effective_fx_ref, m_oldest_fx_references[fx_name].get_fx_version(), host_info.dotnet_root, disable_multilevel_lookup);
if (fx == nullptr)
{
display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root);
trace::error(
INSTALL_OR_UPDATE_NET_ERROR_MESSAGE
_X("\n\n")
_X("App: %s\n")
_X("Architecture: %s"),
app_display_name != nullptr ? app_display_name : host_info.host_path.c_str(),
get_arch());
display_missing_framework_error(fx_name, new_effective_fx_ref.get_fx_version(), pal::string_t(), host_info.dotnet_root, disable_multilevel_lookup);
return FrameworkMissingFailure;
}
......@@ -471,7 +481,7 @@ StatusCode fx_resolver_t::read_framework(
return StatusCode::InvalidConfigFile;
}
rc = read_framework(host_info, override_settings, new_config, &new_effective_fx_ref, fx_definitions);
rc = read_framework(host_info, disable_multilevel_lookup, override_settings, new_config, &new_effective_fx_ref, fx_definitions, app_display_name);
if (rc)
{
break; // Error case
......@@ -511,9 +521,11 @@ fx_resolver_t::fx_resolver_t()
StatusCode fx_resolver_t::resolve_frameworks_for_app(
const host_startup_info_t & host_info,
bool disable_multilevel_lookup,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t & app_config,
fx_definition_vector_t & fx_definitions)
fx_definition_vector_t & fx_definitions,
const pal::char_t* app_display_name)
{
fx_resolver_t resolver;
......@@ -523,7 +535,7 @@ StatusCode fx_resolver_t::resolve_frameworks_for_app(
do
{
fx_definitions.resize(1); // Erase any existing frameworks for re-try
rc = resolver.read_framework(host_info, override_settings, app_config, /*effective_parent_fx_ref*/ nullptr, fx_definitions);
rc = resolver.read_framework(host_info, disable_multilevel_lookup, override_settings, app_config, /*effective_parent_fx_ref*/ nullptr, fx_definitions, app_display_name);
} while (rc == StatusCode::FrameworkCompatRetry && retry_count++ < Max_Framework_Resolve_Retries);
assert(retry_count < Max_Framework_Resolve_Retries);
......
......@@ -16,9 +16,11 @@ class fx_resolver_t
public:
static StatusCode resolve_frameworks_for_app(
const host_startup_info_t& host_info,
bool disable_multilevel_lookup,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t& app_config,
fx_definition_vector_t& fx_definitions);
fx_definition_vector_t& fx_definitions,
const pal::char_t* app_display_name = nullptr);
static bool is_config_compatible_with_frameworks(
const runtime_config_t& config,
......@@ -31,10 +33,12 @@ private:
const runtime_config_t& config);
StatusCode read_framework(
const host_startup_info_t& host_info,
bool disable_multilevel_lookup,
const runtime_config_t::settings_t& override_settings,
const runtime_config_t& config,
const fx_reference_t * effective_parent_fx_ref,
fx_definition_vector_t& fx_definitions);
fx_definition_vector_t& fx_definitions,
const pal::char_t* app_display_name);
static StatusCode reconcile_fx_references_helper(
const fx_reference_t& lower_fx_ref,
......@@ -49,7 +53,8 @@ private:
const pal::string_t& fx_name,
const pal::string_t& fx_version,
const pal::string_t& fx_dir,
const pal::string_t& dotnet_root);
const pal::string_t& dotnet_root,
bool disable_multilevel_lookup);
static void display_incompatible_framework_error(
const pal::string_t& higher,
const fx_reference_t& lower);
......
......@@ -93,51 +93,57 @@ void fx_resolver_t::display_missing_framework_error(
const pal::string_t& fx_name,
const pal::string_t& fx_version,
const pal::string_t& fx_dir,
const pal::string_t& dotnet_root)
const pal::string_t& dotnet_root,
bool disable_multilevel_lookup)
{
std::vector<framework_info> framework_infos;
pal::string_t fx_ver_dirs;
if (fx_dir.length())
{
fx_ver_dirs = fx_dir;
framework_info::get_all_framework_infos(get_directory(fx_dir), fx_name, &framework_infos);
framework_info::get_all_framework_infos(get_directory(fx_dir), fx_name, disable_multilevel_lookup, &framework_infos);
}
else
{
fx_ver_dirs = dotnet_root;
}
framework_info::get_all_framework_infos(dotnet_root, fx_name, &framework_infos);
framework_info::get_all_framework_infos(dotnet_root, fx_name, disable_multilevel_lookup, &framework_infos);
// Display the error message about missing FX.
if (fx_version.length())
{
trace::error(_X("The framework '%s', version '%s' (%s) was not found."), fx_name.c_str(), fx_version.c_str(), get_arch());
trace::error(_X("Framework: '%s', version '%s' (%s)"), fx_name.c_str(), fx_version.c_str(), get_arch());
}
else
{
trace::error(_X("The framework '%s' (%s) was not found."), fx_name.c_str(), get_arch());
trace::error(_X("Framework: '%s', (%s)"), fx_name.c_str(), get_arch());
}
trace::error(_X(".NET location: %s\n"), dotnet_root.c_str());
if (framework_infos.size())
{
trace::error(_X(" - The following frameworks were found:"));
trace::error(_X("The following frameworks were found:"));
for (const framework_info& info : framework_infos)
{
trace::error(_X(" %s at [%s]"), info.version.as_str().c_str(), info.path.c_str());
trace::error(_X(" %s at [%s]"), info.version.as_str().c_str(), info.path.c_str());
}
}
else
{
trace::error(_X(" - No frameworks were found."));
trace::error(_X("No frameworks were found."));
}
pal::string_t url = get_download_url(fx_name.c_str(), fx_version.c_str());
trace::error(_X(""));
trace::error(_X("You can resolve the problem by installing the specified framework and/or SDK."));
trace::error(_X(""));
trace::error(_X("The specified framework can be found at:"));
trace::error(_X(" - %s"), url.c_str());
trace::error(
_X("\n")
_X("Learn about framework resolution:\n")
DOTNET_APP_LAUNCH_FAILED_URL
_X("\n\n")
_X("To install missing framework, download:\n")
_X("%s"),
url.c_str());
}
void fx_resolver_t::display_incompatible_loaded_framework_error(
......
......@@ -425,7 +425,7 @@ SHARED_API int32_t HOSTFXR_CALLTYPE hostfxr_get_dotnet_environment_info(
}
std::vector<framework_info> framework_infos;
framework_info::get_all_framework_infos(dotnet_dir, _X(""), &framework_infos);
framework_info::get_all_framework_infos(dotnet_dir, _X(""), /*disable_multilevel_lookup*/ true, &framework_infos);
std::vector<hostfxr_dotnet_environment_framework_info> environment_framework_infos;
std::vector<pal::string_t> framework_versions;
......
......@@ -45,7 +45,7 @@ void sdk_info::get_all_sdk_infos(
std::vector<sdk_info>* sdk_infos)
{
std::vector<pal::string_t> hive_dir;
get_framework_and_sdk_locations(own_dir, &hive_dir);
get_framework_and_sdk_locations(own_dir, /*disable_multilevel_lookup*/ true, &hive_dir);
int32_t hive_depth = 0;
......
......@@ -61,7 +61,7 @@ pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root, bool print
fx_ver_t resolved_version;
vector<pal::string_t> locations;
get_framework_and_sdk_locations(dotnet_root, &locations);
get_framework_and_sdk_locations(dotnet_root, /*disable_multilevel_lookup*/ true, &locations);
for (auto&& dir : locations)
{
......@@ -85,38 +85,65 @@ pal::string_t sdk_resolver::resolve(const pal::string_t& dotnet_root, bool print
return {};
}
void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *prefix) const
void sdk_resolver::print_global_file_path()
{
trace::println(
_X("\n")
_X("global.json file:\n")
_X(" %s"),
global_file.empty() ? _X("Not found") : global_file.c_str());
}
void sdk_resolver::print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *main_error_prefix) const
{
bool sdk_exists = false;
const pal::char_t *no_sdk_message = _X("It was not possible to find any installed .NET SDKs.");
const pal::char_t *no_sdk_message = _X("No .NET SDKs were found.");
if (!version.is_empty())
{
pal::string_t requested = version.as_str();
if (!global_file.empty())
trace::error(
_X("%sA compatible .NET SDK was not found.\n")
_X("\n")
_X("Requested SDK version: %s"),
main_error_prefix,
requested.c_str());
bool has_global_file = !global_file.empty();
if (has_global_file)
trace::error(_X("global.json file: %s"), global_file.c_str());
trace::error(_X("\nInstalled SDKs:"));
sdk_exists = sdk_info::print_all_sdks(dotnet_root, _X(""));
if (!sdk_exists)
trace::error(no_sdk_message);
trace::error(_X(""));
if (has_global_file)
{
trace::error(_X("%sA compatible installed .NET SDK for global.json version [%s] from [%s] was not found."), prefix, requested.c_str(), global_file.c_str());
trace::error(_X("%sInstall the [%s] .NET SDK or update [%s] with an installed .NET SDK:"), prefix, requested.c_str(), global_file.c_str());
trace::error(_X("Install the [%s] .NET SDK or update [%s] to match an installed SDK."), requested.c_str(), global_file.c_str());
}
else
{
trace::error(_X("%sA compatible installed .NET SDK version [%s] was not found."), prefix, requested.c_str());
trace::error(_X("%sInstall the [%s] .NET SDK or create a global.json file with an installed .NET SDK:"), prefix, requested.c_str());
trace::error(_X("Install the [%s] .NET SDK or create a global.json file matching an installed SDK."), requested.c_str());
}
sdk_exists = sdk_info::print_all_sdks(dotnet_root, pal::string_t{prefix}.append(_X(" ")));
if (!sdk_exists)
trace::error(_X("%s %s"), prefix, no_sdk_message);
}
else
{
trace::error(_X("%s%s"), prefix, no_sdk_message);
trace::error(_X("%s%s"), main_error_prefix, no_sdk_message);
}
if (!sdk_exists)
{
trace::error(_X("%sInstall a .NET SDK from:"), prefix);
trace::error(_X("%s %s"), prefix, DOTNET_CORE_DOWNLOAD_URL);
trace::error(
_X("\n")
_X("Download a .NET SDK:\n")
DOTNET_CORE_DOWNLOAD_URL);
}
trace::error(
_X("\n")
_X("Learn about SDK resolution:\n")
DOTNET_SDK_NOT_FOUND_URL);
}
sdk_resolver sdk_resolver::from_nearest_global_file(bool allow_prerelease)
......
......@@ -41,6 +41,7 @@ public:
pal::string_t resolve(const pal::string_t& dotnet_root, bool print_errors = true) const;
void print_global_file_path();
void print_resolution_error(const pal::string_t& dotnet_root, const pal::char_t *prefix) const;
static sdk_resolver from_nearest_global_file(bool allow_prerelease = true);
......
......@@ -112,8 +112,8 @@ bool fxr_resolver::try_get_path(const pal::string_t& root_path, pal::string_t* o
dotnet_root_env_var_name.c_str(),
self_registered_message.c_str());
trace::error(_X(""));
trace::error(_X("The .NET runtime can be found at:"));
trace::error(_X(" - %s&apphost_version=%s"), get_download_url().c_str(), _STRINGIFY(COMMON_HOST_PKG_VER));
trace::error(_X("Download the .NET runtime:"));
trace::error(_X("%s&apphost_version=%s"), get_download_url().c_str(), _STRINGIFY(COMMON_HOST_PKG_VER));
return false;
}
......
......@@ -27,8 +27,8 @@ struct host_startup_info_t
static int get_host_path(int argc, const pal::char_t* argv[], pal::string_t* host_path);
pal::string_t host_path; // The path to the current hosting binary.
pal::string_t dotnet_root; // The path to the framework.
pal::string_t app_path; // For apphost, the path to the app dll; for muxer, not applicable as this information is not yet parsed.
pal::string_t dotnet_root; // The path to the .NET install.
pal::string_t app_path; // For apphost, the path to the app dll. For muxer, this is invalid and does not point to the app (the app path is not yet parsed).
};
#endif // __HOST_STARTUP_INFO_H_
......@@ -29,23 +29,32 @@ bool coreclr_exists_in_dir(const pal::string_t& candidate)
return pal::file_exists(test);
}
bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case)
bool utils::starts_with(const pal::string_t& value, const pal::char_t* prefix, size_t prefix_len, bool match_case)
{
// Cannot start with an empty string.
if (prefix_len == 0)
return false;
auto cmp = match_case ? pal::strncmp : pal::strncasecmp;
return (value.size() >= prefix_len) &&
cmp(value.c_str(), prefix, prefix_len) == 0;
}
bool utils::ends_with(const pal::string_t& value, const pal::char_t* suffix, size_t suffix_len, bool match_case)
{
auto cmp = match_case ? pal::strcmp : pal::strcasecmp;
return (value.size() >= suffix.size()) &&
cmp(value.c_str() + value.size() - suffix.size(), suffix.c_str()) == 0;
return (value.size() >= suffix_len) &&
cmp(value.c_str() + value.size() - suffix_len, suffix) == 0;
}
bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case)
{
return utils::ends_with(value, suffix.c_str(), suffix.size(), match_case);
}
bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case)
{
if (prefix.empty())
{
// Cannot start with an empty string.
return false;
}
auto cmp = match_case ? pal::strncmp : pal::strncasecmp;
return (value.size() >= prefix.size()) &&
cmp(value.c_str(), prefix.c_str(), prefix.size()) == 0;
return utils::starts_with(value, prefix.c_str(), prefix.size(), match_case);
}
void append_path(pal::string_t* path1, const pal::char_t* path2)
......@@ -284,9 +293,9 @@ bool multilevel_lookup_enabled()
return multilevel_lookup;
}
void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vector<pal::string_t>* locations)
void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, const bool disable_multilevel_lookup, std::vector<pal::string_t>* locations)
{
bool multilevel_lookup = multilevel_lookup_enabled();
bool multilevel_lookup = disable_multilevel_lookup ? false : multilevel_lookup_enabled();
// Multi-level lookup will look for the most appropriate version in several locations
// by following the priority rank below:
......@@ -304,8 +313,11 @@ void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vecto
locations->push_back(dotnet_dir_temp);
}
if (!multilevel_lookup)
return;
std::vector<pal::string_t> global_dirs;
if (multilevel_lookup && pal::get_global_dotnet_dirs(&global_dirs))
if (pal::get_global_dotnet_dirs(&global_dirs))
{
for (pal::string_t dir : global_dirs)
{
......
......@@ -19,10 +19,42 @@
#define DOTNET_CORE_DOWNLOAD_URL _X("https://aka.ms/dotnet-download")
#define DOTNET_CORE_APPLAUNCH_URL _X("https://aka.ms/dotnet-core-applaunch")
#define DOTNET_INFO_URL _X("https://aka.ms/dotnet/runtimes-sdk-info")
#define DOTNET_APP_LAUNCH_FAILED_URL _X("https://aka.ms/dotnet/app-launch-failed")
#define DOTNET_SDK_NOT_FOUND_URL _X("https://aka.ms/dotnet/sdk-not-found")
// This message is defined here for consistency between errors on the command line and GUI (Windows apphost).
#define INSTALL_OR_UPDATE_NET_ERROR_MESSAGE _X("You must install or update .NET to run this application.")
#define RUNTIME_STORE_DIRECTORY_NAME _X("store")
bool ends_with(const pal::string_t& value, const pal::string_t& suffix, bool match_case);
bool starts_with(const pal::string_t& value, const pal::string_t& prefix, bool match_case);
namespace utils
{
template<size_t L>
inline constexpr size_t strlen(const pal::char_t(&)[L])
{
return L - 1;
}
bool ends_with(const pal::string_t& value, const pal::char_t *suffix, size_t suffix_len, bool match_case);
bool starts_with(const pal::string_t& value, const pal::char_t* prefix, size_t prefix_len, bool match_case);
template<size_t L>
bool ends_with(const pal::string_t& value, const pal::char_t (&suffix)[L], bool match_case)
{
return ends_with(value, suffix, L - 1, match_case);
}
template<size_t L>
bool starts_with(const pal::string_t& value, const pal::char_t (&prefix)[L], bool match_case)
{
return starts_with(value, prefix, L - 1, match_case);
}
}
pal::string_t strip_executable_ext(const pal::string_t& filename);
pal::string_t get_directory(const pal::string_t& path);
pal::string_t strip_file_ext(const pal::string_t& path);
......@@ -39,7 +71,7 @@ pal::string_t get_current_runtime_id(bool use_fallback);
bool get_env_shared_store_dirs(std::vector<pal::string_t>* dirs, const pal::string_t& arch, const pal::string_t& tfm);
bool get_global_shared_store_dirs(std::vector<pal::string_t>* dirs, const pal::string_t& arch, const pal::string_t& tfm);
bool multilevel_lookup_enabled();
void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, std::vector<pal::string_t>* locations);
void get_framework_and_sdk_locations(const pal::string_t& dotnet_dir, const bool disable_multilevel_lookup, std::vector<pal::string_t>* locations);
bool get_file_path_from_env(const pal::char_t* env_key, pal::string_t* recv);
size_t index_of_non_numeric(const pal::string_t& str, size_t i);
bool try_stou(const pal::string_t& str, unsigned* num);
......
......@@ -429,6 +429,47 @@ const pal::string_t& runtime_config_t::get_tfm() const
return m_tfm;
}
const uint32_t runtime_config_t::get_compat_major_version_from_tfm() const
{
assert(m_valid);
// TFM is in form
// - netcoreapp#.# for <= 3.1
// - net#.# for >= 5.0
// In theory it could contain a suffix like `net6.0-windows` (or more than one)
// or it may lack the minor version like `net6`. SDK will normalize this, but the runtime should not 100% rely on it
if (m_tfm.empty())
return runtime_config_t::unknown_version;
size_t majorVersionStartIndex;
const pal::char_t netcoreapp_prefix[] = _X("netcoreapp");
if (utils::starts_with(m_tfm, netcoreapp_prefix, true))
{
majorVersionStartIndex = utils::strlen(netcoreapp_prefix);
}
else
{
majorVersionStartIndex = utils::strlen(_X("net"));
}
if (majorVersionStartIndex >= m_tfm.length())
return runtime_config_t::unknown_version;
size_t majorVersionEndIndex = index_of_non_numeric(m_tfm, majorVersionStartIndex);
if (majorVersionEndIndex == pal::string_t::npos || majorVersionEndIndex == majorVersionStartIndex)
return runtime_config_t::unknown_version;
return static_cast<uint32_t>(std::stoul(m_tfm.substr(majorVersionStartIndex, majorVersionEndIndex - majorVersionStartIndex)));
}
bool runtime_config_t::get_is_multilevel_lookup_disabled() const
{
// Starting with .NET 7, multi-level lookup is fully disabled
unsigned long compat_major_version = get_compat_major_version_from_tfm();
return (compat_major_version >= 7 || compat_major_version == runtime_config_t::unknown_version);
}
bool runtime_config_t::get_is_framework_dependent() const
{
return m_is_framework_dependent;
......
......@@ -33,6 +33,7 @@ public:
const pal::string_t& get_path() const { return m_path; }
const pal::string_t& get_dev_path() const { return m_dev_path; }
const pal::string_t& get_tfm() const;
bool get_is_multilevel_lookup_disabled() const;
const std::list<pal::string_t>& get_probe_paths() const;
bool get_is_framework_dependent() const;
bool parse_opts(const json_parser_t::value_t& opts);
......@@ -41,7 +42,10 @@ public:
const fx_reference_vector_t& get_included_frameworks() const { return m_included_frameworks; }
void set_fx_version(pal::string_t version);
static constexpr int unknown_version = std::numeric_limits<int>::max();
private:
const uint32_t get_compat_major_version_from_tfm() const;
bool ensure_parsed(); //todo: const runtime_config_t* defaults
bool ensure_dev_config_parsed();
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册