-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
.NET Core 2+ Version Binding #43525
Comments
I had requested this type of runtime lookup some months ago: https://github.com/dotnet/core-setup/issues/680. |
@richlander What are the MSBuild properties involved when using the CLI commands as demonstrated? These and how they affect other properties / nuget package versions should be part of the proposal as well. |
@dasMulli Great question. That hasn't been worked out yet. At present, this is more of a UX design. And in full transparency, this is very much a true open design process. I haven't talked to the team that would need to decide that. Once I get feedback that we're on the right path, I'll work with the team, get that defined and update the proposal. |
Great thing to have, however my naming OCD complains that there is no verb in that command (list, get, fetch, determine)
Does the knowledge of the actual latest value come from the version of the SDK used or from the same network source as
So global.json selects the CLI("SDK") version now. There's also an MSBuild SDK resolver now that calls into hostfxr to determine the CLI version and use it's bundled SDKs (dotnet/cli#6436). Previously there was some discussion of how one would use/reference the version of the SDK if it is no longer bundled with the toolchain (and require CLI, VS msbuild and now also mono 5+ updates for every new SDK version). E.g. resolve SDK versions from NuGet by using a "lineup file" or use the version part of the MSBuild |
I think you are right. This isn't right. I'll fix that.
Latest on the machine. This strengthens your other feedback.
I should check on the MSBuild resolver progress. I participated in the earlier part of that discussion and also have a write-up from that. I'll double check. |
Great to see this design. This would solve the issues our customers are having with .NET Core on AWS. One thing I'm trying to figure out as a hoster is if there is a way I can determine what version is going to run the application and report that back to user. If there saw something like
And it just ran the runtime determination code and printed out 2.0.4 we could do run that as part of our deployment to give feedback back to the customer. That way they can see when new patches are applied. |
Looks good overall. My feedback / comments:
You shouldn't do this for projects that have references to other projects outside of the folder. If you have a globals.json that applies to a project but not to a project that it references, then the referenced project will be built with different tools depending on whether you build it directly or whether it gets built as a result of building the referencing project. This could lead to arbitrary failures that could easily be difficult to detect or diagnose. So you should have a single global.json that is high enough up in the directory structure to cover all projects that reference each other.
Note that the version numbers might not always be in sync. We might release a new version of the SDK without new versions of the target frameworks, or .NET Core and .NET Standard might version at different rates.
This is our intent but I think it's always possible that we would make an exception. For the 1.0 tools, we rolled forward to the 1.1.1 package of .NET Core not because we wanted to make that the minimum runtime version, but because it transitively referenced a package that had a fix we wanted included by default.
The selection of the latest version here will be based on the version of the SDK that is selected. IE each SDK will have knowledge of the latest patch version of each version of .NET Core that is available at the time the SDK ships. If there's a new patch version of .NET Core, the publish process won't automatically use the new version until an updated SDK is obtained that knows about the new version. If you want to specifically use a newer patch version than the SDK knows about, you can use the
I don't think some of this is currently implemented. It's also possible to put these properties in common .props files that are imported by all projects, which might be sufficient to satisfy the described scenarios.
In case it's not clear, the way that global.json selects the CLI/SDK version is that there is an MSBuild SDK resolver (in full framework MSBuild) that calls into hostfxr, and the hostfxr resolution logic respects global.json. For .NET Core MSBuild, the CLI/SDK has already been selected (possibly based on global.json), so the resolver within the CLI just selects the local SDK path. This unbundles the "SDK" (ie the tasks and targets which |
@dsplaisted The way I understand / interpret it is that it shouldn't matter which version of the About the SDK resolvers: Does that guarantee that the NuGet version distributed with the CLI is used and works? I'm asking since the actual nuget dll is outside the SDK directory and might not be compatible with full framework MSBuild while the version that is distributed with the full framework MSBuild via VS might be old and not support features required for that newer SDK (e.g. netstandard 2.0+ support) |
@dsplaisted Slight nuance ... You were writing how things will work in .NET Core 2.0 preview 1. I was not making the design specific to the current builds but describing what I think of as the best model and getting feedback on that. So, it's better to say "the proposed behavior is [good | bad| because ..." or "interesting idea ... not sure how this would be implemented" as opposed to "this isn't the way it works". Now, there are some things that we can still change in .NET Core 2.0 and some things that will need to wait for 2.1 or 3.0 (because the changes could be considered breaking). Once we get to agreement on the best behaviors, we can decide which releases they get added in. For example, the minor version roll-forward can wait until the .NET Core 2.1 SDK.
We disagree here. Strong statement: SDK, Runtime and target framework versions will always match on major and minor boundaries. |
@richlander Let me try to clarify some of my feedback:
If you have a globals.json that applies to a project but not to a project that it references, then the referenced project will be built with different tools depending on whether you build it directly or whether it gets built as a result of building the referencing project. This could lead to arbitrary failures that could easily be difficult to detect or diagnose. Because of this I think our guidance should be essentially: You should have a single global.json that is high enough up in the directory structure to cover all projects that reference each other.
I'd suggest it work like this: The selection of the latest version here will be based on the version of the SDK that is selected. IE each SDK will have knowledge of the latest patch version of each version of .NET Core that is available at the time the SDK ships. If there's a new patch version of .NET Core, the publish process won't automatically use the new version until an updated SDK is obtained that knows about the new version. If you want to specifically use a newer patch version than the SDK knows about, you can use the We could think about instead basing it on the latest patch version that is installed on the machine. My current take on it is that it doesn't seem like it would be very important to do so, and there are some technical hurdles to it (probably nothing we couldn't resolve if it is important, though).
There's no harm in having command line options like this, but I'm not sure that the scenarios they are meant for aren't already addressed by existing functionality.
That's probably a good choice (and more your decision than mine) for .NET Core and the SDK. However, are you planning to have .NET Standard always version in lock-step with .NET Core? Since it represents a standard across different frameworks, I would expect it to version more slowly. |
No, it doesn't. The NuGet restore task is installed as part of Visual Studio for full Framework MSBuild. What's stopping us from shipping that in the SDK instead is that the restore process in Visual Studio doesn't use that task, it uses the NuGet built into Visual Studio. So we don't currently have the ability to update the version of NuGet used by full Framework MSBuild by installing a new .NET Core SDK. The SDK does ship with a version of NuGet, but that's only used for the .NET Core version of MSBuild. |
Did this get implemented for .NET Core 2.0? Since we don't have any patches or minor releases today I don't have an easy way to test this. |
Apologies if I've misunderstood the design above, but I'm not seeing - in practice - the behaviour that I was expecting.
Has this been done yet? I'm struggling to get my app - targeting v2.0.0 - running in the Alpine container (ie v2.1). I was expecting the roll-forward to kick in, but it doesn't seem to work (unless I've missed something). It 'dotnet publish''s ok, but I get the following when I 'dotnet run':
Should the roll-forward behaviour apply in this case? Note that my app is targetFramework=netcoreapp2.0 and the alpine sample is targetFramework=netcoreapp2.1. It's not just the runtime version that will rollforward, right? |
@piers7 It looks like roll-forward doesn't consider pre-releases: https://github.com/dotnet/core-setup/blob/94f2146cd810ae0762a699df210a65fb866e44cd/src/corehost/cli/fxr/fx_muxer.cpp#L518. |
A number of these ideas are part of .NET Core 3.0. @KathleenDollard is responsible to publish designs on those. This doc is no longer up-to-date on current ideas. |
.NET Core 2+ Version Binding
.NET Core applies a set of policies that determine which versions of the .NET Core runtime and SDK are used in various scenarios. These scenarios and policies are defined in this document.
One can think of these policies performing the following roles:
Experience
.NET Core provides a single entry-point for operating on projects and built applications, specifically with the
dotnet
command. This experience relies on an installation structure that can include multiple runtime and SDK versions. This structure is machine global and in the path by default, enabling a developer to access all .NET Core versions from any command prompt. It also enables updating all apps to use a new .NET Core patch version by installing it in this central structure/location. This differs from other development platforms that require re-setting the path or setting an environmental value to use different platform versions.There is allowance for other scenarios, for example, for private .NET Core installations without use of the path. This document focusses more on the default behavior, but does call out opportunities to override this behavior.
The
dotnet
command must select a version in the following cases:dotnet new
).dotnet build
).dotnet run
).dotnet publish
).Selecting an SDK
The first developer experience with .NET Core is typically with an SDK command, for example
dotnet new
,dotnet build
ordotnet run
. Thedotnet
command must select a version of the SDK to use to satisfy these commands. .NET Core has a "use the latest SDK" policy. This means that you will use the .NET Core 99.9 SDK if you install it on a machine, even if you are using the SDK command to do .NET Core 2.0 development.The advantage of this policy is that you can take advantage of the latest SDK features and improvements while continuing to target earlier .NET Core versions. This is logically similar to using C# 7 with .NET Framework 4.5 (which also works). It is quite likely many developers will target multiple versions of .NET Core across a set of projects. It's nice that you can use the same tools for all of them.
You can configure
dotnet
to use a different version of the SDK by specifying that version in a global.json file. As a consequence of the "use latest" policy, you would only ever useglobal.json
to specify a .NET Core version that is earlier than the latest installed version.You can use
global.json
at a variety of scopes:You can see the
global.json
syntax in the following example:The
global.json
file is an existing format and associated concept that is part of the .NET Core 1.0 tools. It is not new with .NET Core 2.0. The file fulfills a critical role, to enable developers to lock a specific app or set of apps to an earlier SDK than the latest on the machine. The need for this capability is most obvious for developers that need to continue using the preview2 sdk to use project.json-based applications. We will revisit the design of this file in a later release. We will also want to understand how often it is even used to better understand how important the scenario is. The less it is used, the more likely the file will stay as-is.Implementer notes:
dotnet
binds to latest SDK by default.Building an application
You can build an application from source with
dotnet build
ordotnet run
(the latter does more than is covered in this section).There are multiple points of version binding to consider:
The process of selecting an SDK is described earlier in the document.
The target framework is defined in the project file by setting a property in the following form:
Multiple target frameworks can also be set if you need to build different code for different targets. This scenario is more common for libraries, but can be done with applications as well. You can do this by using a
TargetFrameworks
property (plural of TargetFramework). The target frameworks will be semicolon-delimited as you can see the following example:A given SDK will support a fixed set of frameworks, typically capped to the target framework of the runtime(s) it includes. For example, the .NET Core 2.0 SDK includes the .NET Core 2.0 runtime, which is an implementation of the
netcoreapp2.0
target framework. The .NET Core 2.0 SDK will supportnetcoreapp1.0
,netcoreapp1.1
andnetcoreapp2.0
but notnetcoreapp2.1
(or higher). You will need to install the .NET Core 2.1 SDK in order to build fornetcoreapp2.1
..NET Standard target frameworks are also capped in the same way. The .NET Core 2.0 SDK will be capped to
netstandard2.0
.A given SDK will define a fixed minimum runtime patch version for each target framework that it supports. The minimum runtime patch version will be maintained at the
.0
version (for example,2.0.0
) for the lifetime of a major/minor runtime version family. This policy makes deploying patches a web hoster concern and not a developer concern.Hosters and other users have reported challenges with the .NET Core 1.x behavior, where the minimum patch version increases with each .NET Core SDK release. See: Reconsider implicitly using the latest patch version of the targeted version of .NET Core in the SDK. This proposal is intended to resolve their feedback.
The SDK stores the minimum patch version for each supported target framework in the
~\dotnet\sdk\1.0.0\Sdks\Microsoft.NET.Sdk\build\Microsoft.NET.Sdk.DefaultItems.targets
file.The minimum runtime patch version is stored in the application directory in the
*.runtimeconfig.json
file. This value is used as part of runtime selection. You are not intended to edit this file.You can override the minimum runtime patch version (to higher or lower versions) in the project file, as you can see in the following example:
In the case that you target multiple target frameworks, you need to condition the minimum runtime patch version to the specific .NET Core target framework, as you can see in the following example:
Implementer notes:
Run from Source with dotnet run
You can run an application from source with
dotnet run
.dotnet run
both builds and runs an application.There are multiple points of version binding to consider:
The process of selecting an SDK and building an application is described earlier in the document.
The built application will be run on machines that satisfy the minimum required runtime patch version, stored in the
*.runtimeconfig.json
file. The highest patch version will be selected. For example, the2.0.4
runtime will be selected in the case that the minimum runtime version is2.0.0
while2.0.0
,2.0.1
and2.0.4
are all resident on the machine. Higher minor or major versions (for example,2.1.1
or3.0.1
) will not be considered. Lower versions will also not be considered.Implementer notes:
dotnet
binds to latest runtime patch version (within a given major.minor version family).Run from bin directory with dotnet myapp.dll
You can build and then run an application, essentially performing
dotnet run
as two separate steps, as seen in the following example:This scenario is an equivalence class for
dotnet run
.Run a published application with dotnet myapp.dll
You can publish an application to prepare it for distribution to other runtime environments, as seen in the following example:
This scenario is an equivalence class for
dotnet run
.Run a published application with the required runtime missing
You may run an application on a machine that is missing the runtime required by the application. If the required runtime is missing and a higher minor version is installed, the minor version should be used instead of failing to load the application. Higher major versions will not be used to load applications. This behavior shall be called "minor version roll-forward."
To illustrate this behavior, for an application requiring 2.0.4,
dotnet
would search and bind to a runtime in this order (picking the first it finds or showing error):A few usage examples may help to demonstrate the desired behavior:
There is a trade-off for this behavior between compatibility and convenience. .NET Core minor versions are intended to be very compatible. It is therefore reasonable to choose the convenience experience without significant concern for compatibility breaks.
The following example is a somewhat unfortunate side-effect behavior of the algorithm that is important to realize:
The treatment of NuGet dependencies needs to be understood as well. A given application might be built for .NET Core 2.0 but run on .NET Core 2.1 due to this algorithm. There may be later versions of an application's NuGet dependencies that target .NET Core 2.1. These later versions will not be considered or consulted. All NuGet dependencies should be already resolved as part of the published application layout, residing as assemblies (.dlls) in a flat directory structure. These assemblies should run on .NET Core 2.1 due to its compatibility promise for existing applications and binaries.
There are some scenarios where ASP.NET packages will be deployed via a web host rather than with a published application. In those cases, it is recommended that web hosters correctly configure their environments based on published guidance such that all supported ASP.NET applications run correctly.
Implementer notes:
Publish a self-contained application
You can publish an application as a self-contained distribution. The advantage of this approach is that it includes .NET Core so does not require it is a dependency in runtime environments. As a result, runtime binding occurs at publishing time not run-time.
There are multiple points of version binding to consider:
The process of selecting an SDK and building an application is described earlier in the document.
The publishing process will select the latest patch version of the given runtime family. For example,
dotnet publish
will select .NET Core 2.0.4 if it is the latest patch version in the .NET Core 2.0 runtime family, independent of the existence of earlier patch versions or minimum runtime patch version values. This differs fromdotnet build
runtime selection for two reasons:Note that the runtimeframeworkversion element will override the default version policy if that element is present in the project file.
Implementer Notes:
dotnet publish
binds to latest runtime patch version (within a given major.minor version family).dotnet publish
does not support the roll-forward semantics ofdotnet run
Publish an application with a specific .NET Core version
Developers can specify a .NET Core runtime version within a project file. That's convenient for developers because they typically work on one project at a time and typically edit project files, but not for CI/CD flows or human software deployers who manage many projects and who do not typically edit project files. Instead
dotnet publish
accepts a version to use for publishing for both self-contained and framework-dependent apps. One can imagine Visual Studio Team Systems offering a setting to specify a runtime version within a build configuration UI that affects publish without needing to update source code.You can publish with the latest runtime patch version for the given target framework, by specifying
--latest
. This argument has the same default runtime binding behavior for publishing self-contained applications. You can see this usage in the following example:dotnet publish -c release -o app --latest
You can publish with a specific runtime patch version for the given target framework, by specifying
--version=[version]
. It is an error if this version is missing. It is an error if the given version doesn't support the target framework (as a lower bound). It is acceptable to specify higher versions (even major versions), provided they exist on the machine. You can see this usage in the following example:dotnet publish -c release -o app --version=3.1.0
Implementer notes:
Determine latest Patch Versions
You may want to learn the latest patch version for a given target framework that is available (independent of what is installed on the machine). This command will make a network call and simply print a version, must like
dotnet --version
does today.dotnet version --framework netcoreapp2.0 --latest
There are likely other changes that are coming for printing .NET Core versions. This command should align with these plans. The syntax above provides the general idea of the user experience.
Forward-looking: It should be easy to write scripts that determine if the latest patch version is installed, then installs it at the command-line if it is not.
Open Issues
These issues are related but will be covered in another design document.
Technical Details
This document takes some liberties, referring to
dotnet
as the version manager. From a user experience standpoint, this is true. From a technical standpoint, it is not. The version manager is implement in the following location, dependent on operating system:The text was updated successfully, but these errors were encountered: