]> granicus.if.org Git - postgresql/blob - src/tools/msvc/Install.pm
Add vcregress.pl target for checking pg_upgrade.
[postgresql] / src / tools / msvc / Install.pm
1 package Install;
2
3 #
4 # Package that provides 'make install' functionality for msvc builds
5 #
6 # src/tools/msvc/Install.pm
7 #
8 use strict;
9 use warnings;
10 use Carp;
11 use File::Basename;
12 use File::Copy;
13 use File::Find ();
14
15 use Exporter;
16 our (@ISA, @EXPORT_OK);
17 @ISA       = qw(Exporter);
18 @EXPORT_OK = qw(Install);
19
20 sub lcopy
21 {
22         my $src    = shift;
23         my $target = shift;
24
25         if (-f $target)
26         {
27                 unlink $target || confess "Could not delete $target\n";
28         }
29
30         copy($src, $target)
31           || confess "Could not copy $src to $target\n";
32
33 }
34
35 sub Install
36 {
37         $| = 1;
38
39         my $target = shift;
40         # if called from vcregress, the config will be passed to us
41         # so no need to re-include these
42         our $config = shift;
43         unless ($config)
44         {
45                 # suppress warning about harmless redeclaration of $config
46                 no warnings 'misc'; 
47                 require "config_default.pl";
48                 require "config.pl" if (-f "config.pl");
49         }
50
51         chdir("../../..")    if (-f "../../../configure");
52         chdir("../../../..") if (-f "../../../../configure");
53         my $conf = "";
54         if (-d "debug")
55         {
56                 $conf = "debug";
57         }
58         if (-d "release")
59         {
60                 $conf = "release";
61         }
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";
65
66         EnsureDirectories(
67                 $target,              'bin',
68                 'lib',                'share',
69                 'share/timezonesets', 'share/extension',
70                 'share/contrib',      'doc',
71                 'doc/extension',      'doc/contrib',
72                 'symbols',            'share/tsearch_data');
73
74         CopySolutionOutput($conf, $target);
75         lcopy($target . '/lib/libpq.dll', $target . '/bin/libpq.dll');
76         my $sample_files = [];
77         File::Find::find(
78                 {   wanted => sub {
79                                 /^.*\.sample\z/s
80                                   && push(@$sample_files, $File::Find::name);
81                           }
82                 },
83                 "src");
84         CopySetOfFiles('config files', $sample_files, $target . '/share/');
85         CopyFiles(
86                 'Import libraries', $target . '/lib/',
87                 "$conf\\",          "postgres\\postgres.lib",
88                 "libpq\\libpq.lib", "libecpg\\libecpg.lib",
89                 "libpgport\\libpgport.lib");
90         CopySetOfFiles(
91                 'timezone names',
92                 [ glob('src\timezone\tznames\*.txt') ],
93                 $target . '/share/timezonesets/');
94         CopyFiles(
95                 'timezone sets',
96                 $target . '/share/timezonesets/',
97                 'src/timezone/tznames/', 'Default', 'Australia', 'India');
98         CopySetOfFiles(
99                 'BKI files',
100                 [ glob("src\\backend\\catalog\\postgres.*") ],
101                 $target . '/share/');
102         CopySetOfFiles(
103                 'SQL files',
104                 [ glob("src\\backend\\catalog\\*.sql") ],
105                 $target . '/share/');
106         CopyFiles(
107                 'Information schema data', $target . '/share/',
108                 'src/backend/catalog/',    'sql_features.txt');
109         GenerateConversionScript($target);
110         GenerateTimezoneFiles($target, $conf);
111         GenerateTsearchFiles($target);
112         CopySetOfFiles(
113                 'Stopword files',
114                 [ glob("src\\backend\\snowball\\stopwords\\*.stop") ],
115                 $target . '/share/tsearch_data/');
116         CopySetOfFiles(
117                 'Dictionaries sample files',
118                 [ glob("src\\backend\\tsearch\\*_sample.*") ],
119                 $target . '/share/tsearch_data/');
120         CopyContribFiles($config, $target);
121         CopyIncludeFiles($target);
122
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};
128         File::Find::find(
129                 {   wanted => sub {
130                                 /^(.*--.*\.sql|.*\.control)\z/s
131                                   && push(@$pl_extension_files, $File::Find::name);
132                           }
133                 },
134                 @pldirs);
135         CopySetOfFiles('PL Extension files',
136                 $pl_extension_files, $target . '/share/extension/');
137
138         GenerateNLSFiles($target, $config->{nls}, $majorver) if ($config->{nls});
139
140         print "Installation complete.\n";
141 }
142
143 sub EnsureDirectories
144 {
145         my $target = shift;
146         mkdir $target unless -d ($target);
147         while (my $d = shift)
148         {
149                 mkdir $target . '/' . $d unless -d ($target . '/' . $d);
150         }
151 }
152
153 sub CopyFiles
154 {
155         my $what    = shift;
156         my $target  = shift;
157         my $basedir = shift;
158
159         print "Copying $what";
160         while (my $f = shift)
161         {
162                 print ".";
163                 $f = $basedir . $f;
164                 die "No file $f\n" if (!-f $f);
165                 lcopy($f, $target . basename($f));
166         }
167         print "\n";
168 }
169
170 sub CopySetOfFiles
171 {
172         my $what   = shift;
173         my $flist  = shift;
174         my $target = shift;
175         print "Copying $what" if $what;
176         foreach (@$flist)
177         {
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($_);
181                 print ".";
182                 lcopy($_, $tgt) || croak "Could not copy $_: $!\n";
183         }
184         print "\n";
185 }
186
187 sub CopySolutionOutput
188 {
189         my $conf   = shift;
190         my $target = shift;
191         my $rem =
192           qr{Project\("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}"\) = "([^"]+)"};
193
194         my $sln = read_file("pgsql.sln") || croak "Could not open pgsql.sln\n";
195
196         my $vcproj = 'vcproj';
197         if ($sln =~
198                 /Microsoft Visual Studio Solution File, Format Version (\d+)\.\d+/
199                 && $1 >= 11)
200         {
201                 $vcproj = 'vcxproj';
202         }
203
204         print "Copying build output files...";
205         while ($sln =~ $rem)
206         {
207                 my $pf = $1;
208                 my $dir;
209                 my $ext;
210
211                 $sln =~ s/$rem//;
212
213                 my $proj = read_file("$pf.$vcproj")
214                   || croak "Could not open $pf.$vcproj\n";
215                 if ($vcproj eq 'vcproj' && $proj =~ qr{ConfigurationType="([^"]+)"})
216                 {
217                         if ($1 == 1)
218                         {
219                                 $dir = "bin";
220                                 $ext = "exe";
221                         }
222                         elsif ($1 == 2)
223                         {
224                                 $dir = "lib";
225                                 $ext = "dll";
226                         }
227                         else
228                         {
229
230 # Static lib, such as libpgport, only used internally during build, don't install
231                                 next;
232                         }
233                 }
234                 elsif ($vcproj eq 'vcxproj'
235                         && $proj =~ qr{<ConfigurationType>(\w+)</ConfigurationType>})
236                 {
237                         if ($1 eq 'Application')
238                         {
239                                 $dir = "bin";
240                                 $ext = "exe";
241                         }
242                         elsif ($1 eq 'DynamicLibrary')
243                         {
244                                 $dir = "lib";
245                                 $ext = "dll";
246                         }
247                         else    # 'StaticLibrary'
248                         {
249
250 # Static lib, such as libpgport, only used internally during build, don't install
251                                 next;
252                         }
253                 }
254                 else
255                 {
256                         croak "Could not parse $pf.$vcproj\n";
257                 }
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";
262                 print ".";
263         }
264         print "\n";
265 }
266
267 sub GenerateConversionScript
268 {
269         my $target = shift;
270         my $sql    = "";
271         my $F;
272
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;
279         while ($#pieces > 0)
280         {
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";
287                 $sql .=
288 "CREATE OR REPLACE FUNCTION $func (INTEGER, INTEGER, CSTRING, INTERNAL, INTEGER) RETURNS VOID AS '\$libdir/$obj', '$func' LANGUAGE C STRICT;\n";
289                 $sql .=
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";
292                 $sql .=
293 "CREATE DEFAULT CONVERSION pg_catalog.$name FOR '$se' TO '$de' FROM $func;\n";
294                 $sql .=
295 "COMMENT ON CONVERSION pg_catalog.$name IS 'conversion for $se to $de';\n";
296         }
297         open($F, ">$target/share/conversion_create.sql")
298           || die "Could not write to conversion_create.sql\n";
299         print $F $sql;
300         close($F);
301         print "\n";
302 }
303
304 sub GenerateTimezoneFiles
305 {
306         my $target = shift;
307         my $conf   = shift;
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));
317         print "\n";
318 }
319
320 sub GenerateTsearchFiles
321 {
322         my $target = shift;
323
324         print "Generating tsearch script...";
325         my $F;
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');
335
336         while ($#pieces > 0)
337         {
338                 my $lang    = shift @pieces || last;
339                 my $asclang = shift @pieces || last;
340                 my $txt     = $tmpl;
341                 my $stop    = '';
342
343                 if (-s "src/backend/snowball/stopwords/$lang.stop")
344                 {
345                         $stop = ", StopWords=$lang";
346                 }
347
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;
354                 print $F $txt;
355                 print ".";
356         }
357         close($F);
358         print "\n";
359 }
360
361 sub CopyContribFiles
362 {
363         my $config = shift;
364         my $target = shift;
365
366         print "Copying contrib data files...";
367         my $D;
368         opendir($D, 'contrib') || croak "Could not opendir on contrib!\n";
369         while (my $d = readdir($D))
370         {
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");
377
378                 my $mf = read_file("contrib/$d/Makefile");
379                 $mf =~ s{\\s*[\r\n]+}{}mg;
380
381                 # Note: we currently don't support setting MODULEDIR in the makefile
382                 my $moduledir = 'contrib';
383
384                 my $flist = '';
385                 if ($mf =~ /^EXTENSION\s*=\s*(.*)$/m) { $flist .= $1 }
386                 if ($flist ne '')
387                 {
388                         $moduledir = 'extension';
389                         $flist = ParseAndCleanRule($flist, $mf);
390
391                         foreach my $f (split /\s+/, $flist)
392                         {
393                                 lcopy(
394                                         'contrib/' . $d . '/' . $f . '.control',
395                                         $target . '/share/extension/' . $f . '.control'
396                                 ) || croak("Could not copy file $f.control in contrib $d");
397                                 print '.';
398                         }
399                 }
400
401                 $flist = '';
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
405
406                 if ($flist ne '')
407                 {
408                         $flist = ParseAndCleanRule($flist, $mf);
409
410                         foreach my $f (split /\s+/, $flist)
411                         {
412                                 lcopy('contrib/' . $d . '/' . $f,
413                                         $target . '/share/' . $moduledir . '/' . basename($f))
414                                   || croak("Could not copy file $f in contrib $d");
415                                 print '.';
416                         }
417                 }
418
419                 $flist = '';
420                 if ($mf =~ /^DATA_TSEARCH\s*=\s*(.*)$/m) { $flist .= $1 }
421                 if ($flist ne '')
422                 {
423                         $flist = ParseAndCleanRule($flist, $mf);
424
425                         foreach my $f (split /\s+/, $flist)
426                         {
427                                 lcopy('contrib/' . $d . '/' . $f,
428                                         $target . '/share/tsearch_data/' . basename($f))
429                                   || croak("Could not copy file $f in contrib $d");
430                                 print '.';
431                         }
432                 }
433
434                 $flist = '';
435                 if ($mf =~ /^DOCS\s*=\s*(.*)$/mg) { $flist .= $1 }
436                 if ($flist ne '')
437                 {
438                         $flist = ParseAndCleanRule($flist, $mf);
439
440                         # Special case for contrib/spi
441                         $flist =
442 "autoinc.example insert_username.example moddatetime.example refint.example timetravel.example"
443                           if ($d eq 'spi');
444                         foreach my $f (split /\s+/, $flist)
445                         {
446                                 lcopy('contrib/' . $d . '/' . $f,
447                                         $target . '/doc/' . $moduledir . '/' . $f)
448                                   || croak("Could not copy file $f in contrib $d");
449                                 print '.';
450                         }
451                 }
452         }
453         closedir($D);
454         print "\n";
455 }
456
457 sub ParseAndCleanRule
458 {
459         my $flist = shift;
460         my $mf    = shift;
461
462         # Strip out $(addsuffix) rules
463         if (index($flist, '$(addsuffix ') >= 0)
464         {
465                 my $pcount = 0;
466                 my $i;
467                 for (
468                         $i = index($flist, '$(addsuffix ') + 12;
469                         $i < length($flist);
470                         $i++)
471                 {
472                         $pcount++ if (substr($flist, $i, 1) eq '(');
473                         $pcount-- if (substr($flist, $i, 1) eq ')');
474                         last      if ($pcount < 0);
475                 }
476                 $flist =
477                     substr($flist, 0, index($flist, '$(addsuffix '))
478                   . substr($flist, $i + 1);
479         }
480         return $flist;
481 }
482
483 sub CopyIncludeFiles
484 {
485         my $target = shift;
486
487         EnsureDirectories($target, 'include', 'include/libpq', 'include/internal',
488                 'include/internal/libpq', 'include/server', 'include/server/parser');
489
490         CopyFiles(
491                 'Public headers',
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';
497
498         CopyFiles(
499                 'Libpq headers',
500                 $target . '/include/',
501                 'src/interfaces/libpq/', 'libpq-fe.h', 'libpq-events.h');
502         CopyFiles(
503                 'Libpq internal headers',
504                 $target . '/include/internal/',
505                 'src/interfaces/libpq/', 'libpq-int.h', 'pqexpbuffer.h');
506
507         CopyFiles(
508                 'Internal headers',
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';
513
514         CopyFiles(
515                 'Server headers',
516                 $target . '/include/server/',
517                 'src/include/', 'pg_config.h', 'pg_config_os.h');
518         CopyFiles(
519                 'Grammar header',
520                 $target . '/include/server/parser/',
521                 'src/backend/parser/', 'gram.h');
522         CopySetOfFiles(
523                 '',
524                 [ glob("src\\include\\*.h") ],
525                 $target . '/include/server/');
526         my $D;
527         opendir($D, 'src/include') || croak "Could not opendir on src/include!\n";
528
529         CopyFiles(
530                 'PL/pgSQL header',
531                 $target . '/include/server/',
532                 'src/pl/plpgsql/src/', 'plpgsql.h');
533
534         # some xcopy progs don't like mixed slash style paths
535         (my $ctarget = $target) =~ s!/!\\!g;
536         while (my $d = readdir($D))
537         {
538                 next if ($d =~ /^\./);
539                 next if ($d eq '.git');
540                 next if ($d eq 'CVS');
541                 next unless (-d "src/include/$d");
542
543                 EnsureDirectories("$target/include/server/$d");
544                 system(
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");
547         }
548         closedir($D);
549
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";
554         CopyFiles(
555                 'ECPG headers',
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');
562         CopyFiles(
563                 'ECPG informix headers',
564                 $target . '/include/informix/esql/',
565                 'src/interfaces/ecpg/include/',
566                 split /\s+/, $1);
567 }
568
569 sub GenerateNLSFiles
570 {
571         my $target   = shift;
572         my $nlspath  = shift;
573         my $majorver = shift;
574
575         print "Installing NLS files...";
576         EnsureDirectories($target, "share/locale");
577         my @flist;
578         File::Find::find(
579                 {   wanted => sub {
580                                 /^nls\.mk\z/s
581                                   && !push(@flist, $File::Find::name);
582                           }
583                 },
584                 "src");
585         foreach (@flist)
586         {
587                 my $prgm = DetermineCatalogName($_);
588                 s/nls.mk/po/;
589                 my $dir = $_;
590                 next unless ($dir =~ /([^\/]+)\/po$/);
591                 foreach (glob("$dir/*.po"))
592                 {
593                         my $lang;
594                         next unless /([^\/]+)\.po/;
595                         $lang = $1;
596
597                         EnsureDirectories($target, "share/locale/$lang",
598                                 "share/locale/$lang/LC_MESSAGES");
599                         system(
600 "\"$nlspath\\bin\\msgfmt\" -o \"$target\\share\\locale\\$lang\\LC_MESSAGES\\$prgm-$majorver.mo\" $_"
601                         ) && croak("Could not run msgfmt on $dir\\$_");
602                         print ".";
603                 }
604         }
605         print "\n";
606 }
607
608 sub DetermineMajorVersion
609 {
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';
614         return $1;
615 }
616
617 sub DetermineCatalogName
618 {
619         my $filename = shift;
620
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";
624         return $1;
625 }
626
627 sub read_file
628 {
629         my $filename = shift;
630         my $F;
631         my $t = $/;
632
633         undef $/;
634         open($F, $filename) || die "Could not open file $filename\n";
635         my $txt = <$F>;
636         close($F);
637         $/ = $t;
638
639         return $txt;
640 }
641
642 1;