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
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.
stringexpectedMissingFramework=$"'{Constants.MicrosoftNETCoreApp}', version '{sharedTestState.RepoDirectories.MicrosoftNETCoreAppVersion}' ({fixture.RepoDirProvider.BuildArchitecture})";
varresult=command.WaitForExit(true)
.Should().Fail()
.And.HaveStdErrContaining($"Showing error dialog for application: '{Path.GetFileName(appExe)}' - error code: 0x{expectedErrorCode}")
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");
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?"));
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());
pal::string_thost_path;// The path to the current hosting binary.
pal::string_tdotnet_root;// The path to the framework.
pal::string_tapp_path;// For apphost, the path to the app dll; for muxer, not applicable as this information is not yet parsed.
pal::string_tdotnet_root;// The path to the .NET install.
pal::string_tapp_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).