source: branches/ali/GDEHELP/pp.pl

Last change on this file was 18842, checked in by westram, 3 years ago
  • allow conditional build of OPENMP version of FastTree
    • tweak preprocessor: allow to #define via CLI.
    • globally define whether OPENMP shall be used
      • export to submakefiles.
      • disable under OSX
        • capability to build OPENMP binary depends on compiler
        • LLVM/Clang 3.7 required
    • forward USE_OPENMP into preprocessor.
    • in fasttree.menu: when not defined USE_OPENMP → skip MP option from GUI + hardcode binary.
    • conditionally build OPENMP binary depending on USE_OPENMP.
  • Property svn:executable set to *
File size: 8.6 KB
Line 
1#!/usr/bin/perl
2# ============================================================ #
3#                                                              #
4#   File      : pp.pl                                          #
5#   Purpose   : a simple pseudo-C-preprocessor                 #
6#   Motivation:                                                #
7#     The C-preprocessor behaves different on different        #
8#     systems (e.g. clang, suse) while creating "arb.menu".    #
9#     That resulted in various failures,                       #
10#     some detected at compile-time, others at run-time.       #
11#                                                              #
12#   Coded by Ralf Westram (coder@reallysoft.de) in June 2012   #
13#   Institute of Microbiology (Technical University Munich)    #
14#   http://www.arb-home.de/                                    #
15#                                                              #
16# ============================================================ #
17
18# Restrictions:
19# - '#if' unsupported
20# - comment parsing is error-prone
21
22use strict;
23use warnings;
24
25sub parseOneParam(\$) {
26  my ($code_r) = @_;
27
28  my $inside = '';
29  my @instack = ();
30
31  my $param = '';
32
33  while ($$code_r =~ /[()[\],\"\']/o) {
34    my ($before,$sep,$after) = ($`,$&,$');
35    my ($do_pop,$do_shift) = (0,0);
36
37    if ($before =~ /\\$/o) { $do_shift = 1; }
38    elsif ($inside eq '"' or $inside eq '\'') {
39      if ($sep eq $inside) { $do_pop = 1; }
40      else { $do_shift = 1; }
41    }
42    else {
43      if ($sep eq ',') {
44        $$code_r = $after;
45        return $param.$before;
46      }
47      if ($sep eq '\'' or $sep eq '"' or $sep eq '(' or $sep eq '[') {
48        push @instack, $inside;
49        $inside = $sep;
50        $do_shift = 1;
51      }
52      elsif ($sep eq ')') {
53        if ($inside eq '') {
54          $$code_r = $sep.$after;
55          return $param.$before;
56        }
57        if ($inside ne '(') { die "Misplaced ')' in '$$code_r'\n"; }
58        $do_pop = 1;
59      }
60      elsif ($sep eq ']') {
61        if ($inside ne '[') { die "Misplaced ']' in '$$code_r'\n"; }
62        $do_pop = 1;
63      }
64      else {
65        die "unhandled separator: param='$param'\nbefore='$before'\nsep='$sep'\nafter='$after'\ncode_r='$$code_r'";
66      }
67    }
68
69    if ($do_pop==1) {
70      $inside = pop @instack;
71      $do_shift = 1;
72    }
73    if ($do_shift==1) {
74      $param .= $before.$sep;
75      $$code_r = $after;
76    }
77  }
78
79  $param .= $$code_r;
80  $$code_r = '';
81
82  return $param;
83}
84
85sub parseMacroParams($\@) {
86  my ($code,$param_r) = @_;
87
88  if (not $code =~ /^\(/o) { die "Expected '(', seen '$code'"; }
89  $code = $';
90
91 PARAM: while (1) {
92    $code =~ s/^\s+//o;
93    if ($code =~ /^\)/o) { $code = $'; last PARAM; }
94    if ($code eq '') { die "Missing or misplaced ')'"; }
95
96    my $param = parseOneParam($code);
97    push @$param_r, $param;
98  }
99  return $code;
100}
101
102sub apply_define($\@);
103sub apply_define($\@) {
104  my ($line,$defr) = @_;
105
106  my $name = $$defr[0];
107  if ($line =~ /\b$name\b/) {
108    my ($prefix,$suffix) = ($`,$');
109    my $pcount = $$defr[1];
110    if ($pcount==0) {
111      return $prefix.$$defr[2].apply_define($suffix,@$defr);
112    }
113
114    my @param = ();
115    $suffix = parseMacroParams($suffix,@param);
116
117    my $paramCount = scalar(@param);
118    if ($paramCount ne $pcount) {
119      die "Expected $pcount arguments for macro '$name' (found $paramCount)\n";
120    }
121
122    my $expanded = $$defr[$pcount+2];
123    for (my $p=0; $p<$pcount; $p++) {
124      my $search = $$defr[$p+2];
125      my $replace = $param[$p];
126      $expanded =~ s#$search#$replace#g;
127    }
128
129    return $prefix.$expanded.apply_define($suffix,@$defr);
130  }
131  return $line;
132}
133
134my @define = (); # list of defines (applied in order). contains array refs to [ name, pcount, [ pnames...,] content ]
135my %define = (); # known defines
136
137sub apply_defines($) {
138  my ($line) = @_;
139  foreach my $defr (@define) {
140    $line = apply_define($line, @$defr);
141  }
142  return $line;
143}
144
145sub def_define {
146  my @def = @_;
147  unshift @define, \@def;
148  $define{$def[0]} = 1;
149}
150
151sub add_define($) {
152  my ($rest) = @_;
153
154  if ($rest =~ /^[A-Z0-9_]+/io) {
155    my ($name,$param) = ($&,$');
156    if ($param eq '') {
157      def_define($name, 0, '');
158    }
159    elsif ($param =~ /^\s+/o) {
160      def_define($name, 0, apply_defines($'));
161    }
162    elsif ($param =~ /^\(([a-z0-9,_]+)\)\s+/io) {
163      my ($args,$def) = ($1,$');
164      $args =~ s/\s+//oig;
165      my @args = split /,/,$args;
166      my $count = scalar(@args);
167
168      my @array = ( $name, $count );
169      foreach (@args) { push @array, $_; }
170      push @array, apply_defines($def);
171      def_define(@array);
172    }
173    else {
174      die "invalid macro parameter '$param'";
175    }
176  }
177  else {
178    die "invalid define '$rest'\n";
179  }
180}
181sub rm_define($) {
182  my ($rest) = @_;
183  if ($rest =~ /^[A-Z0-9_]+/io) {
184    my $name = $&;
185    if (exists $define{$name}) {
186      @define = map {
187        my $def_r = $_;
188        if ($$def_r[0] eq $name) { ; }
189        else { $def_r; }
190      } @define;
191      delete $define{$name};
192    }
193    else {
194      die "'$name' has not been defined";
195    }
196  }
197  else {
198    die "invalid undef '$rest'\n";
199  }
200}
201sub is_defined($) {
202  my ($rest) = @_;
203  if ($rest =~ /^[A-Z0-9_]+/io) {
204    my $name = $&;
205    exists $define{$name};
206  }
207  else {
208    die "invalid ifdef '$rest'\n";
209  }
210}
211
212my $inMultiLineComment = 0;
213
214sub remove_comments($);
215sub remove_comments($) {
216  my ($line) = @_;
217  if ($inMultiLineComment) {
218    if ($line =~ /\*\//o) {
219      $inMultiLineComment--;
220      $line = $';
221    }
222    if ($inMultiLineComment) {
223      return '';
224    }
225  }
226  if ($line =~ /^[^'"]*\/\//o) {
227    return $`."\n";
228  }
229  if ($line =~ /\/\*/o) {
230    $inMultiLineComment++;
231    return remove_comments($');
232  }
233  return $line;
234}
235
236sub preprocess($);
237
238my @include = (); # list of include directories
239
240sub include_via_ipath($) {
241  my ($name) = @_;
242  foreach (@include) {
243    my $rel = $_.'/'.$name;
244    if (-f $rel) {
245      preprocess($rel);
246      return;
247    }
248  }
249  die "Could not find include file '$name'\n";
250}
251
252sub include($) {
253  my ($spec) = @_;
254  if ($spec =~ /^\"([^\"]+)\"/o) {
255    my $name = $1;
256    if (-f $name) { preprocess($name); }
257    else { include_via_ipath($name); }
258  }
259  elsif ($spec =~ /^<([^>]+)>/o) {
260    my $name = $1;
261    include_via_ipath($name);
262  }
263  else { die "no idea how to include '$spec'\n"; }
264}
265
266sub preprocess($) {
267  my ($src) = @_;
268
269  my $skip = 0;
270  my @skipstack = ();
271
272  open(my $IN,'<'.$src) || die "can't read '$src' (Reason: $!)";
273  my $line;
274  while (defined($line=<$IN>)) {
275    while ($line =~ /\\\n/o) { # concat multilines
276      my ($body) = $`;
277      my $nextLine = <$IN>;
278      if (not defined $nextLine) { die "runaway multiline"; }
279      $line = $body.$nextLine;
280    }
281
282    eval {
283      if ($line =~ /^\s*[#]\s*([^\s]*)\s+/o) {
284        my ($token,$rest) = ($1,$');
285        chomp($rest);
286        if ($token eq 'define') { add_define($rest) if not $skip; }
287        elsif ($token eq 'undef') { rm_define($rest) if not $skip; }
288        elsif ($token eq 'include') {
289          my $oline = $.;
290          eval { include($rest) if not $skip; };
291          $. = $oline;
292          if ($@) { die "included from here\n$@"; }
293        }
294        elsif ($token eq 'ifdef') {
295          push @skipstack, $skip;
296          if (not $skip) {
297            $skip = is_defined($rest) ? 0 : 1;
298          }
299        }
300        elsif ($token eq 'else') {
301          if (scalar(@skipstack)==0) { die "else w/o if\n"; }
302          my $prevskip = $skipstack[$#skipstack];
303          if (not $prevskip) {
304            $skip = 1-$skip;
305          }
306        }
307        elsif ($token eq 'endif') {
308          if (scalar(@skipstack)==0) { die "endif w/o if\n"; }
309          $skip = pop @skipstack;
310        }
311        else { die "unknown preprocessor token='$token' rest='$rest'\n"; }
312      }
313      else {
314        if ($skip==0) {
315          $line = remove_comments($line);
316          print apply_defines($line);
317        }
318      }
319    };
320    if ($@) { die "$src:$.: $@\n"; }
321  }
322  if (scalar(@skipstack)!=0) { die "EOF reached while inside if\n"; }
323  close($IN);
324}
325
326sub addIncludePaths($) {
327  my ($pathlist) = @_;
328  my @paths = split /;/, $pathlist;
329  foreach (@paths) { push @include, $_; }
330}
331
332sub main() {
333  eval {
334    my $src = undef;
335    foreach (@ARGV) {
336      if ($_ =~ /^-I/) {
337        addIncludePaths($');
338      }
339      elsif ($_ =~ /^-D/) {
340        my $rest = $';
341        $rest =~ s/=/ /; # replace '=' by ' ' -> same syntax as #define inside code
342        add_define($rest);
343      }
344      else {
345        if (defined $src) { die "Multiple sources specified ('$src' and '$_')\n"; }
346        $src = $_;
347      }
348    }
349
350    if (not defined $src) {
351      die "No source file specified on CLI";
352    }
353    preprocess($src);
354  };
355  if ($@) { die "$@ (in pp.pl)\n"; }
356}
357main();
358
359
Note: See TracBrowser for help on using the repository browser.