summaryrefslogtreecommitdiffstats
path: root/src/tools/msvc/Install.pm
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/tools/msvc/Install.pm777
1 files changed, 777 insertions, 0 deletions
diff --git a/src/tools/msvc/Install.pm b/src/tools/msvc/Install.pm
new file mode 100644
index 0000000..de22c9b
--- /dev/null
+++ b/src/tools/msvc/Install.pm
@@ -0,0 +1,777 @@
+
+# Copyright (c) 2021, PostgreSQL Global Development Group
+
+package Install;
+
+#
+# Package that provides 'make install' functionality for msvc builds
+#
+# src/tools/msvc/Install.pm
+#
+use strict;
+use warnings;
+use Carp;
+use File::Basename;
+use File::Copy;
+use File::Find ();
+
+use Exporter;
+our (@ISA, @EXPORT_OK);
+@ISA = qw(Exporter);
+@EXPORT_OK = qw(Install);
+
+my $insttype;
+my @client_contribs = ('oid2name', 'pgbench', 'vacuumlo');
+my @client_program_files = (
+ 'clusterdb', 'createdb', 'createuser', 'dropdb',
+ 'dropuser', 'ecpg', 'libecpg', 'libecpg_compat',
+ 'libpgtypes', 'libpq', 'pg_amcheck', 'pg_basebackup',
+ 'pg_config', 'pg_dump', 'pg_dumpall', 'pg_isready',
+ 'pg_receivewal', 'pg_recvlogical', 'pg_restore', 'psql',
+ 'reindexdb', 'vacuumdb', @client_contribs);
+
+sub lcopy
+{
+ my $src = shift;
+ my $target = shift;
+
+ if (-f $target)
+ {
+ unlink $target || confess "Could not delete $target\n";
+ }
+
+ (my $retval = copy($src, $target))
+ || confess "Could not copy $src to $target\n";
+
+ return $retval;
+}
+
+sub Install
+{
+ $| = 1;
+
+ my $target = shift;
+ $insttype = shift;
+ $insttype = "all" unless ($insttype);
+
+ # if called from vcregress, the config will be passed to us
+ # so no need to re-include these
+ our $config = shift;
+ unless ($config)
+ {
+
+ # suppress warning about harmless redeclaration of $config
+ no warnings 'misc';
+ do "./config_default.pl";
+ do "./config.pl" if (-f "config.pl");
+ }
+
+ # Move to the root path depending on the current location.
+ if (-f "../../../configure")
+ {
+ chdir("../../..");
+ }
+ elsif (-f "../../../../configure")
+ {
+ chdir("../../../..");
+ }
+
+ my $conf = "";
+ if (-d "debug")
+ {
+ $conf = "debug";
+ }
+ if (-d "release")
+ {
+ $conf = "release";
+ }
+ die "Could not find debug or release binaries" if ($conf eq "");
+ my $majorver = DetermineMajorVersion();
+ print "Installing version $majorver for $conf in $target\n";
+
+ my @client_dirs = ('bin', 'lib', 'share', 'symbols');
+ my @all_dirs = (
+ @client_dirs, 'doc', 'doc/contrib', 'doc/extension', 'share/contrib',
+ 'share/extension', 'share/timezonesets', 'share/tsearch_data');
+ if ($insttype eq "client")
+ {
+ EnsureDirectories($target, @client_dirs);
+ }
+ else
+ {
+ EnsureDirectories($target, @all_dirs);
+ }
+
+ CopySolutionOutput($conf, $target);
+ my $sample_files = [];
+ my @top_dir = ("src");
+ @top_dir = ("src\\bin", "src\\interfaces") if ($insttype eq "client");
+ File::Find::find(
+ {
+ wanted => sub {
+ /^.*\.sample\z/s
+ && push(@$sample_files, $File::Find::name);
+
+ # Don't find files of in-tree temporary installations.
+ $_ eq 'share' and $File::Find::prune = 1;
+ }
+ },
+ @top_dir);
+ CopySetOfFiles('config files', $sample_files, $target . '/share/');
+ CopyFiles(
+ 'Import libraries',
+ $target . '/lib/',
+ "$conf\\", "postgres\\postgres.lib", "libpgcommon\\libpgcommon.lib",
+ "libpgport\\libpgport.lib");
+ CopyContribFiles($config, $target);
+ CopyIncludeFiles($target);
+
+ if ($insttype ne "client")
+ {
+ CopySetOfFiles(
+ 'timezone names',
+ [ glob('src\timezone\tznames\*.txt') ],
+ $target . '/share/timezonesets/');
+ CopyFiles(
+ 'timezone sets',
+ $target . '/share/timezonesets/',
+ 'src/timezone/tznames/', 'Default', 'Australia', 'India');
+ CopySetOfFiles(
+ 'BKI files',
+ [ glob("src\\backend\\catalog\\postgres.*") ],
+ $target . '/share/');
+ CopySetOfFiles(
+ 'SQL files',
+ [ glob("src\\backend\\catalog\\*.sql") ],
+ $target . '/share/');
+ CopyFiles(
+ 'Information schema data', $target . '/share/',
+ 'src/backend/catalog/', 'sql_features.txt');
+ CopyFiles(
+ 'Error code data', $target . '/share/',
+ 'src/backend/utils/', 'errcodes.txt');
+ GenerateTimezoneFiles($target, $conf);
+ GenerateTsearchFiles($target);
+ CopySetOfFiles(
+ 'Stopword files',
+ [ glob("src\\backend\\snowball\\stopwords\\*.stop") ],
+ $target . '/share/tsearch_data/');
+ CopySetOfFiles(
+ 'Dictionaries sample files',
+ [ glob("src\\backend\\tsearch\\dicts\\*_sample*") ],
+ $target . '/share/tsearch_data/');
+
+ my $pl_extension_files = [];
+ my @pldirs = ('src/pl/plpgsql/src');
+ push @pldirs, "src/pl/plperl" if $config->{perl};
+ push @pldirs, "src/pl/plpython" if $config->{python};
+ push @pldirs, "src/pl/tcl" if $config->{tcl};
+ File::Find::find(
+ {
+ wanted => sub {
+ /^(.*--.*\.sql|.*\.control)\z/s
+ && push(@$pl_extension_files, $File::Find::name);
+
+ # Don't find files of in-tree temporary installations.
+ $_ eq 'share' and $File::Find::prune = 1;
+ }
+ },
+ @pldirs);
+ CopySetOfFiles('PL Extension files',
+ $pl_extension_files, $target . '/share/extension/');
+ }
+
+ GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls});
+
+ print "Installation complete.\n";
+ return;
+}
+
+sub EnsureDirectories
+{
+ my $target = shift;
+ mkdir $target unless -d ($target);
+ while (my $d = shift)
+ {
+ mkdir $target . '/' . $d unless -d ($target . '/' . $d);
+ }
+ return;
+}
+
+sub CopyFiles
+{
+ my $what = shift;
+ my $target = shift;
+ my $basedir = shift;
+
+ print "Copying $what";
+ while (my $f = shift)
+ {
+ print ".";
+ $f = $basedir . $f;
+ die "No file $f\n" if (!-f $f);
+ lcopy($f, $target . basename($f)) || croak "Could not copy $f: $!\n";
+ }
+ print "\n";
+ return;
+}
+
+sub CopySetOfFiles
+{
+ my $what = shift;
+ my $flist = shift;
+ my $target = shift;
+ print "Copying $what" if $what;
+ foreach (@$flist)
+ {
+ my $tgt = $target . basename($_);
+ print ".";
+ lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
+ }
+ print "\n";
+ return;
+}
+
+sub CopySolutionOutput
+{
+ my $conf = shift;
+ my $target = shift;
+ my $rem =
+ qr{Project\("\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942\}"\) = "([^"]+)"};
+
+ my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
+
+ my $vcproj = 'vcproj';
+ if ($sln =~
+ /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/
+ && $1 >= 11)
+ {
+ $vcproj = 'vcxproj';
+ }
+
+ print "Copying build output files...";
+ while ($sln =~ $rem)
+ {
+ my $pf = $1;
+
+ # Hash-of-arrays listing where to install things. For each
+ # subdirectory there's a hash key, and the value is an array
+ # of file extensions to install in that subdirectory. Example:
+ # { 'bin' => [ 'dll', 'lib' ],
+ # 'lib' => [ 'lib' ] }
+ my %install_list;
+ my $is_sharedlib = 0;
+
+ $sln =~ s/$rem//;
+
+ next
+ if ($insttype eq "client" && !grep { $_ eq $pf }
+ @client_program_files);
+
+ my $proj = read_file("$pf.$vcproj")
+ || croak "Could not open $pf.$vcproj\n";
+
+ # Check if this project uses a shared library by looking if
+ # SO_MAJOR_VERSION is defined in its Makefile, whose path
+ # can be found using the resource file of this project.
+ if (( $vcproj eq 'vcxproj'
+ && $proj =~ qr{ResourceCompile\s*Include="([^"]+)"})
+ || ( $vcproj eq 'vcproj'
+ && $proj =~ qr{File\s*RelativePath="([^\"]+)\.rc"}))
+ {
+ my $projpath = dirname($1);
+ my $mfname =
+ -e "$projpath/GNUmakefile"
+ ? "$projpath/GNUmakefile"
+ : "$projpath/Makefile";
+ my $mf = read_file($mfname) || croak "Could not open $mfname\n";
+
+ $is_sharedlib = 1 if ($mf =~ /^SO_MAJOR_VERSION\s*=\s*(.*)$/mg);
+ }
+
+ if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"})
+ {
+ if ($1 == 1)
+ {
+ push(@{ $install_list{'bin'} }, "exe");
+ }
+ elsif ($1 == 2)
+ {
+ push(@{ $install_list{'lib'} }, "dll");
+ if ($is_sharedlib)
+ {
+ push(@{ $install_list{'bin'} }, "dll");
+ push(@{ $install_list{'lib'} }, "lib");
+ }
+ }
+ else
+ {
+
+ # Static libraries, such as libpgport, only used internally
+ # during build, don't install.
+ next;
+ }
+ }
+ elsif ($vcproj eq 'vcxproj'
+ && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>})
+ {
+ if ($1 eq 'Application')
+ {
+ push(@{ $install_list{'bin'} }, "exe");
+ }
+ elsif ($1 eq 'DynamicLibrary')
+ {
+ push(@{ $install_list{'lib'} }, "dll");
+ if ($is_sharedlib)
+ {
+ push(@{ $install_list{'bin'} }, "dll");
+ push(@{ $install_list{'lib'} }, "lib");
+ }
+ }
+ else # 'StaticLibrary'
+ {
+
+ # Static lib, such as libpgport, only used internally
+ # during build, don't install.
+ next;
+ }
+ }
+ else
+ {
+ croak "Could not parse $pf.$vcproj\n";
+ }
+
+ # Install each element
+ foreach my $dir (keys %install_list)
+ {
+ foreach my $ext (@{ $install_list{$dir} })
+ {
+ lcopy("$conf\\$pf\\$pf.$ext", "$target\\$dir\\$pf.$ext")
+ || croak "Could not copy $pf.$ext\n";
+ }
+ }
+ lcopy("$conf\\$pf\\$pf.pdb", "$target\\symbols\\$pf.pdb")
+ || croak "Could not copy $pf.pdb\n";
+ print ".";
+ }
+ print "\n";
+ return;
+}
+
+sub GenerateTimezoneFiles
+{
+ my $target = shift;
+ my $conf = shift;
+ my $mf = read_file("src/timezone/Makefile");
+ $mf =~ s{\\\r?\n}{}g;
+
+ $mf =~ /^TZDATAFILES\s*:?=\s*(.*)$/m
+ || die "Could not find TZDATAFILES line in timezone makefile\n";
+ my @tzfiles = split /\s+/, $1;
+
+ print "Generating timezone files...";
+
+ my @args = ("$conf/zic/zic", '-d', "$target/share/timezone");
+ foreach (@tzfiles)
+ {
+ my $tzfile = $_;
+ $tzfile =~ s|\$\(srcdir\)|src/timezone|;
+ push(@args, $tzfile);
+ }
+
+ system(@args);
+ print "\n";
+ return;
+}
+
+sub GenerateTsearchFiles
+{
+ my $target = shift;
+
+ print "Generating tsearch script...";
+ my $F;
+ my $tmpl = read_file('src/backend/snowball/snowball.sql.in');
+ my $mf = read_file('src/backend/snowball/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ /^LANGUAGES\s*=\s*(.*)$/m
+ || die "Could not find LANGUAGES line in snowball Makefile\n";
+ my @pieces = split /\s+/, $1;
+ open($F, '>', "$target/share/snowball_create.sql")
+ || die "Could not write snowball_create.sql";
+ print $F read_file('src/backend/snowball/snowball_func.sql.in');
+
+ while ($#pieces > 0)
+ {
+ my $lang = shift @pieces || last;
+ my $asclang = shift @pieces || last;
+ my $txt = $tmpl;
+ my $stop = '';
+
+ if (-s "src/backend/snowball/stopwords/$lang.stop")
+ {
+ $stop = ", StopWords=$lang";
+ }
+
+ $txt =~ s#_LANGNAME_#${lang}#gs;
+ $txt =~ s#_DICTNAME_#${lang}_stem#gs;
+ $txt =~ s#_CFGNAME_#${lang}#gs;
+ $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs;
+ $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs;
+ $txt =~ s#_STOPWORDS_#$stop#gs;
+ print $F $txt;
+ print ".";
+ }
+ close($F);
+ print "\n";
+ return;
+}
+
+sub CopyContribFiles
+{
+ my $config = shift;
+ my $target = shift;
+
+ print "Copying contrib data files...";
+ foreach my $subdir ('contrib', 'src/test/modules')
+ {
+ my $D;
+ opendir($D, $subdir) || croak "Could not opendir on $subdir!\n";
+ while (my $d = readdir($D))
+ {
+ # These configuration-based exclusions must match vcregress.pl
+ next if ($d eq "uuid-ossp" && !defined($config->{uuid}));
+ next if ($d eq "sslinfo" && !defined($config->{openssl}));
+ next if ($d eq "xml2" && !defined($config->{xml}));
+ next if ($d =~ /_plperl$/ && !defined($config->{perl}));
+ next if ($d =~ /_plpython$/ && !defined($config->{python}));
+ next if ($d eq "sepgsql");
+
+ CopySubdirFiles($subdir, $d, $config, $target);
+ }
+ }
+ print "\n";
+ return;
+}
+
+sub CopySubdirFiles
+{
+ my $subdir = shift;
+ my $module = shift;
+ my $config = shift;
+ my $target = shift;
+
+ return if ($module =~ /^\./);
+ return unless (-f "$subdir/$module/Makefile");
+ return
+ if ($insttype eq "client" && !grep { $_ eq $module } @client_contribs);
+
+ my $mf = read_file("$subdir/$module/Makefile");
+ $mf =~ s{\\\r?\n}{}g;
+
+ # Note: we currently don't support setting MODULEDIR in the makefile
+ my $moduledir = 'contrib';
+
+ my $flist = '';
+ if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 }
+ if ($flist ne '')
+ {
+ $moduledir = 'extension';
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f.control",
+ "$target/share/extension/$f.control")
+ || croak("Could not copy file $f.control in contrib $module");
+ print '.';
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) { $flist .= $1 }
+ if ($mf =~ /^DATA\s*=\s*(.*)$/m) { $flist .= " $1" }
+ $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built
+
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f",
+ "$target/share/$moduledir/" . basename($f))
+ || croak("Could not copy file $f in contrib $module");
+ print '.';
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 }
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f",
+ "$target/share/tsearch_data/" . basename($f))
+ || croak("Could not copy file $f in $subdir $module");
+ print '.';
+ }
+ }
+
+ {
+ $flist = '';
+ if ($mf =~ /^HEADERS\s*=\s*(.*)$/m) { $flist .= $1 }
+ my @modlist = ();
+ my %fmodlist = ();
+ while ($mf =~ /^HEADERS_([^\s=]+)\s*=\s*(.*)$/mg)
+ {
+ $fmodlist{$1} .= $2;
+ }
+
+ if ($mf =~ /^MODULE_big\s*=\s*(.*)$/m)
+ {
+ push @modlist, $1;
+ if ($flist ne '')
+ {
+ $fmodlist{$1} = $flist;
+ $flist = '';
+ }
+ }
+ elsif ($mf =~ /^MODULES\s*=\s*(.*)$/m)
+ {
+ push @modlist, split /\s+/, $1;
+ }
+
+ croak "HEADERS requires MODULE_big in $subdir $module"
+ if $flist ne '';
+
+ foreach my $mod (keys %fmodlist)
+ {
+ croak "HEADERS_$mod for unknown module in $subdir $module"
+ unless grep { $_ eq $mod } @modlist;
+ $flist = ParseAndCleanRule($fmodlist{$mod}, $mf);
+ EnsureDirectories($target, "include", "include/server",
+ "include/server/$moduledir",
+ "include/server/$moduledir/$mod");
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f",
+ "$target/include/server/$moduledir/$mod/" . basename($f))
+ || croak("Could not copy file $f in $subdir $module");
+ print '.';
+ }
+ }
+ }
+
+ $flist = '';
+ if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 }
+ if ($flist ne '')
+ {
+ $flist = ParseAndCleanRule($flist, $mf);
+
+ # Special case for contrib/spi
+ $flist =
+ "autoinc.example insert_username.example moddatetime.example refint.example"
+ if ($module eq 'spi');
+ foreach my $f (split /\s+/, $flist)
+ {
+ lcopy("$subdir/$module/$f", "$target/doc/$moduledir/$f")
+ || croak("Could not copy file $f in contrib $module");
+ print '.';
+ }
+ }
+ return;
+}
+
+sub ParseAndCleanRule
+{
+ my $flist = shift;
+ my $mf = shift;
+
+ # Strip out $(addsuffix) rules
+ if (index($flist, '$(addsuffix ') >= 0)
+ {
+ my $pcount = 0;
+ my $i;
+ for (
+ $i = index($flist, '$(addsuffix ') + 12;
+ $i < length($flist);
+ $i++)
+ {
+ $pcount++ if (substr($flist, $i, 1) eq '(');
+ $pcount-- if (substr($flist, $i, 1) eq ')');
+ last if ($pcount < 0);
+ }
+ $flist =
+ substr($flist, 0, index($flist, '$(addsuffix '))
+ . substr($flist, $i + 1);
+ }
+ return $flist;
+}
+
+sub CopyIncludeFiles
+{
+ my $target = shift;
+
+ EnsureDirectories($target, 'include', 'include/libpq', 'include/internal',
+ 'include/internal/libpq', 'include/server', 'include/server/parser');
+
+ CopyFiles(
+ 'Public headers', $target . '/include/',
+ 'src/include/', 'postgres_ext.h',
+ 'pg_config.h', 'pg_config_ext.h',
+ 'pg_config_os.h', 'pg_config_manual.h');
+ lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
+ || croak 'Could not copy libpq-fs.h';
+
+ CopyFiles(
+ 'Libpq headers',
+ $target . '/include/',
+ 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h');
+ CopyFiles(
+ 'Libpq internal headers',
+ $target . '/include/internal/',
+ 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h');
+
+ CopyFiles(
+ 'Internal headers',
+ $target . '/include/internal/',
+ 'src/include/', 'c.h', 'port.h', 'postgres_fe.h');
+ lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
+ || croak 'Could not copy pqcomm.h';
+
+ CopyFiles(
+ 'Server headers',
+ $target . '/include/server/',
+ 'src/include/', 'pg_config.h', 'pg_config_ext.h', 'pg_config_os.h');
+ CopyFiles(
+ 'Grammar header',
+ $target . '/include/server/parser/',
+ 'src/backend/parser/', 'gram.h');
+ CopySetOfFiles(
+ '',
+ [ glob("src\\include\\*.h") ],
+ $target . '/include/server/');
+ my $D;
+ opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
+
+ CopyFiles(
+ 'PL/pgSQL header',
+ $target . '/include/server/',
+ 'src/pl/plpgsql/src/', 'plpgsql.h');
+
+ # some xcopy progs don't like mixed slash style paths
+ (my $ctarget = $target) =~ s!/!\\!g;
+ while (my $d = readdir($D))
+ {
+ next if ($d =~ /^\./);
+ next if ($d eq '.git');
+ next if ($d eq 'CVS');
+ next unless (-d "src/include/$d");
+
+ EnsureDirectories("$target/include/server/$d");
+ my @args = (
+ 'xcopy', '/s', '/i', '/q', '/r', '/y', "src\\include\\$d\\*.h",
+ "$ctarget\\include\\server\\$d\\");
+ system(@args) && croak("Failed to copy include directory $d\n");
+ }
+ closedir($D);
+
+ my $mf = read_file('src/interfaces/ecpg/include/Makefile');
+ $mf =~ s{\\\r?\n}{}g;
+ $mf =~ /^ecpg_headers\s*=\s*(.*)$/m
+ || croak "Could not find ecpg_headers line\n";
+ CopyFiles(
+ 'ECPG headers',
+ $target . '/include/',
+ 'src/interfaces/ecpg/include/',
+ 'ecpg_config.h', split /\s+/, $1);
+ $mf =~ /^informix_headers\s*=\s*(.*)$/m
+ || croak "Could not find informix_headers line\n";
+ EnsureDirectories($target . '/include', 'informix', 'informix/esql');
+ CopyFiles(
+ 'ECPG informix headers',
+ $target . '/include/informix/esql/',
+ 'src/interfaces/ecpg/include/',
+ split /\s+/, $1);
+ return;
+}
+
+sub GenerateNLSFiles
+{
+ my $target = shift;
+ my $nlspath = shift;
+ my $majorver = shift;
+
+ print "Installing NLS files...";
+ EnsureDirectories($target, "share/locale");
+ my @flist;
+ File::Find::find(
+ {
+ wanted => sub {
+ /^nls\.mk\z/s
+ && !push(@flist, $File::Find::name);
+ }
+ },
+ "src");
+ foreach (@flist)
+ {
+ my $prgm = DetermineCatalogName($_);
+ s/nls.mk/po/;
+ my $dir = $_;
+ next unless ($dir =~ /([^\/]+)\/po$/);
+ foreach (glob("$dir/*.po"))
+ {
+ my $lang;
+ next unless /([^\/]+)\.po/;
+ $lang = $1;
+
+ EnsureDirectories($target, "share/locale/$lang",
+ "share/locale/$lang/LC_MESSAGES");
+ my @args = (
+ "$nlspath\\bin\\msgfmt",
+ '-o',
+ "$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo",
+ $_);
+ system(@args) && croak("Could not run msgfmt on $dir\\$_");
+ print ".";
+ }
+ }
+ print "\n";
+ return;
+}
+
+sub DetermineMajorVersion
+{
+ my $f = read_file('src/include/pg_config.h')
+ || croak 'Could not open pg_config.h';
+ $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m
+ || croak 'Could not determine major version';
+ return $1;
+}
+
+sub DetermineCatalogName
+{
+ my $filename = shift;
+
+ my $f = read_file($filename) || croak "Could not open $filename";
+ $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m
+ || croak "Could not determine catalog name in $filename";
+ return $1;
+}
+
+sub read_file
+{
+ my $filename = shift;
+ my $F;
+ local $/ = undef;
+ open($F, '<', $filename) || die "Could not open file $filename\n";
+ my $txt = <$F>;
+ close($F);
+
+ return $txt;
+}
+
+1;