-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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
NuGet-based SDK resolver design spec #2803
Comments
FYI @nguerrera |
Think this design should encompass bringing in the .NET Core SDK via a NuGet package. Today it must be installed SxS with MSBuild and that creates a lot of friction in our initial build experience. Literally everything about the build except the .NET Core SDK can be bootstrapped. This is true even though the .NET Core SDK has supported xcopy distributions. |
I'm working on 2 project systems, and I'd like to be able to create MSBuild SDKs which can be consumed by the project systems on both Visual Studio and the dotnet CLI. Installing the SDKs to the SDKs folder is not really easy (as it requires admin permission, and for the dotnet CLI it's even harder, as it would have to be installed on all the dotnet SDK installations). As one of the project systems is an extension for managed project systems, I use I see that the milestone for this feature is MSBuild 15.6, is it planned for the 15.6 release? |
Yes I'm working to get this out in the next release |
A singleton instance handles all resolution. A service hosted in the main node handles build related evaluation on the main node and a service hosted in the out-of-proc node sends requests to the main node for processing. A cache exists on the main node so that requests are only resolved once per build. The out-of-proc node also caches responses so that an SDK is only resolved once. Non-build evaluations are not cached and could be slow since there is no way to know if two evaluations are related. Address the first step in #2803 * Log a warning if different versions of the same SDK are specified * Log a warning if the SDK version specified does not match what was resolved * Improve Microsoft.Build.UnitTests.Preprocessor.Preprocessor_Tests.SdkImportsAreInPreprocessedOutput by using TestEnvironment to mock SDK resolution * Add escape hatch to disable caching the result on the main node `MSBUILDDISABLESDKCACHE`
SDK resolvers can get and set the `State` property on the `SdkResolverContext` they receive. This state is preserved for the same resolver during the same build submission. When there is no build submission, the state is not preserved. Resolvers will need to take account for these considerations. This will allow the NuGet-based SDK resolver to only look for and parse `global.json` once per build. Address item 2 in #2803
PR is out for a NuGet-based SDK resolver... #2850 |
An SDK resolver that activates only when a user has specified a version. This resolver is ordered to come before the DotNetSdkResolver but if a user does not specify a version (the default), then the NuGet SDK resolver is skipped. The NuGet SDK resolver can be disabled with an environment variable just in case its causing issues or a user wants to override its functionality. NuGet assemblies are loaded at runtime from their installed location. They either come come [VisualStudioInstallRoot\Common7\IDE\CommonExtensions\Microsoft\NuGet or next to MSBuild.exe. There is also an environment variable that can be set to specify a folder to use instead. I've also placed references to NuGet into private subclasses so that assemblies are not loaded unless they are needed. This means that if you have no version specified and no `global.json` with versions, `Newtonsoft.Json` and `NuGet.*.dll` are not loaded. You only pay JIT penalty if you're using the functionality and hopefully we'll make up that time later in the build through caching. Addresses item 6 in #2803 Related to #2278
A few questions.
|
@natemcmaster I've been working on documentation and some of the answers are in there: https://github.com/MicrosoftDocs/visualstudio-docs/blob/master/docs/msbuild/how-to-use-project-sdk.md
The order is:
If no version exists in the project, then the one in
At the moment, no properties are used for the SDK package restores. When resolving an SDK, project properties haven't been read yet. Global properties could be used but only generally come into play with command-line builds. So for now you can only configure SDK restore with a |
nuget package folders are all camelCased. We shouldn't break that convention |
@nguerrera the folder name comes from what the SDK resolvers use: https://github.com/dotnet/cli/blob/master/src/Microsoft.DotNet.MSBuildSdkResolver/MSBuildSdkResolver.cs#L96 Interestingly, the Sdk folder is the only one that's proper cased:
So the ship might have already sailed. The NuGetSdkResolver could use lower case names but I'm not sure if we can get that change in? What do you think @AndyGerlicher ? Maybe we should even use a different name in the NuGet packages like just |
Yeah, I told everyone the folder should be 'sdk' when we first introduced these, but the feedback did not get applied. :( build already exists as a nuget concept for PackageName.targets/PackageName.props, but I think build/Sdk.props build/Sdk.targets would work. |
Maybe we just need to check both on case-sensitive file systems and document 'sdk' as the good way? |
@jeffkl thanks for clarifying. I totally get why, but it's too bad we can't use MSBuild settings to influence the restore inside the NuGet SDK resolver. NuGet.config isn't quite expressive enough, and MSBuild project evaluation has to come after SDKs are resolved. Looks like we'll have to go back to using a console tool to manipulate NuGet.config files before invoking MSBuild. |
@natemcmaster can you open an issue so I can track the request and investigate a solution? |
Ok. #2914 |
What is the ordering works w.r.t. multiple SDK's. First one goes outer (first props/last targets?) One big issue I have with MSBuild.Sdk.Extras is that I want my props to be after the .NET SDK props and my targets before the .NET SDK targets. I wonder if this would do that: Second, it would be really useful if NuGet could add/update these from both the project location and the Should also show version's/updates for |
@onovotny IMO using packageType metadata is a good first step. If we agree to do NuGet/Home#6484, NuGet could use this to add tooling experiences for these types of packages (and by tooling I mean VS, nuget.org search, command-line experiences, etc). I don't think any of those experiences are planned yet, though. |
so is this shipping already in current 15.6 previews? |
@kzu yes it went out in 15.6 Preview 3 and we have some minor improvements coming to it in Preview 4. I've started working on SDKs: https://github.com/Microsoft/MSBuildSdks |
@jeffkl What's the right compat story to support both usages? Just add an Sdks directory and the sdk props/targets? I'd like to migrate my MSBuild.Sdk.Extras to it, but I can't break existing users using as a PackageReference. |
@onovotny yes at the moment you can just add add an Sdk folder with SDK packages don't honor dependencies yet and MSBuild considers them to be pretty much standalone when it adds the implicit imports. We don't currently support an SDK depending on an SDK and having multiple imports in dependency order. But I might work on that if the need arises. |
What happens if a sdk project file also has an sdk import? #inception |
How about ordering? Like if mine needs to be after the .net sdk ones, so that I get their props and my targets run before the .net sdk targets? This is partially a tooling issue; if/when NuGet installs it, how does it know to prepend/append. I need to be able to specify the .net sdk goes before mine. |
if that works (sdk import in sdk), @onovotny you could in theory make the extras package the single sdk reference and then add an |
@onovotny I think this would work best: <Project Sdk="Microsoft.NET.Sdk">
<Sdk Name="MSBuild.Extras.Sdk" Version="1.0.0" />
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
</Project> In that case, the implicit imports should be like: <Project Sdk="Microsoft.NET.Sdk">
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<Import Project="Sdk.props" Sdk="MSBuild.Extras.Sdk" Version="1.0.0" />
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="MSBuild.Extras.Sdk" Version="1.0.0" />
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
</Project> If you need to be imported after Microsoft.NET.Sdk, you probably just want to stick with |
@jeffkl I don't want to be a PackageReference, as I do want to control how restore works (and include additional metapackages, like UWP). Also, PackageReference does not have the right extensibility points for the build imports, it pulls me in too late for the targets. I have to tell people to add this to the end of their project file to get in earlier:
Does it have to be |
Better yet would be |
As long as what you're referencing is not another NuGet-delivered SDK, @dasMulli's suggestion #2803 (comment) should enable |
@rainersigwald Neat, that looks like it could work then, assuming that What happens if I do that but the user still has |
@onovotny you do not have to declare a version in the project, Your suggestion of doing both SDKs as a list is probably what you want. I tend to use the So this: <Project Sdk="MSBuild.Extras.Sdk;Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
</Project> Should expand to this: <Project>
<Import Project="Sdk.props" Sdk="MSBuild.Extras.Sdk" />
<Import Project="Sdk.props" Sdk="Microsoft.NET.Sdk" />
<PropertyGroup>
<TargetFramework>net46</TargetFramework>
</PropertyGroup>
<Import Project="Sdk.targets" Sdk="Microsoft.NET.Sdk" />
<Import Project="Sdk.targets" Sdk="MSBuild.Extras.Sdk" />
</Project> You will be able to confirm the ordering in the preprocessor in 15.6 preview 4 (I had to fix a bug) |
Is an error raised then, since no matching SDK would be found? A silent error seems bad...? |
You get an error that the SDK could not be found. The NuGet SDK resolver notices that no version is specified so MSBuild continues on in the list of resolvers:
|
So what that leaves is what the best practice/recommendation should be? I would need Do I rely on that, or as @dasMulli suggested, put the Finally, it's about the tooling doing the right thing with the package on install (correct order or replacement of the existing sdk item). |
In my opinion, having I imagine you'll want to play around with it a little bit and decide what gives your users the best experience when consuming MSBuild.Extras.Sdk. And of course please circle back and let me know if you have any feedback. You should install 15.6 Preview 3 and try it out if you haven't already. |
Closing this since we've completed all work on our end and remaining items have associated in other repos. |
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:
In this example, a NuGet package named
My.Custom.Sdk
version2.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
:The
sdk
object controls which version of the .NET CLI to use and themsbuild-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.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.
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
Project
constructor to return and be notified when its ready. We already added aProjectEvaluationFinished
event.Sdk
orMSBuildSdk
. This could help differentiate the packages so that users don't attempt to add aPackageReference
to something likeMicrosoft.NET.Sdk
. Create new package type for MSBuild project SDKs NuGet/Home#6484a. 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
msbuild-sdks
section ofglobal.json
. Add documentation for using MSBuild project SDKs MicrosoftDocs/visualstudio-docs#487If 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 withDirectory.Build.rsp
. Resolvers would not be able to be specified in an import because resolvers load before projects are evaluated.The text was updated successfully, but these errors were encountered: