Skip to content

NuGet-based SDK resolver design spec #2803

@jeffkl

Description

@jeffkl

Overview

At the moment, there are two SDK resolvers, both of which assume that the SDKs are on disk. It would be very useful to have SDKs resolved as NuGet packages as well which would allow third party SDKs to be acquired.

Related issues:

User Experience

Users would have to opt into the new behavior by specifying a version of the SDK to use. The resolver would never automatically get the latest because this would introduce very non-deterministic builds.

Users could control the version on a per-project basis:

<Project Sdk="My.Custom.Sdk/2.3.4">
  ...
</Project>

In this example, a NuGet package named My.Custom.Sdk version 2.3.4 would be retrieved and imported.

It would scale much better if users could specify a version per SDK for the whole repository. They could do this in their global.json:

{
  "sdk" : {
    "version:" "1.0.0"
  },
  "msbuild-sdks": {
    "My.Custom.Sdk" : "2.3.4"
  }
}

The sdk object controls which version of the .NET CLI to use and the msbuild-sdks object controls which versions of the MSBuild SDKs to use. If the resolver detected that multiple versions of the same SDK are requested for the same build episode, a warning would be logged and the first version specified would be used.

MSB4528: warning: A version of the SDK 'My.Custom.Sdk' was already specified in 'D:\Foo\global.json'.  The version specified at 'C:\Foo\bar.csproj (1.7)' was ignored.

In Visual Studio, users would see projects in a Loading state while SDKs are resolved. Any errors that occur during acquisition would be reported in the Output window just like other package restore or build errors. Projects would fail to load if an SDK could not be resolved. Users could fix the issue and reload the project in Visual Studio.

image

Since the SDKs would be NuGet packages, the standard caching mechanisms and configurations would be in place. Users would control which feeds to use and other NuGet settings in their NuGet.config.

Implementation

  • 1. The current SdkResolvers infrastructure within MSBuild would need to be improved to support caching and parallelization. Currently, the same SDK is resolved multiple times per build episode which can slow down evaluations. Make SDK resolution a service #2847
  • 2. Add functionality so that resolvers can maintain state in between resolutions. This would allow resolvers to only do expensive operations once per build. Allow SDK resolvers to preserve state #2849
  • 3. Implement a new logging message to indicate to callers when an SDK is done resolving. This would allow UI bound threads to wait for the Project constructor to return and be notified when its ready. We already added a ProjectEvaluationFinished event.
  • 4. NuGet needs to implement an API for requesting a package to be downloaded. This work is tracked here: NuGet Package Download API NuGet/Home#5919
  • 5. We would make a new package type named Sdk or MSBuildSdk. This could help differentiate the packages so that users don't attempt to add a PackageReference to something like Microsoft.NET.Sdk. Create new package type for MSBuild project SDKs NuGet/Home#6484
  • 6. Develop an SDK resolver that uses the NuGet object model to download packages into the global package cache and return the path to them. If the package was already on disk, it would not connect to any remote resource. Enough information would be logged that a user could diagnose problems like package feeds being offline, packages not existing, etc. Implement a NuGet-based SDK resolver #2850
    a. The resolver would ship with MSBuild so that the functionality is always available since it would be a core piece of project evaluation. It could be a new assembly or be in Microsoft.Build.dll
  • 7. Document how to take advantage of the resolver and how to make SDK packages. This documentation should include the new msbuild-sdks section of global.json. Add documentation for using MSBuild project SDKs MicrosoftDocs/visualstudio-docs#487

If time permits, I'd like to add a command-line argument like /resolver which would allow users to specify custom resolvers to address #2278. Repository owners could control resolvers with Directory.Build.rsp. Resolvers would not be able to be specified in an import because resolvers load before projects are evaluated.

Metadata

Metadata

Assignees

Labels

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions