msbuild icon indicating copy to clipboard operation
msbuild copied to clipboard

[Discussion] Clean up sln (VisualStudio solution) files

Open srivatsn opened this issue 7 years ago • 71 comments

From @chrisaut on February 21, 2017 4:32

Now that the csproj files are somewhat clean, are there plans to similarly clean up .sln files?

A sample solution with just two projects looks like this today:

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26206.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coreDemo", "coreDemo\coreDemo.csproj", "{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "netStdLib", "netStdLib\netStdLib.csproj", "{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}"
EndProject
Global
	GlobalSection(SolutionConfigurationPlatforms) = preSolution
		Debug|Any CPU = Debug|Any CPU
		Debug|x64 = Debug|x64
		Debug|x86 = Debug|x86
		Release|Any CPU = Release|Any CPU
		Release|x64 = Release|x64
		Release|x86 = Release|x86
	EndGlobalSection
	GlobalSection(ProjectConfigurationPlatforms) = postSolution
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Debug|x64.ActiveCfg = Debug|x64
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Debug|x64.Build.0 = Debug|x64
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Debug|x86.ActiveCfg = Debug|x86
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Debug|x86.Build.0 = Debug|x86
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Release|Any CPU.Build.0 = Release|Any CPU
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Release|x64.ActiveCfg = Release|x64
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Release|x64.Build.0 = Release|x64
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Release|x86.ActiveCfg = Release|x86
		{1A6AEDEC-9638-465A-9EEE-7CC718C12DED}.Release|x86.Build.0 = Release|x86
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Debug|Any CPU.Build.0 = Debug|Any CPU
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Debug|x64.ActiveCfg = Debug|x64
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Debug|x64.Build.0 = Debug|x64
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Debug|x86.ActiveCfg = Debug|x86
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Debug|x86.Build.0 = Debug|x86
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Release|Any CPU.ActiveCfg = Release|Any CPU
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Release|Any CPU.Build.0 = Release|Any CPU
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Release|x64.ActiveCfg = Release|x64
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Release|x64.Build.0 = Release|x64
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Release|x86.ActiveCfg = Release|x86
		{EEE8E2FD-7DAF-4AAC-8A2C-8B5D4A159B56}.Release|x86.Build.0 = Release|x86
	EndGlobalSection
	GlobalSection(SolutionProperties) = preSolution
		HideSolutionNode = FALSE
	EndGlobalSection
EndGlobal

I see 3 "sections": 1) Information about VS, which version created it and the minimum version 2) List or projects and their locations 3) Solution and ProjectConfigurationPlatforms

  1. Is the VisualStudio stuff really needed? I regularly run into changes here when opening solutions in different versions, it creates a change for no apparent reason.

  2. Can we get rid of the GUIDs for the project lists? Also the logical name (coreDemo, netStdLib) in addition to the csproj, can this just be infered for the probably 99.99% case when the two match?

  3. I can't say much about the ConfigurationPlatform stuff, except that it looks messy. I feel like this should be pulled out somehow.

Since everything is going xml (....I know, I know), perhaps the format of the sln file should also be xml. A minimalistic solution could look something like this:

<Solution MinimumVisualStudioVersion="10.0.40219.1">
  <Projects>
    <Project Location="coreDemo\coreDemo.csproj" Type="netCoreConsole" ProjectConfigurationPlatforms="Default" />
    <Project Location="netStdLib\netStdLib.csproj" Type="netStandardLib" >
        <ProjectConfigurationPlatforms>
            <ConfigurationPlatform Configuration="Release" Platform="x64" />
            <ConfigurationPlatform Configuration="Release" Platform="x86" />
        </ProjectConfigurationPlatforms>
    </Project>
  </Projects>
  <SolutionConfigurationPlatforms>
    <ConfigurationPlatform Configuration="Release" Platform="x64" />
    <ConfigurationPlatform Configuration="Release" Platform="x86" />
  </SolutionConfigurationPlatforms>
</Solution>

