summaryrefslogtreecommitdiffstats
path: root/src/tools/msvc/Project.pm
diff options
context:
space:
mode:
Diffstat (limited to 'src/tools/msvc/Project.pm')
-rw-r--r--src/tools/msvc/Project.pm448
1 files changed, 448 insertions, 0 deletions
diff --git a/src/tools/msvc/Project.pm b/src/tools/msvc/Project.pm
new file mode 100644
index 0000000..2f1679a
--- /dev/null
+++ b/src/tools/msvc/Project.pm
@@ -0,0 +1,448 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+package Project;
+
+#
+# Package that encapsulates a Visual C++ project file generation
+#
+# src/tools/msvc/Project.pm
+#
+use Carp;
+use strict;
+use warnings;
+use File::Basename;
+
+sub _new
+{
+ my ($classname, $name, $type, $solution) = @_;
+ my $good_types = {
+ lib => 1,
+ exe => 1,
+ dll => 1,
+ };
+ confess("Bad project type: $type\n") unless exists $good_types->{$type};
+ my $self = {
+ name => $name,
+ type => $type,
+ guid => $^O eq "MSWin32" ? Win32::GuidGen() : 'FAKE',
+ files => {},
+ references => [],
+ libraries => [],
+ suffixlib => [],
+ includes => '',
+ prefixincludes => '',
+ defines => ';',
+ solution => $solution,
+ disablewarnings => '4018;4244;4273;4102;4090;4267',
+ disablelinkerwarnings => '',
+ platform => $solution->{platform},
+ };
+
+ bless($self, $classname);
+ return $self;
+}
+
+sub AddFile
+{
+ my ($self, $filename) = @_;
+
+ $self->{files}->{$filename} = 1;
+ return;
+}
+
+sub AddFiles
+{
+ my $self = shift;
+ my $dir = shift;
+
+ while (my $f = shift)
+ {
+ $self->{files}->{ $dir . "/" . $f } = 1;
+ }
+ return;
+}
+
+sub ReplaceFile
+{
+ my ($self, $filename, $newname) = @_;
+ my $re = "\\/$filename\$";
+
+ foreach my $file (keys %{ $self->{files} })
+ {
+
+ # Match complete filename
+ if ($filename =~ m!/!)
+ {
+ if ($file eq $filename)
+ {
+ delete $self->{files}{$file};
+ $self->{files}{$newname} = 1;
+ return;
+ }
+ }
+ elsif ($file =~ m/($re)/)
+ {
+ delete $self->{files}{$file};
+ $self->{files}{"$newname/$filename"} = 1;
+ return;
+ }
+ }
+ confess("Could not find file $filename to replace\n");
+}
+
+sub RemoveFile
+{
+ my ($self, $filename) = @_;
+ my $orig = scalar keys %{ $self->{files} };
+ delete $self->{files}->{$filename};
+ if ($orig > scalar keys %{ $self->{files} })
+ {
+ return;
+ }
+ confess("Could not find file $filename to remove\n");
+}
+
+sub RelocateFiles
+{
+ my ($self, $targetdir, $proc) = @_;
+ foreach my $f (keys %{ $self->{files} })
+ {
+ my $r = &$proc($f);
+ if ($r)
+ {
+ $self->RemoveFile($f);
+ $self->AddFile($targetdir . '/' . basename($f));
+ }
+ }
+ return;
+}
+
+sub AddReference
+{
+ my $self = shift;
+
+ while (my $ref = shift)
+ {
+ push @{ $self->{references} }, $ref;
+ $self->AddLibrary(
+ "__CFGNAME__/" . $ref->{name} . "/" . $ref->{name} . ".lib");
+ }
+ return;
+}
+
+sub AddLibrary
+{
+ my ($self, $lib, $dbgsuffix) = @_;
+
+ # quote lib name if it has spaces and isn't already quoted
+ if ($lib =~ m/\s/ && $lib !~ m/^[&]quot;/)
+ {
+ $lib = '"' . $lib . """;
+ }
+
+ push @{ $self->{libraries} }, $lib;
+ if ($dbgsuffix)
+ {
+ push @{ $self->{suffixlib} }, $lib;
+ }
+ return;
+}
+
+sub AddIncludeDir
+{
+ my ($self, $inc) = @_;
+
+ if ($self->{includes} ne '')
+ {
+ $self->{includes} .= ';';
+ }
+ $self->{includes} .= $inc;
+ return;
+}
+
+sub AddPrefixInclude
+{
+ my ($self, $inc) = @_;
+
+ $self->{prefixincludes} = $inc . ';' . $self->{prefixincludes};
+ return;
+}
+
+sub AddDefine
+{
+ my ($self, $def) = @_;
+
+ $def =~ s/"/""/g;
+ $self->{defines} .= $def . ';';
+ return;
+}
+
+sub FullExportDLL
+{
+ my ($self, $libname) = @_;
+
+ $self->{builddef} = 1;
+ $self->{def} = "./__CFGNAME__/$self->{name}/$self->{name}.def";
+ $self->{implib} = "__CFGNAME__/$self->{name}/$libname";
+ return;
+}
+
+sub UseDef
+{
+ my ($self, $def) = @_;
+
+ $self->{def} = $def;
+ return;
+}
+
+sub AddDir
+{
+ my ($self, $reldir) = @_;
+ my $mf = read_makefile($reldir);
+
+ $mf =~ s{\\\r?\n}{}g;
+ if ($mf =~ m{^(?:SUB)?DIRS[^=]*=\s*(.*)$}mg)
+ {
+ foreach my $subdir (split /\s+/, $1)
+ {
+ next
+ if $subdir eq "\$(top_builddir)/src/timezone"
+ ; #special case for non-standard include
+ next
+ if $reldir . "/" . $subdir eq "src/backend/port/darwin";
+
+ $self->AddDir($reldir . "/" . $subdir);
+ }
+ }
+ while ($mf =~ m{^(?:EXTRA_)?OBJS[^=]*=\s*(.*)$}m)
+ {
+ my $s = $1;
+ my $filter_re = qr{\$\(filter ([^,]+),\s+\$\(([^\)]+)\)\)};
+ while ($s =~ /$filter_re/)
+ {
+
+ # Process $(filter a b c, $(VAR)) expressions
+ my $list = $1;
+ my $filter = $2;
+ $list =~ s/\.o/\.c/g;
+ my @pieces = split /\s+/, $list;
+ my $matches = "";
+ foreach my $p (@pieces)
+ {
+
+ if ($filter eq "LIBOBJS")
+ {
+ no warnings qw(once);
+ if (grep(/$p/, @main::pgportfiles) == 1)
+ {
+ $p =~ s/\.c/\.o/;
+ $matches .= $p . " ";
+ }
+ }
+ else
+ {
+ confess "Unknown filter $filter\n";
+ }
+ }
+ $s =~ s/$filter_re/$matches/;
+ }
+ foreach my $f (split /\s+/, $s)
+ {
+ next if $f =~ /^\s*$/;
+ next if $f eq "\\";
+ next if $f =~ /\/SUBSYS.o$/;
+ $f =~ s/,$//
+ ; # Remove trailing comma that can show up from filter stuff
+ next unless $f =~ /.*\.o$/;
+ $f =~ s/\.o$/\.c/;
+ if ($f =~ /^\$\(top_builddir\)\/(.*)/)
+ {
+ $f = $1;
+ $self->{files}->{$f} = 1;
+ }
+ else
+ {
+ $self->{files}->{"$reldir/$f"} = 1;
+ }
+ }
+ $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m;
+ }
+
+ # Match rules that pull in source files from different directories, eg
+ # pgstrcasecmp.c rint.c snprintf.c: % : $(top_srcdir)/src/port/%
+ my $replace_re =
+ qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+\n}m;
+ while ($mf =~ m{$replace_re}m)
+ {
+ my $match = $1;
+ my $top = $2;
+ my $target = $3;
+ my @pieces = split /\s+/, $match;
+ foreach my $fn (@pieces)
+ {
+ if ($top eq "(top_srcdir)")
+ {
+ eval { $self->ReplaceFile($fn, $target) };
+ }
+ elsif ($top eq "(backend_src)")
+ {
+ eval { $self->ReplaceFile($fn, "src/backend/$target") };
+ }
+ else
+ {
+ confess "Bad replacement top: $top, on line $_\n";
+ }
+ }
+ $mf =~ s{$replace_re}{}m;
+ }
+
+ $self->AddDirResourceFile($reldir);
+ return;
+}
+
+# If the directory's Makefile bears a description string, add a resource file.
+sub AddDirResourceFile
+{
+ my ($self, $reldir) = @_;
+ my $mf = read_makefile($reldir);
+
+ if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m)
+ {
+ my $desc = $1;
+ my $ico;
+ if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; }
+ $self->AddResourceFile($reldir, $desc, $ico);
+ }
+ return;
+}
+
+sub AddResourceFile
+{
+ my ($self, $dir, $desc, $ico) = @_;
+
+ my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) =
+ localtime(time);
+ my $d = sprintf("%02d%03d", ($year - 100), $yday);
+
+ if (Solution::IsNewer("$dir/win32ver.rc", 'src/port/win32ver.rc'))
+ {
+ print "Generating win32ver.rc for $dir\n";
+ open(my $i, '<', 'src/port/win32ver.rc')
+ || confess "Could not open win32ver.rc";
+ open(my $o, '>', "$dir/win32ver.rc")
+ || confess "Could not write win32ver.rc";
+ my $icostr = $ico ? "IDI_ICON ICON \"src/port/$ico.ico\"" : "";
+ while (<$i>)
+ {
+ s/FILEDESC/"$desc"/gm;
+ s/_ICO_/$icostr/gm;
+ s/(VERSION.*),0/$1,$d/;
+ if ($self->{type} eq "dll")
+ {
+ s/VFT_APP/VFT_DLL/gm;
+ my $name = $self->{name};
+ s/_INTERNAL_NAME_/"$name"/;
+ s/_ORIGINAL_NAME_/"$name.dll"/;
+ }
+ else
+ {
+ /_INTERNAL_NAME_/ && next;
+ /_ORIGINAL_NAME_/ && next;
+ }
+ print $o $_;
+ }
+ close($o);
+ close($i);
+ }
+ $self->AddFile("$dir/win32ver.rc");
+ return;
+}
+
+sub DisableLinkerWarnings
+{
+ my ($self, $warnings) = @_;
+
+ $self->{disablelinkerwarnings} .= ','
+ unless ($self->{disablelinkerwarnings} eq '');
+ $self->{disablelinkerwarnings} .= $warnings;
+ return;
+}
+
+sub Save
+{
+ my ($self) = @_;
+
+ # If doing DLL and haven't specified a DEF file, do a full export of all symbols
+ # in the project.
+ if ($self->{type} eq "dll" && !$self->{def})
+ {
+ $self->FullExportDLL($self->{name} . ".lib");
+ }
+
+ # Warning 4197 is about double exporting, disable this per
+ # http://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx?FeedbackID=99193
+ $self->DisableLinkerWarnings('4197') if ($self->{platform} eq 'x64');
+
+ # Dump the project
+ open(my $f, '>', "$self->{name}$self->{filenameExtension}")
+ || croak(
+ "Could not write to $self->{name}$self->{filenameExtension}\n");
+ $self->WriteHeader($f);
+ $self->WriteFiles($f);
+ $self->Footer($f);
+ close($f);
+ return;
+}
+
+sub GetAdditionalLinkerDependencies
+{
+ my ($self, $cfgname, $separator) = @_;
+ my $libcfg = (uc $cfgname eq "RELEASE") ? "MD" : "MDd";
+ my $libs = '';
+ foreach my $lib (@{ $self->{libraries} })
+ {
+ my $xlib = $lib;
+ foreach my $slib (@{ $self->{suffixlib} })
+ {
+ if ($slib eq $lib)
+ {
+ $xlib =~ s/\.lib$/$libcfg.lib/;
+ last;
+ }
+ }
+ $libs .= $xlib . $separator;
+ }
+ $libs =~ s/.$//;
+ $libs =~ s/__CFGNAME__/$cfgname/g;
+ return $libs;
+}
+
+# Utility function that loads a complete file
+sub read_file
+{
+ my $filename = shift;
+ my $F;
+ local $/ = undef;
+ open($F, '<', $filename) || croak "Could not open file $filename\n";
+ my $txt = <$F>;
+ close($F);
+
+ return $txt;
+}
+
+sub read_makefile
+{
+ my $reldir = shift;
+ my $F;
+ local $/ = undef;
+ open($F, '<', "$reldir/GNUmakefile")
+ || open($F, '<', "$reldir/Makefile")
+ || confess "Could not open $reldir/Makefile\n";
+ my $txt = <$F>;
+ close($F);
+
+ return $txt;
+}
+
+1;