-
Notifications
You must be signed in to change notification settings - Fork 162
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
Easy Acquisition of .NET Framework Targeting Pack #33
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
# Easy Acquisition of .NET Framework Targeting Packs | ||
|
||
**PM** [Immo Landwerth](https://github.com/terrajobst) | | ||
**Dev** [Daniel Plaisted](https://github.com/dsplaisted) | ||
|
||
This proposal is about enabling acquisition of the .NET Framework targeting | ||
packs via NuGet so that compiling code for a specific version of the .NET | ||
Framework doesn't require a Windows machine with a machine-wide installation of | ||
the corresponding targeting pack. | ||
|
||
The .NET Framework supports targeting different versions of the platform | ||
regardless of the .NET Framework version that is installed on the developer's | ||
machine. This allows developers to target a lower version than what they use | ||
themselves. This is especially useful for in-place updates where the older | ||
version of the framework cannot be installed side-by- side. | ||
|
||
That mechanism is called *targeting packs*, which is simply a set of assemblies | ||
that describe the public APIs of a particular version of the .NET Framework. | ||
Since those assemblies are only used by the compiler, they don't need to contain | ||
any code, which we call [reference assemblies](https://www.youtube.com/watch?v=EBpY1UMHDY8). | ||
|
||
Unfortunately, the targeting packs aren't available by themselves; they are | ||
redistributed as part of the .NET Framework Developer Packs which bundle both | ||
the runtime as well as the targeting pack. This design ensures that developers | ||
can never be in a state where they have a targeting pack that is higher than the | ||
.NET Framework version they have installed. That design made sense coming from a | ||
Visual Studio-first perspective but doesn't serve us well today: | ||
|
||
* **It's not cross-platform friendly**. The .NET Framework Developer Packs are | ||
only available for Windows which makes the targeting packs indirectly Windows- | ||
only too. | ||
|
||
* **It's not CI friendly**. Requiring a machine-wide install isn't friendly for | ||
cloud-hosted CI machines as developers typically lack permissions to install | ||
software. | ||
|
||
This document proposes to offer the targeting packs via NuGet that they can be | ||
acquired in a non-impactful way. It builds on the following existing specs: | ||
|
||
* [Phillip Carter](https://github.com/cartermp): [Add Target Packs on NuGet](https://github.com/dotnet/core/pull/64) | ||
* [Daniel Plaisted](https://github.com/dsplaisted): [Reference Assembly NuGet packages](https://gist.github.com/dsplaisted/83d67bbcff9ec1d0aff1bea1bf4ad79a) | ||
* [Prototype](https://github.com/dsplaisted/ReferenceAssemblyPackages) | ||
|
||
## Scenarios and User Experience | ||
|
||
* Build a project which is multi-targeted to .NET Core or .NET Standard as well | ||
as .NET Framework on Mac OS or Linux using the .NET CLI | ||
|
||
* Build a project targeting Mono (which uses the same Target Framework | ||
Identifier as .NET Framework) using the .NET CLI | ||
|
||
## Requirements | ||
|
||
### Goals | ||
|
||
* Doesn't require running an installer | ||
* Works cross-platform | ||
* Can be used in SDK-style multi-targeting projects | ||
* Prefer the centralized targeting pack if it's present. First, this ensures we | ||
don't change the performance characteristic of exiting solutions but also | ||
allows working around some issue due to build extensions by installing the | ||
targeting pack. | ||
* Supports `<PreserveCompilationContext>` so that ASP.NET Razor compilation | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've filed dotnet/sdk#2054 to track fixing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure what you mean by "block the rest of the work", but we can't make using the NuGet targeting pack the default in SDK projects without this issue being addressed. Or else you will break every ASP.NET MVC/Razor Page app running on the desktop .NET Framework. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As I understand it, the idea for the first turn of the crank is that you can build without the program files TP but razor will still need it to be there to run. It needn't break anything that already works. You can build against nuget and dynamically compile against the same content in program files at runtime. Step 2 would be to write out the actual nuget location that build used, and plumb that through for runtime compilation consumption. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The current plan is to only reference the NuGet packages if the targeting pack isn't found in program files. So if you have the reference assemblies installed, everything would continue to work. I think everything would still work even if we did use the reference assembly packages for compilation as long as you have the reference assemblies installed. Different parts of the build would be getting the reference assemblies from different locations, but they should be the same files. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (I tried to say the same thing, but Daniel worded it better. :)) #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sounds good. I just wanted to call out that this area is a bug factory, and often gets overlooked. #Resolved |
||
still works correctly even without the targeting pack installed. | ||
|
||
### Non-Goals | ||
|
||
* Supporting NuGet-based acquisition of the .NET Framework runtime | ||
* Supporting NuGet-based acquisition from non-SDK-style projects | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I understand this is a non-goal (and why), but what would prevent this from "just working"? #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should allow this to work if it's not difficult. It should be possible with the proof of concept packages I created. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We were able to do this with our NuGet packages in old-school project files. Don't see this as too hard. There is really just one MSBuild variable you have to toggle. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wrote this before NuGet was doubling down on deprecating and replacing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd really like to see this supported in old style There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @NickCraver The current plan is that it will be supported, you'll just have to explicitly add the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh that changes things a bunch and adds other issues. |
||
* Support pre-.NET Framework 4.5 targeting packs | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😭 Libraries like NUnit could really use this. This is what is preventing the dotnet CLI from being able to build net40 and earlier. What's the cost of two or three more targeting packs? #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please provide all PCL and .NET Framework 2+ packages. They are already shipped out and not going to get any updates anyway, so hopefully no additional cost than just package once and push to nuget.. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should provide the .NET 4.0 targeting pack, and possibly other 4.0.x versions in reference assemblies. We already have targeting packs for these, so I don't think there are any obstacles to this. For .NET 3.5.1 and lower, we didn't ship targeting packs in the same way. PCL reference assemblies are also a bit different. I would propose that initially we not support them, in order to get basic .NET Framework support out more quickly. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @dsplaisted If net35 is merely deprioritized without closing the door on it, I can cheer up. 😃 (net20 would also make some folks happier, and by extension, me.) #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This would also fix cases, like in Roslyn, where custom packages are created #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have talked to @jaredpar and it seems you guys don't need pre-.NET Framework 4.5 support. Is that not true? #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It's mostly consistency in our offerings; we don't want to breath more life into frameworks that are that old. That being said, we can always ship more targeting packs in the future if that becomes necessary. Based on adoption I don't see enough evidence that this is necessary though. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That's a fair point. How about this: I leave the wording in the spec as-is; if we can easily build the 4.0 set, great, we'll ship it. If not or we run out of time, we don't. Usage wise, I don't see compelling reasons to spend a great deal of time on 4.0. And 4.5 just makes sense from a .NET Standard support matrix stand point. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
So long as VSTest ships a net35-compiled test execution engine, that puts pressure on NUnit to ship a net35 assembly so that we aren't the ones preventing people from testing on CLR v2 if they need to. A net35 targeting pack would be preferable to what we're doing now, using Mono's edition of net35. #WontFix There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure I buy that: xUnit only supports .NET Standard 1.1. And the number of 3.5 installs is quite low. I think I'm fine with 4.0 but I'm definitely opposed to go lower, especially because the whole 2.0, 3.0, 3.5 TPs are much more convoluted. #Resolved |
||
* Support non-.NET Framework targeting packs (Xamarin, Unity) | ||
- More specifically, this feature will not include targeting packs for APIs | ||
that are specific to Xamarin, Mono, or Unity such as Xamarin.iOS.dll, | ||
Mono.Posix.dll. | ||
|
||
## Design | ||
|
||
Daniel has created a [prototype](https://github.com/dsplaisted/ReferenceAssemblyPackages): | ||
|
||
* We have an aggregate package that itself is empty and just depends on | ||
different packages split by TFM. | ||
* This allows downloading only the assemblies for a specific version while also | ||
having a single package which simplifies installation outside of SDK- style | ||
projects. | ||
|
||
### Details | ||
|
||
* We need to fix ASP.NET Core Razor compilation to locate reference assemblies | ||
without their custom locater that assumes the only place to look for is the | ||
global TP location. | ||
- See [this bug for details](https://github.com/dotnet/sdk/issues/2054) | ||
* There will be separate NuGet packages with reference assemblies for each | ||
version of .NET Framework. This means that projects targeting a single version | ||
of .NET Framework don't need to download and spend disk space on the reference | ||
assemblies and intellisense files for all the other possible versions of .NET | ||
Framework. It also means that when a new version of .NET Framework is | ||
released, the reference assemblies for the previous versions don't need to be | ||
re-shipped in an updated package along with the new assemblies. | ||
* There will be a single "metapackage" that can be referenced, that will have | ||
conditional dependencies on each of the version-specific reference assembly | ||
packages. | ||
* The metapackage will automatically be referenced by the .NET SDK when required | ||
(ie on non-Windows OS's or missing targting pack). On Windows, we will make | ||
the .NET SDK continue to rely on the reference assemblies from the targeting | ||
packs, if and only if, it's present. Otherwise we fall pack to the package- | ||
based reference. This ensures existing code continues to build the same way. | ||
* For the package IDs, I suggest the following: | ||
- For the metapackage: `Microsoft.NETFramework.ReferenceAssemblies` | ||
- For the version-specific packages: | ||
`Microsoft.NETFramework.ReferencesAssemblies.net462` (where net462 is | ||
replaced with the corresponding NuGet short framework Identifier) | ||
* The NuGet packages will include a `.targets` file that sets the | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we may need to set FrameworkPathOverride as well to avoid other weird issues like the explicit mscorlib reference. #Resolved |
||
`TargetFrameworkRootPath` to a path within the NuGet package. This will allow | ||
the existing MSBuild logic in the `GetReferenceAssemblyPaths` target and other | ||
logic such as automatically referencing the facades if necessary to continue | ||
to work as normal. | ||
- We may need to set `FrameworkPathOverride` as well to avoid other weird | ||
issues like the explicit `mscorlib` reference. | ||
* The logic in the `.targets` file in the NuGet packages will only set the | ||
`TargetFrameworkRootPath` if the `TargetFrameworkIdentifier` and | ||
`TargetFrameworkVersion` properties match the reference assemblies that the | ||
package provides. | ||
* The reference assembly packages should include the `.xml` intellisense | ||
documentation files. I believe the `.xml` documentation files have to be in | ||
the same folder as the reference assemblies, so there isn't a way to deliver | ||
them as a separate package. | ||
* If we want to support localized intellisense, we would need to create a | ||
separate set of packages and corresponding meta-package for each language | ||
supported. The IDs of these packages could follow the pattern | ||
`Microsoft.NETFramework.ReferenceAssemblies.pt-br`. The SDK could use a | ||
property to select which language's metapackage to reference. | ||
* The reference assembly packages should not show up as dependencies of "normal" | ||
packages. Thus, the reference assembly packages should set | ||
`developmentDependency` to true in it's metadata. Likewise, when the .NET SDK | ||
automatically references the reference assembly metapackage, it should use | ||
`PrivateAssets="All"`. | ||
* The reference assembly packages should include the same layout of files that | ||
are installed by the targeting packs under `C:\Program Files (x86)\Reference | ||
Assemblies\Microsoft\Framework`. This should be rooted at the path specified | ||
by the `TargetFrameworkRootPath` property in the package. For example, if the | ||
`.targets` file, which is in the `build` folder of the NuGet package, sets the | ||
`TargetFrameworkRootPath` to `$(MSBuildThisFileDirectory)`, then the .NET | ||
4.6.2 reference assembly package should have the reference assemblies and | ||
intellisense files in the `build\.NETFramework\v4.6.2` folder, as well as have | ||
the `Facades`, `PermissionSets`, and `RedistList` folders with corresponding | ||
files under that folder. | ||
* The version number of each package should start at 1.0.0. When a new version | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Was any thought given to just release the net462 reference assemblies in a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this would result in (overall) fewer names, easier to find/reason about, etc #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There have been (rare) occasions where reference assemblies had a bug that was serviced. I think that would be blocked by TFM version == package version. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, using the target framework version as the package version would prevent us from servicing the package if we needed to. Also, we wouldn't want someone to manually reference the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, each package feels like it's a separate "product" to me. The package targeting .NET 4.5.1 can't be used if you're trying to target .NET 4.5, for example - it's not just "a patched version of the same thing". It's a package you'd use for a different purpose. So +1 to "start at 1.0.0". #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The same could be said for .NETStandard Library and any future servicing fixes that come out for it... I would assume that whatever plan is required there would also apply here. I don't think that these are separate products at all, they are simply the reference assemblies for different versions of the .NET Framework #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Netstandard.Library package version is not 1:1 with .NETStandard TFM version either. #ByDesign There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yep, and the fact that we tried to "look" close to the TFM version has caused more problem than it solved. |
||
of the .NET Framework is released, a corresponding reference assembly package | ||
(versioned at 1.0.0) should be released, and a new metapackage that includes | ||
the additional dependency for the newly supported version should be released. | ||
The new version of the metapackage should have it's minor version incremented. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ideally, the metapackage would also have a targets file to check for the maximum supported version and report an error if a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hey @dsplaisted, is this needed or is this handled by the way the targets work? #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In my proof of concept, this is basically handled handled by conditioning setting the reference assembly path on the exact target framework version. The package for 4.7.1, for example, has the following in the .targets file: <PropertyGroup Condition=" ('$(UseReferenceAssembliesFromPackage)' == 'true') And ('$(TargetFrameworkIdentifier)' == '.NETFramework') And ('$(TargetFrameworkVersion)' == 'v4.7.1') ">
<TargetFrameworkRootPath>$(MSBuildThisFileDirectory)</TargetFrameworkRootPath>
<_NeedToRemoveExplicitReference>true</_NeedToRemoveExplicitReference>
</PropertyGroup> So you wouldn't get the wrong reference assemblies, you'd just get the same error you get today if reference assemblies aren't available. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should improve on the error that you get today. It warns, "hey, you'll get refs from the GAC, which is totally wrong and stupid, but YOLO the build might succeed." This is especially silly in SDK projects where the GAC is excluded from search paths. #Resolved There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (and where there often is no GAC (.NET Core msbuild)) #Resolved |
||
If we need to fix an issue with the packages, we can ship new versions with | ||
the patch version incremented, and a new metapackage with dependencies on the | ||
patched packages. | ||
* We need to determine what license to use for these packages. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there any reason not to use an open source license, like MIT, for these reference assemblies? |
||
* We will also need to determine the details of the package metadata, such as | ||
the description, project URL, icon, etc. | ||
|
||
## Q & A |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about PCL profiles? Is that a goal or non-goal? Several popular nuget packages still target PCLs because quite a few customers still use VS2015. Personally, I own several packages that build from .NET SDK projects and multi-target
.netstandard*
,net4*
, andportable-*
, and testing on mac+linux is important to us.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
.NET 2, 3, 3.5 and PCL profiles would be great! Partial support will only result in partial reliance, which is not good enough in many popular packages like Newtonsoft.JSON etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that there are reference assemblies / targeting packs for <= 3.5
MSBuild even requires to run a version of MSBuildTaskHost.exe on the system install of the CLR 2 (.NET 2, 3, 3.5) in order to build .NET 3.5 projects.