1package Install; 2 3# 4# Package that provides 'make install' functionality for msvc builds 5# 6# src/tools/msvc/Install.pm 7# 8use strict; 9use warnings; 10use Carp; 11use File::Basename; 12use File::Copy; 13use File::Find (); 14 15use Exporter; 16our (@ISA, @EXPORT_OK); 17@ISA = qw(Exporter); 18@EXPORT_OK = qw(Install); 19 20my $insttype; 21my @client_contribs = ('oid2name', 'pgbench', 'vacuumlo'); 22my @client_program_files = ( 23 'clusterdb', 'createdb', 'createlang', 'createuser', 24 'dropdb', 'droplang', 'dropuser', 'ecpg', 25 'libecpg', 'libecpg_compat', 'libpgtypes', 'libpq', 26 'pg_basebackup', 'pg_config', 'pg_dump', 'pg_dumpall', 27 'pg_isready', 'pg_receivexlog', 'pg_recvlogical', 'pg_restore', 28 'psql', 'reindexdb', 'vacuumdb', @client_contribs); 29 30sub lcopy 31{ 32 my $src = shift; 33 my $target = shift; 34 35 if (-f $target) 36 { 37 unlink $target || confess "Could not delete $target\n"; 38 } 39 40 copy($src, $target) 41 || confess "Could not copy $src to $target\n"; 42 43} 44 45sub Install 46{ 47 $| = 1; 48 49 my $target = shift; 50 $insttype = shift; 51 $insttype = "all" unless ($insttype); 52 53 # if called from vcregress, the config will be passed to us 54 # so no need to re-include these 55 our $config = shift; 56 unless ($config) 57 { 58 59 # suppress warning about harmless redeclaration of $config 60 no warnings 'misc'; 61 do "./config_default.pl"; 62 do "./config.pl" if (-f "config.pl"); 63 } 64 65 # Move to the root path depending on the current location. 66 if (-f "../../../configure") 67 { 68 chdir("../../.."); 69 } 70 elsif (-f "../../../../configure") 71 { 72 chdir("../../../.."); 73 } 74 75 my $conf = ""; 76 if (-d "debug") 77 { 78 $conf = "debug"; 79 } 80 if (-d "release") 81 { 82 $conf = "release"; 83 } 84 die "Could not find debug or release binaries" if ($conf eq ""); 85 my $majorver = DetermineMajorVersion(); 86 print "Installing version $majorver for $conf in $target\n"; 87 88 my @client_dirs = ('bin', 'lib', 'share', 'symbols'); 89 my @all_dirs = ( 90 @client_dirs, 'doc', 'doc/contrib', 'doc/extension', 'share/contrib', 91 'share/extension', 'share/timezonesets', 'share/tsearch_data'); 92 if ($insttype eq "client") 93 { 94 EnsureDirectories($target, @client_dirs); 95 } 96 else 97 { 98 EnsureDirectories($target, @all_dirs); 99 } 100 101 CopySolutionOutput($conf, $target); 102 my $sample_files = []; 103 my @top_dir = ("src"); 104 @top_dir = ("src\\bin", "src\\interfaces") if ($insttype eq "client"); 105 File::Find::find( 106 { wanted => sub { 107 /^.*\.sample\z/s 108 && push(@$sample_files, $File::Find::name); 109 110 # Don't find files of in-tree temporary installations. 111 $_ eq 'share' and $File::Find::prune = 1; 112 } 113 }, 114 @top_dir); 115 CopySetOfFiles('config files', $sample_files, $target . '/share/'); 116 CopyFiles( 117 'Import libraries', 118 $target . '/lib/', 119 "$conf\\", "postgres\\postgres.lib", "libpgcommon\\libpgcommon.lib", 120 "libpgport\\libpgport.lib"); 121 CopyContribFiles($config, $target); 122 CopyIncludeFiles($target); 123 124 if ($insttype ne "client") 125 { 126 CopySetOfFiles( 127 'timezone names', 128 [ glob('src\timezone\tznames\*.txt') ], 129 $target . '/share/timezonesets/'); 130 CopyFiles( 131 'timezone sets', 132 $target . '/share/timezonesets/', 133 'src/timezone/tznames/', 'Default', 'Australia', 'India'); 134 CopySetOfFiles( 135 'BKI files', 136 [ glob("src\\backend\\catalog\\postgres.*") ], 137 $target . '/share/'); 138 CopySetOfFiles( 139 'SQL files', 140 [ glob("src\\backend\\catalog\\*.sql") ], 141 $target . '/share/'); 142 CopyFiles( 143 'Information schema data', $target . '/share/', 144 'src/backend/catalog/', 'sql_features.txt'); 145 GenerateConversionScript($target); 146 GenerateTimezoneFiles($target, $conf); 147 GenerateTsearchFiles($target); 148 CopySetOfFiles( 149 'Stopword files', 150 [ glob("src\\backend\\snowball\\stopwords\\*.stop") ], 151 $target . '/share/tsearch_data/'); 152 CopySetOfFiles( 153 'Dictionaries sample files', 154 [ glob("src\\backend\\tsearch\\dicts\\*_sample*") ], 155 $target . '/share/tsearch_data/'); 156 157 my $pl_extension_files = []; 158 my @pldirs = ('src/pl/plpgsql/src'); 159 push @pldirs, "src/pl/plperl" if $config->{perl}; 160 push @pldirs, "src/pl/plpython" if $config->{python}; 161 push @pldirs, "src/pl/tcl" if $config->{tcl}; 162 File::Find::find( 163 { wanted => sub { 164 /^(.*--.*\.sql|.*\.control)\z/s 165 && push(@$pl_extension_files, $File::Find::name); 166 167 # Don't find files of in-tree temporary installations. 168 $_ eq 'share' and $File::Find::prune = 1; 169 } 170 }, 171 @pldirs); 172 CopySetOfFiles('PL Extension files', 173 $pl_extension_files, $target . '/share/extension/'); 174 } 175 176 GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls}); 177 178 print "Installation complete.\n"; 179} 180 181sub EnsureDirectories 182{ 183 my $target = shift; 184 mkdir $target unless -d ($target); 185 while (my $d = shift) 186 { 187 mkdir $target . '/' . $d unless -d ($target . '/' . $d); 188 } 189} 190 191sub CopyFiles 192{ 193 my $what = shift; 194 my $target = shift; 195 my $basedir = shift; 196 197 print "Copying $what"; 198 while (my $f = shift) 199 { 200 print "."; 201 $f = $basedir . $f; 202 die "No file $f\n" if (!-f $f); 203 lcopy($f, $target . basename($f)); 204 } 205 print "\n"; 206} 207 208sub CopySetOfFiles 209{ 210 my $what = shift; 211 my $flist = shift; 212 my $target = shift; 213 print "Copying $what" if $what; 214 foreach (@$flist) 215 { 216 my $tgt = $target . basename($_); 217 print "."; 218 lcopy($_, $tgt) || croak "Could not copy $_: $!\n"; 219 } 220 print "\n"; 221} 222 223sub CopySolutionOutput 224{ 225 my $conf = shift; 226 my $target = shift; 227 my $rem = 228 qr{Project\("\{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942\}"\) = "([^"]+)"}; 229 230 my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n"; 231 232 my $vcproj = 'vcproj'; 233 if ($sln =~ 234 /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/ 235 && $1 >= 11) 236 { 237 $vcproj = 'vcxproj'; 238 } 239 240 print "Copying build output files..."; 241 while ($sln =~ $rem) 242 { 243 my $pf = $1; 244 245 # Hash-of-arrays listing where to install things. For each 246 # subdirectory there's a hash key, and the value is an array 247 # of file extensions to install in that subdirectory. Example: 248 # { 'bin' => [ 'dll', 'lib' ], 249 # 'lib' => [ 'lib' ] } 250 my %install_list; 251 my $is_sharedlib = 0; 252 253 $sln =~ s/$rem//; 254 255 next 256 if ($insttype eq "client" && !grep { $_ eq $pf } 257 @client_program_files); 258 259 my $proj = read_file("$pf.$vcproj") 260 || croak "Could not open $pf.$vcproj\n"; 261 262 # Check if this project uses a shared library by looking if 263 # SO_MAJOR_VERSION is defined in its Makefile, whose path 264 # can be found using the resource file of this project. 265 if (( $vcproj eq 'vcxproj' 266 && $proj =~ qr{ResourceCompile\s*Include="([^"]+)"}) 267 || ( $vcproj eq 'vcproj' 268 && $proj =~ qr{File\s*RelativePath="([^\"]+)\.rc"})) 269 { 270 my $projpath = dirname($1); 271 my $mfname = 272 -e "$projpath/GNUmakefile" 273 ? "$projpath/GNUmakefile" 274 : "$projpath/Makefile"; 275 my $mf = read_file($mfname) || croak "Could not open $mfname\n"; 276 277 $is_sharedlib = 1 if ($mf =~ /^SO_MAJOR_VERSION\s*=\s*(.*)$/mg); 278 } 279 280 if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"}) 281 { 282 if ($1 == 1) 283 { 284 push(@{ $install_list{'bin'} }, "exe"); 285 } 286 elsif ($1 == 2) 287 { 288 push(@{ $install_list{'lib'} }, "dll"); 289 if ($is_sharedlib) 290 { 291 push(@{ $install_list{'bin'} }, "dll"); 292 push(@{ $install_list{'lib'} }, "lib"); 293 } 294 } 295 else 296 { 297 298 # Static libraries, such as libpgport, only used internally 299 # during build, don't install. 300 next; 301 } 302 } 303 elsif ($vcproj eq 'vcxproj' 304 && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>}) 305 { 306 if ($1 eq 'Application') 307 { 308 push(@{ $install_list{'bin'} }, "exe"); 309 } 310 elsif ($1 eq 'DynamicLibrary') 311 { 312 push(@{ $install_list{'lib'} }, "dll"); 313 if ($is_sharedlib) 314 { 315 push(@{ $install_list{'bin'} }, "dll"); 316 push(@{ $install_list{'lib'} }, "lib"); 317 } 318 } 319 else # 'StaticLibrary' 320 { 321 322 # Static lib, such as libpgport, only used internally 323 # during build, don't install. 324 next; 325 } 326 } 327 else 328 { 329 croak "Could not parse $pf.$vcproj\n"; 330 } 331 332 # Install each element 333 foreach my $dir (keys %install_list) 334 { 335 foreach my $ext (@{ $install_list{$dir} }) 336 { 337 lcopy("$conf\\$pf\\$pf.$ext", "$target\\$dir\\$pf.$ext") 338 || croak "Could not copy $pf.$ext\n"; 339 } 340 } 341 lcopy("$conf\\$pf\\$pf.pdb", "$target\\symbols\\$pf.pdb") 342 || croak "Could not copy $pf.pdb\n"; 343 print "."; 344 } 345 print "\n"; 346} 347 348sub GenerateConversionScript 349{ 350 my $target = shift; 351 my $sql = ""; 352 my $F; 353 354 print "Generating conversion proc script..."; 355 my $mf = read_file('src/backend/utils/mb/conversion_procs/Makefile'); 356 $mf =~ s{\\\r?\n}{}g; 357 $mf =~ /^CONVERSIONS\s*=\s*(.*)$/m 358 || die "Could not find CONVERSIONS line in conversions Makefile\n"; 359 my @pieces = split /\s+/, $1; 360 while ($#pieces > 0) 361 { 362 my $name = shift @pieces; 363 my $se = shift @pieces; 364 my $de = shift @pieces; 365 my $func = shift @pieces; 366 my $obj = shift @pieces; 367 $sql .= "-- $se --> $de\n"; 368 $sql .= 369"CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n"; 370 $sql .= 371"COMMENT ON FUNCTION $func(INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) IS 'internal conversion function for $se to $de';\n"; 372 $sql .= "DROP CONVERSION pg_catalog.$name;\n"; 373 $sql .= 374"CREATE DEFAULT CONVERSION pg_catalog.$name FOR '$se' TO '$de' FROM $func;\n"; 375 $sql .= 376"COMMENT ON CONVERSION pg_catalog.$name IS 'conversion for $se to $de';\n\n"; 377 } 378 open($F, ">$target/share/conversion_create.sql") 379 || die "Could not write to conversion_create.sql\n"; 380 print $F $sql; 381 close($F); 382 print "\n"; 383} 384 385sub GenerateTimezoneFiles 386{ 387 my $target = shift; 388 my $conf = shift; 389 my $mf = read_file("src/timezone/Makefile"); 390 $mf =~ s{\\\r?\n}{}g; 391 392 $mf =~ /^TZDATAFILES\s*:?=\s*(.*)$/m 393 || die "Could not find TZDATAFILES line in timezone makefile\n"; 394 my @tzfiles = split /\s+/, $1; 395 396 $mf =~ /^POSIXRULES\s*:?=\s*(.*)$/m 397 || die "Could not find POSIXRULES line in timezone makefile\n"; 398 my $posixrules = $1; 399 $posixrules =~ s/\s+//g; 400 401 print "Generating timezone files..."; 402 403 my @args = ("$conf/zic/zic", '-d', "$target/share/timezone", 404 '-p', "$posixrules", '-b', 'fat'); 405 foreach (@tzfiles) 406 { 407 my $tzfile = $_; 408 $tzfile =~ s|\$\(srcdir\)|src/timezone|; 409 push(@args, $tzfile); 410 } 411 412 system(@args); 413 print "\n"; 414} 415 416sub GenerateTsearchFiles 417{ 418 my $target = shift; 419 420 print "Generating tsearch script..."; 421 my $F; 422 my $tmpl = read_file('src/backend/snowball/snowball.sql.in'); 423 my $mf = read_file('src/backend/snowball/Makefile'); 424 $mf =~ s{\\\r?\n}{}g; 425 $mf =~ /^LANGUAGES\s*=\s*(.*)$/m 426 || die "Could not find LANGUAGES line in snowball Makefile\n"; 427 my @pieces = split /\s+/, $1; 428 open($F, ">$target/share/snowball_create.sql") 429 || die "Could not write snowball_create.sql"; 430 print $F read_file('src/backend/snowball/snowball_func.sql.in'); 431 432 while ($#pieces > 0) 433 { 434 my $lang = shift @pieces || last; 435 my $asclang = shift @pieces || last; 436 my $txt = $tmpl; 437 my $stop = ''; 438 439 if (-s "src/backend/snowball/stopwords/$lang.stop") 440 { 441 $stop = ", StopWords=$lang"; 442 } 443 444 $txt =~ s#_LANGNAME_#${lang}#gs; 445 $txt =~ s#_DICTNAME_#${lang}_stem#gs; 446 $txt =~ s#_CFGNAME_#${lang}#gs; 447 $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs; 448 $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs; 449 $txt =~ s#_STOPWORDS_#$stop#gs; 450 print $F $txt; 451 print "."; 452 } 453 close($F); 454 print "\n"; 455} 456 457sub CopyContribFiles 458{ 459 my $config = shift; 460 my $target = shift; 461 462 print "Copying contrib data files..."; 463 foreach my $subdir ('contrib', 'src/test/modules') 464 { 465 my $D; 466 opendir($D, $subdir) || croak "Could not opendir on $subdir!\n"; 467 while (my $d = readdir($D)) 468 { 469 # These configuration-based exclusions must match vcregress.pl 470 next if ($d eq "uuid-ossp" && !defined($config->{uuid})); 471 next if ($d eq "sslinfo" && !defined($config->{openssl})); 472 next if ($d eq "xml2" && !defined($config->{xml})); 473 next if ($d =~ /_plperl$/ && !defined($config->{perl})); 474 next if ($d =~ /_plpython$/ && !defined($config->{python})); 475 next if ($d eq "sepgsql"); 476 477 CopySubdirFiles($subdir, $d, $config, $target); 478 } 479 } 480 print "\n"; 481} 482 483sub CopySubdirFiles 484{ 485 my $subdir = shift; 486 my $module = shift; 487 my $config = shift; 488 my $target = shift; 489 490 return if ($module =~ /^\./); 491 return unless (-f "$subdir/$module/Makefile"); 492 return 493 if ($insttype eq "client" && !grep { $_ eq $module } @client_contribs); 494 495 my $mf = read_file("$subdir/$module/Makefile"); 496 $mf =~ s{\\\r?\n}{}g; 497 498 # Note: we currently don't support setting MODULEDIR in the makefile 499 my $moduledir = 'contrib'; 500 501 my $flist = ''; 502 if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 } 503 if ($flist ne '') 504 { 505 $moduledir = 'extension'; 506 $flist = ParseAndCleanRule($flist, $mf); 507 508 foreach my $f (split /\s+/, $flist) 509 { 510 lcopy("$subdir/$module/$f.control", 511 "$target/share/extension/$f.control") 512 || croak("Could not copy file $f.control in contrib $module"); 513 print '.'; 514 } 515 } 516 517 $flist = ''; 518 if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) { $flist .= $1 } 519 if ($mf =~ /^DATA\s*=\s*(.*)$/m) { $flist .= " $1" } 520 $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built 521 522 if ($flist ne '') 523 { 524 $flist = ParseAndCleanRule($flist, $mf); 525 526 foreach my $f (split /\s+/, $flist) 527 { 528 lcopy("$subdir/$module/$f", 529 "$target/share/$moduledir/" . basename($f)) 530 || croak("Could not copy file $f in contrib $module"); 531 print '.'; 532 } 533 } 534 535 $flist = ''; 536 if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 } 537 if ($flist ne '') 538 { 539 $flist = ParseAndCleanRule($flist, $mf); 540 541 foreach my $f (split /\s+/, $flist) 542 { 543 lcopy("$subdir/$module/$f", 544 "$target/share/tsearch_data/" . basename($f)) 545 || croak("Could not copy file $f in $subdir $module"); 546 print '.'; 547 } 548 } 549 550 $flist = ''; 551 if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 } 552 if ($flist ne '') 553 { 554 $flist = ParseAndCleanRule($flist, $mf); 555 556 # Special case for contrib/spi 557 $flist = 558"autoinc.example insert_username.example moddatetime.example refint.example timetravel.example" 559 if ($module eq 'spi'); 560 foreach my $f (split /\s+/, $flist) 561 { 562 lcopy("$subdir/$module/$f", "$target/doc/$moduledir/$f") 563 || croak("Could not copy file $f in contrib $module"); 564 print '.'; 565 } 566 } 567} 568 569sub ParseAndCleanRule 570{ 571 my $flist = shift; 572 my $mf = shift; 573 574 # Strip out $(addsuffix) rules 575 if (index($flist, '$(addsuffix ') >= 0) 576 { 577 my $pcount = 0; 578 my $i; 579 for ( 580 $i = index($flist, '$(addsuffix ') + 12; 581 $i < length($flist); 582 $i++) 583 { 584 $pcount++ if (substr($flist, $i, 1) eq '('); 585 $pcount-- if (substr($flist, $i, 1) eq ')'); 586 last if ($pcount < 0); 587 } 588 $flist = 589 substr($flist, 0, index($flist, '$(addsuffix ')) 590 . substr($flist, $i + 1); 591 } 592 return $flist; 593} 594 595sub CopyIncludeFiles 596{ 597 my $target = shift; 598 599 EnsureDirectories($target, 'include', 'include/libpq', 'include/internal', 600 'include/internal/libpq', 'include/server', 'include/server/parser'); 601 602 CopyFiles( 603 'Public headers', $target . '/include/', 604 'src/include/', 'postgres_ext.h', 605 'pg_config.h', 'pg_config_ext.h', 606 'pg_config_os.h', 'dynloader.h', 607 'pg_config_manual.h'); 608 lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/') 609 || croak 'Could not copy libpq-fs.h'; 610 611 CopyFiles( 612 'Libpq headers', 613 $target . '/include/', 614 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h'); 615 CopyFiles( 616 'Libpq internal headers', 617 $target . '/include/internal/', 618 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h'); 619 620 CopyFiles( 621 'Internal headers', 622 $target . '/include/internal/', 623 'src/include/', 'c.h', 'port.h', 'postgres_fe.h'); 624 lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/') 625 || croak 'Could not copy pqcomm.h'; 626 627 CopyFiles( 628 'Server headers', 629 $target . '/include/server/', 630 'src/include/', 'pg_config.h', 'pg_config_ext.h', 'pg_config_os.h', 631 'dynloader.h'); 632 CopyFiles( 633 'Grammar header', 634 $target . '/include/server/parser/', 635 'src/backend/parser/', 'gram.h'); 636 CopySetOfFiles( 637 '', 638 [ glob("src\\include\\*.h") ], 639 $target . '/include/server/'); 640 my $D; 641 opendir($D, 'src/include') || croak "Could not opendir on src/include!\n"; 642 643 CopyFiles( 644 'PL/pgSQL header', 645 $target . '/include/server/', 646 'src/pl/plpgsql/src/', 'plpgsql.h'); 647 648 # some xcopy progs don't like mixed slash style paths 649 (my $ctarget = $target) =~ s!/!\\!g; 650 while (my $d = readdir($D)) 651 { 652 next if ($d =~ /^\./); 653 next if ($d eq '.git'); 654 next if ($d eq 'CVS'); 655 next unless (-d "src/include/$d"); 656 657 EnsureDirectories("$target/include/server/$d"); 658 my @args = ( 659 'xcopy', '/s', '/i', '/q', '/r', '/y', "src\\include\\$d\\*.h", 660 "$ctarget\\include\\server\\$d\\"); 661 system(@args) && croak("Failed to copy include directory $d\n"); 662 } 663 closedir($D); 664 665 my $mf = read_file('src/interfaces/ecpg/include/Makefile'); 666 $mf =~ s{\\\r?\n}{}g; 667 $mf =~ /^ecpg_headers\s*=\s*(.*)$/m 668 || croak "Could not find ecpg_headers line\n"; 669 CopyFiles( 670 'ECPG headers', 671 $target . '/include/', 672 'src/interfaces/ecpg/include/', 673 'ecpg_config.h', split /\s+/, $1); 674 $mf =~ /^informix_headers\s*=\s*(.*)$/m 675 || croak "Could not find informix_headers line\n"; 676 EnsureDirectories($target . '/include', 'informix', 'informix/esql'); 677 CopyFiles( 678 'ECPG informix headers', 679 $target . '/include/informix/esql/', 680 'src/interfaces/ecpg/include/', 681 split /\s+/, $1); 682} 683 684sub GenerateNLSFiles 685{ 686 my $target = shift; 687 my $nlspath = shift; 688 my $majorver = shift; 689 690 print "Installing NLS files..."; 691 EnsureDirectories($target, "share/locale"); 692 my @flist; 693 File::Find::find( 694 { wanted => sub { 695 /^nls\.mk\z/s 696 && !push(@flist, $File::Find::name); 697 } 698 }, 699 "src"); 700 foreach (@flist) 701 { 702 my $prgm = DetermineCatalogName($_); 703 s/nls.mk/po/; 704 my $dir = $_; 705 next unless ($dir =~ /([^\/]+)\/po$/); 706 foreach (glob("$dir/*.po")) 707 { 708 my $lang; 709 next unless /([^\/]+)\.po/; 710 $lang = $1; 711 712 EnsureDirectories($target, "share/locale/$lang", 713 "share/locale/$lang/LC_MESSAGES"); 714 my @args = ( 715 "$nlspath\\bin\\msgfmt", 716 '-o', 717"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo", 718 $_); 719 system(@args) && croak("Could not run msgfmt on $dir\\$_"); 720 print "."; 721 } 722 } 723 print "\n"; 724} 725 726sub DetermineMajorVersion 727{ 728 my $f = read_file('src/include/pg_config.h') 729 || croak 'Could not open pg_config.h'; 730 $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m 731 || croak 'Could not determine major version'; 732 return $1; 733} 734 735sub DetermineCatalogName 736{ 737 my $filename = shift; 738 739 my $f = read_file($filename) || croak "Could not open $filename"; 740 $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m 741 || croak "Could not determine catalog name in $filename"; 742 return $1; 743} 744 745sub read_file 746{ 747 my $filename = shift; 748 my $F; 749 my $t = $/; 750 751 undef $/; 752 open($F, $filename) || die "Could not open file $filename\n"; 753 my $txt = <$F>; 754 close($F); 755 $/ = $t; 756 757 return $txt; 758} 759 7601; 761