Team Fortress 2 Source Code as on 22/4/2020
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

  1. #!perl
  2. use XML::Simple;
  3. use Data::Dumper;
  4. use Getopt::Long;
  5. use File::Basename;
  6. use Cwd 'abs_path';
  7. use Cwd;
  8. use Digest::MD5 qw(md5 md5_hex md5_base64);
  9. GetOptions( "verbose"=>\$verbose,
  10. "projlist"=>\$projlist,
  11. "x360"=>\$x360 );
  12. $sln_name=shift || &PrintArgumentSummaryAndExit;
  13. my $curdir=cwd;
  14. $curdir=~ (m@(^.*/[a-z_]*src[0-9]?)@i) || die "Can't determine srcroot from current directory $curdir";
  15. $srcroot=lc($1);
  16. $linker_tool_name="VCLinkerTool";
  17. $linker_tool_name="VCX360LinkerTool" if ($x360);
  18. @output_only_projects_dependent_upon = ();
  19. &ReadVPCProjects;
  20. if ( $sln_name =~ /^\@(\S+)/)
  21. {
  22. $sln_name=$1;
  23. &ReadGroup($1);
  24. foreach $proj (@PROJS)
  25. {
  26. &AddProject(lc(abs_path($proj)),1);
  27. }
  28. if ( $projlist )
  29. {
  30. &WriteProjectListFile( $sln_name );
  31. }
  32. else
  33. {
  34. &WriteSolutionFile($sln_name);
  35. }
  36. }
  37. else
  38. {
  39. # normal mode
  40. while($_ = shift )
  41. {
  42. if ( /^\@(\S+)/)
  43. {
  44. # accept group names
  45. &ReadGroup($1);
  46. foreach $proj (@PROJS)
  47. {
  48. &AddProject(lc(abs_path($proj)),1);
  49. }
  50. }
  51. elsif ( /^\*(\S+)/)
  52. {
  53. push( @output_only_projects_dependent_upon, lc( $1 ) );
  54. &ReadGroup("everything");
  55. foreach $proj (@PROJS)
  56. {
  57. &AddProject(lc(abs_path($proj)),0);
  58. }
  59. }
  60. else
  61. {
  62. unless(/\.vcproj/) # if no extension specified, assume its a project name from projects.vgc
  63. {
  64. $_=$projpath{lc($_)} if length($projpath{lc($_)});
  65. }
  66. foreach $path (split(/\s+/,$_))
  67. {
  68. $path=~s@\s+@@g;
  69. &AddProject(lc(abs_path($path)),1) if length($path);
  70. }
  71. }
  72. }
  73. if ( $projlist )
  74. {
  75. &WriteProjectListFile( $sln_name );
  76. }
  77. else
  78. {
  79. &WriteSolutionFile($sln_name);
  80. }
  81. }
  82. sub WriteSolutionFile
  83. {
  84. local($sln)=@_;
  85. $sln="$sln.sln" unless ( $sln=~/\./); # add extension if needed
  86. if ( ( -e $sln && ( ! ( -w $sln ) ) ) )
  87. {
  88. print STDERR "$sln is write-protected. Doing p4 edit.\n";
  89. print `p4 edit $sln`;
  90. die "Failed to make $sln writeable" if ( ! ( -w $sln ) );
  91. }
  92. open(SLN,">$sln" ) || die "can't open output $sln";
  93. # generate a guid for the sln
  94. my $sln_guid="8BC9CEB8-8B4A-11D0-8D11-".substr(uc(md5_hex(basename($sln))),0,12);
  95. print SLN "\xef\xbb\xbf\nMicrosoft Visual Studio Solution File, Format Version 9.00\n# Visual Studio 2005\n";
  96. foreach $proj (@PROJECTS)
  97. {
  98. # check dependencies for "*" projects
  99. if ( (! length( $force_project_inclusion{$proj} ) ) &&
  100. ( @output_only_projects_dependent_upon ))
  101. {
  102. my $skip_it = 1;
  103. foreach $output_only ( @output_only_projects_dependent_upon )
  104. {
  105. $skip_it = 0 if ( $output_only eq lc( $proj ) );
  106. foreach $lib (split(/,/,$depends_on{$proj}))
  107. {
  108. $skip_it = 0 if ( $output_only eq lc($lib) );
  109. foreach $prvd (split(/,/,$provider{$lib}))
  110. {
  111. $skip_it = 0 if ( $output_only eq $prvd );
  112. }
  113. }
  114. }
  115. next if ( $skip_it );
  116. }
  117. print SLN "Project(\"{",$sln_guid,"}\") = \"$proj\", \"$relpath{$proj}\", \"$guid{$proj}\"\n";
  118. #, now do dependencies
  119. if ( length($depends_on{$proj} ) )
  120. {
  121. print SLN "\tProjectSection(ProjectDependencies) = postProject\n";
  122. foreach $lib (split(/,/,$depends_on{$proj}))
  123. {
  124. if ( length($provider{$lib}) )
  125. {
  126. foreach $prvd (split(/,/,$provider{$lib}))
  127. {
  128. print SLN "\t\t$guid{$prvd} = $guid{$prvd}\n" if ( length($prvd) );
  129. }
  130. }
  131. else
  132. {
  133. print "I don't know who provides $lib for $proj\n" if ( $verbose && length($lib) );
  134. }
  135. }
  136. print SLN "\tEndProjectSection\n";
  137. }
  138. print SLN "EndProject\n";
  139. }
  140. print SLN "Global\n";
  141. print SLN "\tGlobalSection(SolutionProperties) = preSolution\n";
  142. print SLN "\t\tHideSolutionNode = FALSE\n";
  143. print SLN "\tEndGlobalSection\n";
  144. print SLN "EndGlobal\n";
  145. close SLN;
  146. }
  147. sub WriteProjectListFile
  148. {
  149. local($txtfile) = @_;
  150. $txtfile = "$txtfile.txt" unless ( $txtfile=~/\./); # add extension if needed
  151. open(SLN,">$txtfile" ) || die "can't open output $txtfile";
  152. foreach $proj (@PROJECTS)
  153. {
  154. # check dependencies for "*" projects
  155. if ( (! length( $force_project_inclusion{$proj} ) ) &&
  156. ( @output_only_projects_dependent_upon ))
  157. {
  158. my $skip_it = 1;
  159. foreach $output_only ( @output_only_projects_dependent_upon )
  160. {
  161. $skip_it = 0 if ( $output_only eq lc( $proj ) );
  162. foreach $lib (split(/,/,$depends_on{$proj}))
  163. {
  164. $skip_it = 0 if ( $output_only eq lc($lib) );
  165. foreach $prvd (split(/,/,$provider{$lib}))
  166. {
  167. $skip_it = 0 if ( $output_only eq $prvd );
  168. }
  169. }
  170. }
  171. next if ( $skip_it );
  172. }
  173. push @plist, $proj;
  174. }
  175. # now, we need to satisfy all dependencies
  176. while( $#plist >= 0)
  177. {
  178. @worklist=@plist;
  179. undef @plist;
  180. PROJECT: foreach $proj( @worklist )
  181. {
  182. if ( length($depends_on{$proj} ) )
  183. {
  184. foreach $lib (split(/,/,$depends_on{$proj}))
  185. {
  186. if ( length($provider{$lib}) )
  187. {
  188. foreach $prvd (split(/,/,$provider{$lib}))
  189. {
  190. if ( length( $prvd ) && ( !$already_did{$prvd} ) )
  191. {
  192. push @plist, $proj; # can't do it yet
  193. next PROJECT;
  194. }
  195. }
  196. }
  197. }
  198. }
  199. $already_did{$proj} = 1;
  200. print SLN "$relpath{$proj}\n";
  201. }
  202. }
  203. close SLN;
  204. }
  205. sub PrintArgumentSummaryAndExit
  206. {
  207. print "Format of command is\n";
  208. my $switches="[ -projlist -verbose -x360 ]";
  209. print "\t MKSLN $switches <solutionname.sln > proj1.vcproj proj2.vcproj ...\n";
  210. print "OR\t MKSLN $switches \@vpcgroupname\n";
  211. print "OR\t MKSLN $switches sln_name \@vpcgroupname\n";
  212. print "OR\t MKSLN $switches sln_name *project create a solution including only that project and things dependent on it.\n";
  213. }
  214. sub AddProject
  215. {
  216. local($fname, $force )=@_;
  217. local($/);
  218. print "add project $fname\n" if ( $verbose );
  219. open( VCP_IN, $fname ) || die "can't open $fname";
  220. my $xmltext=<VCP_IN>;
  221. close VCP_IN;
  222. my $xml=XMLin($xmltext, forcearray => [ 'File', 'Filter' ] ); #, keyattr =>[ 'name', 'key', 'id', 'Name']);
  223. my $pname=lc($xml->{Name});
  224. $force_project_inclusion{$pname} = "yes" if ( $force );
  225. return if ($already_processed{$pname} );
  226. $already_processed{$pname}=1;
  227. my $id=$xml->{ProjectGUID};
  228. unless( length($id) )
  229. {
  230. die "project $fname doesn't have a guid. Generated by an old VPC?";
  231. }
  232. $id = "{".$id."}" unless( $id=~ /}/);
  233. $guid{$pname}=$id;
  234. push @PROJECTS,$pname;
  235. $vcprojpath{$pname}=$fname;
  236. #get targetname
  237. my $targetname=$xml->{Name};
  238. # get output target
  239. my $tools=$xml->{Configurations}->{Configuration}[0];
  240. # walk the tool list to see if this project outpus a .lib that something might depend on
  241. my $outputtarget;
  242. foreach $tool (@{$tools->{'Tool'}})
  243. {
  244. if ( $tool->{Name} eq "VCLibrarianTool" )
  245. {
  246. my $outputtarget=lc(basename($tool->{OutputFile}));
  247. $provider{$outputtarget}.=",$pname";
  248. print "$pname provides $outputtarget\n" if ($verbose);
  249. }
  250. if ( $tool->{Name} eq $linker_tool_name )
  251. {
  252. my $outputtarget=$tool->{'ImportLibrary'};
  253. if ( length($outputtarget) )
  254. {
  255. $outputtarget=~s/\$\(TargetName\)/$targetname/i;
  256. $outputtarget=lc(basename($outputtarget));
  257. $outputtarget =~ s/\.lib/_360.lib/ if ( $x360 );
  258. $provider{$outputtarget}=basename($pname);
  259. print "$pname provides $outputtarget\n" if ($verbose);
  260. }
  261. }
  262. }
  263. foreach $filter (@{$xml->{Files}->{Filter}})
  264. {
  265. foreach $file (@{$filter->{File}})
  266. {
  267. my $f = lc($file->{RelativePath});
  268. if ( $f=~/\.lib$/i) # library dependency
  269. {
  270. my $libname=basename($f);
  271. $depends_on{$pname}.=",".$libname;
  272. print "$pname depends on $libname\n" if ($verbose);
  273. }
  274. }
  275. }
  276. # generate relative pathname
  277. $fname=~s@^$srcroot/@@i;
  278. $fname=~s@/@\\@g;
  279. $relpath{$pname}=$fname;
  280. }
  281. sub ReadGroup
  282. {
  283. local($matchgroup)=@_;
  284. my $curmatch=0;
  285. open(GROUPS,"$srcroot/vpc_scripts/groups.vgc") || die "can't open groups.vgc";
  286. while(<GROUPS>)
  287. {
  288. &FixupVPCLine;
  289. if (/^\$Group\s+(.*)$/)
  290. {
  291. my $groups=" $1 ";
  292. $groups=~s@\"@@g;
  293. $curmatch=0;
  294. $curmatch=1 if ( $groups=~/ $matchgroup /i );
  295. }
  296. elsif ( $curmatch && (/^\s*\"([^\"]+)\"/) )
  297. {
  298. my $proj=lc($1);
  299. my $path=$projpath{$proj};
  300. if (length($path))
  301. {
  302. foreach $prj (split(/\s+/, $path))
  303. {
  304. $prj=~s@\s+@@g;
  305. next unless (length($prj));
  306. if ( -e $prj )
  307. {
  308. push @PROJS,$prj;
  309. print "found proj $prj\n" if ($verbose);
  310. }
  311. else
  312. {
  313. print STDERR "can't find $prj\n";
  314. }
  315. }
  316. }
  317. else
  318. {
  319. print STDERR "couldn't find project name $proj (group = $matchgroup )\n";
  320. }
  321. }
  322. else
  323. {
  324. $curmatch = 0 if (/\}/);
  325. }
  326. }
  327. }
  328. sub ReadVPCProjects
  329. {
  330. # group mode. ugh 100x harder to parse vpc than .vcproj
  331. open(PROJS,"$srcroot/vpc_scripts/projects.vgc" ) || die "can't open projects.vgc";
  332. while(<PROJS>)
  333. {
  334. &FixupVPCLine;
  335. if (/^\s*\$Project\s+\"([^\"]+)\"/)
  336. {
  337. $curproj=$1;
  338. }
  339. elsif (/^\s*\"([^\"]+)\.vpc\"/)
  340. {
  341. my $base = $1;
  342. $base="$base"."_x360" if ( $x360 );
  343. $projpath{lc($curproj)}.=" $base.vcproj";
  344. }
  345. }
  346. close PROJS;
  347. }
  348. sub FixupVPCLine
  349. {
  350. s@[\n\r]@@g;
  351. s@//.*$@@g; # kill comments
  352. # use [] skips. need something smarter here. for now, implicit /allgames except hl1 and portalmp
  353. $_=undef if ( /\$HL1/);
  354. $_=undef if ( /\$PORTALMP/);
  355. }