source: trunk/essentials/sys-devel/automake-1.9/lib/Automake/Condition.pm

Last change on this file was 3086, checked in by bird, 18 years ago

automake 1.9.6

File size: 15.3 KB
Line 
1# Copyright (C) 1997, 2001, 2002, 2003 Free Software Foundation, Inc.
2
3# This program is free software; you can redistribute it and/or modify
4# it under the terms of the GNU General Public License as published by
5# the Free Software Foundation; either version 2, or (at your option)
6# any later version.
7
8# This program is distributed in the hope that it will be useful,
9# but WITHOUT ANY WARRANTY; without even the implied warranty of
10# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11# GNU General Public License for more details.
12
13# You should have received a copy of the GNU General Public License
14# along with this program; if not, write to the Free Software
15# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
16# 02110-1301, USA.
17
18package Automake::Condition;
19use strict;
20use Carp;
21
22require Exporter;
23use vars '@ISA', '@EXPORT_OK';
24@ISA = qw/Exporter/;
25@EXPORT_OK = qw/TRUE FALSE reduce_and reduce_or/;
26
27=head1 NAME
28
29Automake::Condition - record a conjunction of conditionals
30
31=head1 SYNOPSIS
32
33 use Automake::Condition;
34
35 # Create a condition to represent "COND1 and not COND2".
36 my $cond = new Automake::Condition "COND1_TRUE", "COND2_FALSE";
37 # Create a condition to represent "not COND3".
38 my $other = new Automake::Condition "COND3_FALSE";
39
40 # Create a condition to represent
41 # "COND1 and not COND2 and not COND3".
42 my $both = $cond->merge ($other);
43
44 # Likewise, but using a list of conditional strings
45 my $both2 = $cond->merge_conds ("COND3_FALSE");
46
47 # Strip from $both any subconditions which are in $other.
48 # This is the opposite of merge.
49 $cond = $both->strip ($other);
50
51 # Return the list of conditions ("COND1_TRUE", "COND2_FALSE"):
52 my @conds = $cond->conds;
53
54 # Is $cond always true? (Not in this example)
55 if ($cond->true) { ... }
56
57 # Is $cond always false? (Not in this example)
58 if ($cond->false) { ... }
59
60 # Return the list of conditionals as a string:
61 # "COND1_TRUE COND2_FALSE"
62 my $str = $cond->string;
63
64 # Return the list of conditionals as a human readable string:
65 # "COND1 and !COND2"
66 my $str = $cond->human;
67
68 # Return the list of conditionals as a AC_SUBST-style string:
69 # "@COND1_TRUE@@COND2_FALSE@"
70 my $subst = $cond->subst_string;
71
72 # Is $cond true when $both is true? (Yes in this example)
73 if ($cond->true_when ($both)) { ... }
74
75 # Is $cond redundant w.r.t. {$other, $both}?
76 # (Yes in this example)
77 if ($cond->redundant_wrt ($other, $both)) { ... }
78
79 # Does $cond imply any of {$other, $both}?
80 # (Not in this example)
81 if ($cond->implies_any ($other, $both)) { ... }
82
83 # Remove superfluous conditionals assuming they will eventually
84 # be multiplied together.
85 # (Returns @conds = ($both) in this example, because
86 # $other and $cond are implied by $both.)
87 @conds = Automake::Condition::reduce_and ($other, $both, $cond);
88
89 # Remove superfluous conditionals assuming they will eventually
90 # be summed together.
91 # (Returns @conds = ($cond, $other) in this example, because
92 # $both is a subset condition of $cond: $cond is true whenever $both
93 # is true.)
94 @conds = Automake::Condition::reduce_or ($other, $both, $cond);
95
96 # Invert a Condition. This returns a list of Conditions.
97 @conds = $both->not;
98
99=head1 DESCRIPTION
100
101A C<Condition> is a conjunction of conditionals (i.e., atomic conditions
102defined in F<configure.ac> by C<AM_CONDITIONAL>. In Automake they
103are used to represent the conditions into which F<Makefile> variables and
104F<Makefile> rules are defined.
105
106If the variable C<VAR> is defined as
107
108 if COND1
109 if COND2
110 VAR = value
111 endif
112 endif
113
114then it will be associated a C<Condition> created with
115the following statement.
116
117 new Automake::Condition "COND1_TRUE", "COND2_TRUE";
118
119Remember that a C<Condition> is a I<conjunction> of conditionals, so
120the above C<Condition> means C<VAR> is defined when C<COND1>
121B<and> C<COND2> are true. There is no way to express disjunctions
122(i.e., I<or>s) with this class (but see L<DisjConditions>).
123
124Another point worth to mention is that each C<Condition> object is
125unique with respect to its conditionals. Two C<Condition> objects
126created for the same set of conditionals will have the same adress.
127This makes it easy to compare C<Condition>s, just compare the
128references.
129
130 my $c1 = new Automake::Condition "COND1_TRUE", "COND2_TRUE";
131 my $c2 = new Automake::Condition "COND1_TRUE", "COND2_TRUE";
132 $c1 == $c2; # True!
133
134=head2 Methods
135
136=over 4
137
138=item C<$cond = new Automake::Condition [@conds]>
139
140Return a C<Condition> objects for the conjunctions of conditionals
141listed in C<@conds> as strings.
142
143An item in C<@conds> should be either C<"FALSE">, C<"TRUE">, or have
144the form C<"NAME_FALSE"> or C<"NAME_TRUE"> where C<NAME> can be
145anything (in practice C<NAME> should be the name of a conditional
146declared in F<configure.ac> with C<AM_CONDITIONAL>, but it's not
147C<Automake::Condition>'s responsability to ensure this).
148
149An empty C<@conds> means C<"TRUE">.
150
151As explained previously, the reference (object) returned is unique
152with respect to C<@conds>. For this purpose, duplicate elements are
153ignored, and C<@conds> is rewriten as C<("FALSE")> if it contains
154C<"FALSE"> or two contradictory conditionals (such as C<"NAME_FALSE">
155and C<"NAME_TRUE">.)
156
157Therefore the following two statements create the same object (they
158both create the C<"FALSE"> condition).
159
160 my $c3 = new Automake::Condition "COND1_TRUE", "COND1_FALSE";
161 my $c4 = new Automake::Condition "COND2_TRUE", "FALSE";
162 $c3 == $c4; # True!
163 $c3 == FALSE; # True!
164
165=cut
166
167# Keys in this hash are conditional strings. Values are the
168# associated object conditions. This is used by `new' to reuse
169# Condition objects with identical conditionals.
170use vars '%_condition_singletons';
171# Do NOT reset this hash here. It's already empty by default,
172# and any setting would otherwise occur AFTER the `TRUE' and `FALSE'
173# constants definitions.
174# %_condition_singletons = ();
175
176sub new ($;@)
177{
178 my ($class, @conds) = @_;
179 my $self = {
180 hash => {},
181 };
182 bless $self, $class;
183
184 # Accept strings like "FOO BAR" as shorthand for ("FOO", "BAR").
185 @conds = map { split (' ', $_) } @conds;
186
187 for my $cond (@conds)
188 {
189 next if $cond eq 'TRUE';
190
191 # Catch some common programming errors:
192 # - A Condition passed to new
193 confess "`$cond' is a reference, expected a string" if ref $cond;
194 # - A Condition passed as a string to new
195 confess "`$cond' does not look like a condition" if $cond =~ /::/;
196
197 # Detect cases when @conds can be simplified to FALSE.
198 if (($cond eq 'FALSE' && $#conds > 0)
199 || ($cond =~ /^(.*)_TRUE$/ && exists $self->{'hash'}{"${1}_FALSE"})
200 || ($cond =~ /^(.*)_FALSE$/ && exists $self->{'hash'}{"${1}_TRUE"}))
201 {
202 return &FALSE;
203 }
204
205 $self->{'hash'}{$cond} = 1;
206 }
207
208 my $key = $self->string;
209 if (exists $_condition_singletons{$key})
210 {
211 return $_condition_singletons{$key};
212 }
213 $_condition_singletons{$key} = $self;
214 return $self;
215}
216
217=item C<$newcond = $cond-E<gt>merge (@otherconds)>
218
219Return a new condition which is the conjunction of
220C<$cond> and C<@otherconds>.
221
222=cut
223
224sub merge ($@)
225{
226 my ($self, @otherconds) = @_;
227 new Automake::Condition (map { $_->conds } ($self, @otherconds));
228}
229
230=item C<$newcond = $cond-E<gt>merge_conds (@conds)>
231
232Return a new condition which is the conjunction of C<$cond> and
233C<@conds>, where C<@conds> is a list of conditional strings, as
234passed to C<new>.
235
236=cut
237
238sub merge_conds ($@)
239{
240 my ($self, @conds) = @_;
241 new Automake::Condition $self->conds, @conds;
242}
243
244=item C<$newcond = $cond-E<gt>strip ($minuscond)>
245
246Return a new condition which has all the conditionals of C<$cond>
247except those of C<$minuscond>. This is the opposite of C<merge>.
248
249=cut
250
251sub strip ($$)
252{
253 my ($self, $minus) = @_;
254 my @res = grep { not $minus->has ($_) } $self->conds;
255 return new Automake::Condition @res;
256}
257
258=item C<@list = $cond-E<gt>conds>
259
260Return the set of conditionals defining C<$cond>, as strings. Note that
261this might not be exactly the list passed to C<new> (or a
262concatenation of such lists if C<merge> was used), because of the
263cleanup mentioned in C<new>'s description.
264
265For instance C<$c3-E<gt>conds> will simply return C<("FALSE")>.
266
267=cut
268
269sub conds ($ )
270{
271 my ($self) = @_;
272 my @conds = keys %{$self->{'hash'}};
273 return ("TRUE") unless @conds;
274 return sort @conds;
275}
276
277# Undocumented, shouldn't be needed out of this class.
278sub has ($$)
279{
280 my ($self, $cond) = @_;
281 return exists $self->{'hash'}{$cond};
282}
283
284=item C<$cond-E<gt>false>
285
286Return 1 iff this condition is always false.
287
288=cut
289
290sub false ($ )
291{
292 my ($self) = @_;
293 return $self->has ('FALSE');
294}
295
296=item C<$cond-E<gt>true>
297
298Return 1 iff this condition is always true.
299
300=cut
301
302sub true ($ )
303{
304 my ($self) = @_;
305 return 0 == keys %{$self->{'hash'}};
306}
307
308=item C<$cond-E<gt>string>
309
310Build a string which denotes the condition.
311
312For instance using the C<$cond> definition from L<SYNOPSYS>,
313C<$cond-E<gt>string> will return C<"COND1_TRUE COND2_FALSE">.
314
315=cut
316
317sub string ($ )
318{
319 my ($self) = @_;
320
321 return $self->{'string'} if defined $self->{'string'};
322
323 my $res = '';
324 if ($self->false)
325 {
326 $res = 'FALSE';
327 }
328 else
329 {
330 $res = join (' ', $self->conds);
331 }
332 $self->{'string'} = $res;
333 return $res;
334}
335
336=item C<$cond-E<gt>human>
337
338Build a human readable string which denotes the condition.
339
340For instance using the C<$cond> definition from L<SYNOPSYS>,
341C<$cond-E<gt>string> will return C<"COND1 and !COND2">.
342
343=cut
344
345sub _to_human ($ )
346{
347 my ($s) = @_;
348 if ($s =~ /^(.*)_(TRUE|FALSE)$/)
349 {
350 return (($2 eq 'FALSE') ? '!' : '') . $1;
351 }
352 else
353 {
354 return $s;
355 }
356}
357
358sub human ($ )
359{
360 my ($self) = @_;
361
362 return $self->{'human'} if defined $self->{'human'};
363
364 my $res = '';
365 if ($self->false)
366 {
367 $res = 'FALSE';
368 }
369 else
370 {
371 $res = join (' and ', map { _to_human $_ } $self->conds);
372 }
373 $self->{'human'} = $res;
374 return $res;
375}
376
377=item C<$cond-E<gt>subst_string>
378
379Build a C<AC_SUBST>-style string for output in F<Makefile.in>.
380
381For instance using the C<$cond> definition from L<SYNOPSYS>,
382C<$cond-E<gt>subst_string> will return C<"@COND1_TRUE@@COND2_FALSE@">.
383
384=cut
385
386sub subst_string ($ )
387{
388 my ($self) = @_;
389
390 return $self->{'subst_string'} if defined $self->{'subst_string'};
391
392 my $res = '';
393 if ($self->false)
394 {
395 $res = '#';
396 }
397 elsif (! $self->true)
398 {
399 $res = '@' . join ('@@', sort $self->conds) . '@';
400 }
401 $self->{'subst_string'} = $res;
402 return $res;
403}
404
405=item C<$cond-E<gt>true_when ($when)>
406
407Return 1 iff C<$cond> is true when C<$when> is true.
408Return 0 otherwise.
409
410Using the definitions from L<SYNOPSYS>, C<$cond> is true
411when C<$both> is true, but the converse is wrong.
412
413=cut
414
415sub true_when ($$)
416{
417 my ($self, $when) = @_;
418
419 # Nothing is true when FALSE (not even FALSE itself, but it
420 # shouldn't hurt if you decide to change that).
421 return 0 if $self->false || $when->false;
422
423 # If we are true, we stay true when $when is true :)
424 return 1 if $self->true;
425
426 # $SELF is true under $WHEN if each conditional component of $SELF
427 # exists in $WHEN.
428 foreach my $cond ($self->conds)
429 {
430 return 0 unless $when->has ($cond);
431 }
432 return 1;
433}
434
435=item C<$cond-E<gt>redundant_wrt (@conds)>
436
437Return 1 iff C<$cond> is true for any condition in C<@conds>.
438If @conds is empty, return 1 iff C<$cond> is C<FALSE>.
439Return 0 otherwise.
440
441=cut
442
443sub redundant_wrt ($@)
444{
445 my ($self, @conds) = @_;
446
447 foreach my $cond (@conds)
448 {
449 return 1 if $self->true_when ($cond);
450 }
451 return $self->false;
452}
453
454=item C<$cond-E<gt>implies_any (@conds)>
455
456Return 1 iff C<$cond> implies any of the conditions in C<@conds>.
457Return 0 otherwise.
458
459=cut
460
461sub implies_any ($@)
462{
463 my ($self, @conds) = @_;
464
465 foreach my $cond (@conds)
466 {
467 return 1 if $cond->true_when ($self);
468 }
469 return 0;
470}
471
472=item C<$cond-E<gt>not>
473
474Return a negation of C<$cond> as a list of C<Condition>s.
475This list should be used to construct a C<DisjConditions>
476(we cannot return a C<DisjConditions> from C<Automake::Condition>,
477because that would make these two packages interdependent).
478
479=cut
480
481sub not ($ )
482{
483 my ($self) = @_;
484 return @{$self->{'not'}} if defined $self->{'not'};
485 my @res =
486 map { new Automake::Condition &conditional_negate ($_) } $self->conds;
487 $self->{'not'} = [@res];
488 return @res;
489}
490
491=item C<$cond-E<gt>multiply (@conds)>
492
493Assumption: C<@conds> represent a disjunction of conditions.
494
495Return the result of multiplying C<$cond> with that disjunction.
496The result will be a list of conditions suitable to construct a
497C<DisjConditions>.
498
499=cut
500
501sub multiply ($@)
502{
503 my ($self, @set) = @_;
504 my %res = ();
505 for my $cond (@set)
506 {
507 my $ans = $self->merge ($cond);
508 $res{$ans} = $ans;
509 }
510
511 # FALSE can always be removed from a disjunction.
512 delete $res{FALSE};
513
514 # Now, $self is a common factor of the remaining conditions.
515 # If one of the conditions is $self, we can discard the rest.
516 return ($self, ())
517 if exists $res{$self};
518
519 return (values %res);
520}
521
522=head2 Other helper functions
523
524=over 4
525
526=item C<TRUE>
527
528The C<"TRUE"> conditional.
529
530=item C<FALSE>
531
532The C<"FALSE"> conditional.
533
534=cut
535
536use constant TRUE => new Automake::Condition "TRUE";
537use constant FALSE => new Automake::Condition "FALSE";
538
539=item C<reduce_and (@conds)>
540
541Return a subset of @conds with the property that the conjunction of
542the subset is the same as the conjunction of @conds. For example, if
543both C<COND1_TRUE COND2_TRUE> and C<COND1_TRUE> are in the list,
544discard the latter. If the input list is empty, return C<(TRUE)>.
545
546=cut
547
548sub reduce_and (@)
549{
550 my (@conds) = @_;
551 my @ret = ();
552 my $cond;
553 while (@conds > 0)
554 {
555 $cond = shift @conds;
556
557 # FALSE is absorbent.
558 return FALSE
559 if $cond == FALSE;
560
561 if (! $cond->redundant_wrt (@ret, @conds))
562 {
563 push (@ret, $cond);
564 }
565 }
566
567 return TRUE if @ret == 0;
568 return @ret;
569}
570
571=item C<reduce_or (@conds)>
572
573Return a subset of @conds with the property that the disjunction of
574the subset is equivalent to the disjunction of @conds. For example,
575if both C<COND1_TRUE COND2_TRUE> and C<COND1_TRUE> are in the list,
576discard the former. If the input list is empty, return C<(FALSE)>.
577
578=cut
579
580sub reduce_or (@)
581{
582 my (@conds) = @_;
583 my @ret = ();
584 my $cond;
585 while (@conds > 0)
586 {
587 $cond = shift @conds;
588
589 next
590 if $cond == FALSE;
591 return TRUE
592 if $cond == TRUE;
593
594 push (@ret, $cond)
595 unless $cond->implies_any (@ret, @conds);
596 }
597
598 return FALSE if @ret == 0;
599 return @ret;
600}
601
602=item C<conditional_negate ($condstr)>
603
604Negate a conditional string.
605
606=cut
607
608sub conditional_negate ($)
609{
610 my ($cond) = @_;
611
612 $cond =~ s/TRUE$/TRUEO/;
613 $cond =~ s/FALSE$/TRUE/;
614 $cond =~ s/TRUEO$/FALSE/;
615
616 return $cond;
617}
618
619=head1 SEE ALSO
620
621L<Automake::DisjConditions>.
622
623=head1 HISTORY
624
625C<AM_CONDITIONAL>s and supporting code were added to Automake 1.1o by
626Ian Lance Taylor <ian@cygnus.org> in 1997. Since then it has been
627improved by Tom Tromey <tromey@redhat.com>, Richard Boulton
628<richard@tartarus.org>, Raja R Harinath <harinath@cs.umn.edu>,
629Akim Demaille <akim@epita.fr>, and Alexandre Duret-Lutz <adl@gnu.org>.
630
631=cut
632
6331;
634
635### Setup "GNU" style for perl-mode and cperl-mode.
636## Local Variables:
637## perl-indent-level: 2
638## perl-continued-statement-offset: 2
639## perl-continued-brace-offset: 0
640## perl-brace-offset: 0
641## perl-brace-imaginary-offset: 0
642## perl-label-offset: -2
643## cperl-indent-level: 2
644## cperl-brace-offset: 0
645## cperl-continued-brace-offset: 0
646## cperl-label-offset: -2
647## cperl-extra-newline-before-brace: t
648## cperl-merge-trailing-else: nil
649## cperl-continued-statement-offset: 2
650## End:
Note: See TracBrowser for help on using the repository browser.