4 # Package that provides 'make install' functionality for msvc builds
6 # src/tools/msvc/Install.pm
16 our (@ISA, @EXPORT_OK);
18 @EXPORT_OK = qw(Install);
27 unlink $target || confess "Could not delete $target\n";
31 || confess "Could not copy $src to $target\n";
40 # if called from vcregress, the config will be passed to us
41 # so no need to re-include these
45 # suppress warning about harmless redeclaration of $config
47 require "config_default.pl";
48 require "config.pl" if (-f "config.pl");
51 chdir("../../..") if (-f "../../../configure");
52 chdir("../../../..") if (-f "../../../../configure");
62 die "Could not find debug or release binaries" if ($conf eq "");
63 my $majorver = DetermineMajorVersion();
64 print "Installing version $majorver for $conf in $target\n";
69 'share/timezonesets', 'share/extension',
70 'share/contrib', 'doc',
71 'doc/extension', 'doc/contrib',
72 'symbols', 'share/tsearch_data');
74 CopySolutionOutput($conf, $target);
75 lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
76 my $sample_files = [];
80 && push(@$sample_files, $File::Find::name);
84 CopySetOfFiles('config files', $sample_files, $target . '/share/');
86 'Import libraries', $target . '/lib/',
87 "$conf\\", "postgres\\postgres.lib",
88 "libpq\\libpq.lib", "libecpg\\libecpg.lib",
89 "libpgport\\libpgport.lib");
92 [ glob('src\timezone\tznames\*.txt') ],
93 $target . '/share/timezonesets/');
96 $target . '/share/timezonesets/',
97 'src/timezone/tznames/', 'Default', 'Australia', 'India');
100 [ glob("src\\backend\\catalog\\postgres.*") ],
101 $target . '/share/');
104 [ glob("src\\backend\\catalog\\*.sql") ],
105 $target . '/share/');
107 'Information schema data', $target . '/share/',
108 'src/backend/catalog/', 'sql_features.txt');
109 GenerateConversionScript($target);
110 GenerateTimezoneFiles($target, $conf);
111 GenerateTsearchFiles($target);
114 [ glob("src\\backend\\snowball\\stopwords\\*.stop") ],
115 $target . '/share/tsearch_data/');
117 'Dictionaries sample files',
118 [ glob("src\\backend\\tsearch\\*_sample.*") ],
119 $target . '/share/tsearch_data/');
120 CopyContribFiles($config, $target);
121 CopyIncludeFiles($target);
123 my $pl_extension_files = [];
124 my @pldirs = ('src/pl/plpgsql/src');
125 push @pldirs, "src/pl/plperl" if $config->{perl};
126 push @pldirs, "src/pl/plpython" if $config->{python};
127 push @pldirs, "src/pl/tcl" if $config->{tcl};
130 /^(.*--.*\.sql|.*\.control)\z/s
131 && push(@$pl_extension_files, $File::Find::name);
135 CopySetOfFiles('PL Extension files',
136 $pl_extension_files, $target . '/share/extension/');
138 GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls});
140 print "Installation complete.\n";
143 sub EnsureDirectories
146 mkdir $target unless -d ($target);
147 while (my $d = shift)
149 mkdir $target . '/' . $d unless -d ($target . '/' . $d);
159 print "Copying $what";
160 while (my $f = shift)
164 die "No file $f\n" if (!-f $f);
165 lcopy($f, $target . basename($f));
175 print "Copying $what" if $what;
178 next if /regress/; # Skip temporary install in regression subdir
179 next if /ecpg.test/; # Skip temporary install in regression subdir
180 my $tgt = $target . basename($_);
182 lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
187 sub CopySolutionOutput
192 qr{Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\) = "([^"]+)"};
194 my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
196 my $vcproj = 'vcproj';
198 /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/
204 print "Copying build output files...";
213 my $proj = read_file("$pf.$vcproj")
214 || croak "Could not open $pf.$vcproj\n";
215 if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"})
230 # Static lib, such as libpgport, only used internally during build, don't install
234 elsif ($vcproj eq 'vcxproj'
235 && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>})
237 if ($1 eq 'Application')
242 elsif ($1 eq 'DynamicLibrary')
247 else # 'StaticLibrary'
250 # Static lib, such as libpgport, only used internally during build, don't install
256 croak "Could not parse $pf.$vcproj\n";
258 lcopy("$conf\\$pf\\$pf.$ext", "$target\\$dir\\$pf.$ext")
259 || croak "Could not copy $pf.$ext\n";
260 lcopy("$conf\\$pf\\$pf.pdb", "$target\\symbols\\$pf.pdb")
261 || croak "Could not copy $pf.pdb\n";
267 sub GenerateConversionScript
273 print "Generating conversion proc script...";
274 my $mf = read_file('src/backend/utils/mb/conversion_procs/Makefile');
275 $mf =~ s{\\\s*[\r\n]+}{}mg;
276 $mf =~ /^CONVERSIONS\s*=\s*(.*)$/m
277 || die "Could not find CONVERSIONS line in conversions Makefile\n";
278 my @pieces = split /\s+/, $1;
281 my $name = shift @pieces;
282 my $se = shift @pieces;
283 my $de = shift @pieces;
284 my $func = shift @pieces;
285 my $obj = shift @pieces;
286 $sql .= "-- $se --> $de\n";
288 "CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n";
290 "COMMENT ON FUNCTION $func(INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) IS 'internal conversion function for $se to $de';\n";
291 $sql .= "DROP CONVERSION pg_catalog.$name;\n";
293 "CREATE DEFAULT CONVERSION pg_catalog.$name FOR '$se' TO '$de' FROM $func;\n";
295 "COMMENT ON CONVERSION pg_catalog.$name IS 'conversion for $se to $de';\n";
297 open($F, ">$target/share/conversion_create.sql")
298 || die "Could not write to conversion_create.sql\n";
304 sub GenerateTimezoneFiles
308 my $mf = read_file("src/timezone/Makefile");
309 $mf =~ s{\\\s*[\r\n]+}{}mg;
310 $mf =~ /^TZDATA\s*:?=\s*(.*)$/m
311 || die "Could not find TZDATA row in timezone makefile\n";
312 my @tzfiles = split /\s+/, $1;
313 unshift @tzfiles, '';
314 print "Generating timezone files...";
315 system("$conf\\zic\\zic -d \"$target/share/timezone\" "
316 . join(" src/timezone/data/", @tzfiles));
320 sub GenerateTsearchFiles
324 print "Generating tsearch script...";
326 my $tmpl = read_file('src/backend/snowball/snowball.sql.in');
327 my $mf = read_file('src/backend/snowball/Makefile');
328 $mf =~ s{\\\s*[\r\n]+}{}mg;
329 $mf =~ /^LANGUAGES\s*=\s*(.*)$/m
330 || die "Could not find LANGUAGES line in snowball Makefile\n";
331 my @pieces = split /\s+/, $1;
332 open($F, ">$target/share/snowball_create.sql")
333 || die "Could not write snowball_create.sql";
334 print $F read_file('src/backend/snowball/snowball_func.sql.in');
338 my $lang = shift @pieces || last;
339 my $asclang = shift @pieces || last;
343 if (-s "src/backend/snowball/stopwords/$lang.stop")
345 $stop = ", StopWords=$lang";
348 $txt =~ s#_LANGNAME_#${lang}#gs;
349 $txt =~ s#_DICTNAME_#${lang}_stem#gs;
350 $txt =~ s#_CFGNAME_#${lang}#gs;
351 $txt =~ s#_ASCDICTNAME_#${asclang}_stem#gs;
352 $txt =~ s#_NONASCDICTNAME_#${lang}_stem#gs;
353 $txt =~ s#_STOPWORDS_#$stop#gs;
366 print "Copying contrib data files...";
368 opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
369 while (my $d = readdir($D))
371 next if ($d =~ /^\./);
372 next unless (-f "contrib/$d/Makefile");
373 next if ($d eq "uuid-ossp" && !defined($config->{uuid}));
374 next if ($d eq "sslinfo" && !defined($config->{openssl}));
375 next if ($d eq "xml2" && !defined($config->{xml}));
376 next if ($d eq "sepgsql");
378 my $mf = read_file("contrib/$d/Makefile");
379 $mf =~ s{\\s*[\r\n]+}{}mg;
381 # Note: we currently don't support setting MODULEDIR in the makefile
382 my $moduledir = 'contrib';
385 if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 }
388 $moduledir = 'extension';
389 $flist = ParseAndCleanRule($flist, $mf);
391 foreach my $f (split /\s+/, $flist)
394 'contrib/' . $d . '/' . $f . '.control',
395 $target . '/share/extension/' . $f . '.control'
396 ) || croak("Could not copy file $f.control in contrib $d");
402 if ($mf =~ /^DATA_built\s*=\s*(.*)$/m) { $flist .= $1 }
403 if ($mf =~ /^DATA\s*=\s*(.*)$/m) { $flist .= " $1" }
404 $flist =~ s/^\s*//; # Remove leading spaces if we had only DATA_built
408 $flist = ParseAndCleanRule($flist, $mf);
410 foreach my $f (split /\s+/, $flist)
412 lcopy('contrib/' . $d . '/' . $f,
413 $target . '/share/' . $moduledir . '/' . basename($f))
414 || croak("Could not copy file $f in contrib $d");
420 if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 }
423 $flist = ParseAndCleanRule($flist, $mf);
425 foreach my $f (split /\s+/, $flist)
427 lcopy('contrib/' . $d . '/' . $f,
428 $target . '/share/tsearch_data/' . basename($f))
429 || croak("Could not copy file $f in contrib $d");
435 if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 }
438 $flist = ParseAndCleanRule($flist, $mf);
440 # Special case for contrib/spi
442 "autoinc.example insert_username.example moddatetime.example refint.example timetravel.example"
444 foreach my $f (split /\s+/, $flist)
446 lcopy('contrib/' . $d . '/' . $f,
447 $target . '/doc/' . $moduledir . '/' . $f)
448 || croak("Could not copy file $f in contrib $d");
457 sub ParseAndCleanRule
462 # Strip out $(addsuffix) rules
463 if (index($flist, '$(addsuffix ') >= 0)
468 $i = index($flist, '$(addsuffix ') + 12;
472 $pcount++ if (substr($flist, $i, 1) eq '(');
473 $pcount-- if (substr($flist, $i, 1) eq ')');
474 last if ($pcount < 0);
477 substr($flist, 0, index($flist, '$(addsuffix '))
478 . substr($flist, $i + 1);
487 EnsureDirectories($target, 'include', 'include/libpq', 'include/internal',
488 'include/internal/libpq', 'include/server', 'include/server/parser');
492 $target . '/include/',
493 'src/include/', 'postgres_ext.h', 'pg_config.h', 'pg_config_os.h',
494 'pg_config_manual.h');
495 lcopy('src/include/libpq/libpq-fs.h', $target . '/include/libpq/')
496 || croak 'Could not copy libpq-fs.h';
500 $target . '/include/',
501 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h');
503 'Libpq internal headers',
504 $target . '/include/internal/',
505 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h');
509 $target . '/include/internal/',
510 'src/include/', 'c.h', 'port.h', 'postgres_fe.h');
511 lcopy('src/include/libpq/pqcomm.h', $target . '/include/internal/libpq/')
512 || croak 'Could not copy pqcomm.h';
516 $target . '/include/server/',
517 'src/include/', 'pg_config.h', 'pg_config_os.h');
520 $target . '/include/server/parser/',
521 'src/backend/parser/', 'gram.h');
524 [ glob("src\\include\\*.h") ],
525 $target . '/include/server/');
527 opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
531 $target . '/include/server/',
532 'src/pl/plpgsql/src/', 'plpgsql.h');
534 # some xcopy progs don't like mixed slash style paths
535 (my $ctarget = $target) =~ s!/!\\!g;
536 while (my $d = readdir($D))
538 next if ($d =~ /^\./);
539 next if ($d eq '.git');
540 next if ($d eq 'CVS');
541 next unless (-d "src/include/$d");
543 EnsureDirectories("$target/include/server/$d");
545 qq{xcopy /s /i /q /r /y src\\include\\$d\\*.h "$ctarget\\include\\server\\$d\\"}
546 ) && croak("Failed to copy include directory $d\n");
550 my $mf = read_file('src/interfaces/ecpg/include/Makefile');
551 $mf =~ s{\\s*[\r\n]+}{}mg;
552 $mf =~ /^ecpg_headers\s*=\s*(.*)$/m
553 || croak "Could not find ecpg_headers line\n";
556 $target . '/include/',
557 'src/interfaces/ecpg/include/',
558 'ecpg_config.h', split /\s+/, $1);
559 $mf =~ /^informix_headers\s*=\s*(.*)$/m
560 || croak "Could not find informix_headers line\n";
561 EnsureDirectories($target . '/include', 'informix', 'informix/esql');
563 'ECPG informix headers',
564 $target . '/include/informix/esql/',
565 'src/interfaces/ecpg/include/',
573 my $majorver = shift;
575 print "Installing NLS files...";
576 EnsureDirectories($target, "share/locale");
581 && !push(@flist, $File::Find::name);
587 my $prgm = DetermineCatalogName($_);
590 next unless ($dir =~ /([^\/]+)\/po$/);
591 foreach (glob("$dir/*.po"))
594 next unless /([^\/]+)\.po/;
597 EnsureDirectories($target, "share/locale/$lang",
598 "share/locale/$lang/LC_MESSAGES");
600 "\"$nlspath\\bin\\msgfmt\" -o \"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo\" $_"
601 ) && croak("Could not run msgfmt on $dir\\$_");
608 sub DetermineMajorVersion
610 my $f = read_file('src/include/pg_config.h')
611 || croak 'Could not open pg_config.h';
612 $f =~ /^#define\s+PG_MAJORVERSION\s+"([^"]+)"/m
613 || croak 'Could not determine major version';
617 sub DetermineCatalogName
619 my $filename = shift;
621 my $f = read_file($filename) || croak "Could not open $filename";
622 $f =~ /CATALOG_NAME\s*\:?=\s*(\S+)/m
623 || croak "Could not determine catalog name in $filename";
629 my $filename = shift;
634 open($F, $filename) || die "Could not open file $filename\n";