From c66939c4aad7abe4095fcf09c088eb36959b0721 Mon Sep 17 00:00:00 2001 From: Bruce Momjian Date: Mon, 4 Sep 2006 21:30:40 +0000 Subject: [PATCH] Add MSVC build tools. Magnus Hagander --- src/tools/msvc/Project.pm | 384 ++++++++++++++++++++++++++++++++++++++++++++ src/tools/msvc/Solution.pm | 301 ++++++++++++++++++++++++++++++++++ src/tools/msvc/build.bat | 12 ++ src/tools/msvc/config.pl | 18 +++ src/tools/msvc/gendef.pl | 50 ++++++ src/tools/msvc/mkvcbuild.pl | 235 +++++++++++++++++++++++++++ src/tools/msvc/pgbison.bat | 27 ++++ src/tools/msvc/pgflex.bat | 21 +++ 8 files changed, 1048 insertions(+) create mode 100644 src/tools/msvc/Project.pm create mode 100644 src/tools/msvc/Solution.pm create mode 100755 src/tools/msvc/build.bat create mode 100644 src/tools/msvc/config.pl create mode 100644 src/tools/msvc/gendef.pl create mode 100644 src/tools/msvc/mkvcbuild.pl create mode 100755 src/tools/msvc/pgbison.bat create mode 100755 src/tools/msvc/pgflex.bat diff --git a/src/tools/msvc/Project.pm b/src/tools/msvc/Project.pm new file mode 100644 index 0000000000..239a4737df --- /dev/null +++ b/src/tools/msvc/Project.pm @@ -0,0 +1,384 @@ +package Project; + +use Carp; +use strict; +use warnings; + +sub new { + my ($junk, $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 => Win32::GuidGen(), + files => {}, + references => [], + libraries => '', + includes => '', + defines => ';', + solution => $solution, + disablewarnings => '4018;4244', + }; + + bless $self; + return $self; +} + +sub AddFile { + my ($self, $filename) = @_; + + $self->{files}->{$filename} = 1; +} + +sub AddFiles { + my $self = shift; + my $dir = shift; + + while (my $f = shift) { + $self->{files}->{$dir . "\\" . $f} = 1; + } +} + +sub ReplaceFile { + my ($self, $filename, $newname) = @_; + my $re = "\\\\$filename\$"; + + foreach my $file ( keys %{ $self->{files} } ) { + # Match complete filename + if ($filename =~ /\\/) { + 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 AddReference { + my $self = shift; + + while (my $ref = shift) { + push @{$self->{references}},$ref; + $self->AddLibrary("debug\\" . $ref->{name} . "\\" . $ref->{name} . ".lib") if ($ref->{type} eq "exe"); + } +} + +sub AddLibrary { + my ($self, $lib) = @_; + + if ($self->{libraries} ne '') { + $self->{libraries} .= ' '; + } + $self->{libraries} .= $lib; +} + +sub AddIncludeDir { + my ($self, $inc) = @_; + + if ($self->{includes} ne '') { + $self->{includes} .= ';'; + } + $self->{includes} .= $inc; +} + +sub AddDefine { + my ($self, $def) = @_; + + $self->{defines} .= $def . ';'; +} + +sub FullExportDLL { + my ($self, $libname) = @_; + + $self->{builddef} = 1; + $self->{def} = ".\\debug\\$self->{name}\\$self->{name}.def"; + $self->{implib} = "debug\\$self->{name}\\$libname"; +} + +sub UseDef { + my ($self, $def) = @_; + + $self->{def} = $def; +} + +sub AddDir { + my ($self, $reldir) = @_; + my $MF; + + my $t = $/;undef $/; + open($MF,"$reldir\\Makefile") || open($MF,"$reldir\\GNUMakefile") || croak "Could not open $reldir\\Makefile\n"; + my $mf = <$MF>; + close($MF); + + $mf =~ s{\\\s*[\r\n]+}{}mg; + 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 + $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") { + 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; + $f =~ s/\//\\/g; + $self->{files}->{$f} = 1; + } + else { + $f =~ s/\//\\/g; + $self->{files}->{"$reldir\\$f"} = 1; + } + } + $mf =~ s{OBJS[^=]*=\s*(.*)$}{}m; + } + +# Match rules that pull in source files from different directories + my $replace_re = qr{^([^:\n\$]+\.c)\s*:\s*(?:%\s*: )?\$(\([^\)]+\))\/(.*)\/[^\/]+$}; + while ($mf =~ m{$replace_re}m) { + my $match = $1; + my $top = $2; + my $target = $3; + $target =~ s{/}{\\}g; + 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; + } + +# See if this Makefile contains a description, and should have a RC file + if ($mf =~ /^PGFILEDESC\s*=\s*\"([^\"]+)\"/m) { + my $desc = $1; + my $ico; + if ($mf =~ /^PGAPPICON\s*=\s*(.*)$/m) { $ico = $1; } + $self->AddResourceFile($reldir,$desc,$ico); + } + $/ = $t; +} + +sub AddResourceFile { + my ($self, $dir, $desc, $ico) = @_; + + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + my $d = ($year - 100) . "$yday"; + + if (Solution::IsNewer("$dir\\win32ver.rc",'src\port\win32ver.rc')) { + print "Generating win32ver.rc for $dir\n"; + open(I,'src\port\win32ver.rc') || confess "Could not open win32ver.rc"; + open(O,">$dir\\win32ver.rc") || confess "Could not write win32ver.rc"; + my $icostr = $ico?"IDI_ICON ICON \"src/port/$ico.ico\"":""; + while () { + s/FILEDESC/"$desc"/gm; + s/_ICO_/$icostr/gm; + s/(VERSION.*),0/$1,$d/; + if ($self->{type} eq "dll") { + s/VFT_APP/VFT_DLL/gm; + } + print O; + } + } + close(O); + close(I); + $self->AddFile("$dir\\win32ver.rc"); +} + +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"); + } + +# Dump the project + open(F, ">$self->{name}.vcproj") || croak("Could not write to $self->{name}.vcproj\n"); + $self->WriteHeader(*F); + $self->WriteReferences(*F); + print F < +EOF + my @dirstack = (); + my %uniquefiles; + foreach my $f (sort keys %{ $self->{files} }) { + confess "Bad format filename '$f'\n" unless ($f =~ /^(.*)\\([^\\]+)\.[r]?[cyl]$/); + my $dir = $1; + my $file = $2; + +# Walk backwards down the directory stack and close any dirs we're done with + while ($#dirstack >= 0) { + if (join('\\',@dirstack) eq substr($dir, 0, length(join('\\',@dirstack)))) { + last if (length($dir) == length(join('\\',@dirstack))); + last if (substr($dir, length(join('\\',@dirstack)),1) eq '\\'); + } + print F ' ' x $#dirstack . " \n"; + pop @dirstack; + } +# Now walk forwards and create whatever directories are needed + while (join('\\',@dirstack) ne $dir) { + my $left = substr($dir, length(join('\\',@dirstack))); + $left =~ s/^\\//; + my @pieces = split /\\/, $left; + push @dirstack, $pieces[0]; + print F ' ' x $#dirstack . " \n"; + } + + print F ' ' x $#dirstack . " ' . "\n"; + } + elsif ($f =~ /\.l$/) { + my $of = $f; + $of =~ s/\.l$/.c/; + $of =~ s{^src\\pl\\plpgsql\\src\\scan.c$}{src\\pl\\plpgsql\\src\\pl_scan.c}; + print F ">\n"; + } + elsif (defined($uniquefiles{$file})) { +# File already exists, so fake a new name + my $obj = $dir; + $obj =~ s/\\/_/g; + print F ">{name}\\$obj" . "_$file.obj\" />\n"; + } + else { + $uniquefiles{$file} = 1; + print F " />\n"; + } + } + while ($#dirstack >= 0) { + print F ' ' x $#dirstack . " \n"; + pop @dirstack; + } + $self->Footer(*F); + close(F); +} + +sub WriteReferences { + my ($self, $f) = @_; + print $f " \n"; + foreach my $ref (@{$self->{references}}) { + print $f " {guid}\" Name=\"$ref->{name}\" />\n"; + } + print $f " \n"; +} + +sub WriteHeader { + my ($self, $f) = @_; + + my $cfgtype = ($self->{type} eq "exe")?1:($self->{type} eq "dll"?2:4); + + print $f < + + + + + + {implib}) { + print $f "\t\tImportLibrary=\"$self->{implib}\"\n"; + } + if ($self->{def}) { + print $f "\t\tModuleDefinitionFile=\"$self->{def}\"\n"; + } + + print $f "\t/>\n"; + print $f "\t{name}\\$self->{name}.lib\" IgnoreDefaultLibraryNames=\"libc\" />\n"; + print $f "\t\n"; + if ($self->{builddef}) { + print $f "\t{name}\" />\n"; + } + print $f < + +EOF +} + +sub Footer { + my ($self, $f) = @_; + + print $f < + + +EOF +} + + +1; diff --git a/src/tools/msvc/Solution.pm b/src/tools/msvc/Solution.pm new file mode 100644 index 0000000000..2b1357b09b --- /dev/null +++ b/src/tools/msvc/Solution.pm @@ -0,0 +1,301 @@ +package Solution; +use Carp; +use strict; +use warnings; + +sub new { + my $junk = shift; + my $options = shift; + die "Pthreads is required.\n" unless $options->{pthread}; + my $self = { + projects => {}, + options => $options, + numver => '', + strver => '', + }; + bless $self; + return $self; +} + +# Return 1 if $oldfile is newer than $newfile, or if $newfile doesn't exist. +# Special case - if config.pl has changed, always return 1 +sub IsNewer { + my ($newfile, $oldfile) = @_; + if ($oldfile ne 'vcbuild\config.pl') { + return 1 if IsNewer($newfile, 'vcbuild\config.pl'); + } + return 1 if (!(-e $newfile)); + my @nstat = stat($newfile); + my @ostat = stat($oldfile); + return 1 if ($nstat[9] < $ostat[9]); + return 0; +} + +# Copy a file, *not* preserving date. Only works for text files. +sub copyFile { + my ($src, $dest) = @_; + open(I,$src) || croak "Could not open $src"; + open(O,">$dest") || croak "Could not open $dest"; + while () { + print O; + } + close(I); + close(O); +} + +sub GenerateFiles { + my $self = shift; + +# Parse configure.in to get version numbers + open(C,"configure.in") || confess("Could not open configure.in for reading\n"); + while () { + if (/^AC_INIT\(\[PostgreSQL\], \[([^\]]+)\]/) { + $self->{strver} = $1; + if ($self->{strver} !~ /^(\d+)\.(\d+)(?:\.(\d+))?/) { + confess "Bad format of version: $self->{strver}\n" + } + $self->{numver} = sprintf("%d%02d%02d", $1, $2, $3?$3:0); + } + } + close(C); + confess "Unable to parse configure.in for all variables!" + if ($self->{strver} eq '' || $self->{numver} eq ''); + + if (IsNewer("src\\include\\pg_config_os.h","src\\include\\port\\win32.h")) { + print "Copying pg_config_os.h...\n"; + copyFile("src\\include\\port\\win32.h","src\\include\\pg_config_os.h"); + } + + if (IsNewer("src\\include\\pg_config.h","src\\include\\pg_config.h.win32")) { + print "Generating pg_config.h...\n"; + open(I,"src\\include\\pg_config.h.win32") || confess "Could not open pg_config.h.win32\n"; + open(O,">src\\include\\pg_config.h") || confess "Could not write to pg_config.h\n"; + while () { + s{PG_VERSION "[^"]+"}{PG_VERSION "$self->{strver}"}; + s{PG_VERSION_NUM \d+}{PG_VERSION_NUM $self->{numver}}; + s{PG_VERSION_STR "[^"]+"}{__STRINGIFY(x) #x\n#define __STRINGIFY2(z) __STRINGIFY(z)\n#define PG_VERSION_STR "PostgreSQL $self->{strver}, compiled by Visual C++ build " __STRINGIFY2(_MSC_VER)}; + print O; + } + print O "/* defines added by config steps */\n"; + print O "#define USE_ASSERT_CHECKING 1\n" if ($self->{options}->{asserts}); + print O "#define USE_LDAP 1\n" if ($self->{options}->{ldap}); + print O "#define HAVE_LIBZ 1\n" if ($self->{options}->{zlib}); + print O "#define USE_SSL 1\n" if ($self->{options}->{openssl}); + print O "#define ENABLE_NLS 1\n" if ($self->{options}->{nls}); + print O "#define LOCALEDIR \"/usr/local/pgsql/share/locale\"\n" if ($self->{options}->{nls}); + if ($self->{options}->{nls}) { + print O "#define KRB5 1\n"; + print O "#define HAVE_KRB5_ERROR_TEXT_DATA 1\n"; + print O "#define HAVE_KRB5_TICKET_ENC_PART2 1\n"; + print O "#define PG_KRB_SRVNAM \"postgres\"\n"; + } + close(O); + close(I); + } + + if (IsNewer("src\\interfaces\\libpq\\libpqdll.def","src\\interfaces\\libpq\\exports.txt")) { + print "Generating libpqdll.def...\n"; + open(I,"src\\interfaces\\libpq\\exports.txt") || confess("Could not open exports.txt\n"); + open(O,">src\\interfaces\\libpq\\libpqdll.def") || confess("Could not open libpqdll.def\n"); + print O "LIBRARY LIBPQ\nEXPORTS\n"; + while () { + next if (/^#/); + my ($f, $o) = split; + print O " $f @ $o\n"; + } + close(O); + close(I); + } + + if (IsNewer("src\\backend\\utils\\fmgrtab.c","src\\include\\catalog\\pg_proc.h")) { + print "Generating fmgrtab.c and fmgroids.h...\n"; + open(I,"src\\include\\catalog\\pg_proc.h") || confess "Could not open pg_proc.h"; + my @fmgr = (); + my %seenit; + while () { + next unless (/^DATA/); + s/^.*OID[^=]*=[^0-9]*//; + s/\(//g; + s/[ \t]*\).*$//; + my @p = split; + next if ($p[4] ne "12"); + push @fmgr,{ + oid => $p[0], + proname => $p[1], + prosrc => $p[$#p-2], + nargs => $p[10], + strict => $p[7], + retset => $p[8], + }; + } + close(I); + + open(H,'>', 'src\include\utils\fmgroids.h') || + confess "Could not open fmgroids.h"; + print H "/* fmgroids.h generated for Visual C++ */\n#ifndef FMGROIDS_H\n#define FMGROIDS_H\n\n"; + open(T,">src\\backend\\utils\\fmgrtab.c") || confess "Could not open fmgrtab.c"; + print T "/* fmgrtab.c generated for Visual C++ */\n#include \"postgres.h\"\n#include \"utils/fmgrtab.h\"\n\n"; + foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr) { + next if $seenit{$s->{prosrc}}; + $seenit{$s->{prosrc}} = 1; + print H "#define F_" . uc $s->{prosrc} . " $s->{oid}\n"; + print T "extern Datum $s->{prosrc} (PG_FUNCTION_ARGS);\n"; + } + print H "\n#endif\n /* FMGROIDS_H */\n"; + close(H); + print T "const FmgrBuiltin fmgr_builtins[] = {\n"; + my %bmap; + $bmap{'t'} = 'true'; + $bmap{'f'} = 'false'; + foreach my $s (sort {$a->{oid} <=> $b->{oid}} @fmgr) { + print T " { $s->{oid}, \"$s->{prosrc}\", $s->{nargs}, $bmap{$s->{strict}}, $bmap{$s->{retset}}, $s->{prosrc} },\n"; + } + + + print T " { 0, NULL, 0, false, false, NULL }\n};\n\nconst int fmgr_nbuiltins = (sizeof(fmgr_builtins) / sizeof(FmgrBuiltin)) - 1;\n"; + close(T); + } + + if (IsNewer('src\interfaces\libpq\libpq.rc','src\interfaces\libpq\libpq.rc.in')) { + print "Generating libpq.rc...\n"; + my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime(time); + my $d = ($year - 100) . "$yday"; + open(I,'<', 'src\interfaces\libpq\libpq.rc.in') || confess "Could not open libpq.rc.in"; + open(O,'>', 'src\interfaces\libpq\libpq.rc') || confess "Could not open libpq.rc"; + while () { + s/(VERSION.*),0/$1,$d/; + print O; + } + close(I); + close(O); + } + + if (IsNewer('src\bin\psql\sql_help.h','src\bin\psql\create_help.pl')) { + print "Generating sql_help.h...\n"; + chdir('src\bin\psql'); + system("perl create_help.pl ../../../doc/src/sgml/ref sql_help.h"); + chdir('..\..\..'); + } + + unless (-f "src\\port\\pg_config_paths.h") { + print "Generating pg_config_paths.h...\n"; + open(O,'>', 'src\port\pg_config_paths.h') || confess "Could not open pg_config_paths.h"; + print O <{projects}->{$folder}}, $proj; + $proj->AddDir($initialdir) if ($initialdir); + if ($self->{options}->{zlib}) { + $proj->AddIncludeDir($self->{options}->{zlib} . '\include'); + $proj->AddLibrary($self->{options}->{zlib} . '\lib\zdll.lib'); + } + if ($self->{options}->{openssl}) { + $proj->AddIncludeDir($self->{options}->{openssl} . '\include'); + $proj->AddLibrary($self->{options}->{openssl} . '\lib\VC\ssleay32.lib'); + $proj->AddLibrary($self->{options}->{openssl} . '\lib\VC\libeay32.lib'); + } + if ($self->{options}->{nls}) { + $proj->AddIncludeDir($self->{options}->{nls} . '\include'); + $proj->AddLibrary($self->{options}->{nls} . '\lib\intl.lib'); + } + if ($self->{options}->{krb5}) { + $proj->AddIncludeDir($self->{options}->{krb5} . '\inc\krb5'); + $proj->AddLibrary($self->{options}->{krb5} . '\lib\i386\krb5_32.lib'); + $proj->AddLibrary($self->{options}->{krb5} . '\lib\i386\comerr32.lib'); + } + return $proj; +} + +sub Save { + my ($self) = @_; + my %flduid; + + $self->GenerateFiles(); + foreach my $fld (keys %{$self->{projects}}) { + foreach my $proj (@{$self->{projects}->{$fld}}) { + $proj->Save(); + } + } + + open(SLN,">pgsql.sln") || croak "Could not write to pgsql.sln\n"; + print SLN <{projects}}) { + foreach my $proj (@{$self->{projects}->{$fld}}) { + print SLN <{name}.vcproj", "$proj->{guid}" +EndProject +EOF + } + if ($fld ne "") { + $flduid{$fld} = Win32::GuidGen(); + print SLN <{projects}}) { + foreach my $proj (@{$self->{projects}->{$fld}}) { + print SLN <{guid}.Debug|Win32.ActiveCfg = Debug|Win32 + $proj->{guid}.Debug|Win32.Build.0 = Debug|Win32 + $proj->{guid}.Release|Win32.Build.0 = Release|Win32 +EOF + } + } + + print SLN <{projects}}) { + next if ($fld eq ""); + foreach my $proj (@{$self->{projects}->{$fld}}) { + print SLN "\t\t$proj->{guid} = $flduid{$fld}\n"; + } + } + + print SLN <1, # --enable-cassert + nls=>undef, # --enable-nls= + tcl=>'c:\tcl', # --with-tls= + perl=>1, # --with-perl + python=>'c:\python24', # --with-python= + krb5=>'c:\prog\pgsql\depend\krb5', # --with-krb5= + ldap=>1, # --with-ldap + openssl=>'c:\openssl', # --with-ssl= + pthread=>'c:\prog\pgsql\depend\pthread', # foo baz? + zlib=>'c:\prog\pgsql\depend\zlib'# --with-zlib= +}; + +1; diff --git a/src/tools/msvc/gendef.pl b/src/tools/msvc/gendef.pl new file mode 100644 index 0000000000..fd97497f70 --- /dev/null +++ b/src/tools/msvc/gendef.pl @@ -0,0 +1,50 @@ +my @def; + +die "Usage: gendef.pl \n" unless ($ARGV[0] =~ /\\([^\\]+$)/); +my $defname = uc $1; + +if (-f "$ARGV[0]/$defname.def") { + print "Not re-generating $defname.DEF, file already exists.\n"; + exit(0); +} + +print "Generating $defname.DEF from directory $ARGV[0]\n"; + +while (<$ARGV[0]/*>) { + print "."; + open(F,"dumpbin /symbols $_|") || die "Could not open $_\n"; + while () { + s/\(\)//g; + next unless /^\d/; + my @pieces = split ; + next unless $pieces[6]; + next if ($pieces[2] eq "UNDEF"); + next unless ($pieces[4] eq "External"); + next if $pieces[6] =~ /^@/; + next if $pieces[6] =~ /^\(/; + next if $pieces[6] =~ /^__real/; + next if $pieces[6] =~ /^__imp/; + next if $pieces[6] =~ /NULL_THUNK_DATA$/; + next if $pieces[6] =~ /^__IMPORT_DESCRIPTOR/; + next if $pieces[6] =~ /^__NULL_IMPORT/; + + push @def, $pieces[6]; + } + close(F); +} +print "\n"; + +open(DEF,">$ARGV[0]/$defname.def") || die "Could not write to $defname\n"; +print DEF "EXPORTS\n"; +my $i = 0; +my $last = ""; +foreach my $f (sort @def) { + next if ($f eq $last); + $last = $f; + $f =~ s/^_//; + $i++; +# print DEF " $f \@ $i\n"; # ordinaled exports? + print DEF " $f\n"; +} +close(DEF); +print "Generated $i symbols\n"; diff --git a/src/tools/msvc/mkvcbuild.pl b/src/tools/msvc/mkvcbuild.pl new file mode 100644 index 0000000000..3eac344df0 --- /dev/null +++ b/src/tools/msvc/mkvcbuild.pl @@ -0,0 +1,235 @@ +use Carp; +use Win32; +use strict; +use warnings; +use Project; +use Solution; + +chdir('..') if (-d '..\vcbuild' && -d '..\src'); +die 'Must run from root directory or vcbuild directory' unless (-d 'vcbuild' && -d 'src'); +die 'Could not find config.pl' unless (-f 'vcbuild/config.pl'); + +our $config; +require 'vcbuild/config.pl'; + +my $solution = new Solution($config); + +our @pgportfiles = qw( + crypt.c fseeko.c getrusage.c inet_aton.c random.c srandom.c + unsetenv.c getaddrinfo.c gettimeofday.c kill.c open.c rand.c + snprintf.c copydir.c dirmod.c exec.c noblock.c path.c pipe.c + pgsleep.c pgstrcasecmp.c sprompt.c thread.c getopt.c getopt_long.c + dirent.c rint.c win32error.c); + +my $libpgport = $solution->AddProject('libpgport','lib','misc'); +$libpgport->AddDefine('FRONTEND'); +$libpgport->AddFiles('src\port',@pgportfiles); + +my $postgres = $solution->AddProject('postgres','exe','','src\backend'); +$postgres->AddIncludeDir('src\backend'); +$postgres->AddDir('src\backend\port\win32'); +$postgres->AddFile('src\backend\utils\fmgrtab.c'); +$postgres->ReplaceFile('src\backend\port\dynloader.c','src\backend\port\dynloader\win32.c'); +$postgres->ReplaceFile('src\backend\port\pg_sema.c','src\backend\port\win32_sema.c'); +$postgres->ReplaceFile('src\backend\port\pg_shmem.c','src\backend\port\sysv_shmem.c'); +$postgres->AddFiles('src\port',@pgportfiles); +$postgres->AddDir('src\timezone'); +$postgres->AddFiles('src\backend\parser','scan.l','gram.y'); +$postgres->AddFiles('src\backend\bootstrap','bootscanner.l','bootparse.y'); +$postgres->AddFiles('src\backend\utils\misc','guc-file.l'); +$postgres->AddDefine('BUILDING_DLL'); +$postgres->AddLibrary('wsock32.lib ws2_32.lib'); +$postgres->AddLibrary('wldap32.lib') if ($solution->{options}->{ldap}); +$postgres->FullExportDLL('postgres.lib'); + +my $plpgsql = $solution->AddProject('plpgsql','dll','PLs','src\pl\plpgsql\src'); +$plpgsql->AddFiles('src\pl\plpgsql\src','scan.l','gram.y'); +$plpgsql->AddReference($postgres); + +if ($solution->{options}->{perl}) { +# Already running in perl, so use the version that we already have information for. + use Config; + my $plperl = $solution->AddProject('plperl','dll','PLs','src\pl\plperl'); + $plperl->AddIncludeDir($Config{archlibexp} . '\CORE'); + $plperl->AddDefine('PLPERL_HAVE_UID_GID'); + if (Solution::IsNewer('src\pl\plperl\SPI.c','src\pl\plperl\SPI.xs')) { + print 'Building src\pl\plperl\SPI.c...' . "\n"; + system('perl ' . $Config{privlibexp} . '/ExtUtils/xsubpp -typemap ' . $Config{privlibexp} . '/ExtUtils/typemap src\pl\plperl\SPI.xs >src\pl\plperl\SPI.c'); + die 'Failed to create SPI.c' . "\n" if ((!(-f 'src\pl\plperl\SPI.c')) || -z 'src\pl\plperl\SPI.c'); + } + $plperl->AddReference($postgres); + $plperl->AddLibrary($Config{archlibexp} . '\CORE\perl58.lib'); +} + +if ($solution->{options}->{python}) { + my $plpython = $solution->AddProject('plpython','dll','PLs','src\pl\plpython'); + $plpython->AddIncludeDir($solution->{options}->{python} . '\include'); + $plpython->AddLibrary($solution->{options}->{python} . '\Libs\python24.lib'); + $plpython->AddReference($postgres); +} + +if ($solution->{options}->{tcl}) { + my $pltcl = $solution->AddProject('pltcl','dll','PLs','src\pl\tcl'); + $pltcl->AddIncludeDir($solution->{options}->{tcl} . '\include'); + $pltcl->AddReference($postgres); + $pltcl->AddLibrary($solution->{options}->{tcl} . '\lib\tcl84.lib'); +} + +my $libpq = $solution->AddProject('libpq','dll','interfaces','src\interfaces\libpq'); +$libpq->AddDefine('FRONTEND'); +$libpq->AddIncludeDir('src\port'); +$libpq->AddLibrary('wsock32.lib'); +$libpq->UseDef('src\interfaces\libpq\libpqdll.def'); +$libpq->ReplaceFile('src\interfaces\libpq\libpqrc.c','src\interfaces\libpq\libpq.rc'); + +my $pgtypes = $solution->AddProject('libpgtypes','dll','interfaces','src\interfaces\ecpg\pgtypeslib'); +$pgtypes->AddDefine('FRONTEND'); +$pgtypes->AddReference($postgres,$libpgport); +$pgtypes->AddIncludeDir('src\interfaces\ecpg\include'); + +my $libecpg = $solution->AddProject('libecpg','dll','interfaces','src\interfaces\ecpg\ecpglib'); +$libecpg->AddDefine('FRONTEND'); +$libecpg->AddIncludeDir('src\interfaces\ecpg\include'); +$libecpg->AddIncludeDir('src\interfaces\libpq'); +$libecpg->AddIncludeDir('src\port'); +$libecpg->AddLibrary('wsock32.lib'); +$libecpg->AddLibrary($config->{'pthread'} . '\pthreadVC2.lib'); +$libecpg->AddReference($libpq,$pgtypes); + +my $libecpgcompat = $solution->AddProject('libecpg_compat','dll','interfaces','src\interfaces\ecpg\compatlib'); +$libecpgcompat->AddIncludeDir('src\interfaces\ecpg\include'); +$libecpgcompat->AddIncludeDir('src\interfaces\libpq'); +$libecpgcompat->AddReference($pgtypes,$libecpg); + +my $ecpg = $solution->AddProject('ecpg','exe','interfaces','src\interfaces\ecpg\preproc'); +$ecpg->AddIncludeDir('src\interfaces\ecpg\include'); +$ecpg->AddIncludeDir('src\interfaces\libpq'); +$ecpg->AddFiles('src\interfaces\ecpg\preproc','pgc.l','preproc.y'); +$ecpg->AddDefine('MAJOR_VERSION=4'); +$ecpg->AddDefine('MINOR_VERSION=2'); +$ecpg->AddDefine('PATCHLEVEL=1'); +$ecpg->AddReference($libpgport); + + +# src/bin +my $initdb = AddSimpleFrontend('initdb', 1); + +my $pgconfig = AddSimpleFrontend('pg_config'); + +my $pgcontrol = AddSimpleFrontend('pg_controldata'); + +my $pgctl = AddSimpleFrontend('pg_ctl', 1); + +my $pgreset = AddSimpleFrontend('pg_resetxlog'); + +my $psql = AddSimpleFrontend('psql', 1); +$psql->AddIncludeDir('src\bin\pg_dump'); +$psql->AddFile('src\bin\psql\psqlscan.l'); + +my $pgdump = AddSimpleFrontend('pg_dump', 1); +$pgdump->AddFile('src\bin\pg_dump\pg_dump.c'); +$pgdump->AddFile('src\bin\pg_dump\common.c'); +$pgdump->AddFile('src\bin\pg_dump\pg_dump_sort.c'); + +my $pgdumpall = AddSimpleFrontend('pg_dump', 1); +$pgdumpall->{name} = 'pg_dumpall'; +$pgdumpall->AddFile('src\bin\pg_dump\pg_dumpall.c'); + +my $pgrestore = AddSimpleFrontend('pg_dump', 1); +$pgrestore->{name} = 'pg_restore'; +$pgrestore->AddFile('src\bin\pg_dump\pg_restore.c'); + +open(MF,'src\backend\utils\mb\conversion_procs\Makefile') || die 'Could not open src\backend\utils\mb\conversion_procs\Makefile'; +my $t = $/;undef $/; +my $mf = ; +close(MF); +$mf =~ s{\\s*[\r\n]+}{}mg; +$mf =~ m{DIRS\s*=\s*(.*)$}m || die 'Could not match in conversion makefile' . "\n"; +foreach my $sub (split /\s+/,$1) { + open(MF,'src\backend\utils\mb\conversion_procs\\' . $sub . '\Makefile') || die 'Could not open Makefile for $sub'; + $mf = ; + close(MF); + my $p = $solution->AddProject($sub, 'dll', 'conversion procs'); + $p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $sub . '.c'); + if ($mf =~ m{^SRCS\s*\+=\s*(.*)$}m) { + $p->AddFile('src\backend\utils\mb\conversion_procs\\' . $sub . '\\' . $1); + } + $p->AddReference($postgres); +} + +open(MF,'src\bin\scripts\Makefile') || die 'Could not open src\bin\scripts\Makefile'; +$mf = ; +close(MF); +$mf =~ s{\\s*[\r\n]+}{}mg; +$mf =~ m{PROGRAMS\s*=\s*(.*)$}m || die 'Could not match in bin\scripts\Makefile' . "\n"; +foreach my $prg (split /\s+/,$1) { + my $proj = $solution->AddProject($prg,'exe','bin'); + $mf =~ m{$prg\s*:\s*(.*)$}m || die 'Could not find script define for $prg' . "\n"; + my @files = split /\s+/,$1; + foreach my $f (@files) { + if ($f =~ /\/keywords\.o$/) { + $proj->AddFile('src\backend\parser\keywords.c'); + } + else { + $f =~ s/\.o$/\.c/; + if ($f eq 'dumputils.c') { + $proj->AddFile('src\bin\pg_dump\dumputils.c'); + } + elsif ($f =~ /print\.c$/) { # Also catches mbprint.c + $proj->AddFile('src\bin\psql\\' . $f); + } + else { + $proj->AddFile('src\bin\scripts\\' . $f); + } + } + } + $proj->AddIncludeDir('src\interfaces\libpq'); + $proj->AddIncludeDir('src\bin\pg_dump'); + $proj->AddIncludeDir('src\bin\psql'); + $proj->AddReference($libpq,$libpgport); + $proj->AddResourceFile('src\bin\scripts','PostgreSQL Utility'); + $proj->AddLibrary('debug\libpgport\libpgport.lib'); + $proj->AddLibrary('debug\libpq\libpq.lib'); +} +$/ = $t; + + +# Regression DLLs +my $regress = $solution->AddProject('regress','dll','misc'); +$regress->AddFile('src\test\regress\regress.c'); +$regress->AddReference($postgres); + +my $refint = $solution->AddProject('refint','dll','contrib'); +$refint->AddFile('contrib\spi\refint.c'); +$refint->AddReference($postgres); +$refint->AddDefine('REFINT_VERBOSE'); + +my $autoinc = $solution->AddProject('autoinc','dll','contrib'); +$autoinc ->AddFile('contrib\spi\autoinc.c'); +$autoinc->AddReference($postgres); + + +$solution->Save(); + +##################### +# Utility functions # +##################### + +# Add a simple frontend project (exe) +sub AddSimpleFrontend { + my $n = shift; + my $uselibpq= shift; + + my $p = $solution->AddProject($n,'exe','bin'); + $p->AddDir('src\bin\\' . $n); + $p->AddDefine('FRONTEND'); + $p->AddReference($libpgport); + $p->AddLibrary('debug\libpgport\libpgport.lib'); + if ($uselibpq) { + $p->AddIncludeDir('src\interfaces\libpq'); + $p->AddReference($libpq); + $p->AddLibrary('debug\libpq\libpq.lib'); + } + return $p; +} + diff --git a/src/tools/msvc/pgbison.bat b/src/tools/msvc/pgbison.bat new file mode 100755 index 0000000000..8e6bfc82f9 --- /dev/null +++ b/src/tools/msvc/pgbison.bat @@ -0,0 +1,27 @@ +@echo off +bison -V > NUL +if errorlevel 1 goto nobison + +if "%1" == "src\backend\parser\gram.y" call :generate %1 src\backend\parser\gram.c src\include\parser\parse.h +if "%1" == "src\backend\bootstrap\bootparse.y" call :generate %1 src\backend\bootstrap\bootparse.c src\backend\bootstrap\bootstrap_tokens.h +if "%1" == "src\pl\plpgsql\src\gram.y" call :generate %1 src\pl\plpgsql\src\pl_gram.c src\pl\plpgsql\src\pl.tab.h +if "%1" == "src\interfaces\ecpg\preproc\preproc.y" call :generate %1 src\interfaces\ecpg\preproc\preproc.c src\interfaces\ecpg\preproc\preproc.h + +echo Unknown bison input: %1 +exit 1 + +:generate +SET fn=%1 +bison -d %fn% +if errorlevel 1 exit 1 +copy /y %fn:~0,-2%.tab.c %2 +if errorlevel 1 exit 1 +copy /y %fn:~0,-2%.tab.h %3 +if errorlevel 1 exit 1 +del %fn:~0,-2%.tab.* +exit 0 + + +:nobison +echo WARNING! Bison install not found, attempting to build without! +exit 0 diff --git a/src/tools/msvc/pgflex.bat b/src/tools/msvc/pgflex.bat new file mode 100755 index 0000000000..90479b6bc0 --- /dev/null +++ b/src/tools/msvc/pgflex.bat @@ -0,0 +1,21 @@ +@echo off +flex -V > NUL +if errorlevel 1 goto noflex + +if "%1" == "src\backend\parser\scan.l" call :generate %1 src\backend\parser\scan.c -CF +if "%1" == "src\backend\bootstrap\bootscanner.l" call :generate %1 src\backend\bootstrap\bootscanner.c +if "%1" == "src\backend\utils\misc\guc-file.l" call :generate %1 src\backend\utils\misc\guc-file.c +if "%1" == "src\pl\plpgsql\src\scan.l" call :generate %1 src\pl\plpgsql\src\pl_scan.c +if "%1" == "src\interfaces\ecpg\preproc\pgc.l" call :generate %1 src\interfaces\ecpg\preproc\pgc.c +if "%1" == "src\bin\psql\psqlscan.l" call :generate %1 src\bin\psql\psqlscan.c + +echo Unknown flex input: %1 +exit 1 + +:generate +flex %3 -o%2 %1 +exit %errorlevel% + +:noflex +echo WARNING! flex install not found, attempting to build without +exit 0 -- 2.11.0