Skip to content

Commit 751ee0b

Browse files
author
ggrignoli
committed
Changed to CommandLineParser. Added help & README.md.
1 parent 22848ea commit 751ee0b

File tree

6 files changed

+68
-50
lines changed

6 files changed

+68
-50
lines changed

README.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Create a Visual Studio Solution from Existing Projects
2+
3+
This console application traverse a folder tree finding every existing Visual Studio Project and adding them to a new solution. It creates 'solution folders' so the projects are grouped in the same hierarchy as in the file system.
4+
5+
Usage:
6+
```
7+
-f, --folder Required. Folder to traverse and where the solution will be
8+
generated.
9+
10+
-o, --output (Default: AllProjects.sln) FileName of the solution to
11+
create.
12+
13+
-e, --exclude (Default: .git,bin,obj,packages,node_modules) Comma
14+
separated list of folders to exclude.
15+
16+
-v, --verbose Verbose
17+
```
18+
19+
Example:
20+
```
21+
SolutionGenerator.exe --folder C:\git\SomeSolutionRoot --output MySolutionFile.sln
22+
```
23+
24+
## Supported Project Types:
25+
Right now it only supports *.csproj file types. It can be extended to other project types by correctly handling the each Project Type GUIDs.

Sources/Program.cs

Lines changed: 16 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,32 @@
1-
using Fclp;
1+
using CommandLine;
22
using System;
3-
using System.Collections.Generic;
4-
using System.IO;
5-
using System.Linq;
6-
using System.Text;
7-
using System.Threading.Tasks;
3+
4+
[assembly: CommandLine.AssemblyUsageAttribute("Finds all Visual Studio projects in a folder tree and adds them to a new solution file.")]
85

96
namespace SolutionGenerator
107
{
11-
public class Arguments
8+
public class Options
129
{
10+
[Option('f', "folder", HelpText = "Folder to traverse and where the solution will be generated.", Required = true)]
1311
public string Folder { get; set; }
12+
[Option('o', "output", HelpText = "FileName of the solution to create.", DefaultValue = "AllProjects.sln")]
1413
public string SolutionFileName { get; set; }
14+
[Option('e', "exclude", HelpText = "Comma separated list of folders to exclude.", DefaultValue = ".git,bin,obj,packages,node_modules")]
15+
public string ExcludedFolders { get; set; }
16+
[Option('v', "verbose", HelpText = "Verbose")]
17+
public bool Verbose { get; set; }
18+
1519
}
1620

1721
class Program
1822
{
1923
static void Main(string[] args)
2024
{
21-
var parser = new FluentCommandLineParser<Arguments>();
22-
23-
parser.Setup(a => a.Folder)
24-
.As('f', "folder")
25-
.SetDefault(Directory.GetCurrentDirectory());
26-
27-
parser.Setup(a => a.SolutionFileName)
28-
.As('d', "dest")
29-
.Required();
30-
31-
var result = parser.Parse(args);
32-
33-
if (result.HasErrors)
34-
{
35-
Console.WriteLine( result.ErrorText);
36-
Console.Read();
37-
return;
38-
}
39-
40-
parser.Object.Folder = Path.GetFullPath(parser.Object.Folder);
41-
42-
new SolutionGenerator(parser.Object).Render();
25+
var options = new Options();
26+
var isValid = Parser.Default.ParseArgumentsStrict(args, options);
27+
if (isValid)
28+
new SolutionGenerator(options).Render();
4329
}
30+
4431
}
4532
}

Sources/Properties/AssemblyInfo.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,9 @@
1010
[assembly: AssemblyConfiguration("")]
1111
[assembly: AssemblyCompany("")]
1212
[assembly: AssemblyProduct("SolutionGenerator")]
13-
[assembly: AssemblyCopyright("Copyright © 2017")]
13+
[assembly: AssemblyCopyright("Copyright 2017 - Gerardo Grignoli")]
1414
[assembly: AssemblyTrademark("")]
1515
[assembly: AssemblyCulture("")]
16-
1716
// Setting ComVisible to false makes the types in this assembly not visible
1817
// to COM components. If you need to access a type in this assembly from
1918
// COM, set the ComVisible attribute to true on that type.

Sources/SolutionGenerator.cs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,7 @@ class FolderContent
2020
class SolutionGenerator
2121
{
2222
string Template =
23-
@"
24-
Microsoft Visual Studio Solution File, Format Version 12.00
23+
@"Microsoft Visual Studio Solution File, Format Version 12.00
2524
# Visual Studio 14
2625
VisualStudioVersion = 14.0.25420.1
2726
MinimumVisualStudioVersion = 10.0.40219.1
@@ -31,23 +30,23 @@ class SolutionGenerator
3130
HideSolutionNode = FALSE
3231
EndGlobalSection
3332
GlobalSection(NestedProjects) = preSolution
34-
{1}
35-
EndGlobalSection
33+
{1} EndGlobalSection
3634
EndGlobal
3735
";
3836

3937

40-
private readonly Arguments _args;
38+
private readonly Options _args;
4139
private HashSet<string> _excludedFolders;
42-
public SolutionGenerator(Arguments args)
40+
public SolutionGenerator(Options args)
4341
{
4442
_args = args;
45-
_excludedFolders = new HashSet<string>(".git,bin,obj,packages,node_modules".Split(','));
43+
_excludedFolders = new HashSet<string>(_args.ExcludedFolders.Split(','));
4644
}
4745

4846
public void Render()
4947
{
5048
var content = GetContent(_args.Folder);
49+
content.FolderId = string.Empty; // Root folder is a special case
5150

5251
StringBuilder projectSection = new StringBuilder();
5352
StringBuilder folderSection = new StringBuilder();
@@ -58,6 +57,9 @@ public void Render()
5857
Path.Combine(_args.Folder, _args.SolutionFileName),
5958
string.Format(Template, projectSection.ToString(), folderSection.ToString())
6059
);
60+
61+
Console.WriteLine("Done.");
62+
6163
}
6264

