GatherProjects - Solution aller Projekte im Ordner

    • VB.NET
    • .NET (FX) 4.5–4.8

      GatherProjects - Solution aller Projekte im Ordner

      Hi!

      Hier ein Winz-Konsole-Programm (eiglich nur eine Methode), welches ein Directory scant, und alle Projekt-Dateien (c# oder vb.net) in einer SolutionDatei zusammenschmeisst.
      So eine "SuperSolution" ist manchmal nützlich, wenn man grossflächig Änderungen vornehmen will (etwa aus allen Projekten, die man hat, den Microsoft.VisualBasic - General-Import rauswerfen)

      Das umzusetzen ist kein Hexenwerk, ein Solution-File *.sln mit zB zwei Projekten sieht im simpelsten Falle so aus:

      Quellcode

      1. Microsoft Visual Studio Solution File, Format Version 12.00
      2. # Visual Studio 15
      3. Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "GatherProjects", "GatherProjects\GatherProjects.vbproj", "{DC1DBFB6-7A2A-4689-B01D-B90BCBE2228F}"
      4. EndProject
      5. Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Helpers", "Helpers\Helpers.vbproj", "{B95FCBC2-67AC-44D8-886F-0BECAB34F3A3}"
      6. EndProject

      Also je Projekt brauche ich 1) die ProjectType-Guid, 2) AssemblyNamen, 3) relativen Pfad zur Projekt-Datei, 4) individuellen ProjectGuid.
      ProjectType-Guids sind fest vorgegeben - vb.net: {F184B08F-C81C-45F6-A57F-5ABD9991F28F}, c#: {FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
      AssemblyNamen und rel. Pfad ergeben sich aussm Pfad der gefundenen Projekt-Datei, und die ProjectGuid liest man aus der Projekt-Datei aus - ist ja Xml.

      Ich zeige einfach mal die Class Program

      VB.NET-Quellcode

      1. #Region "FileHeader"
      2. #If False Then
      3. Scanne das angegebene Directory nach .csproj und .vbproj und generiere daraus ein simples SolutionFile - sowas:
      4. Microsoft Visual Studio Solution File, Format Version 12.00
      5. # Visual Studio 2017
      6. Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Helpers", "Helpers\Helpers.vbproj", "{A7563141-631B-466C-B928-012FE954C193}"
      7. EndProject
      8. Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "GatherProjects", "GatherProjects\GatherProjects.vbproj", "{4BE11612-3597-45DA-B27F-3A457125E64C}"
      9. EndProject
      10. #End If '-- Options, Imports
      11. Imports System.IO
      12. Imports <xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      13. #End Region 'FileHeader
      14. Public Class Program
      15. Public Shared Sub Main(ParamArray commandlineArgs() As String)
      16. Dim diRoot = New DirectoryInfo(commandlineArgs(0)) ' Solution-Ordner: hier hinein wird die Ausgabe-Datei 'GatheredProjects.sln' gelegt
      17. Dim rootPathLen = diRoot.FullName.TrimEnd("\"c).Length + 1
      18. Dim projTypeGuids As New Dictionary(Of String, String) From {{".csproj", "{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}"},
      19. {".vbproj", "{F184B08F-C81C-45F6-A57F-5ABD9991F28F}"}}
      20. Dim outputLines = {"Microsoft Visual Studio Solution File, Format Version 12.00", "# Visual Studio 2017"}.ToList
      21. For Each fiProj In diRoot.EnumerateFiles("*.??proj", SearchOption.AllDirectories)
      22. Try
      23. Dim projTypeGuid As String = Nothing
      24. If Not projTypeGuids.TryGetValue(fiProj.Extension.ToLower, projTypeGuid) Then Continue For
      25. Dim relPath = fiProj.FullName.Substring(rootPathLen) ' relativer Pfad vom Solution-Ordner zur ProjektDatei
      26. Dim xdoc = Xml.Linq.XDocument.Load(fiProj.FullName)
      27. Dim xProjGuid = xdoc.<Project>.<PropertyGroup>.<ProjectGuid>.FirstOrDefault
      28. If xProjGuid Is Nothing Then
      29. outputLines.AddRange({"Error in", fiProj.FullName, "No ProjectGuid found"})
      30. Continue For
      31. End If
      32. 'sowas muss bei rauskommen:
      33. 'Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "GatherProjects", "GatherProjects\GatherProjects.vbproj", "{F4EEB15F-A0CD-4D71-9BEF-70B32F022682}"
      34. 'EndProject
      35. Dim line = $"Project(""{projTypeGuid}"") = ""{fiProj.GetFilenameWithoutExtension}"", ""{relPath}"", ""{xProjGuid.Value}"""
      36. outputLines.AddRange({line, "EndProject"})
      37. Catch ex As Exception
      38. outputLines.AddRange({"Error in", fiProj.FullName, ex.ToString})
      39. Continue For
      40. End Try
      41. Next
      42. With New FileInfo(diRoot.Combine("GatheredProjects.txt"))
      43. .WriteAllText(Lf.Between(outputLines), True)
      44. Diagnostics.Process.Start(.FullName)
      45. .CopyTo(.GetFullnameWithoutExtension & ".sln", True)
      46. End With
      47. End Sub
      48. End Class

      Wie man anne Sub Main() sieht, wird das erste Kommandozeilen-Argument verarbeitet (zeile #18).
      Die Wirkung ist, man kann aussm Datei-Explorer einen Ordner auf die GatherProjects.exe ziehen, und dieser Ordner wird gescannt.
      Im Ordner werden die Dateien GatheredProjects.txt und GatheredProjects.sln angelegt (Zeilen #45 - 47).
      Erstere Datei wird via Process.Start() gleich geöffnet, dass man sehen kann, was generiert wurde (und ob Fehler drinne sind).
      Letztere ist eine lauffähige Solution, mit allen gefundenen Project-Files.

      Points of Interest
      • Mein berühmter FileHeader, wo das wesentliche einer CodeDatei erklärt wird - aber auch die Imports versteckt - sieht im VisualStudio besser aus und kann man zuklappen, wenn mans nicht sehen will.
      • die beiden möglichen ProjectTypeGuids initialisiere ich in ein Dictionary, sodass ich später einfach anhand der Extension den richtigen Guid entnehmen kann bzw das Projekt überspringen (#20, 21 und #26).
      • Der FileSearch-Filter matcht sowohl *.csproj als auch *.vbproj indem er die vmtl. eher unbekannte Wildcard ? verwendet (#23)
      • In die outputLines tu ich immer gleich mehrere Zeilen, sowohl bei Initialisierung als auch später dann mittels .AddRange() (#22, 31, 38, 40)
      • Auslesen des Project-Guids mit Xml.Linq (#29) - beachte auch den Xml-Namespace-Import:

        VB.NET-Quellcode

        1. Imports <xmlns="http://schemas.microsoft.com/developer/msbuild/2003">


      Die angehängte Solution enthält auch ein (wirklich!) kleines Helpers-Project, weil ohne zumindest ein paar meiner Extensions kann ich garnet proggen ;)
      Das bedeutet: Wenn man obiges Snippet nur auskopiert müsste es noch bisserl angepasst werden, dasses ohne meine Extensions auskommt.
      Dateien

      Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von „ErfinderDesRades“ ()