-
Notifications
You must be signed in to change notification settings - Fork 83
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
Add MSBuildLocator.RegisterLatest method #184
Conversation
Where? We should fix that immediately. I wonder if we should consider changing I regret ever making |
In the code blocks here: It also states (bold emphasis mine):
Additionally, the commits linked from here as sample code show the use of I wouldn't mind changing or removing RegisterDefault tbh. That would solve #81 too, and if a new interface is added that exposes the underlying VS components/workloads available that could also solve #22/#109. |
@rainersigwald I created a documentation issue for your "fix immediately" suggestion. In the mean time, I am going top submit a fix for this behavior, which I expect to supercede @yaakov-h PR, since this awful default behavior wasted my Saturday morning debugging docfx.console spurious behavior MSBuild 18 should ship with this behavior by default. If .NET 7 doesn't ship with this behavior, it is a mistake, in my eyes. |
Some thoughts:
|
I think this really gets to the heart of our problem with this. Most people assume the "default" thing should do something sensible, and only advanced users would need something more complicated, but what the default should be (and even what "latest" means) are both up for debate with valid arguments on both sides. There are even more complicated ways we could try to assume default. Perhaps it should be the most recently updated VS, since that's the one the user most likely cares about. Perhaps it should be the "latest" VS enterprise, only falling back to one of the others if no enterprise is present. Perhaps it should be the be the VS most recently opened. There are valid arguments in favor of any of these—admittedly some stronger than others—and "default" just doesn't disambiguate. I would argue that a change like the one Jason recommended would be more rational, but it would also break some people, and we try to avoid that. Ultimately, if we can remove RegisterDefault entirely and force people to choose the MSBuild they want, we can avoid unfortunately scenarios with MSBuildLocator finding an unexpected MSBuild. I, at least, am not currently brave enough to pull the trigger on that, even with intermediate steps like deprecating it first. But I do see other changes here as potentially wasted work if we end up throwing out RegisterDefault entirely. |
Can we start by simply documenting how we think the logic works today? You can glean from Jason and Sam's comments that it was too complex for them to waste time trying to understand, and they literally claimed they did not understand it. For example, I don't even understand what "Dev Console" is supposed to mean. Someone who is a long tenured MS emlpoyee like @rainersigwald may know for sure. For example, the %VSINSTALLDIR% environment variable apparently is no longer used in Visual Studio 2022. https://github.com/MicrosoftDocs/visualstudio-docs/issues/7774 Do you guys get the point I am drawing about what a giant rat's nest of assumptions this is? The way I would start to untangle this is figure out which versions of Visual Studio even support some of these CMD variables:
And then explain this comment, because I don't understand why this PR even exists if this comment accurately describes what should happen: MSBuildLocator/src/MSBuildLocator/DotNetSdkLocationHelper.cs Lines 145 to 148 in 850d27b
|
"Dev Console" is a synonym for "Developer Command Prompt"
This isn't true:
|
That code only applies when resolving MSBuild from a .NET SDK, not from a Visual Studio installation. |
My point was that the behavior should be consistent across MSBuildLocator. nuget.exe does not resolve packages differently whether I use ProGet, MyGet or nuget.org. |
This is not obvious to me. Please elaborate: why should we break detection of Visual Studio within a specific VS installation's command prompt for consistency with what |
Good point. So, in other words, in the use cases I laid out above, I ignored the fact that the primary users of RegisterDefaults is actually Visual Studio itself as a use case. Visual Studio itself as a (primary) use case was not on my mind when I was thinking through when this problem hits me, since it normally hits me from a build pipeline (like docfx.console or some other tool) or command line shell (where I am prototyping what to put into a build pipeline). My main thought process was to guide towards a more deterministic version of MSBuildLocator, with a clear specification for how it should behave. I thought having a nuget.exe-style approach to pulling in references by lowest version specified would be optimal. Lowest known version avoids zero day exploits and possible unintended regressions, in addition to promoting deterministic behavior. However, in building my analogy, I overlooked that in the case of Visual Studio calling RegisterDefaults() itself, it is acting like a user customizing their nuget package versions with a particular valid range. |
This is probably not the right thread to have that discussion, but MSBuildLocator's internal behaviour varies wildly depending on which assembly you use. The .NET Framework build looks for Visual Studio or environment variables set by the Developer Console in order to load a .NET Framework version of MSBuild out of Visual Studio. The .NET Core build looks for the .NET SDK in order to load a .NET Core version of MSBuild out of the SDK. This is indicated here and a handful of other places throughout: MSBuildLocator/src/MSBuildLocator/VisualStudioInstanceQueryOptions.cs Lines 19 to 24 in d83904b
It's possible that the .NET Core code is more deterministic as you've shown above, but the Visual Studio locator code is as described in the OP. Potentially this issue could also be fixed by making |
It's a good point. I think you actually hit the nail on the head in your first post:
BTW, @rainersigwald @Forgind - this type of issue would likely have been caught eventually via docker headless vs installer integration tests, as each CI run would/should generate different Instance IDs in the Windows Registry. |
You would still need to explain how the Sort should behave. |
vswhere is simply a tool that the caller can pass a version range or prerelease options or a required components list to or so on. However, on the very page you've listed, it also lists:
This is the API that MSBuildLocator uses, and vswhere uses this internally as well. Chances are vswhere is also susceptible to the same alphabetical-by-instance-ID problem. However, it is still up to the thing calling the API (i.e. MSBuildLocator, or we can hand that responsibility off to MSBuildLocator's consumer) to filter and sort for the instance you want, and that goes whether an application goes through the COM API or whether it uses vswhere, the same problem still applies.
Same thing I've done in this PR and mentioned in the OP - take the latest by version number. If you use a Preview then you get to uncover any bugs or compatibility issues with the Preview, if you use Stable then hopefully it will Just Work. If you want to make your own decisions, don't use RegisterDefaults or RegisterLatest, and instead pick one yourself, as I've had to do for the last 5 years. Or, as @rainersigwald suggested, remove both APIs and force consumers to make their own decision each and every time. In which case I'd be tempted to publish an opinionated SimpleMSBuildLocator package to wrap it 😄 |
I did not know that; I did not realize the API provides isPrerelease, which vswhere.exe provides. How do I get that flag?
Cheeky, but does not address my observation about IsPrerelease. Ideally SimpleMSBuildLocator would expose such flags. |
@jzabroski The flag is exposed by ISetupInstanceCatalog.IsPrerelease(), which I use as: ISetupInstance2 i2 = /* I assume you already have your instance from the API */;
if (i2 is ISetupInstanceCatalog catalog && catalog.IsPrerelease())
{
// pre-release build
} |
MSBuildLocator exposes |
This PR seems to be outdated. |
MSBuildLocator.RegisterDefault()
is recommended by the Microsoft documentation but is problematic as it simply takes theFirstOrDefault()
instance.Exactly which instance is the
FirstOrDefault()
is not particularly clear to developers when there is more than once instance of Visual Studio installed on the computer.Instances appear to be returned by the Visual Studio Setup API in alphabetical order, which is the order that Windows returns when enumerating the
C:\ProgramData\Microsoft\VisualStudio\Packages\_Instances
directory.Therefore, the instance registered by
RegisterDefault()
is not neccesarily the oldest, nor the newest, nor the one that was installed first, nor the one that was installed most recently. Unless there is some determinism to the instance ID that I am not aware of, it appears to be randomly generated and thus entirely down to luck.At my company we have a large number of machines with both VS2019 and VS2022, and can get inconsistent results across different identically-configured machines which can cause crashes and errors that are particularly difficult to diagnose. As a workaround, in some of our tools we have been manually registering the instance sorted by version number, to great success.
In this PR I have added a new method,
RegisterLatest()
, which enumerates all of the instances and registers the one with the highest version number. This will provide a far greater level of determinism and consistency, and gives the user some level of control over which version is actually selected.