A few things to note: The Project Type Guids ("FAE04EC0-301F-11D3-BF4B-00C04F79EFBC", at least that's what I think those are) are messy, if this is really needed by VS for some reason let's please make it a string that makes sense for humans ("netCoreConsole" in the example). We pull the ProjectConfigurations into the projects where they logically belong. Also, while at times custom platform/configurations are needed, most of the time people I think just use the default (x86/x64/Any CPU)/(Debug/Release). So we just make those the default. For the project list we could also do the filepattern thing (**/*.csproj) but I feel like projects aren't added/removed often enough to warrant this, so perhaps being explicit here is the better choice.

I just want to start this issue so that a discussion can be started, I'm by no means an expert on this stuff.

Copied from original issue: dotnet/roslyn-project-system#1594

srivatsn avatar Feb 21 '17 18:02 srivatsn

Moving to the msbuild repo where the design for this needs to happen.

srivatsn avatar Feb 21 '17 18:02 srivatsn

This isn't in our power alone, unfortunately. MSBuild is one consumer of the solution format, but arguably not the most important one: Visual Studio itself reads solutions when loading and that code is the canonical copy.

This has been put off for almost 15 years now, so I'm pessimistic--but it's a totally reasonable request, and maybe now is the time it can actually get done.

Personally, I'd like to see a native MSBuild format rather than a distinct XML schema. But representing all the data in MSBuild could be pretty tricky.

rainersigwald avatar Feb 21 '17 19:02 rainersigwald

+1 for replacing the .sln file with native msbuild files. This probably implies implementing lineup files in one form or another (see lineup files in #1493). And then, probably, also the notion "project cones".

cdmihai avatar Feb 24 '17 01:02 cdmihai

Clearly I'm misunderstanding what the role of the sln file actually is, I thought it was merely for VisualStudio but at build time it would pass the relevant csproj files down to msbuild, and that msbuild only deals with those.

But yes, I understand this is not a quick thing to do and requires major efforts across various teams (and companies), but given the effort to simplify csproj files was so well received, we should still strive for it.

@rainersigwald do you have a quick example of what an msbuild format for a solution might look like?

chrisaut avatar Feb 24 '17 03:02 chrisaut

I thought it was merely for VisualStudio but at build time it would pass the relevant csproj files down to msbuild, and that msbuild only deals with those.

The reason it's more complex than that is that VS builds individual projects with configuration derived from the solution. That enables the things you can do with the Solution Configuration Manager, like "don't build this project in this configuration" or "when the solution is in Release configuration, still build this individual project as Debug". See MSBuild's AssignProjectConfiguration task for a bit of the code required to deal with this.

do you have a quick example of what an msbuild format for a solution might look like?

I do not, in large part because one of the sources of complexity here is that I don't know all of the configuration capabilities of the .sln format. I learned a new one yesterday investigating https://github.com/Microsoft/msbuild/issues/626#issuecomment-282117160! Building all of the capabilities of the current format into a new one is one reason this has been put off for over a decade (the other big reason is that the solution-handling code in VS is ancient and has tentacles into lots of things, so it's not easy to drop in a replacement).

we should still strive for it.

I agree, and I think everyone agrees in principle--I haven't met defenders of the current format. It's just a matter of prioritization.

rainersigwald avatar Feb 24 '17 16:02 rainersigwald

As @rainersigwald says, this has been put off repeatedly and it really is something for the VS team to drive as they would do almost all the work (since it's safe to assume they would move to a format that MSBuild already understands)

Although the solution has tendrils throughout VS, to my knowledge, knowledge of the solution format is pretty localized (as you would hope) so changing the format should not be a huge effort.

Next decision to make would be how VS would handle changing the build process of a solution. Right now you can't do that or at least VS will ignore it: it invokes MSBuild individually on each project in the solution -- it does not "build" the solution itself, as MSBuild does. Lots of parts of VS listen to events through the build of all the projects and if the build process changes, they might get confused about that. (They don't generally pay attention to what happens within the build of a particular project, so this would be a new issue.)

However they may want to go further. MSBuild doesn't care what your project structure is, it can be n-level for example. VS doesn't work quite the same when you reference projects that aren't listed in your solution; it doesn't represent visually any kind of hierarchy or structure beyond 2 levels; solution folders are fake folders. So if VS wanted to invest in the solution file, they might want to think beyond just changing the format of the file and instead allow VS to just open any number of random MSBuild files at once and make some sense of it.

danmoseley avatar Mar 02 '17 01:03 danmoseley

+1 for a native MSBuild format. It would be awesome to have it as "simple" as

<Project Sdk="Microsoft.Sln.Sdk">
  <ItemGroup>
    <SolutionProject Include="**\*.*proj" />
  </ItemGroup>
</Project>

dasMulli avatar Mar 19 '17 11:03 dasMulli

Since this seems to be more of a long term goal, perhaps in the meantime we can make surgical improvements to the existing format.

Can anyone explain/confirm if

  1. type Guids are not needed/can be inferred
  2. VS version information is not needed

chrisaut avatar Mar 21 '17 03:03 chrisaut

I got exactly the same question, will we have a human-ready sln format?

UncleFirefox avatar Oct 20 '17 14:10 UncleFirefox

SLN currently has that archaic syntax. GitHub linguist classifier even doesn't understands it.. was it vb, inf, ruby or which language was the source of inspiration back in 1900-who-knows. Dotnet-cli team had to create a custom parser and writer to add support for dotnet new sln -- actually took from Mono: https://github.com/dotnet/cli/blob/b674f5d/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs

XML is something used in proj and working out decently with new format, SLN should be updated. It's now or never!

So if VS wanted to invest in the solution file, they might want to think beyond just changing the format of the file and instead allow VS to just open any number of random MSBuild files at once and make some sense of it.

@danmosemsft, since we don't have any idea whose scones to butter to make it ever happen, but we would stand by you and vote if you further this discussion with right people in VS team. If they just start with the drop-in replacement of SLN for vNext of VS that understands everything what current SLN provides today, it would improve a lot!

Once that gets shipped and both VS and MSBuild get familiarized with this format, they can add exciting new features "stuff that wasn't possible with old format".

Or VS guys can open source their core solution handling module to enter the community-driven-development bliss.

ghost avatar Nov 17 '17 00:11 ghost

This idea is listed in the Visual Studio User Voice. Vote for it.

BrunoZell avatar Apr 18 '18 10:04 BrunoZell

I know that only some of the information is written, depending on the (solution) configuration and platform selected, but the output seen with MSBuildEmitSolution when using MSBuild might be a place to start. Obviously the information in there would need to be generated by the Microsoft.Sln.Sdk if it wasn't present in the file directly, and as some of it is proxy targets for individual projects, it might need to be generated and cached (maybe like the nuget project.assets.json?)

I for one would love there to be something like

  <ItemDefinitionGroup>
    <SolutionProject>
      <Configuration>$(Configuration)</Configuration>
      <Platform>$(Platform)</Platform>
      <SolutionConfiguration>$(Configuration)</SolutionConfiguration>
      <SolutionPlatform>$(Platform)</SolutionPlatform>
      <SolutionDir>$(MSBuildThisFileDirectory)</SolutionDir>
      <Build>True</Build>
    </SolutionProject>
  </ItemDefinitionGroup>

In the defaults which would allow overriding on a per project basis, and all project metadata to be passed to the project build.

And maybe even a nice project globbing pattern based on installed languages / extensions.

CZEMacLeod avatar Apr 18 '18 11:04 CZEMacLeod

I have more use cases here that a MSBuild format would make possible! For example, take a look at MiniProfiler: https://github.com/MiniProfiler/dotnet. It's a library that supports ASP.NET Core and non-Core (new and old) versions. Now because of that, I need sample projects for both. So the solution contains a sample for ASP.NET and a sample for ASP.NET Core, for the people, you know, the lovely people! They like samples. They're good folk.

Now, a use case! The dotnet build tooling does almost everything you'd want here to build a solution. But, since dotnet build cannot build a ASP.NET non-Core sample, it throws an error every time (which error doesn't matter, it's not supported and that's not the point of this comment). Also, AFAIK, you cannot filter what would be built, aside from carrying the baggage of additional build configs just for packaging...which is a bad state to be in when testing vs. deployment.

If the .sln file (I'm gonna lobby for .slnx here!) was XML/MSBuild, I could do something as simple as /p:IncludeSamples=false around a <Projects Condition="$(IncludeSamples) == 'true'"> section (like proposed in the original issue) to exclude them. Any configuration could be treated this way really. I'm not saying that's an easy task, but it would be a huge usability and versatility improvement to build a solution however you wanted.

Plus, ya know, no GUIDs because ewww.

NickCraver avatar May 25 '18 19:05 NickCraver

@NickCraver Would something like https://github.com/Microsoft/MSBuildSdks/tree/master/src/Traversal be what you're looking for?

Seems like that would be a good model for what solution files should end up being.

bording avatar May 25 '18 19:05 bording

I believe in VS organization, the team owning SLN would have more context on full list of supported verbs and variation.

Lets assume there is no team, folks who invented SLN two decades ago left and nobody is maintaining it anymore.

Then here is a #Hackathon idea for @karelz:

Implementation of new format can be done and validated against 3 million SLN files on public GitHub https://github.com/search?q=fileextension%3Asln+project. A cronjob can be written to collect distinct verbs/tokens from 3M files. For parsing, we can use https://github.com/dotnet/cli/blob/b674f5d/src/Microsoft.DotNet.Cli.Sln.Internal/SlnFile.cs.

I think if it still misses some obscure verb for example that was only supported in past century VS 97, it is most likely acceptable.

With GitHub v3 API, it is bit more work. First it needs to traverse through organizations: https://api.github.com/search/code?q=extension:sln+org:dotnet then call: https://api.github.com/search/code?q=extension:sln+org:$ORGANIZATION to get url value, navigating to which gives us download_url.With 100 threads it will take 30K iterations.

Once there is an exhaustive list, an XML schema can be established, which someone in VS team can implement with 5-10 lines of C# code to replace archaic SLN syntax.

ghost avatar May 25 '18 20:05 ghost

@NickCraver a workaround you could use for your case today is to customize MSBuild's view of the solution when it builds from the command line (as in dotnet build).

You can do this with a solution build customization. Since your solution is MiniProfiler.sln, create a file named after.MiniProfiler.sln.targets that manipulates the ProjectReference item before MSBuild starts building things:

<Project InitialTargets="RemoveIncompatibleProjectsFromDotnetCoreBuild">
  <Target Name="RemoveIncompatibleProjectsFromDotnetCoreBuild"
          Condition="'$(MSBuildRuntimeType)' == 'Core'">
    <ItemGroup>
      <!-- These projects don't successfully build in `dotnet build`, so don't build them
          when MSBuild is running on Core. -->
      <ProjectReferenceToAvoidInDotnetBuild Include="samples\Samples.Mvc5.EFCore\Samples.Mvc5.EFCore.csproj" />
      <ProjectReferenceToAvoidInDotnetBuild Include="samples\Samples.Mvc5\Samples.Mvc5.csproj" />

      <ProjectReference Remove="@(ProjectReferenceToAvoidInDotnetBuild->'%(FullPath)')" />
    </ItemGroup>
  </Target>
</Project>

rainersigwald avatar May 25 '18 21:05 rainersigwald

@bording @rainersigwald Thanks! I'll give both of these a look today and see what works. Traversal does seem to be the more appealing option if it works. I'll give it a shot and report back :)

NickCraver avatar May 26 '18 11:05 NickCraver

@bording Unfortunately I hit issues with Traversal but indeed that's very appealing. I filed an issue with what I hit; maybe I can help resolve this one given I have an easy (I think) repro: https://github.com/Microsoft/MSBuildSdks/issues/34 I'm not sure if this points to a larger issue of tree traversal shenanigans that currently happens via .sln resolution of what to build or not.

NickCraver avatar May 26 '18 12:05 NickCraver

@NickCraver .slnx reminds me of .doc -> .docx, not in a good way. .slnproj is the way to go.

m0sa avatar May 30 '18 19:05 m0sa

A noticeable thing is that if I move a project file's position, project references with GUID will automatically correct its position, seems using GUID from the solution. But I think Visual Studio should has a feature to move a project and correct references together.

huoyaoyuan avatar May 31 '18 01:05 huoyaoyuan

@rainersigwald so the last info on this is paraphrased "we're not in charge to decide this" - which is totally fine. But can we at least know who is, maybe a link to uservoice if its directly decided by the VS programm ownership.

Also: Maybe we can do this in two steps? Lots of negative feedback for SLN has to do with the large amount of GUIDs in the sln file. Maybe (as the GUID went away from regular projects) we can make an effort to get rid of the GUIDs first and then proceed with the big one in a larger timeframe?

MeikTranel avatar Sep 01 '18 21:09 MeikTranel

I made an sdk-style msbuild solution file format, the use is limited since there is no VisualStudio support, but it can still be used with vs code, Visual Studio Open Folder or build-only solution.

https://github.com/JeffCyr/MSBuild.SolutionSdk

A solution file can be as lean as this:

<Project Sdk="MSBuild.SolutionSdk/0.0.2" />

By default, all csproj/vbproj/fsproj/vcxproj under the folder hierarchy of the solution file will be included in the solution. For simple solutions, the .slnproj file may never have to be edited again.

JeffCyr avatar Sep 17 '18 11:09 JeffCyr

@JeffCyr there's a ms-built version at https://github.com/Microsoft/MSBuildSdks/tree/master/src/Traversal that does that same you are doing but also covers some more edge cases (but is certainly less user-friendly / documented and lacks default items)

<Project Sdk="Microsoft.Build.Traversal/1.0.43">
  <ItemGroup>
    <ProjectReference Include="src\Common\Common.csproj" />
    <ProjectReference Include="src\App\App.csproj" />
    <ProjectReference Include="src\WebApplication\WebApplication.csproj" />
  </ItemGroup>
</Project>

dasMulli avatar Sep 17 '18 21:09 dasMulli

@dasMulli Indeed, Traversal seems great for creating a simple build script. The main difference with MSBuild.SolutionSdk is that it tries to keep the same experience as current sln files with configuration management and build dependencies. My hope is that this project can serve as a playground to eventually be standardized and replace sln files in Visual Studio.

JeffCyr avatar Sep 18 '18 15:09 JeffCyr

With the new msbuild Sdk pluggability, the solution proposed above by @JeffCyr and the related MSBuildSDKs/Traversal initiatives are exactly where msbuild sln should go.

I would love to see this adopted. There is the IDE integrations story (VS, VSCode, Rider...etc.) but the opportunities/scenarios offered by such a solution (😅) are just tremendously exciting!

Is there any plan to prioritize this work? cc: @davkean @KirillOsenkov

xoofx avatar Sep 19 '18 16:09 xoofx

I still routinely hit issues where someone forgot to commit changes to the sln, or there are merge conflicts. I was hoping this would be tackled for VS19, but doesn't seem to be the case.

Is there anyone that could champion this and bring it up with the VS team or whoever is responsible?

chrisaut avatar Feb 07 '19 03:02 chrisaut

Would this be a good candidate to tackle for the .NET 5 timeframe?

Pretty please

chrisaut avatar Aug 05 '19 00:08 chrisaut

This has been put off for almost 15 years now, so I'm pessimistic--but it's a totally reasonable request, and maybe now is the time it can actually get done.

@rainersigwald 3 years later the situation seems to be unchanged, has the chance of this happening increased or decreased?

It's the second most :+1: issue here, only surpassed by #613. The UserVoice someone linked no longer exists. What can we do as customers to give this request a higher priority?

stijnherreman avatar Mar 04 '20 14:03 stijnherreman

Since the uservoice site apparently is gone and I wasn't able to find a relevant issue for this on the new https://developercommunity.visualstudio.com/ I've gone ahead and created a feature request there: https://developercommunity.visualstudio.com/idea/988209/clean-up-sln-visualstudio-solution-files.html

Please upvote it just in case it helps get the relevant parties more aware of this.

chrisaut avatar Apr 13 '20 10:04 chrisaut

I never understood why Microsoft created Solution files in the first place. A solution file should just be a .csproj file containing references to child .csproj files. Configuration data is cascaded down from down the hierarchy with each .csproj having the ability to override it's parent configuration in the same way .config files do. This would make configuration a lot easier than it is now.

Joebeazelman avatar Aug 09 '20 21:08 Joebeazelman