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