File Coverage

blib/lib/Makefile/AST.pm
Criterion Covered Total %
statement 122 525 23.2
branch 19 176 10.8
condition 8 35 22.8
subroutine 28 45 62.2
pod 1 20 5.0
total 178 801 22.2


line stmt bran cond sub pod time code
1             package Makefile::AST;
2              
3 1     1   491 use strict;
  1         1  
  1         34  
4 1     1   4 use warnings;
  1         1  
  1         45  
5              
6             our $VERSION = '0.216';
7              
8             #use Smart::Comments;
9             #use Smart::Comments '####';
10              
11 1     1   462 use Makefile::AST::StemMatch;
  1         2  
  1         8  
12 1     1   360 use Makefile::AST::Rule::Implicit;
  1         2  
  1         5  
13 1     1   354 use Makefile::AST::Rule;
  1         2  
  1         6  
14 1     1   392 use Makefile::AST::Variable;
  1         2  
  1         5  
15              
16 1     1   26 use base 'Class::Accessor::Fast';
  1         1  
  1         70  
17              
18             __PACKAGE__->mk_ro_accessors(qw{
19             phony_targets targets prereqs makefile
20             pad_stack named_pads pad_triggers
21             });
22              
23             __PACKAGE__->mk_accessors(qw{
24             default_goal
25             });
26              
27 1     1   4 use List::Util 'first';
  1         6  
  1         52  
28 1     1   4 use List::MoreUtils qw( uniq pairwise ) ;
  1         1  
  1         50  
29 1     1   8 use Cwd qw/ realpath /;
  1         2  
  1         32  
30 1     1   3 use File::Spec;
  1         1  
  1         19  
31 1     1   392 use MDOM::Util 'trim_tokens';
  1         212  
  1         46  
32 1     1   448 use MDOM::Document::Gmake;
  1         41235  
  1         1587  
