7

In some solutions I use direct references to internal projects during development and switch to nuget references for release. However, adding 30+ projects to new solutions manually is a tedious task.

I was wondering whether there is a quicker way to do this. With projects it's pretty easy because you just copy/paste the xml but solution files are not that easy to edit but maybe there is some trick?

JamesFaix
  • 8,050
  • 9
  • 37
  • 73
t3chb0t
  • 16,340
  • 13
  • 78
  • 118
  • @AhmadIbrahim sure, but it's not like the `*.sln` format is as self explanatory as `*.csproj` files are. With all the `guid`s it's pretty confusing what to edit :| – t3chb0t Apr 19 '19 at 16:15
  • I have not heard of any visual studio extension that helps with this. Editing the SLN file by hand may be as much or more work than doing it manually through VS. You would have to open each csproj and file the GUID and enter that into the SLN, along with the path, and also setup any build configurations in the SLN. Maybe create a script and make it open source? – JamesFaix Apr 19 '19 at 16:15
  • Can you accomplish what you want with 2 separate SLN files? Or separate solution configurations? – JamesFaix Apr 19 '19 at 16:16
  • @JamesFaix I would... do you think there is an API that one can use in such a script? – t3chb0t Apr 19 '19 at 16:18
  • I would use Fake if you like F# or Cake if you prefer C#. They make lots of automation tasks easier. You could also just do a small C# console app. You mostly just need `System.IO` classes to manipulate files. – JamesFaix Apr 19 '19 at 16:20
  • `System.Xml.Linq` may also be useful for dealing with the XML in csproj files at a higher level than just raw text. – JamesFaix Apr 19 '19 at 16:21
  • @JamesFaix oh, I was thinking more in terms of `visual-studio` API like `AddProjectReference(fileName)` that I could use from the cmd-line level ;-] – t3chb0t Apr 19 '19 at 16:21
  • I am not familiar with the APIs for VS extensions and automation. – JamesFaix Apr 19 '19 at 16:21
  • 1
    https://learn.microsoft.com/en-us/visualstudio/extensibility/starting-to-develop-visual-studio-extensions?view=vs-2019 – JamesFaix Apr 19 '19 at 16:22
  • @JamesFaix done! You can take a look if you're curious ;-) – t3chb0t Apr 19 '19 at 18:40

2 Answers2

3

You can use the dotnet CLI.

For Linux:

dotnet sln MySolution.sln add **/*.csproj

For Windows PowerShell:

dotnet sln MySolution.sln add (Get-ChildItem -Recurse *.csproj)
CodeFox
  • 3,321
  • 1
  • 29
  • 41
AchoVasilev
  • 454
  • 5
  • 15
  • 2
    This works on Windows too! Also perfect timing because I was going to update some old solutions and this will make it a piece of cake ;-] – t3chb0t Aug 10 '22 at 18:41
  • Probably also interesting for people wanting to create a _super solution_ from scratch - just create an empty solution before invoking the `add` command: `dotnet new sln --name MySolution` – CodeFox Oct 13 '22 at 11:46
0

I've done some reaserch on the visualstudio extensibility based on @JamesFaix link and I managed to put together this small piece of code:

using System;
using System.IO;
using System.Linq;
using EnvDTE;
using EnvDTE80;

class Program
{
    static void Main(string[] args)
    {
        // VS2019
        var dteType = Type.GetTypeFromProgID("VisualStudio.DTE.16.0", true);
        var dte = (EnvDTE.DTE)System.Activator.CreateInstance(dteType);

        var sln = (SolutionClass)dte.Solution;

        // Solution to add projects to
        sln.Open(@"C:\Projects\MyProject\MySolution.sln");

        // Projects should be added to the "lib" solution-folder.
        var lib = FindSolutionFolderOrCreate(sln, "lib");

        // These projects should be added.
        var projectPaths = new[]
        {
            @"C:\Projects\MyLibs\Lib1.csproj",
            @"C:\Projects\MyLibs\Lib2.csproj"
        };

        foreach (var path in projectPaths)
        {
            var name = Path.GetFileNameWithoutExtension(path);
            // If project not already in the solution-folder then add it.
            if(!(lib.Parent.ProjectItems.Cast<ProjectItem>().Any(pi => pi.Name == name)))
            {
                lib.AddFromFile(path);
            }
        }

        dte.Solution.Close(true);
    }

    private static SolutionFolder FindSolutionFolderOrCreate(SolutionClass sln, string folderName)
    {
        foreach (var x in sln.Projects)
        {
            if (x is Project p && p.Name.Equals(folderName, StringComparison.OrdinalIgnoreCase))
            {
                return (SolutionFolder)p.Object;
            }

        }

        var proj = (sln as Solution2).AddSolutionFolder(folderName);

        return (SolutionFolder)proj.Object;
    }
}

*.csproj file of this utility:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <!--<TargetFramework>netcoreapp2.2</TargetFramework>-->
    <TargetFramework>net47</TargetFramework>
    <EnableUnmanagedDebugging>true</EnableUnmanagedDebugging>
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DebugType>full</DebugType>
    <DebugSymbols>true</DebugSymbols>
  </PropertyGroup>

  <ItemGroup>
    <Reference Include="EnvDTE">
      <HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\PublicAssemblies\envdte.dll</HintPath>
    </Reference>
    <Reference Include="EnvDTE80">
      <HintPath>..\..\..\..\..\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\PublicAssemblies\envdte80.dll</HintPath>
    </Reference>
  </ItemGroup>

</Project>

I'm using net47 here because netcoreapp doesn't allow you to debug COM objects which makes it really a painful experience.

You'll also need references to the two EnvDTE files.

t3chb0t
  • 16,340
  • 13
  • 78
  • 118
  • You have to admit, this is such a mess this API. Without other SE questions I would probably could never find out that you need to cast a `SolutionClass` into `(sln as Solution2)` - what a mindfuck :-o – t3chb0t Apr 19 '19 at 18:49
  • I've put it on GitHub too, it's [here](https://github.com/he-dev/visual-studio-automation/tree/master/ProjectsInSolutionFolder) – t3chb0t Apr 19 '19 at 19:21
  • tried the code on latest vs2019 - failed with CS1752 Interop type 'SolutionClass' cannot be embedded. Use the applicable interface – galsi Apr 05 '22 at 08:14