6365
private void WriteContent(FolderContent content, StringBuilder projectSection, StringBuilder folderSection)
@@ -67,7 +69,10 @@ private void WriteContent(FolderContent content, StringBuilder projectSection, S
6769
projectSection.AppendLine($"Project(\"{{2150E333-8FDC-42A3-9474-1A3956D46DE8}}\") = \"{ Path.GetFileName(dir.Path) }\", \"{ Path.GetFileName(dir.Path) }\", \"{{{dir.FolderId}}}\"");
6870
projectSection.AppendLine("EndProject");
6971

70-
folderSection.AppendLine($" {{{dir.FolderId}}} = {{{content.FolderId}}}");
72+
if (!string.IsNullOrEmpty(content.FolderId))
73+
{
74+
folderSection.AppendLine($" {{{dir.FolderId}}} = {{{content.FolderId}}}");
75+
}
7176
WriteContent(dir, projectSection, folderSection);
7277
}
7378

@@ -78,7 +83,8 @@ private void WriteContent(FolderContent content, StringBuilder projectSection, S
7883

7984
projectSection.AppendLine($"Project(\"{{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}}\") = \"{ Path.GetFileNameWithoutExtension(project) }\", \"{ MakeRelative(project) }\", \"{{{projectId}}}\"");
8085
projectSection.AppendLine("EndProject");
81-
folderSection.AppendLine($" {{{projectId}}} = {{{content.FolderId}}}");
86+
if (!string.IsNullOrEmpty(content.FolderId))
87+
folderSection.AppendLine($" {{{projectId}}} = {{{content.FolderId}}}");
8288
}
8389
}
8490

@@ -89,7 +95,8 @@ private string MakeRelative(string path)
8995

9096
FolderContent GetContent(string folder)
9197
{
92-
Console.WriteLine(folder);
98+
if(_args.Verbose)
99+
Console.WriteLine(folder);
93100
var content = new FolderContent();
94101
content.Path = folder;
95102
try
@@ -102,15 +109,14 @@ FolderContent GetContent(string folder)
102109
var subContent = GetContent(subFolder);
103110
if (subContent.IsEmpty())
104111
continue;
105-
else if (subContent.SubDirectories.Count() == 0 && subContent.Projects.Count()==1)
106-
content.Projects.AddRange(subContent.Projects);
107-
// else if (subContent.Projects.Count == 1 && Path.GetFileNameWithoutExtension(subContent.Projects[0]) == folderName && subContent.SubDirectories.Count == 0)
108-
// content.Projects.Add(subContent.Projects[0]);
112+
else if (subContent.SubDirectories.Count() == 0 && subContent.Projects.Count()==1)
113+
content.Projects.AddRange(subContent.Projects); // Flatten folders with only one project.
109114
else
110115
content.SubDirectories.Add(subContent);
111116
}
112117
}
113-
catch (PathTooLongException) { }
118+
catch (PathTooLongException)
119+
{ Console.Error.WriteLine($"PathTooLongException: {folder}"); }
114120

115121
try
116122
{
@@ -119,7 +125,8 @@ FolderContent GetContent(string folder)
119125
content.Projects.Add(subProj);
120126
}
121127
}
122-
catch (PathTooLongException) { }
128+
catch (PathTooLongException)
129+
{ Console.Error.WriteLine($"PathTooLongException: {folder}"); }
123130
return content;
124131
}
125132
}

Sources/SolutionGenerator.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333
<WarningLevel>4</WarningLevel>
3434
</PropertyGroup>
3535
<ItemGroup>
36-
<Reference Include="FluentCommandLineParser, Version=1.4.3.0, Culture=neutral, processorArchitecture=MSIL">
37-
<HintPath>packages\FluentCommandLineParser.1.4.3\lib\net35\FluentCommandLineParser.dll</HintPath>
36+
<Reference Include="CommandLine, Version=1.9.71.2, Culture=neutral, PublicKeyToken=de6f01bd326f8c32, processorArchitecture=MSIL">
37+
<HintPath>packages\CommandLineParser.1.9.71\lib\net45\CommandLine.dll</HintPath>
3838
<Private>True</Private>
3939
</Reference>
4040
<Reference Include="System" />

Sources/packages.config

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="FluentCommandLineParser" version="1.4.3" targetFramework="net46" />
3+
<package id="CommandLineParser" version="1.9.71" targetFramework="net46" />
44
</packages>

0 commit comments

Comments
 (0)