|
| 1 | +package Project; |
| 2 | + |
| 3 | +use Carp; |
| 4 | +use strict; |
| 5 | +use warnings; |
| 6 | + |
| 7 | +sub new { |
| 8 | + my ($junk, $name, $type, $solution) = @_; |
| 9 | + my $good_types = { |
| 10 | + lib => 1, |
| 11 | + exe => 1, |
| 12 | + dll => 1, |
| 13 | + }; |
| 14 | + confess("Bad project type: $type\n") unless exists $good_types->{$type}; |
| 15 | + my $self = { |
| 16 | + name => $name, |
| 17 | + type => $type, |
| 18 | + guid => Win32::GuidGen(), |
| 19 | + files => {}, |
| 20 | + references => [], |
| 21 | + libraries => '', |
| 22 | + includes => '', |
| 23 | + defines => ';', |
| 24 | + solution => $solution, |
| 25 | + disablewarnings => '4018;4244', |
| 26 | + }; |
| 27 | + |
| 28 | + bless $self; |
| 29 | + return $self; |
| 30 | +} |
| 31 | + |
| 32 | +sub AddFile { |
| 33 | + my ($self, $filename) = @_; |
| 34 | + |
| 35 | + $self->{files}->{$filename} = 1; |
| 36 | +} |
| 37 | + |
| 38 | +sub AddFiles { |
| 39 | + my $self = shift; |
| 40 | + my $dir = shift; |
| 41 | + |
| 42 | + while (my $f = shift) { |
| 43 | + $self->{files}->{$dir . "\\" . $f} = 1; |
| 44 | + } |
| 45 | +} |
| 46 | + |
| 47 | +sub ReplaceFile { |
| 48 | + my ($self, $filename, $newname) = @_; |
| 49 | + my $re = "\\\\$filename\$"; |
| 50 | + |
| 51 | + foreach my $file ( keys %{ $self->{files} } ) { |
| 52 | + # Match complete filename |
| 53 | + if ($filename =~ /\\/) { |
| 54 | + if ($file eq $filename) { |
| 55 | + delete $self->{files}{$file}; |
| 56 | + $self->{files}{$newname} = 1; |
| 57 | + return; |
| 58 | + } |
| 59 | + } |
| 60 | + elsif ($file =~ m/($re)/) { |
| 61 | + delete $self->{files}{$file}; |
| 62 | + $self->{files}{ "$newname\\$filename" } = 1; |
| 63 | + return; |
| 64 | + } |
| 65 | + } |
| 66 | + confess("Could not find file $filename to replace\n"); |
| 67 | +} |
| 68 | + |
| 69 | +sub RemoveFile { |
| 70 | + my ($self, $filename) = @_; |
| 71 | + my $orig = scalar keys %{ $self->{files} }; |
| 72 | + delete $self->{files}->{$filename}; |
| 73 | + if ($orig > scalar keys %{$self->{files}} ) { |
| 74 | + return; |
| 75 | + } |
| 76 | + confess("Could not find file $filename to remove\n"); |
| 77 | +} |
| 78 | + |
| 79 | +sub AddReference { |
| 80 | + my $self = shift; |
| 81 | + |
| 82 | + while (my $ref = shift) { |
| 83 | + push @{$self->{references}},$ref; |
| 84 | + $self->AddLibrary("debug\\" . $ref->{name} . "\\" . $ref->{name} . ".lib") if ($ref->{type} eq "exe"); |
| 85 | + } |
| 86 | +} |
| 87 | + |
| 88 | +sub AddLibrary { |
| 89 | + my ($self, $lib) = @_; |
| 90 | + |
| 91 | + if ($self->{libraries} ne '') { |
| 92 | + $self->{libraries} .= ' '; |
| 93 | + } |
| 94 | + $self->{libraries} .= $lib; |
| 95 | +} |
| 96 | + |
| 97 | +sub AddIncludeDir { |
| 98 | + my ($self, $inc) = @_; |
| 99 | + |
| 100 | + if ($self->{includes} ne '') { |
| 101 | + $self->{includes} .= ';'; |
| 102 | + } |
| 103 | + $self->{includes} .= $inc; |
| 104 | +} |
| 105 | + |
| 106 | +sub AddDefine { |
| 107 | + my ($self, $def) = @_; |
| 108 | + |
| 109 | + $self->{defines} .= $def . ';'; |
| 110 | +} |
| 111 | + |
| 112 | +sub FullExportDLL { |
| 113 | + my ($self, $libname) = @_; |
| 114 | + |
| 115 | + $self->{builddef} = 1; |
| 116 | + $self->{def} = ".\\debug\\$self->{name}\\$self->{name}.def"; |
| 117 | + $self->{implib} = "debug\\$self->{name}\\$libname"; |
| 118 | +} |
| 119 | + |
| 120 | +sub UseDef { |
| 121 | + my ($self, $def) = @_; |
| 122 | + |
| 123 | + $self->{def} = $def; |
| 124 | +} |
| 125 | + |
| 126 | +sub AddDir { |
| 127 | + my ($self, $reldir) = @_; |
| 128 | + my $MF; |
| 129 | + |
| 130 | + my $t = $/;undef $/; |
| 131 | + open($MF,"$reldir\\Makefile") || open($MF,"$reldir\\GNUMakefile") || croak "Could not open $reldir\\Makefile\n"; |
| 132 | + my $mf = <$MF>; |
| 133 | + close($MF); |
| 134 | + |
| 135 | + $mf =~ s{\\\s*[\r\n]+}{}mg; |
| 136 | + if ($mf =~ m{^(?:SUB)?DIRS[^=]*=\s*(.*)$}mg) { |
| 137 | + foreach my $subdir (split /\s+/,$1) { |
| 138 | + next if $subdir eq "\$(top_builddir)/src/timezone"; #special case for non-standard include |
| 139 | + $self->AddDir($reldir . "\\" . $subdir); |
| 140 | + } |
| 141 | + } |
| 142 | + while ($mf =~ m{^(?:EXTRA_)?OBJS[^=]*=\s*(.*)$}m) { |
| 143 | + my $s = $1; |
| 144 | + my $filter_re = qr{\$\(filter ([^,]+),\s+\$\(([^\)]+)\)\)}; |
| 145 | + while ($s =~ /$filter_re/) { |
| 146 | +# Process $(filter a b c, $(VAR)) expressions |
| 147 | + my $list = $1; |
| 148 | + my $filter = $2; |
| 149 | + $list =~ s/\.o/\.c/g; |
| 150 | + my @pieces = split /\s+/, $list; |
| 151 | + my $matches = ""; |
| 152 | + foreach my $p (@pieces) { |
| 153 | + if ($filter eq "LIBOBJS") { |
| 154 | + if (grep(/$p/, @main::pgportfiles) == 1) { |
| 155 | + $p =~ s/\.c/\.o/; |
| 156 | + $matches .= $p . " "; |
| 157 | + } |
| 158 | + } |
| 159 | + else { |
| 160 | + confess "Unknown filter $filter\n"; |
| 161 | + } |
| 162 | + } |
| 163 | + $s =~ s/$filter_re/$matches/; |
| 164 | + } |
| 165 | + foreach my $f (split /\s+/,$s) { |
| 166 | + next if $f =~ /^\s*$/; |
| 167 | + next if $f eq "\\"; |
| 168 | + next if $f =~ /\/SUBSYS.o$/; |
| 169 | + $f =~ s/,$//; # Remove trailing comma that can show up from filter stuff |
| 170 | + next unless $f =~ /.*\.o$/; |
| 171 | + $f =~ s/\.o$/\.c/; |
| 172 | + if ($f =~ /^\$\(top_builddir\)\/(.*)/) { |
| 173 | + $f = $1; |
| 174 | + $f =~ s/\//\\/g; |
| 175 | + $self->{files}->{$f} = 1; |
| 176 | + } |
| 177 | + else { |
| 178 | + $f =~ s/\//\\/g; |
| 179 | + $self->{files}->{"$reldir\\$f"} = 1; |
| 180 | + } |
| 181 | + } |
| 182 | + $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m; |
| 183 | + } |
| 184 | + |
| 185 | +# Match rules that pull in source files from different directories |
| 186 | + my $replace_re = qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+$}; |
| 187 | + while ($mf =~ m{$replace_re}m) { |
| 188 | + my $match = $1; |
| 189 | + my $top = $2; |
| 190 | + my $target = $3; |
| 191 | + $target =~ s{/}{\\}g; |
| 192 | + my @pieces = split /\s+/,$match; |
| 193 | + foreach my $fn (@pieces) { |
| 194 | + if ($top eq "(top_srcdir)") { |
| 195 | + eval { $self->ReplaceFile($fn, $target) }; |
| 196 | + } |
| 197 | + elsif ($top eq "(backend_src)") { |
| 198 | + eval { $self->ReplaceFile($fn, "src\\backend\\$target") }; |
| 199 | + } |
| 200 | + else { |
| 201 | + confess "Bad replacement top: $top, on line $_\n"; |
| 202 | + } |
| 203 | + } |
| 204 | + $mf =~ s{$replace_re}{}m; |
| 205 | + } |
| 206 | + |
| 207 | +# See if this Makefile contains a description, and should have a RC file |
| 208 | + if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m) { |
| 209 | + my $desc = $1; |
| 210 | + my $ico; |
| 211 | + if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; } |
| 212 | + $self->AddResourceFile($reldir,$desc,$ico); |
| 213 | + } |
| 214 | + $/ = $t; |
| 215 | +} |
| 216 | + |
| 217 | +sub AddResourceFile { |
| 218 | + my ($self, $dir, $desc, $ico) = @_; |
| 219 | + |
| 220 | + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); |
| 221 | + my $d = ($year - 100) . "$yday"; |
| 222 | + |
| 223 | + if (Solution::IsNewer("$dir\\win32ver.rc",'src\port\win32ver.rc')) { |
| 224 | + print "Generating win32ver.rc for $dir\n"; |
| 225 | + open(I,'src\port\win32ver.rc') || confess "Could not open win32ver.rc"; |
| 226 | + open(O,">$dir\\win32ver.rc") || confess "Could not write win32ver.rc"; |
| 227 | + my $icostr = $ico?"IDI_ICON ICON \"src/port/$ico.ico\"":""; |
| 228 | + while (<I>) { |
| 229 | + s/FILEDESC/"$desc"/gm; |
| 230 | + s/_ICO_/$icostr/gm; |
| 231 | + s/(VERSION.*),0/$1,$d/; |
| 232 | + if ($self->{type} eq "dll") { |
| 233 | + s/VFT_APP/VFT_DLL/gm; |
| 234 | + } |
| 235 | + print O; |
| 236 | + } |
| 237 | + } |
| 238 | + close(O); |
| 239 | + close(I); |
| 240 | + $self->AddFile("$dir\\win32ver.rc"); |
| 241 | +} |
| 242 | + |
| 243 | +sub Save { |
| 244 | + my ($self) = @_; |
| 245 | + |
| 246 | +# If doing DLL and haven't specified a DEF file, do a full export of all symbols |
| 247 | +# in the project. |
| 248 | + if ($self->{type} eq "dll" && !$self->{def}) { |
| 249 | + $self->FullExportDLL($self->{name} . ".lib"); |
| 250 | + } |
| 251 | + |
| 252 | +# Dump the project |
| 253 | + open(F, ">$self->{name}.vcproj") || croak("Could not write to $self->{name}.vcproj\n"); |
| 254 | + $self->WriteHeader(*F); |
| 255 | + $self->WriteReferences(*F); |
| 256 | + print F <<EOF; |
| 257 | + <Files> |
| 258 | +EOF |
| 259 | + my @dirstack = (); |
| 260 | + my %uniquefiles; |
| 261 | + foreach my $f (sort keys %{ $self->{files} }) { |
| 262 | + confess "Bad format filename '$f'\n" unless ($f =~ /^(.*)\\([^\\]+)\.[r]?[cyl]$/); |
| 263 | + my $dir = $1; |
| 264 | + my $file = $2; |
| 265 | + |
| 266 | +# Walk backwards down the directory stack and close any dirs we're done with |
| 267 | + while ($#dirstack >= 0) { |
| 268 | + if (join('\\',@dirstack) eq substr($dir, 0, length(join('\\',@dirstack)))) { |
| 269 | + last if (length($dir) == length(join('\\',@dirstack))); |
| 270 | + last if (substr($dir, length(join('\\',@dirstack)),1) eq '\\'); |
| 271 | + } |
| 272 | + print F ' ' x $#dirstack . " </Filter>\n"; |
| 273 | + pop @dirstack; |
| 274 | + } |
| 275 | +# Now walk forwards and create whatever directories are needed |
| 276 | + while (join('\\',@dirstack) ne $dir) { |
| 277 | + my $left = substr($dir, length(join('\\',@dirstack))); |
| 278 | + $left =~ s/^\\//; |
| 279 | + my @pieces = split /\\/, $left; |
| 280 | + push @dirstack, $pieces[0]; |
| 281 | + print F ' ' x $#dirstack . " <Filter Name=\"$pieces[0]\" Filter=\"\">\n"; |
| 282 | + } |
| 283 | + |
| 284 | + print F ' ' x $#dirstack . " <File RelativePath=\"$f\""; |
| 285 | + if ($f =~ /\.y$/) { |
| 286 | + my $of = $f; |
| 287 | + $of =~ s/\.y$/.c/; |
| 288 | + $of =~ s{^src\\pl\\plpgsql\\src\\gram.c$}{src\\pl\\plpgsql\\src\\pl_gram.c}; |
| 289 | + print F '><FileConfiguration Name="Debug|Win32"><Tool Name="VCCustomBuildTool" Description="Running bison on ' . $f . '" CommandLine="vcbuild\pgbison.bat ' . $f . '" AdditionalDependencies="" Outputs="' . $of . '" /></FileConfiguration></File>' . "\n"; |
| 290 | + } |
| 291 | + elsif ($f =~ /\.l$/) { |
| 292 | + my $of = $f; |
| 293 | + $of =~ s/\.l$/.c/; |
| 294 | + $of =~ s{^src\\pl\\plpgsql\\src\\scan.c$}{src\\pl\\plpgsql\\src\\pl_scan.c}; |
| 295 | + print F "><FileConfiguration Name=\"Debug|Win32\"><Tool Name=\"VCCustomBuildTool\" Description=\"Running flex on $f\" CommandLine=\"vcbuild\\pgflex.bat $f\" AdditionalDependencies=\"\" Outputs=\"$of\" /></FileConfiguration></File>\n"; |
| 296 | + } |
| 297 | + elsif (defined($uniquefiles{$file})) { |
| 298 | +# File already exists, so fake a new name |
| 299 | + my $obj = $dir; |
| 300 | + $obj =~ s/\\/_/g; |
| 301 | + print F "><FileConfiguration Name=\"Debug|Win32\"><Tool Name=\"VCCLCompilerTool\" ObjectFile=\".\\debug\\$self->{name}\\$obj" . "_$file.obj\" /></FileConfiguration></File>\n"; |
| 302 | + } |
| 303 | + else { |
| 304 | + $uniquefiles{$file} = 1; |
| 305 | + print F " />\n"; |
| 306 | + } |
| 307 | + } |
| 308 | + while ($#dirstack >= 0) { |
| 309 | + print F ' ' x $#dirstack . " </Filter>\n"; |
| 310 | + pop @dirstack; |
| 311 | + } |
| 312 | + $self->Footer(*F); |
| 313 | + close(F); |
| 314 | +} |
| 315 | + |
| 316 | +sub WriteReferences { |
| 317 | + my ($self, $f) = @_; |
| 318 | + print $f " <References>\n"; |
| 319 | + foreach my $ref (@{$self->{references}}) { |
| 320 | + print $f " <ProjectReference ReferencedProjectIdentifier=\"$ref->{guid}\" Name=\"$ref->{name}\" />\n"; |
| 321 | + } |
| 322 | + print $f " </References>\n"; |
| 323 | +} |
| 324 | + |
| 325 | +sub WriteHeader { |
| 326 | + my ($self, $f) = @_; |
| 327 | + |
| 328 | + my $cfgtype = ($self->{type} eq "exe")?1:($self->{type} eq "dll"?2:4); |
| 329 | + |
| 330 | + print $f <<EOF; |
| 331 | +<?xml version="1.0" encoding="Windows-1252"?> |
| 332 | +<VisualStudioProject ProjectType="Visual C++" Version="8.00" Name="$self->{name}" ProjectGUID="$self->{guid}"> |
| 333 | + <Platforms><Platform Name="Win32"/></Platforms> |
| 334 | + <Configurations> |
| 335 | + <Configuration Name="Debug|Win32" OutputDirectory=".\\Debug\\$self->{name}" IntermediateDirectory=".\\Debug\\$self->{name}" |
| 336 | + ConfigurationType="$cfgtype" UseOfMFC="0" ATLMinimizesCRunTimeLibraryUsage="FALSE" CharacterSet="2"> |
| 337 | + <Tool Name="VCCLCompilerTool" Optimization="0" |
| 338 | + AdditionalIncludeDirectories="src/include;src/include/port/win32;src/include/port/win32_msvc;$self->{solution}->{options}->{pthread};$self->{includes}" |
| 339 | + PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;__WINDOWS__;DEBUG=1;__WIN32__;EXEC_BACKEND;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE$self->{defines}" |
| 340 | + RuntimeLibrary="3" DisableSpecificWarnings="$self->{disablewarnings}" |
| 341 | +EOF |
| 342 | + print $f <<EOF; |
| 343 | + AssemblerOutput="0" AssemblerListingLocation=".\\debug\\$self->{name}\\" ObjectFile=".\\debug\\$self->{name}\\" |
| 344 | + ProgramDataBaseFileName=".\\debug\\$self->{name}\\" BrowseInformation="0" |
| 345 | + WarningLevel="3" SuppressStartupBanner="TRUE" DebugInformationFormat="3" CompileAs="0"/> |
| 346 | + <Tool Name="VCLinkerTool" OutputFile=".\\debug\\$self->{name}\\$self->{name}.$self->{type}" |
| 347 | + AdditionalDependencies="$self->{libraries}" |
| 348 | + LinkIncremental="0" SuppressStartupBanner="TRUE" AdditionalLibraryDirectories="" IgnoreDefaultLibraryNames="libc" |
| 349 | + StackReserveSize="4194304" DisableSpecificWarnings="$self->{disablewarnings}" |
| 350 | + GenerateDebugInformation="TRUE" ProgramDatabaseFile=".\\debug\\$self->{name}\\$self->{name}.pdb" |
| 351 | + GenerateMapFile="FALSE" MapFileName=".\\debug\\$self->{name}\\$self->{name}.map" |
| 352 | + SubSystem="1" TargetMachine="1" |
| 353 | +EOF |
| 354 | + if ($self->{implib}) { |
| 355 | + print $f "\t\tImportLibrary=\"$self->{implib}\"\n"; |
| 356 | + } |
| 357 | + if ($self->{def}) { |
| 358 | + print $f "\t\tModuleDefinitionFile=\"$self->{def}\"\n"; |
| 359 | + } |
| 360 | + |
| 361 | + print $f "\t/>\n"; |
| 362 | + print $f "\t<Tool Name=\"VCLibrarianTool\" OutputFile=\".\\Debug\\$self->{name}\\$self->{name}.lib\" IgnoreDefaultLibraryNames=\"libc\" />\n"; |
| 363 | + print $f "\t<Tool Name=\"VCResourceCompilerTool\" AdditionalIncludeDirectories=\"src\\include\" />\n"; |
| 364 | + if ($self->{builddef}) { |
| 365 | + print $f "\t<Tool Name=\"VCPreLinkEventTool\" Description=\"Generate DEF file\" CommandLine=\"perl vcbuild\\gendef.pl debug\\$self->{name}\" />\n"; |
| 366 | + } |
| 367 | + print $f <<EOF; |
| 368 | + </Configuration> |
| 369 | + </Configurations> |
| 370 | +EOF |
| 371 | +} |
| 372 | + |
| 373 | +sub Footer { |
| 374 | + my ($self, $f) = @_; |
| 375 | + |
| 376 | + print $f <<EOF; |
| 377 | + </Files> |
| 378 | + <Globals/> |
| 379 | +</VisualStudioProject> |
| 380 | +EOF |
| 381 | +} |
| 382 | + |
| 383 | + |
| 384 | +1; |
0 commit comments