Update: As commented by ‘pifcnt’ this is for VS2010 files.
Today I had the daunting task of combining multiple solution files into one. The reason for this was that the project I’m working on originally started as multiple separate solutions with shared projects. Now we find it more convenient to have it in a single solution both for automated build and, at least for some, as a preferred work method.
There are 88 projects split over 22 solutions. I started the task by manually adding them to an empty solution thinking that this will take some time, but not that much. Unfortunately Visual Studio crashed early in the task. I’ve previously Googled for an application to do this and found one that didn’t work.
So I brushed off my black belt in Perl regex and 3-4 hours later I had a script that solved my problem.
It combines all Project sections, corrects relative path to projects, skips the GlobalSection(TeamFoundationVersionControl) and keeps all other GlobalSections with only unique entries on each.
Syntax: merge.pl Relative\\Path\\To\\Solution1.sln,And\\Relative\\Path\\To\\Solution1.sln CombinedOutput.sln
Note that you want to be standing in a common root for all projects and you want to put the output file in this path. This script doesn’t check everything, it was made to solve one task – and so it did.
#!/usr/bin/perl
##
## SETTINGS
##
$HEADER = <<EOH;
Microsoft Visual Studio Solution File, Format Version 11.00
# Visual Studio 2010
EOH
##
## END SETTINGS
##
print <<EOT;
Visual Studio Solution File Merge
By Tedd Hansen <tedd\\\@konge.net> Jan 2011
EOT
if ($#ARGV <= 0) {
print "Required parameters: input1.sln,input2.sln output.sln\\
";
exit;
}
my %PROJECTS;
my %GLOBALSECTIONS;
my %GLOBALSECTIONSHEADER;
$INPUT = @ARGV[0];
$OUTPUT = @ARGV[1];
@INPUTS = split /,/, $INPUT;
print "Merging ".($#INPUTS+1)." files: ".$INPUT."\\
";
print "Output: ".$OUTPUT."\\
";
print "\\
";
foreach my $file (@INPUTS)
{
&READ($file);
}
&WRITE($OUTPUT);
sub WRITE
{
open (OF, "> $OUTPUT");
print OF $HEADER."\\\r\\
";
foreach my $proj (sort keys %PROJECTS)
{
print OF $PROJECTS{$proj};
}
print OF "Global\\\r\\
";
foreach $glob (keys %GLOBALSECTIONS)
{
print OF $GLOBALSECTIONSHEADER{$glob};
my %tmp = map { $_ => 1 } split /\\\t/, $GLOBALSECTIONS{$glob};
foreach my $line (keys %tmp)
{
print OF $line;
}
print OF "EndGlobalSection\\\r\\
";
}
print OF "EndGlobal\\\r\\
";
close(OF);
}
sub READ
{
#local $\\\ = "\\\r\\
";
my $file = shift;
open (IF, "< $file");
my $root = $file;
$root =~ s/[^\\\\\\\\\\\/]+\\\\.sln(_deploy)?$//i;
while ($line = <IF>)
{
#$line =~ s/(\\\r|\\
)$//mg;
next if ($line =~ m/^(Microsoft Visual|# Visual Studio)/);
##
### SECTION: PROJECT
##
#Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "PGSA.Contracts"
if ($line =~ m/^\\\s*Project[^=]+=\\\s*"([^"]+)"/)
{
unless (exists($PROJECTS{lc($1)}))
{
$in_project = lc($1);
#replace file
$line =~ m/(^\\\s*Project[^=]+=\\\s*"[^"]+"\\\s*,\\\s*")([^"]+)(".*)/;
my $loc = $root.$2;
my $p1 = $1;
my $p2 = $3;
$loc =~ s/\\\//\\\\\\\/g;
$loc =~ s/\\\\\\\\\\\\\\\\+/\\\\\\\/g;
$loc =~ s/[^\\\\\\\\]+\\\\\\\\\\\\.\\\\.\\\\\\\//g;
$line = $p1.$loc.$p2;
}
}
$PROJECTS{$in_project} .= $line if ($in_project);
undef $in_project if ($line =~ m/^\\\s*EndProject/);
##
### END SECTION: PROJECT
##
##
### SECTION: GLOBALSECTION
##
if ($line =~ m/^\\\s*GlobalSection\\\\(([^\\\\)]+)\\\\)/)
{
$in_global = lc($1);
$GLOBALSECTIONSHEADER{$in_global} .= $line;
}
undef $in_global if ($line =~ m/^\\\s*EndGlobalSection/);
if ($in_global) {
unless ($in_global eq "teamfoundationversioncontrol")
{
$line =~ s/\\\t/ /g;
$GLOBALSECTIONS{$in_global} .= "\\\t".$line;
}
}
##
### END SECTION: GLOBALSECTION
##
}
close(IF);
}