33              
34             # XXX better name?
35             our $Runtime = undef;
36              
37             sub new ($@) {
38 1 50   1 1 11 my $class = ref $_[0] ? ref shift : shift;
39 1         2 my $makefile = shift;
40 1         11 return bless {
41             explicit_rules => {},
42             implicit_rules => [],
43             pad_stack => [{}], # the last scope is
44             # the default GLOBAL
45             # scope
46             named_pads => {}, # hooks for target-specific
47             # variables
48             pad_triggers => {},
49             targets => {},
50             prereqs => {},
51             phony_targets => {},
52             makefile => $makefile,
53             }, $class;
54             }
55              
56             sub is_phony_target ($$) {
57 0     0 0 0 my ($self, $target) = @_;
58 0         0 $self->phony_targets->{$target};
59             }
60              
61             sub set_phony_target ($$) {
62 0     0 0 0 my ($self, $target) = @_;
63 0         0 $self->phony_targets->{$target} = 1;
64             }
65              
66             sub target_exists ($$) {
67 17     17 0 16 my $self = shift;
68             # XXX provide hooks for mocking file systems
69             # XXX access the mtime cache instead in the future
70 17         16 my $target = shift;
71             #### Test if target exists: $target
72             #### Result: -e $target
73 17         108 return -e $target;
74             }
75              
76             sub target_ought_to_exist ($$) {
77 25     25 0 29 my ($self, $target) = @_;
78 25   100     47 my $res = $self->targets->{$target} ||
79             $self->prereqs->{$target};
80             ### Test if $target ought to exist: $res
81 25         228 $res;
82             }
83              
84             sub apply_explicit_rules ($$) {
85 5     5 0 17 my ($self, $target) = @_;
86 5   100     23 my $list = $self->{explicit_rules}->{$target} || [];
87 5 100       13 wantarray ? @$list : $list->[0];
88             }
89              
90             sub get_var ($$) {
91 2     2 0 23 my ($self, $name) = @_;
92 2         4 my $pads = $self->pad_stack;
93 2         8 for my $pad (@$pads) {
94 2 50       6 if (my $var = $pad->{$name}) {
95 2         4 return $var;
96             }
97             }
98 0         0 return undef;
99             }
100              
101             # XXX sub find_var
102             # find_var(name => $name, flavor => $flavor)
103              
104             # enter the pad for a lexical scope
105             sub enter_pad ($@) {
106 0     0 0 0 my ($self, $name) = @_;
107             #### Entering pad named: $name
108 0         0 my $stack = $self->pad_stack;
109 0         0 my $pad;
110 0 0       0 if (defined $name) {
111 0   0     0 $pad =
112             $self->named_pads->{$name} ||= {};
113             } else {
114 0         0 $pad = {};
115             }
116 0         0 unshift @$stack, $pad;
117 0 0       0 if (defined $name) {
118 0         0 my $list = $self->pad_triggers->{$name};
119 0 0       0 if ($list) {
120 0         0 for my $trigger (@$list) {
121             #### Firing pad trigger for: $name
122 0         0 $trigger->($self);
123             }
124             }
125             }
126             }
127              
128             sub leave_pad ($@) {
129 0     0 0 0 my ($self, $count) = @_;
130             #### Leaving pad...
131 0         0 my $stack = $self->pad_stack;
132 0 0       0 $count = 1 if !defined $count;
133 0         0 for (1..$count) {
134 0 0       0 shift @$stack if @$stack > 1;
135             }
136             }
137              
138             sub pad_stack_len ($) {
139 0     0 0 0 scalar(@{ $_[0]->pad_stack });
  0         0  
140             }
141              
142             sub add_pad_trigger ($$$) {
143 0     0 0 0 my ($self, $name, $sub) = @_;
144 0   0     0 my $list = $self->pad_triggers->{$name} ||= [];
145 0         0 push @$list, $sub;
146             }
147              
148             sub add_var ($$) {
149 2     2 0 33 my ($self, $var) = @_;
150             # XXX variable overridding check
151             ## variable name: $var->name()
152 2 50       7 if (!ref $var->value) {
153 2         17 $var->value(
154             [MDOM::Document::Gmake::_tokenize_command(
155             $var->value
156             )]
157             );
158             }
159 2         847 $self->pad_stack->[0]->{$var->name()} = $var;
160             }
161              
162             sub add_auto_var ($$$@) {
163 1     1 0 5 my $self = shift;
164 1         3 my %pairs = @_;
165 1         4 while (my ($name, $value) = each %pairs) {
166 1         6 my $var = Makefile::AST::Variable->new(
167             { name => $name,
168             flavor => 'simple',
169             origin => 'automatic',
170             value => $value,
171             }
172             );
173 1         10 $self->add_var($var);
174             }
175             }
176              
177             sub explicit_rules ($) {
178 0     0 0 0 my $self = shift;
179 0         0 my @items = values %{ $self->{explicit_rules} };
  0         0  
180 0         0 my @rules = map { @$_ } @items;
  0         0  
181 0         0 \@rules;
182             }
183              
184              
185             sub implicit_rules ($) {
186 9     9 0 23 $_[0]->{implicit_rules};
187             }
188              
189             sub add_explicit_rule ($$) {
190 1     1 0 24 my ($self, $rule) = @_;
191 1 50       5 if (!defined $self->default_goal) {
192 1         7 my $target = $rule->target;
193             ### check if it's the default target: $target
194             # XXX skip the makefile itself
195 1 50 33     9 if ($target !~ m{^\./Makefile_\S+} and (substr($target, 0, 1) ne '.' or $target =~ m{/})) {
      33        
196 1         3 $self->default_goal($target);
197             }
198             }
199 1 50       12 if ($rule->colon eq ':') {
200             # XXX check single colon rules for conflicts
201             # XXX merge prereqs if no cmd given
202 1         7 $self->{explicit_rules}->{$rule->target} =
203             [$rule];
204             } else {
205             # XXX check double colon rules for conflicts
206 0   0     0 my $list =
207             $self->{explicit_rules}->{$rule->target} ||=
208             [];
209             # XXX check if $list is an ARRAY ref
210 0         0 push @$list, $rule;
211             }
212 1         4 for my $prereq (@{$rule->normal_prereqs}, @{$rule->order_prereqs}) {
  1         4  
  1         5  
213 2         10 $self->prereqs->{$prereq} = 1;
214             }
215 1         7 $self->targets->{$rule->target} = 1;
216             }
217              
218             sub add_implicit_rule ($$) {
219 1     1 0 40 my ($self, $rule) = @_;
220             # XXX cancel a built-in implicit rule by defining
221             # a pattern rule with the same target and
222             # prerequisites, but no commands
223 1         2 for my $target (@{ $rule->targets }) {
  1         3  
224             # XXX better pattern recognition
225 2 50       16 next if $target =~ /\%/;
226 0         0 $self->targets->{$target} = 1;
227             }
228 1         1 for my $prereq (@{$rule->normal_prereqs}, @{$rule->order_prereqs}) {
  1         7  
  1         19  
229 5 100       19 next if $prereq =~ /\%/;
230 2         5 $self->prereqs->{$prereq} = 1;
231             }
232 1         3 my $list = $self->{implicit_rules};
233 1         3 unshift @$list, $rule;
234             }
235              
236             # implementation for the implicit rule search
237             # algorithm
238             sub apply_implicit_rules ($$) {
239 7     7 0 29 my ($self, $target) = @_;
240              
241             # XXX handle archive(member) here
242              
243             #### step 2...
244 7         18 my @rules = grep { $_->match_target($target) }
  7         12  
245 7         8 @{ $self->implicit_rules };
246             #### rules: map { $_->as_str } @rules
247 7 100       23 return undef if !@rules;
248              
249             #### step 3...
250 4 50   4   31 if (first { ! $_->match_anything } @rules) {
  4         9  
251 4   33     8 @rules = grep {
252 4         6 !( $_->match_anything && !$_->is_terminal )
253             } @rules;
254             }
255             #### rules: map { $_->as_str } @rules
256              
257             #### step 4...
258 4         10 @rules = grep { @{ $_->commands } > 0 } @rules;
  4         4  
  4         12  
259             #### rules: map { $_->as_str } @rules
260              
261             #### step 5...
262             # XXX This is hacky...not sure if it's the right
263             # XXX thing to do (it's unspec'd afaik)
264 0         0 @rules = sort {
265 4         19 -( scalar( @{ $a->normal_prereqs } ) <=>
  0         0  
266 0         0 scalar( @{ $b->normal_prereqs } ) )
267             } @rules;
268 4         7 for my $rule (@rules) {
269             #### target: $target
270             #### rule: $rule->as_str
271             #### file test: -e 'bar.hpp'
272 4         9 my $applied = $rule->apply($self, $target);
273 4 100       45 if ($applied) {
274             #### applied rule: $applied->as_str
275 1         3 return $applied;
276             }
277             }
278              
279             ### step 6...
280 3         4 for my $rule (@rules) {
281 3 50       6 next if $rule->is_terminal;
282             #### applying the implicit rule recursively
283 3         29 my $applied = $rule->apply(
284             $self, $target,
285             { recursive => 1 });
286 3 50       9 if ($applied) {
287 0         0 return $applied;
288             }
289             #### Failed to apply the rule recursively
290             }
291              
292             ### step 7...
293 3         8 my $applied = $self->apply_explicit_rules('.DEFAULT');
294 3 50       7 if ($applied) {
295 0         0 $applied->target($target);
296 0         0 return $applied;
297             }
298 3         6 return undef;
299             }
300              
301             sub _pat2re ($@) {
302 0     0     my ($pat, $capture) = @_;
303 0           $pat = quotemeta $pat;
304 0 0         if ($capture) {
305 0           $pat =~ s/\\\%/(\\S*)/g;
306             } else {
307 0           $pat =~ s/\\\%/\\S*/g;
308             }
309 0           $pat;
310             }
311              
312             sub _split_args($$$$) {
313 0     0     my ($self, $func, $s, $m, $n) = @_;
314 0   0       $n ||= $m;
315 0           my @tokens = '';
316 0           my @args;
317             ### $n
318 0           while (@args <= $n) {
319             ### split args: @args
320             ### split tokens: @tokens
321 0 0         if ($s =~ /\G\s+/gc) {
    0          
    0          
    0          
    0          
    0          
322 0           push @tokens, $&, '';
323             }
324             elsif ($s =~ /\G[^\$,]+/gc) {
325 0           $tokens[-1] .= $&;
326             }
327             elsif ($s =~ /\G,/gc) {
328 0 0         if (@args < $n - 1) {
329 0           push @args, [grep { $_ ne '' } @tokens];
  0            
330 0           @tokens = '';
331             } else {
332 0           $tokens[-1] .= $&;
333             }
334             }
335             elsif (my $res = MDOM::Document::Gmake::extract_interp($s)) {
336             #die $res;
337 0           push @tokens, MDOM::Token::Interpolation->new($res), '';
338             }
339             elsif ($s =~ /\G\$./gc) {
340 0           push @tokens, MDOM::Token::Interpolation->new($&), '';
341             }
342             elsif ($s =~ /\G./gc) {
343 0           $tokens[-1] .= $&;
344             }
345             else {
346 0 0         if (@args <= $n - 1) {
347 0           push @args, [grep { $_ ne '' } @tokens];
  0            
348             }
349 0 0 0       last if @args >= $m and @args <= $n;
350 0           warn $self->makefile, ":$.: ",
351             "*** insufficient number of arguments (",
352             scalar(@args), ") to function `$func'. Stop.\n";
353 0           exit(2);
354             }
355             }
356 0           return @args;
357             }
358              
359             sub eval_var_value ($$) {
360 0     0 0   my ($self, $name) = @_;
361 0 0         if (my $var = $self->get_var($name)) {
362             ### eval_var_value: $var
363 0 0         if ($var->flavor eq 'recursive') {
364             ## HERE! eval_var_value
365             ## eval recursive var: $var
366 0           my $val = $self->solve_refs_in_tokens(
367             $var->value
368             );
369 0           $val =~ s/^\s+|\s+$//gs;
370             #warn "value: $val\n";
371 0           return $val;
372             } else {
373             # don't complain about uninitialized value:
374 1     1   8 no warnings 'uninitialized';
  1         1  
  1         2035  
375 0           my $val = join '', @{$var->value};
  0            
376 0           $val =~ s/^\s+|\s+$//gs;
377 0           return $val;
378             }
379             } else {
380             # process undefined var:
381 0           return '';
382             }
383             }
384              
385             sub _text2words ($) {
386 0     0     my ($text) = @_;
387 0           $text =~ s/^\s+|\s+$//g;
388 0           split /\s+/, $text;
389             }
390              
391             sub _check_numeric ($$$$) {
392 0     0     my ($self, $func, $order, $n) = @_;
393 0 0         if ($n !~ /^\d+$/) {
394 0           warn $self->makefile, ":$.: ",
395             "*** non-numeric $order argument to `$func' function: '$n'. Stop.\n";
396 0           exit(2);
397             }
398             }
399              
400             sub _check_greater_than ($$$$$) {
401 0     0     my ($self, $func, $order, $n, $value) = @_;
402 0 0         if ($n <= $value) {
403 0           warn $self->makefile, ":$.: *** $order argument to `$func' function must be greater than $value. Stop.\n";
404 0           exit(2);
405             }
406             }
407              
408             sub _trim ($@) {
409 0     0     for (@_) {
410 0           s/^\s+|\s+$//g;
411             }
412             }
413              
414             sub _process_func_ref ($$$) {
415 0     0     my ($self, $name, $args) = @_;
416             ### process func ref: $name
417             # XXX $name = $self->_process_refs($name);
418 0           my @args;
419 0           my $nargs = scalar(@args);
420 0 0         if ($name eq 'subst') {
421 0           my @args = $self->_split_args($name, $args, 3);
422 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
423             ### arguments: @args
424 0           my ($from, $to, $text) = @args;
425 0           $from = quotemeta($from);
426 0           $text =~ s/$from/$to/g;
427 0           return $text;
428             }
429 0 0         if ($name eq 'patsubst') {
430 0           my @args = $self->_split_args($name, $args, 3);
431 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
432 0           my ($pattern, $replacement, $text) = @args;
433 0           my $re = _pat2re($pattern, 1);
434 0           $replacement =~ s/\%/\${1}/g;
435 0           $replacement = qq("$replacement");
436             #### pattern: $re
437             #### replacement: $replacement
438             #### text: $text
439 0           my $code = "s/^$re\$/$replacement/e";
440             #### code: $code
441 0           my @words = _text2words($text);
442 0           map { eval $code; } @words;
  0            
443 0           return join ' ', grep { $_ ne '' } @words;
  0            
444             }
445 0 0         if ($name eq 'strip') {
446 0           my @args = $self->_split_args($name, $args, 1);
447 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
448 0           my ($string) = @args;
449 0           $string =~ s/^\s+|\s+$//g;
450 0           $string =~ s/\s+/ /g;
451 0           return $string;
452             }
453 0 0         if ($name eq 'findstring') {
454 0           my @args = $self->_split_args($name, $args, 2);
455 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
456 0           my ($find, $in) = @args;
457 0 0         if (index($in, $find) >= 0) {
458 0           return $find;
459             } else {
460 0           return '';
461             }
462 0           my ($patterns, $text) = @args;
463 0           my @regexes = map { _pat2re($_) }
  0            
464             split /\s+/, $patterns;
465             ## regexes: @regexes
466 0           my $regex = join '|', map { "(?:$_)" } @regexes;
  0            
467             ## regex: $regex
468 0           my @words = _text2words($text);
469 0           return join ' ', grep /^$regex$/, @words;
470              
471             }
472 0 0         if ($name eq 'filter') {
473 0           my @args = $self->_split_args($name, $args, 2);
474 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
475 0           my ($patterns, $text) = @args;
476 0           my @regexes = map { _pat2re($_) }
  0            
477             split /\s+/, $patterns;
478             ## regexes: @regexes
479 0           my $regex = join '|', map { "(?:$_)" } @regexes;
  0            
480             ## regex: $regex
481 0           my @words = _text2words($text);
482 0           return join ' ', grep /^$regex$/, @words;
483             }
484 0 0         if ($name eq 'filter-out') {
485 0           my @args = $self->_split_args($name, $args, 2);
486 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
487 0           my ($patterns, $text) = @args;
488 0           my @regexes = map { _pat2re($_) }
  0            
489             split /\s+/, $patterns;
490             ## regexes: @regexes
491 0           my $regex = join '|', map { "(?:$_)" } @regexes;
  0            
492             ## regex: $regex
493 0           my @words = _text2words($text);
494 0           return join ' ', grep !/^$regex$/, @words;
495             }
496 0 0         if ($name eq 'sort') {
497 0           my @args = $self->_split_args($name, $args, 1);
498 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
499 0           my ($list) = @args;
500 0           _trim($list);
501 0           return join ' ', uniq sort split /\s+/, $list;
502             }
503 0 0         if ($name eq 'words') {
504 0           my @args = $self->_split_args($name, $args, 1);
505 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
506 0           my ($text) = @args;
507 0           my @words = _text2words($text);
508 0           return scalar(@words);
509             }
510 0 0         if ($name eq 'word') {
511 0           my @args = $self->_split_args($name, $args, 2);
512 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
513 0           my ($n, $text) = @args;
514 0           _trim($n);
515 0           $self->_check_numeric('word', 'first', $n);
516 0           $self->_check_greater_than('word', 'first', $n, 0);
517 0           my @words = _text2words($text);
518 0 0         return $n > @words ? '' : $words[$n - 1];
519             }
520 0 0         if ($name eq 'wordlist') {
521 0           my @args = $self->_split_args($name, $args, 3);
522 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
523 0           my ($s, $e, $text) = @args;
524 0           _trim($s, $e, $text);
525 0           $self->_check_numeric('wordlist', 'first', $s);
526 0           $self->_check_numeric('wordlist', 'second', $e);
527 0           $self->_check_greater_than('wordlist', 'first', $s, 0);
528 0           $self->_check_greater_than('wordlist', 'second', $s, -1);
529 0           my @words = _text2words($text);
530 0 0 0       if ($s > $e || $s > @words || $e == 0) {
      0        
531 0           return '';
532             }
533 0 0         $e = @words if $e > @words;
534 0           return join ' ', @words[$s-1..$e-1];
535             }
536 0 0         if ($name eq 'firstword') {
537 0           my @args = $self->_split_args($name, $args, 1);
538 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
539 0           my ($text) = @args;
540 0           my @words = _text2words($text);
541 0 0         return @words > 0 ? $words[0] : '';
542             }
543 0 0         if ($name eq 'lastword') {
544 0           my @args = $self->_split_args($name, $args, 1);
545 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
546 0           my ($text) = @args;
547 0           my @words = _text2words($text);
548 0 0         return @words > 0 ? $words[-1] : '';
549             }
550 0 0         if ($name eq 'dir') {
551 0           my @args = $self->_split_args($name, $args, 1);
552 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
553 0           my ($text) = @args;
554 0           my @names = _text2words($text);
555 0 0         return join ' ', map { /.*\// ? $& : './' } @names;
  0            
556             }
557 0 0         if ($name eq 'notdir') {
558 0           my @args = $self->_split_args($name, $args, 1);
559 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
560 0           my ($text) = @args;
561 0           my @names = _text2words($text);
562 0           return join ' ', map { s/.*\///; $_ } @names;
  0            
  0            
563             }
564 0 0         if ($name eq 'suffix') {
565 0           my @args = $self->_split_args($name, $args, 1);
566 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
567 0           my ($text) = @args;
568 0           my @names = _text2words($text);
569 0 0         my $s = join ' ', map { /.*(\..*)/ ? $1 : '' } @names;
  0            
570 0           $s =~ s/\s+$//g;
571 0           return $s;
572             }
573 0 0         if ($name eq 'basename') {
574 0           my @args = $self->_split_args($name, $args, 1);
575 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
576 0           my ($text) = @args;
577 0           my @names = _text2words($text);
578 0 0         my $s = join ' ', map { /(.*)\./ ? $1 : $_ } @names;
  0            
579 0           $s =~ s/\s+$//g;
580 0           return $s;
581             }
582 0 0         if ($name eq 'addsuffix') {
583 0           my @args = $self->_split_args($name, $args, 2);
584 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
585 0           my ($suffix, $text) = @args;
586             #_trim($suffix);
587 0           my @names = _text2words($text);
588 0           return join ' ', map { $_ . $suffix } @names;
  0            
589             }
590 0 0         if ($name eq 'addprefix') {
591 0           my @args = $self->_split_args($name, $args, 2);
592 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
593 0           my ($suffix, $text) = @args;
594             #_trim($suffix);
595 0           my @names = _text2words($text);
596 0           return join ' ', map { $suffix . $_ } @names;
  0            
597             }
598 0 0         if ($name eq 'join') {
599 0           my @args = $self->_split_args($name, $args, 2);
600 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
601 0           my ($list_1, $list_2) = @args;
602 0           my @list_1 = _text2words($list_1);
603 0           my @list_2 = _text2words($list_2);
604             return join ' ', pairwise {
605 1     1   9 no warnings 'uninitialized';
  1         2  
  1         162  
606 0     0     $a . $b
607 0           } @list_1, @list_2;
608             }
609 0 0         if ($name eq 'wildcard') {
610 0           my @args = $self->_split_args($name, $args, 1);
611 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
612 0           my ($pattern) = @args;
613 0           return join ' ', grep { -e $_ } glob $pattern;
  0            
614             }
615 0 0         if ($name eq 'realpath') {
616 1     1   5 no warnings 'uninitialized';
  1         1  
  1         2202  
617 0           my @args = $self->_split_args($name, $args, 1);
618 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
619 0           my ($text) = @args;
620 0           my @names = _text2words($text);
621 0           return join ' ', map { realpath($_) } @names;
  0            
622             }
623 0 0         if ($name eq 'abspath') {
624 0           my @args = $self->_split_args($name, $args, 1);
625 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
626 0           my ($text) = @args;
627 0           my @names = _text2words($text);
628 0           my @paths = map { File::Spec->rel2abs($_) } @names;
  0            
629 0           for my $path (@paths) {
630 0           my @f = split '/', $path;
631 0           my @new_f;
632 0           for (@f) {
633 0 0         if ($_ eq '..') {
634 0           pop @new_f;
635             } else {
636 0           push @new_f, $_;
637             }
638             }
639 0           $path = join '/', @new_f;
640             }
641 0           return join ' ', @paths;
642             }
643 0 0         if ($name eq 'shell') {
644 0           my @args = $self->_split_args($name, $args, 1);
645 0           map { $_ = $self->solve_refs_in_tokens($_) } @args;
  0            
646 0           my ($cmd) = @args;
647 0           my $output = `$cmd`;
648 0           $output =~ s/(?:\r?\n)+$//g;
649 0           $output =~ s/\r?\n/ /g;
650 0           return $output;
651             }
652 0 0         if ($name eq 'if') {
653 0           my @args = $self->_split_args($name, $args, 2, 3);
654             #map { $_ = $self->solve_refs_in_tokens($_) } @args;
655 0           my ($condition, $then_part, $else_part) = @args;
656 0           trim_tokens($condition);
657 0           $condition = $self->solve_refs_in_tokens($condition);
658 0 0         return $condition eq '' ?
659             $self->solve_refs_in_tokens($else_part)
660             :
661             $self->solve_refs_in_tokens($then_part);
662             }
663 0 0         if ($name eq 'or') {
664 0           my @args = $self->_split_args($name, $args, 1, 1000_000_000);
665             #map { $_ = $self->solve_refs_in_tokens($_) } @args;
666 0           for my $arg (@args) {
667 0           trim_tokens($arg);
668 0           my $value = $self->solve_refs_in_tokens($arg);
669 0 0         return $value if $value ne '';
670             }
671 0           return '';
672             }
673 0 0         if ($name eq 'and') {
674 0           my @args = $self->_split_args($name, $args, 1, 1000_000_000);
675             #map { $_ = $self->solve_refs_in_tokens($_) } @args;
676             ## arguments for 'and': @args
677 0           my $value;
678 0           for my $arg (@args) {
679 0           trim_tokens($arg);
680 0           $value = $self->solve_refs_in_tokens($arg);
681 0 0         return '' if $value eq '';
682             }
683 0           return $value;
684             }
685 0 0         if ($name eq 'foreach') {
686 0           my @args = $self->_split_args($name, $args, 3);
687 0           my ($var, $list, $text) = @args;
688 0           $var = $self->solve_refs_in_tokens($var);
689 0           $list = $self->solve_refs_in_tokens($list);
690 0           my @words = _text2words($list);
691             # save the original status of $var
692 0           my $rvars = $self->{_vars};
693 0           my $not_exist = !exists $rvars->{$var};
694 0           my $old_val = $rvars->{$var};
695              
696 0           my @results;
697 0           for my $word (@words) {
698 0           $rvars->{$var} = $word;
699             #warn "$word";
700 0           push @results, $self->solve_refs_in_tokens($text);
701             }
702              
703             # restore the original status of $var
704 0 0         if ($not_exist) {
705 0           delete $rvars->{$var};
706             } else {
707 0           $rvars->{$var} = $old_val;
708             }
709              
710 0           return join ' ', @results;
711             }
712 0 0         if ($name eq 'error') {
713 0           my ($text) = $self->_split_args($name, $args, 1);
714 0           $text = $self->solve_refs_in_tokens($text);
715 0           warn $self->makefile, ":$.: *** $text. Stop.\n";
716 0 0         exit(2) if $Runtime;
717 0           return '';
718             }
719 0 0         if ($name eq 'warning') {
720 0           my ($text) = $self->_split_args($name, $args, 1);
721 0           $text = $self->solve_refs_in_tokens($text);
722 0           warn $self->makefile, ":$.: $text\n";
723 0           return '';
724             }
725 0 0         if ($name eq 'info') {
726 0           my ($text) = $self->_split_args($name, $args, 1);
727 0           $text = $self->solve_refs_in_tokens($text);
728 0           print "$text\n";
729 0           return '';
730             }
731              
732 0           return undef;
733             }
734              
735             sub solve_refs_in_tokens ($$) {
736 0     0 0   my ($self, $tokens) = @_;
737 0 0         return '' if !$tokens;
738 0           my @new_tokens;
739 0           for my $token (@$tokens) {
740 0 0 0       if (!ref $token or !$token->isa('MDOM::Token::Interpolation')) {
741             ### solve_refs: non-var-ref token: $token
742 0           push @new_tokens, $token;
743 0           next;
744             }
745 0 0         if ($token =~ /^\$[{(](.*)[)}]$/) {
    0          
    0          
746 0           my $s = $1;
747 0 0         if ($s =~ /^([-\w]+)\s+(.*)$/) {
    0          
748 0           my $res = $self->_process_func_ref($1, $2);
749 0 0         if (defined $res) {
750 0           push @new_tokens, $res;
751 0           next;
752             }
753             } elsif ($s =~ /^(\S+?):(\S+?)=(\S+)$/) {
754 0           my ($var, $from, $to) = ($1, $2, $3);
755 0           my $res = $self->_process_func_ref(
756             'patsubst', "\%$from,\%$to,\$($var)"
757             );
758 0 0         if (defined $res) {
759 0           push @new_tokens, $res;
760 0           next;
761             }
762             }
763             ### found variable reference: $1
764             ### evaluating variable : $s
765 0           push @new_tokens, $self->eval_var_value($s);
766 0           next;
767             } elsif ($token =~ /^\$\$$/) {
768 0           push @new_tokens, '$';
769 0           next;
770             } elsif ($token =~ /^\$(.)$/) {
771 0           push @new_tokens, $self->eval_var_value($1);
772 0           next;
773             }
774 0           push @new_tokens, $token;
775             }
776             ### solving results: join '', @new_tokens
777 0           return join '', @new_tokens;
778             }
779              
780             1;
781             __END__