1#  Copyright (c) 1997-2021
2#  Ewgenij Gawrilow, Michael Joswig, and the polymake team
3#  Technische Universität Berlin, Germany
4#  https://polymake.org
5#
6#  This program is free software; you can redistribute it and/or modify it
7#  under the terms of the GNU General Public License as published by the
8#  Free Software Foundation; either version 2, or (at your option) any
9#  later version: http://www.gnu.org/licenses/gpl.txt.
10#
11#  This program is distributed in the hope that it will be useful,
12#  but WITHOUT ANY WARRANTY; without even the implied warranty of
13#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14#  GNU General Public License for more details.
15#-------------------------------------------------------------------------------
16
17use strict;
18use feature 'state';
19use vars qw( $dirmode $group $permmask $root $ext_root $builddir $config_file %ConfigFlags $buildmode
20             $xsmod @callable @fakelibs );
21use POSIX qw ( :fcntl_h read write lseek );
22use File::Path;
23use Config;
24
25use Getopt::Long qw( GetOptions :config require_order no_ignore_case );
26
27sub collect_arglist {
28   my $list = shift;
29   @$list = @_;
30   while (@ARGV && $ARGV[0] !~ /^-/) {
31      push @$list, shift @ARGV;
32   }
33}
34
35unless (GetOptions( 'root=s' => \$root,
36                    'extroot=s' => \$ext_root,
37                    'builddir=s' => \$builddir,
38                    'config=s' => \$config_file,
39                    'mode=s' => \$buildmode,
40                    'group=s' => \$group,
41                    'perms=i' => \$permmask,
42                    'xs=s' => \$xsmod,
43                    'callable=s' => sub { collect_arglist(\@callable, $_[1]) },
44                    'fakelibs=s' => sub { collect_arglist(\@fakelibs, $_[1]) } ) &&
45        !@ARGV &&
46        defined($config_file) &&
47        defined($root) &&
48        defined($builddir) &&
49        defined($buildmode) &&
50        -f $config_file &&
51        -d $root &&
52        -d "$builddir/$buildmode" &&
53        (defined($ext_root) ? -d $ext_root && !defined($xsmod) && !@callable && !@fakelibs
54                            : defined($xsmod) && -f $xsmod)) {
55   die <<".";
56usage: $0 --root PATH --builddir PATH --config PATH/config.ninja [ --extroot PATH --extconfig PATH/config.ninja ]
57          --buildmode Opt|Debug [ --group GROUP ] [ --perms MASK ] [ --xsmod | --callable | --fakelib shared_module ... ]
58.
59}
60
61if (defined($group)) {
62   my $gid = getgrnam($group)
63     or die "$0: unknown group '$group'\n";
64   $group = $gid;
65}
66
67do "$root/support/install_utils.pl";
68die $@ if $@;
69%ConfigFlags = load_config_file($config_file, $root);
70my @BundledExts = $ConfigFlags{BundledExts} =~ /(\S+)/g;
71
72my @default_exclude_for_copy = qw( \. \.\. \.\#.* .*~ \.git.* \.noexport );
73
74# these variables may be used in sourced extension scripts
75use vars qw($InstallTop $InstallInc $InstallArch $InstallBin $InstallLib $buildtop $ExtTop $ExtArch);
76
77foreach (qw(InstallTop InstallInc InstallArch InstallBin InstallLib)) {
78   no strict 'refs';
79   ${$_} = $ConfigFlags{$_};
80}
81$buildtop = "$builddir/$buildmode";
82my $ext_name = $ext_root && basename($ext_root);
83
84my $destdir = $ENV{DESTDIR} // $ConfigFlags{DESTDIR};
85$destdir =~ s{/$}{};
86if (length($destdir)) {
87   foreach ($InstallTop, $InstallInc, $InstallArch, $InstallBin, $InstallLib) {
88      substr($_,0,0) .= $destdir;
89   }
90}
91
92my $perlxpath = "perlx/$Config::Config{version}/$Config::Config{archname}";
93
94if ($buildmode eq "San") {
95   load_sanitizer_flags();
96}
97
98if (defined $permmask) {
99   if (($permmask & 0700) != 0700) {
100      die "--perms must allow full access to the file owner\n";
101   }
102   umask 0;
103   $dirmode = 0777 & $permmask;
104} else {
105   $permmask = 0777&~umask;
106}
107
108if (defined($ext_root)) {
109   install_extension();
110} else {
111   install_core();
112}
113
114sub install_core {
115   make_dir($InstallTop);
116   foreach my $subdir (qw(demo perllib resources scripts upgrades)) {
117      copy_dir("$root/$subdir", "$InstallTop/$subdir", clean_dir => 1);
118   }
119
120   my $clean_dir = 1;
121   foreach my $file (qw(configure.pl.template generate_applib_fake.pl generate_ninja_targets.pl
122                        generate_ninja_targets.pl.template generate_cpperl_modules.pl
123                        guarded_compiler.pl install.pl install.pl.template install_utils.pl rules.ninja)) {
124      copy_file("$root/support/$file", "$InstallTop/support/$file", clean_dir => $clean_dir);
125      $clean_dir = 0;
126   }
127
128   make_dir("$InstallTop/apps", clean_dir => 1);
129   make_dir("$InstallTop/bundled", clean_dir => 1);
130
131   foreach my $bundled (glob("$root/bundled/*")) {
132      my $ext_file = "$bundled/polymake.ext";
133      copy_file($ext_file, $InstallTop.substr($ext_file, length($root)), clean_dir => 1);
134      if (-d (my $scripts = "$root/bundled/scripts")) {
135         copy_dir($scripts, $InstallTop.substr($scripts, length($root)));
136      }
137   }
138
139   foreach my $dir (glob_all_apps("{perllib,rules,scripts}")) {
140      copy_dir($dir, $InstallTop.substr($dir, length($root)), clean_dir => 1);
141   }
142
143   make_dir("$InstallInc/polymake", clean_dir => 1);
144   copy_dir("$root/lib/core/include", "$InstallInc/polymake",
145            exclude => [qw(glue.h Ext.h cout_bridge.h)],
146            wrappers => "$root/include/core-wrappers/polymake"
147           );
148   copy_dir("$root/lib/callable/include", "$InstallInc/polymake");
149   copy_dir("$root/lib/core/skel", "$InstallTop/lib/core/skel", clean_dir => 1);
150
151   foreach my $dir (glob_all_apps("include")) {
152      my ($app_name) = $dir =~ m{/apps/(\w+)/};
153      my $wrappers = "$`/include/app-wrappers/polymake/$app_name";
154      copy_dir($dir, "$InstallInc/polymake/$app_name",
155               -e $wrappers ? (wrappers => $wrappers) : ());
156   }
157
158   foreach my $ext_dir ($ConfigFlags{ExternalHeaders} =~ /(\S+)/g) {
159      copy_dir("$root/include/external/$ext_dir/$ext_dir", "$InstallInc/polymake/external/$ext_dir",
160               clean_dir => 1);
161   }
162
163   foreach my $dir (glob_all_apps("src")) {
164      copy_dir($dir, $InstallTop.substr($dir, length($root)));
165   }
166
167   make_dir($InstallArch);
168   -d $InstallBin || make_dir($InstallBin);
169   install_bin_scripts();
170
171   copy_file($config_file, "$InstallArch/config.ninja",
172             transform => \&transform_core_config_file);
173
174   make_dir("$InstallArch/bundled", clean_dir => 1);
175   foreach my $bundled (@BundledExts) {
176      make_dir("$InstallArch/bundled/$bundled");
177   }
178
179   -d $InstallLib || make_dir($InstallLib);
180
181   if (defined($xsmod)) {
182      $xsmod =~ m{($perlxpath/auto/.*\.$Config::Config{dlext})$}o
183        or die "$0: path of perl extension module does not match the expected pattern\n";
184      copy_file($xsmod, "$InstallArch/$1", mode => 0555, clean_dir => 1);
185   }
186
187   foreach my $lib_file (@callable) {
188      my $to = $InstallLib."/".basename($lib_file);
189      if (-l $lib_file) {
190         copy_link($lib_file, $to);
191      } else {
192         copy_file($lib_file, $to, mode => 0555, $^O eq "darwin" ? (lib_id => $to) : ());
193      }
194   }
195   make_dir("$InstallArch/lib", clean_dir => 1);
196   foreach my $app_dir (glob("$root/apps/*")) {
197      my $app_mod = basename($app_dir).".$Config::Config{dlext}";
198      copy_file("$buildtop/lib/$app_mod", "$InstallArch/lib/$app_mod", mode => 0555);
199   }
200
201   # These symlinks are used by the callable library bootstrap module.
202   # Any change in naming scheme must be reflected in Main.cc as well.
203   rel_symlink($InstallTop, "$InstallArch/shared");
204   if ($^O eq "darwin" && $ConfigFlags{FinkBase}) {
205      rel_symlink($ConfigFlags{FinkBase}, "$InstallArch/fink-base");
206   }
207
208   if (@fakelibs) {
209      my $stub_lib_name;
210      foreach my $lib_file (@fakelibs) {
211         my $basename = basename($lib_file);
212         my $to = "$InstallArch/lib/$basename";
213         if (-l $lib_file) {
214            copy_link($lib_file, $to);
215            copy_link($lib_file, "$InstallLib/$basename");
216         } else {
217            $stub_lib_name //= $to;
218            copy_file($lib_file, $to, mode => 0555, $^O eq "darwin" ? (lib_id => $stub_lib_name) : ());
219            # This symlink is also used by the callable library bootstrap module.
220            rel_symlink($to, "$InstallLib/$basename");
221         }
222      }
223   }
224
225   foreach my $bundled (@BundledExts) {
226      if (-f (my $inst_script = "$root/bundled/$bundled/support/install.pl")) {
227         do $inst_script;
228         die "installation script $inst_script failed: $@" if $@;
229      }
230   }
231}
232
233sub install_extension {
234   $ExtTop = "$InstallTop/ext/$ext_name";
235   copy_file("$ext_root/polymake.ext", "$ExtTop/polymake.ext", clean_dir => 1);
236
237   foreach my $subdir (qw(resources scripts upgrades)) {
238      if (-d "$ext_root/$subdir") {
239         copy_dir("$ext_root/$subdir", "$ExtTop/$subdir");
240      }
241   }
242
243   make_dir("$ExtTop/apps");
244   foreach my $dir (glob("$ext_root/apps/*/{perllib,rules,scripts}")) {
245      copy_dir($dir, $ExtTop.substr($dir, length($ext_root)));
246   }
247
248   foreach my $dir (glob("$ext_root/apps/*/include")) {
249      my ($app_name) = $dir =~ m{/apps/(\w+)/};
250      my $wrappers = "$`/include/app-wrappers/polymake/$app_name";
251      copy_dir($dir, "$ExtTop/include/polymake/$app_name",
252               -e $wrappers ? (wrappers => $wrappers) : ());
253   }
254
255   foreach my $dir (glob("$ext_root/apps/*/src")) {
256      copy_dir($dir, $ExtTop.substr($dir, length($ext_root)),
257               exclude => [ 'perl' ]);
258   }
259
260   $ExtArch = "$InstallArch/ext/$ext_name";
261   copy_file("$builddir/config.ninja", "$ExtArch/config.ninja", clean_dir => 1,
262             transform => \&transform_extension_config_file);
263
264   make_dir("$ExtArch/lib");
265   foreach my $app_dir (glob("$ext_root/apps/*")) {
266      my $app_mod = basename($app_dir).".$Config::Config{dlext}";
267      copy_file("$buildtop/lib/$app_mod", "$ExtArch/lib/$app_mod", mode => 0555);
268   }
269
270   if (-f (my $inst_script = "$ext_root/support/install.pl")) {
271      do $inst_script;
272      die "installation script $inst_script failed: $@" if $@;
273   }
274}
275
276sub glob_all_apps {
277   my ($pattern) = @_;
278   ( glob("$root/apps/*/$pattern"),
279     map { glob("$root/bundled/$_/apps/*/$pattern") } @BundledExts )
280}
281
282sub copy_file {
283   my ($from, $to, %options) = @_;
284   if (-e $from) {
285      if (!-f $from and !-l $from) {
286         die "$0: $from is neither a regular file nor a symbolic link\n";
287      }
288   } else {
289      die "$0: $from does not exist\n";
290   }
291   if ($options{clean_dir}) {
292      make_dir(dirname($to), clean_dir => 1);
293   } elsif (-d $to) {
294      $to .= "/" . basename($from);
295   }
296   copy($from, $to, %options);
297}
298
299sub copy_link {
300   my ($from, $to) = @_;
301   my $target = readlink($from);
302   if (-e $to || -l $to) {
303      unlink $to
304        or die "can't remove old $to: $!\n";
305   }
306   symlink $target, $to
307     or die "$0: can't create a symbolic link $to -> $target: $!\n";
308}
309
310sub rel_symlink {
311   my ($target, $link) = @_;
312   if (-e $link || -l $link) {
313      unlink $link
314        or die "$0: can't delete old $link: $!\n";
315   }
316   my $rel_link = $link;
317   my $common_prefix = 1;
318   while ($rel_link =~ s{^(/[^/]+)(?=/)}{}) {
319      if ($common_prefix && substr($target, 0, length($1)+1) eq "$1/") {
320         $target = substr($target, length($1));
321      } else {
322         $common_prefix = 0;
323         $target =~ s{^/?}{../};
324      }
325   }
326   $target =~ s{^/}{};
327   symlink $target, $link
328     or die "$0: can't create a symbolic link $link -> $target: $!\n";
329}
330
331sub compile_pattern {
332   my $expr = join('|', map { "(?:^$_\$)" } @_);
333   qr/$expr/;
334}
335
336sub exclude_pattern_for_copy {
337   my ($list) = @_;
338   if (defined $list) {
339      compile_pattern(@$list, @default_exclude_for_copy);
340   } else {
341      state $default_pattern = compile_pattern(@default_exclude_for_copy);
342   }
343}
344
345my $z1024 = pack "x1024";
346
347sub copy {
348   my ($from, $to, %options) = @_;
349   my $concat;
350   my $mode = $options{mode};
351
352   if (-e $to) {
353      if (defined(my $conflict = $options{conflict})) {
354         foreach my $c (@$conflict) {
355            if ($to =~ $c->[0]) {
356               if ($c->[1] eq 'abort') {
357                  die "$0: can't install $from: file $to already exists\n";
358               } elsif ($c->[1] eq 'concat') {
359                  $concat = 1;
360               } elsif ($c->[1] eq 'keep') {
361                  return;
362               } elsif ($c->[1] ne 'replace') {
363                  die "$0: unknown conflict action $c->[1]\n";
364               }
365               last;
366            }
367         }
368      }
369      unless ($concat || unlink $to) {
370         die "$0: can't remove old $to: $!\n";
371      }
372   }
373   my ($fmode, $mtime) = (stat $from)[2, 9];
374   if (defined $mode) {
375      # verbatim copy: assuming binary file
376      my $in = POSIX::open $from, O_RDONLY;
377      defined($in)
378         or die "$0: can't read $from: $!\n";
379      my $dummy = O_WRONLY + O_CREAT + O_TRUNC;  # bug in AutoLoader!
380      my $out;
381      if ($concat) {
382         open $out, ">>$to";
383      } else {
384         $out = creat $to, 0600;
385      }
386      defined($out)
387         or die "$0: can't create $to: $!\n";
388
389      my $trailing_zeroes;
390      while ((my $size = read $in, $_, 1024) > 0) {
391         if ($_ eq ($size == 1024 ? $z1024 : pack("x$size"))) {
392            lseek $out, $size, SEEK_CUR;
393            $trailing_zeroes = 1;
394         } else {
395            write $out, $_, $size;
396            $trailing_zeroes = 0;
397         }
398      }
399      if ($trailing_zeroes) {
400         lseek $out, -1, SEEK_CUR;
401         write $out, "\0", 1;
402      }
403      POSIX::close $in;
404      POSIX::close $out;
405
406      if (my $lib_id = $options{lib_id}) {
407         substr($lib_id, 0, length($destdir)) = "";
408         system("install_name_tool -id $lib_id $to")
409           and die "install_name_tool $to failed\n";
410      }
411   } else {
412      # text file: turn on write protection
413      $mode = $fmode & 0555;
414      local $/;
415      open X, "<", $from
416         or die "$0: can't read $from: $!\n";
417      $_ = <X>; close X;
418      if (defined (my $transform = $options{transform})) {
419	 &$transform;
420      }
421      open X, ">", $to
422         or die "$0: can't create $to: $!\n";
423      print X; close X;
424   }
425   $mode &= $permmask;
426   chmod $mode, $to
427     or die "can't modify access rights to $to: $!\n";
428   if (defined($group)) {
429      chown -1, $group, $to
430        or die "$0: can't change group of $to: $!\n";
431   }
432   utime $mtime, $mtime, $to
433     or die "can't modify mtime of $to: $!\n";
434}
435
436sub make_dir {
437   my ($dir, %options) = @_;
438   my $clean = $options{clean_dir};
439
440   if (-e $dir) {
441      if (-d _) {
442         if (defined($dirmode) && ((stat _)[2] & 03777) != $dirmode) {
443            chmod $dirmode, $dir
444               or die "$0: can't change mode of $dir: $!\n";
445         }
446         if (defined($group) and (stat _)[5] != $group) {
447            chown -1, $group, $dir
448              or die "$0: can't change group of $dir: $!\n";
449         }
450         if ($clean) {
451            my $error;
452            if (defined(my $exclude = $options{exclude})) {
453               my $exclude_re = compile_pattern(@$exclude, qw(. ..));
454               opendir my $D, $dir;
455               foreach (readdir $D) {
456                  if ($_ !~ $exclude_re) {
457                     if (-d "$dir/$_") {
458                        File::Path::remove_tree("$dir/_", { error => \$error });
459                        if (@$error) {
460                           die "$0: can't remove old files:\n", map { join(": ", %$_)."\n" } @$error;
461                        }
462                     } else {
463                        unlink "$dir/$_"
464                          or die "$0: can't remove old $dir/$_: $!\n";
465                     }
466                  }
467               }
468            } else {
469               File::Path::remove_tree($dir, { keep_root => 1, error => \$error });
470               if (@$error) {
471                  die "$0: can't clean $dir:\n", map { join(": ", %$_)."\n" } @$error;
472               }
473            }
474         }
475         return;
476      }
477      unlink $dir
478         or die "$0: can't remove old $dir: $!\n";
479   }
480   File::Path::make_path($dir, { defined($dirmode) ? (mode => $dirmode) : (),
481                                 defined($group) ? (group => $group) : () })
482      or die "could not create directory $dir: $!\n";
483}
484
485sub copy_dir {
486   my ($src, $dst, %options) = @_;
487   my $wrappers = delete $options{wrappers};
488   my $clean = $options{clean_dir};
489   my $exclude_re = exclude_pattern_for_copy($options{exclude});
490
491   if (opendir my $S, $src) {
492      make_dir($dst, clean_dir => $clean);
493      my (%noexport, @noexport_patterns, $next_created);
494      if (-f "$src/.noexport") {
495         open my $noexport, "$src/.noexport"
496           or die "can't read $src/.noexport: $!\n";
497         local $/="\n";
498         while (<$noexport>) {
499            if (my ($file, $status) = /^(?!\#)(\S+)\s+(\S+)$/) {
500               if ($file =~ /[?*\[\]]/) {
501                  $file =~ s/./\\./g;
502                  $file =~ tr/?/./;
503                  $file =~ s/\*/.*/g;
504                  push @noexport_patterns, [ qr{^$file$}, $status ];
505               } else {
506                  $noexport{$file} = $status;
507               }
508            }
509         }
510      }
511      foreach my $f (grep { $_ !~ $exclude_re } readdir $S) {
512         my $noexport = $noexport{$f};
513         unless ($noexport) {
514            foreach my $pat (@noexport_patterns) {
515               if ($f =~ $pat->[0]) {
516                  $noexport = $pat->[1];
517                  last;
518               }
519            }
520         }
521         if ($noexport) {
522            next if $noexport ne "local";
523         }
524         my $src_f = "$src/$f";
525         if (-d $src_f) {
526            copy_dir($src_f, "$dst/$f", %options);
527         } elsif ($wrappers && -f "$wrappers/$f") {
528	    $next_created ||= make_dir("$dst/next");
529	    copy($src_f, "$dst/next/$f", %options);
530	    copy("$wrappers/$f", "$dst/$f", %options,
531                 transform => sub { s{^#include_next +"(.*)/([^/]+)"}{#include "$1/next/$2"}m });
532	 } else {
533            copy($src_f, "$dst/$f", %options);
534         }
535      }
536   } else {
537      die "$0: can't traverse $src: $!\n";
538   }
539}
540
541sub install_bin_scripts {
542   local $/;
543   open S, "$root/perl/polymake"
544     or die "can't read $root/perl/polymake: $!\n";
545   $_ = <S>;
546   close S;
547
548   if ($^O eq "darwin" && $ConfigFlags{ARCHFLAGS} =~ /-arch /) {
549      s{^\#!/usr/bin/env perl}{#!/usr/bin/arch $ConfigFlags{ARCHFLAGS} $^X}s;
550   } else {
551      s{^\#!/usr/bin/env perl}{#!$Config::Config{perlpath}}s;
552   }
553
554   my $init_block = <<"---";
555   \$InstallTop='$ConfigFlags{InstallTop}';
556   \$InstallArch='$ConfigFlags{InstallArch}';
557   \$Arch="$ConfigFlags{Arch}";
558   \@BundledExts=qw(@BundledExts);
559---
560   if ($^O eq "darwin" && $ConfigFlags{FinkBase}) {
561      $init_block.="   \@addlibs=qw($ConfigFlags{FinkBase}/lib/perl5);\n";
562   }
563   s/(^BEGIN\s*\{\s*\n)(?s:.*?)(^\}\n)/$1$init_block$2/m;
564
565   if (-e "$InstallBin/polymake") {
566      unlink "$InstallBin/polymake"
567        or die "can't remove old polymake script: $!\n";
568   }
569   open T, ">$InstallBin/polymake"
570     or die "can't create $InstallBin/polymake: $!\n";
571   print T;
572   close T;
573
574   chmod(0555, "$InstallBin/polymake")
575     or die "chmod $InstallBin/polymake failed: $!\n";
576
577   # apply similar transformations to the configuration utility
578
579   my $Version = extract_polymake_version($root);
580
581   open S, "$root/perl/polymake-config"
582     or die "can't read $root/perl/polymake-config: $!\n";
583   $_ = <S>;
584   close S;
585
586   if ($^O eq "darwin" && $ConfigFlags{ARCHFLAGS} =~ /-arch /) {
587      s{^\#!\S+}{#!/usr/bin/arch $ConfigFlags{ARCHFLAGS} $^X}s;
588   } else {
589      s{^\#!\S+}{#!$Config::Config{perlpath}}s;
590   }
591
592   s{=Version(?=;)}{=$Version};
593   s{=InstallTop(?=;)}{='$ConfigFlags{InstallTop}'};
594   s{=InstallArch(?=;)}{='$ConfigFlags{InstallArch}'};
595
596   if (-e "$InstallBin/polymake-config") {
597      unlink "$InstallBin/polymake-config"
598        or die "can't remove old polymake-config script: $!\n";
599   }
600   open T, ">$InstallBin/polymake-config"
601     or die "can't create $InstallBin/polymake-config: $!\n";
602   print T;
603   close T;
604
605   chmod(0555, "$InstallBin/polymake-config")
606     or die "chmod $InstallBin/polymake-config failed: $!\n";
607}
608
609sub load_sanitizer_flags {
610   open my $BB, "<", "$buildtop/build.ninja"
611      or die "can't read $buildtop/build.ninja: $!\n";
612   while (<$BB>) {
613      chomp;
614      if (/^\s*PERLSANITIZE\s*=\s*/) {
615         $ConfigFlags{PERLSANITIZE}=$';
616      } elsif (/^\s*perlxpath\s*=\s*/) {
617         if ($perlxpath ne $') {
618            die "$buildtop/build.ninja does not match the current running environment; expected perlxpath=$perlxpath\n";
619         }
620      } elsif (/^\s*builddir\s*=\s*/) {
621         if ($builddir ne $') {
622            die "$buildtop/build.ninja does not match the current running environment; expected builddir=$builddir\n";
623         }
624      }
625   }
626   $ConfigFlags{PERLSANITIZE} or die "$buildtop/build.ninja does not contain PERLSANITIZE flags\n";
627}
628
629sub transform_core_config_file {
630   s{^\s* configure\.command \s*= .* \n}{}xm;
631
632   s{^\s* root \s*= \K .*}{$ConfigFlags{InstallTop}}xm;
633   s{^\s* core\.includes \s*= \K .*}{-I$ConfigFlags{InstallInc}}xm;
634
635   my $external_includes = $ConfigFlags{ExternalHeaders} =~ /\S/ ? "-I$ConfigFlags{InstallInc}/polymake/external" : "";
636   s{^\s* app\.includes \s*= \K .*}{$external_includes}xm;
637
638   s{^(?=\s*Arch\s*=)}{PERL = $Config::Config{perlpath}\n}m;
639   s{^\s* BuildModes \s*= \K .*}{$buildmode}xm;
640   s{^\s* (?: DESTDIR | CCACHE | CCWRAPPER ) \s*= .*$}{}xm;
641
642   s{[ =] -Wl,-z, \K now}{lazy}x;
643
644   if ($buildmode eq "San") {
645      s{^\s* PERL \s*= .*\n \K}{PERLSANITIZE = $ConfigFlags{PERLSANITIZE}\n}xm;
646   }
647}
648
649sub transform_extension_config_file {
650   s{^\s* root \s*= \K .*}{$ConfigFlags{InstallTop}}xm;
651   s{^\s* extroot \s*= \K .*}{$ConfigFlags{InstallTop}/ext/$ext_name}xm;
652   s{^\s* app\.includes \s*= \K .*}{-I\${extroot}/include \${super.app.includes}}xm;
653   s{^\s* BuildModes \s*= \K .*}{$buildmode}xm;
654}
655
656# Local Variables:
657# cperl-indent-level:3
658# indent-tabs-mode:nil
659# End:
660