1package Project; 2 3# 4# Package that encapsulates a Visual C++ project file generation 5# 6# src/tools/msvc/Project.pm 7# 8use Carp; 9use strict; 10use warnings; 11use File::Basename; 12 13sub _new 14{ 15 my ($classname, $name, $type, $solution) = @_; 16 my $good_types = { 17 lib => 1, 18 exe => 1, 19 dll => 1, 20 }; 21 confess("Bad project type: $type\n") unless exists $good_types->{$type}; 22 my $self = { 23 name => $name, 24 type => $type, 25 guid => $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE', 26 files => {}, 27 references => [], 28 libraries => [], 29 suffixlib => [], 30 includes => '', 31 prefixincludes => '', 32 defines => ';', 33 solution => $solution, 34 disablewarnings => '4018;4244;4273;4102;4090;4267', 35 disablelinkerwarnings => '', 36 platform => $solution->{platform}, 37 }; 38 39 bless($self, $classname); 40 return $self; 41} 42 43sub AddFile 44{ 45 my ($self, $filename) = @_; 46 47 $self->{files}->{$filename} = 1; 48 return; 49} 50 51sub AddFiles 52{ 53 my $self = shift; 54 my $dir = shift; 55 56 while (my $f = shift) 57 { 58 $self->{files}->{ $dir . "/" . $f } = 1; 59 } 60 return; 61} 62 63sub ReplaceFile 64{ 65 my ($self, $filename, $newname) = @_; 66 my $re = "\\/$filename\$"; 67 68 foreach my $file (keys %{ $self->{files} }) 69 { 70 71 # Match complete filename 72 if ($filename =~ m!/!) 73 { 74 if ($file eq $filename) 75 { 76 delete $self->{files}{$file}; 77 $self->{files}{$newname} = 1; 78 return; 79 } 80 } 81 elsif ($file =~ m/($re)/) 82 { 83 delete $self->{files}{$file}; 84 $self->{files}{"$newname/$filename"} = 1; 85 return; 86 } 87 } 88 confess("Could not find file $filename to replace\n"); 89} 90 91sub RemoveFile 92{ 93 my ($self, $filename) = @_; 94 my $orig = scalar keys %{ $self->{files} }; 95 delete $self->{files}->{$filename}; 96 if ($orig > scalar keys %{ $self->{files} }) 97 { 98 return; 99 } 100 confess("Could not find file $filename to remove\n"); 101} 102 103sub RelocateFiles 104{ 105 my ($self, $targetdir, $proc) = @_; 106 foreach my $f (keys %{ $self->{files} }) 107 { 108 my $r = &$proc($f); 109 if ($r) 110 { 111 $self->RemoveFile($f); 112 $self->AddFile($targetdir . '/' . basename($f)); 113 } 114 } 115 return; 116} 117 118sub AddReference 119{ 120 my $self = shift; 121 122 while (my $ref = shift) 123 { 124 push @{ $self->{references} }, $ref; 125 $self->AddLibrary( 126 "__CFGNAME__/" . $ref->{name} . "/" . $ref->{name} . ".lib"); 127 } 128 return; 129} 130 131sub AddLibrary 132{ 133 my ($self, $lib, $dbgsuffix) = @_; 134 135 # quote lib name if it has spaces and isn't already quoted 136 if ($lib =~ m/\s/ && $lib !~ m/^[&]quot;/) 137 { 138 $lib = '"' . $lib . """; 139 } 140 141 push @{ $self->{libraries} }, $lib; 142 if ($dbgsuffix) 143 { 144 push @{ $self->{suffixlib} }, $lib; 145 } 146 return; 147} 148 149sub AddIncludeDir 150{ 151 my ($self, $inc) = @_; 152 153 if ($self->{includes} ne '') 154 { 155 $self->{includes} .= ';'; 156 } 157 $self->{includes} .= $inc; 158 return; 159} 160 161sub AddPrefixInclude 162{ 163 my ($self, $inc) = @_; 164 165 $self->{prefixincludes} = $inc . ';' . $self->{prefixincludes}; 166 return; 167} 168 169sub AddDefine 170{ 171 my ($self, $def) = @_; 172 173 $def =~ s/"/""/g; 174 $self->{defines} .= $def . ';'; 175 return; 176} 177 178sub FullExportDLL 179{ 180 my ($self, $libname) = @_; 181 182 $self->{builddef} = 1; 183 $self->{def} = "./__CFGNAME__/$self->{name}/$self->{name}.def"; 184 $self->{implib} = "__CFGNAME__/$self->{name}/$libname"; 185 return; 186} 187 188sub UseDef 189{ 190 my ($self, $def) = @_; 191 192 $self->{def} = $def; 193 return; 194} 195 196sub AddDir 197{ 198 my ($self, $reldir) = @_; 199 my $mf = read_makefile($reldir); 200 201 $mf =~ s{\\\r?\n}{}g; 202 if ($mf =~ m{^(?:SUB)?DIRS[^=]*=\s*(.*)$}mg) 203 { 204 foreach my $subdir (split /\s+/, $1) 205 { 206 next 207 if $subdir eq "\$(top_builddir)/src/timezone" 208 ; #special case for non-standard include 209 next 210 if $reldir . "/" . $subdir eq "src/backend/port/darwin"; 211 212 $self->AddDir($reldir . "/" . $subdir); 213 } 214 } 215 while ($mf =~ m{^(?:EXTRA_)?OBJS[^=]*=\s*(.*)$}m) 216 { 217 my $s = $1; 218 my $filter_re = qr{\$\(filter ([^,]+),\s+\$\(([^\)]+)\)\)}; 219 while ($s =~ /$filter_re/) 220 { 221 222 # Process $(filter a b c, $(VAR)) expressions 223 my $list = $1; 224 my $filter = $2; 225 $list =~ s/\.o/\.c/g; 226 my @pieces = split /\s+/, $list; 227 my $matches = ""; 228 foreach my $p (@pieces) 229 { 230 231 if ($filter eq "LIBOBJS") 232 { 233 no warnings qw(once); 234 if (grep(/$p/, @main::pgportfiles) == 1) 235 { 236 $p =~ s/\.c/\.o/; 237 $matches .= $p . " "; 238 } 239 } 240 else 241 { 242 confess "Unknown filter $filter\n"; 243 } 244 } 245 $s =~ s/$filter_re/$matches/; 246 } 247 foreach my $f (split /\s+/, $s) 248 { 249 next if $f =~ /^\s*$/; 250 next if $f eq "\\"; 251 next if $f =~ /\/SUBSYS.o$/; 252 $f =~ s/,$// 253 ; # Remove trailing comma that can show up from filter stuff 254 next unless $f =~ /.*\.o$/; 255 $f =~ s/\.o$/\.c/; 256 if ($f =~ /^\$\(top_builddir\)\/(.*)/) 257 { 258 $f = $1; 259 $self->{files}->{$f} = 1; 260 } 261 else 262 { 263 $self->{files}->{"$reldir/$f"} = 1; 264 } 265 } 266 $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m; 267 } 268 269 # Match rules that pull in source files from different directories, eg 270 # pgstrcasecmp.c rint.c snprintf.c: % : $(top_srcdir)/src/port/% 271 my $replace_re = 272 qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+\n}m; 273 while ($mf =~ m{$replace_re}m) 274 { 275 my $match = $1; 276 my $top = $2; 277 my $target = $3; 278 my @pieces = split /\s+/, $match; 279 foreach my $fn (@pieces) 280 { 281 if ($top eq "(top_srcdir)") 282 { 283 eval { $self->ReplaceFile($fn, $target) }; 284 } 285 elsif ($top eq "(backend_src)") 286 { 287 eval { $self->ReplaceFile($fn, "src/backend/$target") }; 288 } 289 else 290 { 291 confess "Bad replacement top: $top, on line $_\n"; 292 } 293 } 294 $mf =~ s{$replace_re}{}m; 295 } 296 297 $self->AddDirResourceFile($reldir); 298 return; 299} 300 301# If the directory's Makefile bears a description string, add a resource file. 302sub AddDirResourceFile 303{ 304 my ($self, $reldir) = @_; 305 my $mf = read_makefile($reldir); 306 307 if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m) 308 { 309 my $desc = $1; 310 my $ico; 311 if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; } 312 $self->AddResourceFile($reldir, $desc, $ico); 313 } 314 return; 315} 316 317sub AddResourceFile 318{ 319 my ($self, $dir, $desc, $ico) = @_; 320 321 my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = 322 localtime(time); 323 my $d = sprintf("%02d%03d", ($year - 100), $yday); 324 325 if (Solution::IsNewer("$dir/win32ver.rc", 'src/port/win32ver.rc')) 326 { 327 print "Generating win32ver.rc for $dir\n"; 328 open(my $i, '<', 'src/port/win32ver.rc') 329 || confess "Could not open win32ver.rc"; 330 open(my $o, '>', "$dir/win32ver.rc") 331 || confess "Could not write win32ver.rc"; 332 my $icostr = $ico ? "IDI_ICON ICON \"src/port/$ico.ico\"" : ""; 333 while (<$i>) 334 { 335 s/FILEDESC/"$desc"/gm; 336 s/_ICO_/$icostr/gm; 337 s/(VERSION.*),0/$1,$d/; 338 if ($self->{type} eq "dll") 339 { 340 s/VFT_APP/VFT_DLL/gm; 341 my $name = $self->{name}; 342 s/_INTERNAL_NAME_/"$name"/; 343 s/_ORIGINAL_NAME_/"$name.dll"/; 344 } 345 else 346 { 347 /_INTERNAL_NAME_/ && next; 348 /_ORIGINAL_NAME_/ && next; 349 } 350 print $o $_; 351 } 352 close($o); 353 close($i); 354 } 355 $self->AddFile("$dir/win32ver.rc"); 356 return; 357} 358 359sub DisableLinkerWarnings 360{ 361 my ($self, $warnings) = @_; 362 363 $self->{disablelinkerwarnings} .= ',' 364 unless ($self->{disablelinkerwarnings} eq ''); 365 $self->{disablelinkerwarnings} .= $warnings; 366 return; 367} 368 369sub Save 370{ 371 my ($self) = @_; 372 373 # If doing DLL and haven't specified a DEF file, do a full export of all symbols 374 # in the project. 375 if ($self->{type} eq "dll" && !$self->{def}) 376 { 377 $self->FullExportDLL($self->{name} . ".lib"); 378 } 379 380 # Warning 4197 is about double exporting, disable this per 381 # http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99193 382 $self->DisableLinkerWarnings('4197') if ($self->{platform} eq 'x64'); 383 384 # Dump the project 385 open(my $f, '>', "$self->{name}$self->{filenameExtension}") 386 || croak( 387 "Could not write to $self->{name}$self->{filenameExtension}\n"); 388 $self->WriteHeader($f); 389 $self->WriteFiles($f); 390 $self->Footer($f); 391 close($f); 392 return; 393} 394 395sub GetAdditionalLinkerDependencies 396{ 397 my ($self, $cfgname, $separator) = @_; 398 my $libcfg = (uc $cfgname eq "RELEASE") ? "MD" : "MDd"; 399 my $libs = ''; 400 foreach my $lib (@{ $self->{libraries} }) 401 { 402 my $xlib = $lib; 403 foreach my $slib (@{ $self->{suffixlib} }) 404 { 405 if ($slib eq $lib) 406 { 407 $xlib =~ s/\.lib$/$libcfg.lib/; 408 last; 409 } 410 } 411 $libs .= $xlib . $separator; 412 } 413 $libs =~ s/.$//; 414 $libs =~ s/__CFGNAME__/$cfgname/g; 415 return $libs; 416} 417 418# Utility function that loads a complete file 419sub read_file 420{ 421 my $filename = shift; 422 my $F; 423 local $/ = undef; 424 open($F, '<', $filename) || croak "Could not open file $filename\n"; 425 my $txt = <$F>; 426 close($F); 427 428 return $txt; 429} 430 431sub read_makefile 432{ 433 my $reldir = shift; 434 my $F; 435 local $/ = undef; 436 open($F, '<', "$reldir/GNUmakefile") 437 || open($F, '<', "$reldir/Makefile") 438 || confess "Could not open $reldir/Makefile\n"; 439 my $txt = <$F>; 440 close($F); 441 442 return $txt; 443} 444 4451; 446