-
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
Unable to build using bootstrap: Method not found: 'NuGet.ProjectModel.LockFile.GetTarget' #6289
Comments
I build using |
Looks like this method is in the SDK: https://github.com/dotnet/sdk/blob/main/src/Tasks/Microsoft.NET.Build.Tasks/LockFileExtensions.cs @dsplaisted in case you know what's going on |
The I'm not sure why this would be breaking, but try to figure out what version of the NuGet APIs are being used in the bootstrapped MSBuild. |
Hey @brianrob I'm narrowing this down to this commit: I'm seeing NuGet.Frameworks.dll loaded from two places: I'm confirming that the code I'm debugging is using LoadFrom: Interesting that in VS NuGet.Frameworks.dll only deployed in So I suspect this is only an issue with bootstrap since it has two copies? |
I've tried various things and this is what worked for me:
Not sure if this should be the official fix, but at least it's something that unblocks local bootstrap for net472. This all seems hacky and fragile, moreover I don't understand how did it use to work fine two months ago? |
Huh, interesting, reverting back to LoadFile also fixes it. Feels like my latest hack and the original LoadFile are equivalent?? In general, I have a feeling that all of this dynamic assembly loading is terribly fragile and fraught with peril. Feels like all of this could be drastically simplified if we just shipped NuGet and MSBuild in the same directory (famous last words??) But anyway, short term let's think about how we can unbreak local bootstrap. I'm afraid that my Assembly.Load() fix will result in the same JITting that #6126 solved. |
@KirillOsenkov, can you tell me how to repro what you're seeing? There are some load behavior differences in terms of |
Sorry I should have added that you need to pass
|
I'm realizing that no matter what we use to load the assembly the real problem is that we have two copies of NuGet.Frameworks.dll loaded in the bootstrap case, which doesn't seem to be the case for VS. We need to figure out how VS ensures that only a single copy is loaded and replicate that mechanism. For bootstrap, we deploy NuGet*.dll assemblies right next to MSBuild.exe and that's not the case in VS. |
@nkolev92 @loic-sharma @rrelyea @zivkan how does VS load NuGet.Frameworks.dll from a single location? When we build MSBuild bootstrap locally, we end up with two NuGet.Frameworks.dlls and we're thinking about how to replicate what VS does. Any tips greatly appreciated! |
We don't do anything special to prevent duplicate loading of NuGet.Frameworks. In fact I'd expect that it happens in some occasions.
If you use global.json in a newer VS, I'd expect you might run into a double loading scenario. Related: NuGet/Home#9611. |
I think in VS there's only a single NuGet.Frameworks.dll loaded when msbuild is compiled against the same assembly version of NuGet.Frameworks.dll that NuGet itself ships. Hence when different assemblies in VS try to load the same NuGet.Frameworks.dll assembly version, the .NET Framework assembly loader knows to just re-use the same loaded version, where ever that was loaded from. Before msbuild took a dependency on NuGet.Frameworks.dll, nothing else in VS was "allowed to" directly use NuGet's NuGet.* packages, other than NuGet.VisualStudio or NuGet.VisualStudio.Contracts. NuGet's APIs are not stable enough (the error message from this issue's first post is a perfect example) to risk allowing other VS components to take a direct dependency and breaking when NuGet ships a new version with a breaking API. Hence we intentionally do not provide binding redirects, and if any VS component asks us about using NuGet APIs, we point them to our VS APIs and if it doesn't cover the scenario they need, to request new VS APIs for what they need. A 3rd party using NuGet in their VS extension had to re-compile NuGet in their extension, to use a different strong name key, to avoid breaking VS. None of that helps msbuild in this case, but I hope it gives background to why in the past only one copy of the dll was loaded. It's because only NuGet's own VS component used it, nothing else does. I think msbuild could use app.config's probingPath (or codebase?) to tell the .NET Framework assembly loader to look for NuGet.Frameworks.dll in NuGet's install directory. The relative path between NuGet's install directory and MSBuild.exe will always be the same. However, then there will be binding redirect (assembly version) issues when msbuild was compiled against a different version of nuget that ships in VS. I'm not sure my message here is helping solve this issue though. I'm happy to go on a call to talk if that helps. |
I have taken some traces of the good and bad cases. Interestingly, The In the functional case, there is one more assembly load than in the failed case: I suspect that I'm going to need to get some more loader expertise on this. I'll report back on what I find out. |
Isn't this just an issue with the way we create the bootstrap version of MSBuild? Would it work if we weren't copying NuGet DLLs to the bootstrapped versions MSBuild bin folder? |
Unfortunately not - it just fails to load the DLL. This feels like a load context limitation that I don't fully understand. FWIW, if I change |
This is also going to mean that we only load one version of NuGet.Frameworks.dll in this build - specifically, this one:
I'm concerned that if we load via |
FYI, I've posted #6301 to fix this. If you're interested, there are details in the PR description on what caused the failure. |
And for the NuGet folks on this thread, here's a longer term issue to rationalize how we deploy NuGet binaries for MSBuild bootstrap: #6302 It may be that we need to stop deploying them next to MSBuild.exe and instead add some kind of probing path or other mechanism (AppDomain.CurrentDomain.AssemblyResolve?) to fish them out from |
Fixes #6289 Context #6126 changes the way that NuGet.Frameworks.dll is loaded to be used by NuGetFrameworkWrapper from a call to LoadFile to a call to LoadFrom. The path passed in to the Load*` call is the same in both cases, but the behavior is different when running on .NET Framework. On .NET Framework, calling LoadFile results in IJW loading rules being followed, and a different copy of NuGet.Frameworks.dll than the one requested being loaded. It also appears to match the one loaded into the LOAD context. Calling LoadFrom results in the specified assembly being loaded, but it doesn't match the copy of the assembly loaded into the LOAD context. Thus, it remains in the LOADFROM context instead of being promoted to the LOAD context. Later on, there is a collision between code from the two instances of NuGet.Frameworks.dll that are loaded into the LOAD context and the LOADFROM context, and this is where the MissingMethodException is thrown. Note, this does not happen on the .NET Core bootstrap build because the loader behavior is significantly simpler. Changes Made Choose the Load* API based on the target framework at build time. On .NET Core, use LoadFrom and on .NET Framework, use LoadFile. This type of precedent already exists in MSBuild where there is different load behavior for .NET Framework and .NET Core.
I have installed VS 16.9.2 on my local machine and now my bootstrapped MSBuild no longer works:
Looks like it's some binary incompatibility?
The text was updated successfully, but these errors were encountered: