-
Notifications
You must be signed in to change notification settings - Fork 258
[Spec] FrameworkReference in NuGet
- Status: Implemented
- Author(s): Nikolche Kolev
7342 - Represent FrameworkReferences in NuGet - pack & restore support
Starting with .NET Core 3.0 the traditional package based approach for .NET Core like target frameworks is changing as described in the following dotnet design. Shared frameworks like ASP.NET & .NETCore App will no longer be represented as a PackageReference, rather as a FrameworkReference in the project file, like below.
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App"/>
</ItemGroup>
This spec discusses the NuGet handling of FrameworkReference in projects and packages.
All .NET Core customers.
- FrameworkReference are part of the representation of a library/app, and as such NuGet will need to represent them in packages.
- FrameworkReference are transitive, both project to project and project to package. Restore will need to represent that in the assets file.
The nuspec schema will be changed as following:
<xs:complexType name="frameworkReference">
<xs:attribute name="name" type="xs:string" use="required" />
</xs:complexType>
<xs:complexType name="frameworkReferenceGroup">
<xs:sequence>
<xs:element name="frameworkReference" minOccurs="0" maxOccurs="unbounded" type="frameworkReference" />
</xs:sequence>
<xs:attribute name="targetFramework" type="xs:string" use="required" />
</xs:complexType>
...
<metadata>
...
<xs:element name="frameworkReferences" maxOccurs="1" minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element name="group" minOccurs="0" maxOccurs="unbounded" type="frameworkReferenceGroup" />
</xs:sequence>
</xs:complexType>
</xs:element>
...
</metadata>
...
This schema mimics the handling of the dependencies section.
Note the targetFramework
attribute on the frameworkReferenceGroup is required compared to dependency where it's not. This is an evolution the product as the huge number of targetFrameworks would lead to NuGet silently ignoring compatibility problems.
A nuspec would look like below:
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2013/05/nuspec.xsd">
<metadata>
<id>LibraryA</id>
<version>1.0.0</version>
<authors>Microsoft</authors>
<owners>Microsoft</owners>
<dependencies>
<group targetFramework=".NETCoreApp3.0">
<dependency id="LibraryB" version="1.0.0" exclude="Build,Analyzers" />
</group>
</dependencies>
<frameworkReferences>
<group targetFramework=".NETCoreApp3.0">
<dependency name="Microsoft.WindowsDesktop.App|WPF"/>
</group>
</frameworkReferences>
</metadata>
</package>
As FrameworkReference are transitive in project-to-project and project-to-package scenarios, the FrameworkReference will be a part of the NuGet identifiable project information or commonly referred as the PackageSpec. A restore for the project requires knowledge of it's transitive project dependencies which include the FrameworkReferences.
The package spec in the json will be updated as follows.
"project": {
"version": "1.0.0",
"restore": {
"projectUniqueName": "F:\\FrameworkReference\\LibraryA\\LibraryA.csproj",
"projectName": "LibraryA",
"projectPath": "F:\\FrameworkReference\\LibraryA\\LibraryA.csproj",
"packagesPath": "F:\\FrameworkReference\\globalPackagesFolder",
"outputPath": "F:\\FrameworkReference\\LibraryA\\obj\\",
"projectStyle": "PackageReference",
"fallbackFolders": [
"C:\\Program Files\\dotnet\\sdk\\NuGetFallbackFolder"
],
"configFilePaths": [
"C:\\Users\\User\\AppData\\Roaming\\NuGet\\NuGet.Config",
"C:\\Program Files (x86)\\NuGet\\Config\\Microsoft.VisualStudio.Offline.config"
],
"originalTargetFrameworks": [
"netcoreapp3.0"
],
"sources": {
"C:\\Program Files (x86)\\Microsoft SDKs\\NuGetPackages\\": {},
"https://api.nuget.org/v3/index.json": {}
},
"frameworks": {
"netcoreapp3.0": {
"projectReferences": {}
}
},
"warningProperties": {
"warnAsError": [
"NU1605"
]
}
},
"frameworks": {
"netcoreapp3.0": {
"frameworkReferences": {
"Microsoft.WindowsDesktop.App|WPF" : {
"privateAssets" : "none"
}
}
}
}
The targets section of the assets file contains the information about all the selected assets from a package/project. The FrameworkReference information will be added there.
"NETCoreApp,Version=v3.0": {
"LibraryA/1.0.0": {
"type": "project",
"framework": ".NETFramework,Version=v4.6",
"compile": {
"bin/placeholder/LibraryA.dll": {}
},
"runtime": {
"bin/placeholder/LibraryA.dll": {}
},
"frameworkReferences": [
"Microsoft.WindowsDesktop.App|WPF"
]
}
}
Similar to the project-to-project case, the targets section will be updated to include FrameworkReference information if needed.
"NETCoreApp,Version=v3.0": {
"LibraryA/1.0.0": {
"type": "package",
"compile": {
"lib/netcoreapp3.0/LibraryA.dll": {}
},
"runtime": {
"lib/netcoreapp3.0/LibraryA.dll": {}
},
"frameworkReferences": [
"Microsoft.WindowsDesktop.App|WPF"
]
}
},
The LockFileTargetLibrary
model will be extended with a list of framework references.
public IList<string> FrameworkReferences { get; }
The FrameworkReference
item supports the PrivateAssets
attribute.
In contrast to PackageReference
, the only applicable values for PrivateAssets
here are all
or none
.
<FrameworkReference Include="Microsoft.NetCore.App" PrivateAssets="all"/>
When all is specified, the FrameworkReference does not flow transitively or get packed.
Since the project-system is how NuGet gets all of it's information about projects in VS, the API will be updated to support FrameworkReference. As they'll be released together, the same API that adds support for PackageDownload
will add support for FrameworkReference.
[ComImport]
[Guid("451ACBA6-FE6A-4412-99D2-3882790BF338")]
public interface IVsTargetFrameworkInfo2 : IVsTargetFrameworkInfo
{
/// <summary>
/// Collection of package downloads.
/// </summary>
IVsReferenceItems PackageDownloads { get; }
/// <summary>
/// Collection of FrameworkReferences
/// </summary>
IVsReferenceItems FrameworkReferences { get; }
}
The NuGet.Build.Tasks.Pack will consider the FrameworkReference item and pack accordingly.
The csproj will usually not contain the FrameworkReference item declarations explicitly as they'll be wired in by the SDK as detailed in their design.
The packing of a specific FrameworkReference can also be disabled by specifying PrivateAssets="all"
.
<FrameworkReference Include="Microsoft.NetCore.App" PrivateAssets="all"/>
The Microsoft.NetCore.App
is not an optional framework reference so the SDK will always specify with PrivateAssets="all"
. NuGet will not account for that.
The nuspec pack will work similarly, validating the standard target framework validation.
NuGet will treat the framework references as case insensitive strings. NuGet will not do any further validation during pack or restore.
The SDK will handle not applicable
FrameworkReference errors. Task
To refer from the design, a FrameworkReference
is a new MSBuild item that represents a reference to a well-known group of framework assemblies that are versioned with the project's TargetFramework
.
NuGet can already handle framework assemblies by reading the Reference
item and representing it in the nuspec.
The weakness in this approach is not having the ability to conditionally include a framework reference for only a certain set of framework ranges.
Take the following example:
<frameworkAssemblies>
<frameworkAssembly assemblyName="Microsoft.WindowsDesktop.App|WinForms" targetFramework="netcoreapp3.0" />
<frameworkAssembly assemblyName="Microsoft.WindowsDesktop.App|WPF" targetFramework="netcoreapp3.0" />
</frameworkAssemblies>
There is no way to say, I want those 2 references for netcoreapp3.0
, but for netcoreapp4.0
I don't want Microsoft.WindowsDesktop.App|WPF
. We could potentially introduce some sort of a special targetFramework value but that could break old readers. Introducing a new attribute would have the same effect.
The solution to the above is to use groups similar to the way dependencies are handled.
Furthermore we need to distinguish between true FrameworkReference and framework assemblies. We could introduce a special character that signals a framework reference but that seems like an error prone solution.
Pack is anyways going to get updates to read FrameworkReference, and I don't see any major drawbacks from having a complete solution.
Would there be framework specific FrameworkReferences? Notable that cross framework family transitivity is not an issue because .NET Core is not a dependant of any framework family in the frameworks matrix. Say .NET Standard becomes non monolithic and we add optional shared frameworks such as WPF. Every implementer will need to support the shared frameworks that the standard declares. The scenario becomes more complex when the frameworks in question are not the standard + implementer. And while there’s no plans for this changing it’s worth considering now so if needed we can account for it early.
How does the fact that NuGet does not understand compatibility between FrameworkReference and a framework affect Package Applicability
This means that certain packages could fail during build time but not at restore time. It could be solved by eventually having the SDK pass a mapping from TFM -> FrameworkReference
Check out the proposals in the accepted
& proposed
folders on the repository, and active PRs for proposals being discussed today.