You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
8.9 KiB
388 lines
8.9 KiB
#!perl |
|
use XML::Simple; |
|
use Data::Dumper; |
|
use Getopt::Long; |
|
use File::Basename; |
|
use Cwd 'abs_path'; |
|
use Cwd; |
|
use Digest::MD5 qw(md5 md5_hex md5_base64); |
|
|
|
|
|
GetOptions( "verbose"=>\$verbose, |
|
"projlist"=>\$projlist, |
|
"x360"=>\$x360 ); |
|
|
|
$sln_name=shift || &PrintArgumentSummaryAndExit; |
|
|
|
my $curdir=cwd; |
|
|
|
$curdir=~ (m@(^.*/[a-z_]*src[0-9]?)@i) || die "Can't determine srcroot from current directory $curdir"; |
|
|
|
$srcroot=lc($1); |
|
|
|
$linker_tool_name="VCLinkerTool"; |
|
$linker_tool_name="VCX360LinkerTool" if ($x360); |
|
|
|
@output_only_projects_dependent_upon = (); |
|
|
|
&ReadVPCProjects; |
|
if ( $sln_name =~ /^\@(\S+)/) |
|
{ |
|
$sln_name=$1; |
|
&ReadGroup($1); |
|
foreach $proj (@PROJS) |
|
{ |
|
&AddProject(lc(abs_path($proj)),1); |
|
} |
|
if ( $projlist ) |
|
{ |
|
&WriteProjectListFile( $sln_name ); |
|
} |
|
else |
|
{ |
|
&WriteSolutionFile($sln_name); |
|
} |
|
} |
|
else |
|
{ |
|
# normal mode |
|
while($_ = shift ) |
|
{ |
|
if ( /^\@(\S+)/) |
|
{ |
|
# accept group names |
|
&ReadGroup($1); |
|
foreach $proj (@PROJS) |
|
{ |
|
&AddProject(lc(abs_path($proj)),1); |
|
} |
|
} |
|
elsif ( /^\*(\S+)/) |
|
{ |
|
push( @output_only_projects_dependent_upon, lc( $1 ) ); |
|
&ReadGroup("everything"); |
|
foreach $proj (@PROJS) |
|
{ |
|
&AddProject(lc(abs_path($proj)),0); |
|
} |
|
} |
|
else |
|
{ |
|
unless(/\.vcproj/) # if no extension specified, assume its a project name from projects.vgc |
|
{ |
|
$_=$projpath{lc($_)} if length($projpath{lc($_)}); |
|
} |
|
foreach $path (split(/\s+/,$_)) |
|
{ |
|
$path=~s@\s+@@g; |
|
&AddProject(lc(abs_path($path)),1) if length($path); |
|
} |
|
} |
|
} |
|
if ( $projlist ) |
|
{ |
|
&WriteProjectListFile( $sln_name ); |
|
} |
|
else |
|
{ |
|
&WriteSolutionFile($sln_name); |
|
} |
|
} |
|
|
|
|
|
sub WriteSolutionFile |
|
{ |
|
local($sln)=@_; |
|
$sln="$sln.sln" unless ( $sln=~/\./); # add extension if needed |
|
if ( ( -e $sln && ( ! ( -w $sln ) ) ) ) |
|
{ |
|
print STDERR "$sln is write-protected. Doing p4 edit.\n"; |
|
print `p4 edit $sln`; |
|
die "Failed to make $sln writeable" if ( ! ( -w $sln ) ); |
|
} |
|
open(SLN,">$sln" ) || die "can't open output $sln"; |
|
# generate a guid for the sln |
|
my $sln_guid="8BC9CEB8-8B4A-11D0-8D11-".substr(uc(md5_hex(basename($sln))),0,12); |
|
|
|
print SLN "\xef\xbb\xbf\nMicrosoft Visual Studio Solution File, Format Version 9.00\n# Visual Studio 2005\n"; |
|
foreach $proj (@PROJECTS) |
|
{ |
|
# check dependencies for "*" projects |
|
if ( (! length( $force_project_inclusion{$proj} ) ) && |
|
( @output_only_projects_dependent_upon )) |
|
{ |
|
my $skip_it = 1; |
|
foreach $output_only ( @output_only_projects_dependent_upon ) |
|
{ |
|
$skip_it = 0 if ( $output_only eq lc( $proj ) ); |
|
foreach $lib (split(/,/,$depends_on{$proj})) |
|
{ |
|
$skip_it = 0 if ( $output_only eq lc($lib) ); |
|
foreach $prvd (split(/,/,$provider{$lib})) |
|
{ |
|
$skip_it = 0 if ( $output_only eq $prvd ); |
|
} |
|
} |
|
} |
|
next if ( $skip_it ); |
|
} |
|
|
|
print SLN "Project(\"{",$sln_guid,"}\") = \"$proj\", \"$relpath{$proj}\", \"$guid{$proj}\"\n"; |
|
|
|
#, now do dependencies |
|
if ( length($depends_on{$proj} ) ) |
|
{ |
|
print SLN "\tProjectSection(ProjectDependencies) = postProject\n"; |
|
foreach $lib (split(/,/,$depends_on{$proj})) |
|
{ |
|
if ( length($provider{$lib}) ) |
|
{ |
|
foreach $prvd (split(/,/,$provider{$lib})) |
|
{ |
|
print SLN "\t\t$guid{$prvd} = $guid{$prvd}\n" if ( length($prvd) ); |
|
} |
|
} |
|
else |
|
{ |
|
print "I don't know who provides $lib for $proj\n" if ( $verbose && length($lib) ); |
|
} |
|
|
|
} |
|
print SLN "\tEndProjectSection\n"; |
|
} |
|
print SLN "EndProject\n"; |
|
} |
|
|
|
print SLN "Global\n"; |
|
print SLN "\tGlobalSection(SolutionProperties) = preSolution\n"; |
|
print SLN "\t\tHideSolutionNode = FALSE\n"; |
|
print SLN "\tEndGlobalSection\n"; |
|
print SLN "EndGlobal\n"; |
|
close SLN; |
|
} |
|
|
|
sub WriteProjectListFile |
|
{ |
|
local($txtfile) = @_; |
|
$txtfile = "$txtfile.txt" unless ( $txtfile=~/\./); # add extension if needed |
|
open(SLN,">$txtfile" ) || die "can't open output $txtfile"; |
|
|
|
foreach $proj (@PROJECTS) |
|
{ |
|
# check dependencies for "*" projects |
|
if ( (! length( $force_project_inclusion{$proj} ) ) && |
|
( @output_only_projects_dependent_upon )) |
|
{ |
|
my $skip_it = 1; |
|
foreach $output_only ( @output_only_projects_dependent_upon ) |
|
{ |
|
$skip_it = 0 if ( $output_only eq lc( $proj ) ); |
|
foreach $lib (split(/,/,$depends_on{$proj})) |
|
{ |
|
$skip_it = 0 if ( $output_only eq lc($lib) ); |
|
foreach $prvd (split(/,/,$provider{$lib})) |
|
{ |
|
$skip_it = 0 if ( $output_only eq $prvd ); |
|
} |
|
} |
|
} |
|
next if ( $skip_it ); |
|
} |
|
|
|
push @plist, $proj; |
|
} |
|
# now, we need to satisfy all dependencies |
|
while( $#plist >= 0) |
|
{ |
|
@worklist=@plist; |
|
undef @plist; |
|
PROJECT: foreach $proj( @worklist ) |
|
{ |
|
if ( length($depends_on{$proj} ) ) |
|
{ |
|
foreach $lib (split(/,/,$depends_on{$proj})) |
|
{ |
|
if ( length($provider{$lib}) ) |
|
{ |
|
foreach $prvd (split(/,/,$provider{$lib})) |
|
{ |
|
if ( length( $prvd ) && ( !$already_did{$prvd} ) ) |
|
{ |
|
push @plist, $proj; # can't do it yet |
|
next PROJECT; |
|
} |
|
} |
|
} |
|
} |
|
} |
|
$already_did{$proj} = 1; |
|
print SLN "$relpath{$proj}\n"; |
|
} |
|
} |
|
close SLN; |
|
} |
|
|
|
sub PrintArgumentSummaryAndExit |
|
{ |
|
print "Format of command is\n"; |
|
my $switches="[ -projlist -verbose -x360 ]"; |
|
print "\t MKSLN $switches <solutionname.sln > proj1.vcproj proj2.vcproj ...\n"; |
|
print "OR\t MKSLN $switches \@vpcgroupname\n"; |
|
print "OR\t MKSLN $switches sln_name \@vpcgroupname\n"; |
|
print "OR\t MKSLN $switches sln_name *project create a solution including only that project and things dependent on it.\n"; |
|
} |
|
|
|
|
|
sub AddProject |
|
{ |
|
local($fname, $force )=@_; |
|
local($/); |
|
print "add project $fname\n" if ( $verbose ); |
|
open( VCP_IN, $fname ) || die "can't open $fname"; |
|
my $xmltext=<VCP_IN>; |
|
close VCP_IN; |
|
my $xml=XMLin($xmltext, forcearray => [ 'File', 'Filter' ] ); #, keyattr =>[ 'name', 'key', 'id', 'Name']); |
|
my $pname=lc($xml->{Name}); |
|
$force_project_inclusion{$pname} = "yes" if ( $force ); |
|
return if ($already_processed{$pname} ); |
|
$already_processed{$pname}=1; |
|
my $id=$xml->{ProjectGUID}; |
|
unless( length($id) ) |
|
{ |
|
die "project $fname doesn't have a guid. Generated by an old VPC?"; |
|
} |
|
$id = "{".$id."}" unless( $id=~ /}/); |
|
$guid{$pname}=$id; |
|
push @PROJECTS,$pname; |
|
$vcprojpath{$pname}=$fname; |
|
|
|
#get targetname |
|
my $targetname=$xml->{Name}; |
|
# get output target |
|
|
|
my $tools=$xml->{Configurations}->{Configuration}[0]; |
|
# walk the tool list to see if this project outpus a .lib that something might depend on |
|
my $outputtarget; |
|
foreach $tool (@{$tools->{'Tool'}}) |
|
{ |
|
if ( $tool->{Name} eq "VCLibrarianTool" ) |
|
{ |
|
my $outputtarget=lc(basename($tool->{OutputFile})); |
|
$provider{$outputtarget}.=",$pname"; |
|
print "$pname provides $outputtarget\n" if ($verbose); |
|
} |
|
if ( $tool->{Name} eq $linker_tool_name ) |
|
{ |
|
my $outputtarget=$tool->{'ImportLibrary'}; |
|
if ( length($outputtarget) ) |
|
{ |
|
$outputtarget=~s/\$\(TargetName\)/$targetname/i; |
|
$outputtarget=lc(basename($outputtarget)); |
|
$outputtarget =~ s/\.lib/_360.lib/ if ( $x360 ); |
|
$provider{$outputtarget}=basename($pname); |
|
print "$pname provides $outputtarget\n" if ($verbose); |
|
} |
|
} |
|
} |
|
|
|
foreach $filter (@{$xml->{Files}->{Filter}}) |
|
{ |
|
foreach $file (@{$filter->{File}}) |
|
{ |
|
my $f = lc($file->{RelativePath}); |
|
if ( $f=~/\.lib$/i) # library dependency |
|
{ |
|
my $libname=basename($f); |
|
$depends_on{$pname}.=",".$libname; |
|
print "$pname depends on $libname\n" if ($verbose); |
|
} |
|
} |
|
} |
|
# generate relative pathname |
|
$fname=~s@^$srcroot/@@i; |
|
$fname=~s@/@\\@g; |
|
$relpath{$pname}=$fname; |
|
|
|
} |
|
|
|
sub ReadGroup |
|
{ |
|
local($matchgroup)=@_; |
|
my $curmatch=0; |
|
open(GROUPS,"$srcroot/vpc_scripts/groups.vgc") || die "can't open groups.vgc"; |
|
while(<GROUPS>) |
|
{ |
|
&FixupVPCLine; |
|
if (/^\$Group\s+(.*)$/) |
|
{ |
|
my $groups=" $1 "; |
|
$groups=~s@\"@@g; |
|
$curmatch=0; |
|
$curmatch=1 if ( $groups=~/ $matchgroup /i ); |
|
} |
|
elsif ( $curmatch && (/^\s*\"([^\"]+)\"/) ) |
|
{ |
|
my $proj=lc($1); |
|
my $path=$projpath{$proj}; |
|
if (length($path)) |
|
{ |
|
foreach $prj (split(/\s+/, $path)) |
|
{ |
|
$prj=~s@\s+@@g; |
|
next unless (length($prj)); |
|
if ( -e $prj ) |
|
{ |
|
push @PROJS,$prj; |
|
print "found proj $prj\n" if ($verbose); |
|
} |
|
else |
|
{ |
|
print STDERR "can't find $prj\n"; |
|
} |
|
|
|
} |
|
} |
|
else |
|
{ |
|
print STDERR "couldn't find project name $proj (group = $matchgroup )\n"; |
|
} |
|
} |
|
else |
|
{ |
|
$curmatch = 0 if (/\}/); |
|
} |
|
|
|
} |
|
} |
|
|
|
sub ReadVPCProjects |
|
{ |
|
# group mode. ugh 100x harder to parse vpc than .vcproj |
|
open(PROJS,"$srcroot/vpc_scripts/projects.vgc" ) || die "can't open projects.vgc"; |
|
while(<PROJS>) |
|
{ |
|
&FixupVPCLine; |
|
if (/^\s*\$Project\s+\"([^\"]+)\"/) |
|
{ |
|
$curproj=$1; |
|
} |
|
elsif (/^\s*\"([^\"]+)\.vpc\"/) |
|
{ |
|
my $base = $1; |
|
$base="$base"."_x360" if ( $x360 ); |
|
$projpath{lc($curproj)}.=" $base.vcproj"; |
|
} |
|
} |
|
close PROJS; |
|
} |
|
|
|
sub FixupVPCLine |
|
{ |
|
s@[\n\r]@@g; |
|
s@//.*$@@g; # kill comments |
|
|
|
# use [] skips. need something smarter here. for now, implicit /allgames except hl1 and portalmp |
|
$_=undef if ( /\$HL1/); |
|
$_=undef if ( /\$PORTALMP/); |
|
} |
|
|
|
|