Skip to content

Instantly share code, notes, and snippets.

@GER-NaN
Last active June 25, 2023 06:59
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save GER-NaN/a1998db21bb2b8770fe9b765211eb16f to your computer and use it in GitHub Desktop.
Save GER-NaN/a1998db21bb2b8770fe9b765211eb16f to your computer and use it in GitHub Desktop.
This will combine multiple .cs files from a solution into a single source file.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.Build.Evaluation;
using Microsoft.Build.Construction;
namespace SourceCombiner
{
public sealed class SourceCombiner
{
private static readonly List<string> SourceFilesToIgnore = new List<string>
{
"AssemblyInfo.cs"
};
static void Main(string[] args)
{
if (args == null || args.Length < 2)
{
Console.WriteLine("You must provide at least 2 arguments. The first is the solution file path and the second is the output file path.");
return;
}
string projectFilePath = args[0];
string outputFilePath = args[1];
bool openFile = false;
if (args.Length > 2)
{
Boolean.TryParse(args[2],out openFile);
}
var filesToParse = GetSourceFileNames(projectFilePath);
var namespaces = GetUniqueNamespaces(filesToParse);
string outputSource = GenerateCombinedSource(namespaces,filesToParse);
File.WriteAllText(outputFilePath,outputSource);
if (openFile)
{
Process.Start(outputFilePath);
}
}
private static string GenerateCombinedSource(List<string> namespaces,List<string> files)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(@"/*");
sb.AppendLine($" * File generated by SourceCombiner.exe using {files.Count} source files.");
sb.AppendLine($" * Created On: {DateTime.Now}");
sb.AppendLine(@"*/");
foreach (var ns in namespaces.OrderBy(s => s))
{
sb.AppendLine("using " + ns + ";");
}
foreach (var file in files)
{
IEnumerable<string> sourceLines = File.ReadAllLines(file);
sb.AppendLine(@"//*** SourceCombiner -> original file " + Path.GetFileName(file) + " ***");
var openingTag = "using ";
foreach (var sourceLine in sourceLines)
{
var trimmedLine = sourceLine.Trim().Replace(" ", " ");
var isUsingDir = trimmedLine.StartsWith(openingTag) && trimmedLine.EndsWith(";");
if (!string.IsNullOrWhiteSpace(sourceLine) && !isUsingDir)
{
sb.AppendLine(sourceLine);
}
}
}
return sb.ToString();
}
private static List<string> GetSourceFileNames(string solutionFilePath)
{
List<string> files = new List<string>();
SolutionFile solutionFile = SolutionFile.Parse(solutionFilePath);
foreach (Project project in solutionFile.ProjectsInOrder.Select(p => new Project(p.AbsolutePath)))
{
foreach (ProjectItem item in project.AllEvaluatedItems.Where(item => item.ItemType == "Compile"))
{
if (!SourceFilesToIgnore.Contains(Path.GetFileName(item.EvaluatedInclude)))
{
string projectFolder = Path.GetDirectoryName(project.FullPath);
string fullpath = Path.Combine(projectFolder, item.EvaluatedInclude);
files.Add(fullpath);
}
}
}
return files;
}
private static List<string> GetUniqueNamespaces(List<string> files)
{
var names = new List<string>();
const string openingTag = "using ";
const int namespaceStartIndex = 6;
foreach (var file in files)
{
IEnumerable<string> sourceLines = File.ReadAllLines(file);
foreach (var sourceLine in sourceLines)
{
var trimmedLine = sourceLine.Trim().Replace(" ", " ");
if (trimmedLine.StartsWith(openingTag) && trimmedLine.EndsWith(";"))
{
var name = trimmedLine.Substring(namespaceStartIndex, trimmedLine.Length - namespaceStartIndex - 1);
if (!names.Contains(name))
{
names.Add(name);
}
}
}
}
return names;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment