File Coverage

File:/tmp/automake/lib/Automake/Condition.pm
Coverage:100.0%

linestmtbrancondsubpodtimecode
1# Copyright (C) 1997, 2001, 2002, 2003, 2006, 2008, 2009 Free Software
2# Foundation, Inc.
3
4# This program is free software; you can redistribute it and/or modify
5# it under the terms of the GNU General Public License as published by
6# the Free Software Foundation; either version 2, or (at your option)
7# any later version.
8
9# This program is distributed in the hope that it will be useful,
10# but WITHOUT ANY WARRANTY; without even the implied warranty of
11# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12# GNU General Public License for more details.
13
14# You should have received a copy of the GNU General Public License
15# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
17package Automake::Condition;
18
1183
1183
1183
3475
1247
3427
use strict;
19
1183
1183
1183
5087
1395
7027
use Carp;
20
21require Exporter;
22
1183
1183
1183
5356
2500
3545
use vars '@ISA', '@EXPORT_OK';
23@ISA = qw/Exporter/;
24@EXPORT_OK = qw/TRUE FALSE reduce_and reduce_or/;
25
26 - 164
=head1 NAME

Automake::Condition - record a conjunction of conditionals

=head1 SYNOPSIS

  use Automake::Condition;

  # Create a condition to represent "COND1 and not COND2".
  my $cond = new Automake::Condition "COND1_TRUE", "COND2_FALSE";
  # Create a condition to represent "not COND3".
  my $other = new Automake::Condition "COND3_FALSE";

  # Create a condition to represent
  #   "COND1 and not COND2 and not COND3".
  my $both = $cond->merge ($other);

  # Likewise, but using a list of conditional strings
  my $both2 = $cond->merge_conds ("COND3_FALSE");

  # Strip from $both any subconditions which are in $other.
  # This is the opposite of merge.
  $cond = $both->strip ($other);

  # Return the list of conditions ("COND1_TRUE", "COND2_FALSE"):
  my @conds = $cond->conds;

  # Is $cond always true?  (Not in this example)
  if ($cond->true) { ... }

  # Is $cond always false? (Not in this example)
  if ($cond->false) { ... }

  # Return the list of conditionals as a string:
  #  "COND1_TRUE COND2_FALSE"
  my $str = $cond->string;

  # Return the list of conditionals as a human readable string:
  #  "COND1 and !COND2"
  my $str = $cond->human;

  # Return the list of conditionals as a AC_SUBST-style string:
  #  "@COND1_TRUE@@COND2_FALSE@"
  my $subst = $cond->subst_string;

  # Is $cond true when $both is true?  (Yes in this example)
  if ($cond->true_when ($both)) { ... }

  # Is $cond redundant w.r.t. {$other, $both}?
  # (Yes in this example)
  if ($cond->redundant_wrt ($other, $both)) { ... }

  # Does $cond imply any of {$other, $both}?
  # (Not in this example)
  if ($cond->implies_any ($other, $both)) { ... }

  # Remove superfluous conditionals assuming they will eventually
  # be multiplied together.
  # (Returns @conds = ($both) in this example, because
  # $other and $cond are implied by $both.)
  @conds = Automake::Condition::reduce_and ($other, $both, $cond);

  # Remove superfluous conditionals assuming they will eventually
  # be summed together.
  # (Returns @conds = ($cond, $other) in this example, because
  # $both is a subset condition of $cond: $cond is true whenever $both
  # is true.)
  @conds = Automake::Condition::reduce_or ($other, $both, $cond);

  # Invert a Condition.  This returns a list of Conditions.
  @conds = $both->not;

=head1 DESCRIPTION

A C<Condition> is a conjunction of conditionals (i.e., atomic conditions
defined in F<configure.ac> by C<AM_CONDITIONAL>.  In Automake they
are used to represent the conditions into which F<Makefile> variables and
F<Makefile> rules are defined.

If the variable C<VAR> is defined as

  if COND1
    if COND2
      VAR = value
    endif
  endif

then it will be associated a C<Condition> created with
the following statement.

  new Automake::Condition "COND1_TRUE", "COND2_TRUE";

Remember that a C<Condition> is a I<conjunction> of conditionals, so
the above C<Condition> means C<VAR> is defined when C<COND1>
B<and> C<COND2> are true. There is no way to express disjunctions
(i.e., I<or>s) with this class (but see L<DisjConditions>).

Another point worth to mention is that each C<Condition> object is
unique with respect to its conditionals.  Two C<Condition> objects
created for the same set of conditionals will have the same address.
This makes it easy to compare C<Condition>s: just compare the
references.

  my $c1 = new Automake::Condition "COND1_TRUE", "COND2_TRUE";
  my $c2 = new Automake::Condition "COND1_TRUE", "COND2_TRUE";
  $c1 == $c2;  # True!

=head2 Methods

=over 4

=item C<$cond = new Automake::Condition [@conds]>

Return a C<Condition> objects for the conjunctions of conditionals
listed in C<@conds> as strings.

An item in C<@conds> should be either C<"FALSE">, C<"TRUE">, or have
the form C<"NAME_FALSE"> or C<"NAME_TRUE"> where C<NAME> can be
anything (in practice C<NAME> should be the name of a conditional
declared in F<configure.ac> with C<AM_CONDITIONAL>, but it's not
C<Automake::Condition>'s responsibility to ensure this).

An empty C<@conds> means C<"TRUE">.

As explained previously, the reference (object) returned is unique
with respect to C<@conds>.  For this purpose, duplicate elements are
ignored, and C<@conds> is rewritten as C<("FALSE")> if it contains
C<"FALSE"> or two contradictory conditionals (such as C<"NAME_FALSE">
and C<"NAME_TRUE">.)

Therefore the following two statements create the same object (they
both create the C<"FALSE"> condition).

  my $c3 = new Automake::Condition "COND1_TRUE", "COND1_FALSE";
  my $c4 = new Automake::Condition "COND2_TRUE", "FALSE";
  $c3 == $c4;   # True!
  $c3 == FALSE; # True!

=cut
165
166# Keys in this hash are conditional strings. Values are the
167# associated object conditions. This is used by `new' to reuse
168# Condition objects with identical conditionals.
169
1183
1183
1183
6049
1527
3259
use vars '%_condition_singletons';
170# Do NOT reset this hash here. It's already empty by default,
171# and any setting would otherwise occur AFTER the `TRUE' and `FALSE'
172# constants definitions.
173# %_condition_singletons = ();
174
175sub new ($;@)
176{
177
224820
1
438717
  my ($class, @conds) = @_;
178
224820
487362
  my $self = {
179    hash => {},
180  };
181
224820
433609
  bless $self, $class;
182
183
224820
363537
  for my $cond (@conds)
184    {
185      # Catch some common programming errors:
186      # - A Condition passed to new
187
246024
415409
      confess "`$cond' is a reference, expected a string" if ref $cond;
188      # - A Condition passed as a string to new
189
246020
617039
      confess "`$cond' does not look like a condition" if $cond =~ /::/;
190    }
191
192  # Accept strings like "FOO BAR" as shorthand for ("FOO", "BAR").
193
224812
246016
360927
597741
  @conds = map { split (' ', $_) } @conds;
194
195
224812
350448
  for my $cond (@conds)
196    {
197
238830
492038
      next if $cond eq 'TRUE';
198
199      # Detect cases when @conds can be simplified to FALSE.
200
154719
1993522
      if (($cond eq 'FALSE' && $#conds > 0)
201          || ($cond =~ /^(.*)_TRUE$/ && exists $self->{'hash'}{"${1}_FALSE"})
202          || ($cond =~ /^(.*)_FALSE$/ && exists $self->{'hash'}{"${1}_TRUE"}))
203        {
204
18866
104048
          return &FALSE;
205        }
206
207
135853
381953
      $self->{'hash'}{$cond} = 1;
208    }
209
210
205946
402622
  my $key = $self->string;
211
205946
476559
  if (exists $_condition_singletons{$key})
212    {
213
196413
813159
      return $_condition_singletons{$key};
214    }
215
9533
17930
  $_condition_singletons{$key} = $self;
216
9533
29769
  return $self;
217}
218
219 - 224
=item C<$newcond = $cond-E<gt>merge (@otherconds)>

Return a new condition which is the conjunction of
C<$cond> and C<@otherconds>.

=cut
225
226sub merge ($@)
227{
228
42123
1
71781
  my ($self, @otherconds) = @_;
229
42123
84246
61561
130120
  new Automake::Condition (map { $_->conds } ($self, @otherconds));
230}
231
232 - 238
=item C<$newcond = $cond-E<gt>merge_conds (@conds)>

Return a new condition which is the conjunction of C<$cond> and
C<@conds>, where C<@conds> is a list of conditional strings, as
passed to C<new>.

=cut
239
240sub merge_conds ($@)
241{
242
4
1
8
  my ($self, @conds) = @_;
243
4
9
  new Automake::Condition $self->conds, @conds;
244}
245
246 - 251
=item C<$newcond = $cond-E<gt>strip ($minuscond)>

Return a new condition which has all the conditionals of C<$cond>
except those of C<$minuscond>.  This is the opposite of C<merge>.

=cut
252
253sub strip ($$)
254{
255
946
1
1529
  my ($self, $minus) = @_;
256
946
4604
1801
7292
  my @res = grep { not $minus->_has ($_) } $self->conds;
257
946
2746
  return new Automake::Condition @res;
258}
259
260 - 269
=item C<@list = $cond-E<gt>conds>

Return the set of conditionals defining C<$cond>, as strings.  Note that
this might not be exactly the list passed to C<new> (or a
concatenation of such lists if C<merge> was used), because of the
cleanup mentioned in C<new>'s description.

For instance C<$c3-E<gt>conds> will simply return C<("FALSE")>.

=cut
270
271sub conds ($ )
272{
273
360934
1
481957
  my ($self) = @_;
274
360934
360934
335978
896459
  my @conds = keys %{$self->{'hash'}};
275
360934
880312
  return ("TRUE") unless @conds;
276
218135
764590
  return sort @conds;
277}
278
279# Undocumented, shouldn't be needed outside of this class.
280sub _has ($$)
281{
282
987600
1419315
  my ($self, $cond) = @_;
283
987600
3890676
  return exists $self->{'hash'}{$cond};
284}
285
286 - 290
=item C<$cond-E<gt>false>

Return 1 iff this condition is always false.

=cut
291
292sub false ($ )
293{
294
868113
1
1176442
  my ($self) = @_;
295
868113
1473100
  return $self->_has ('FALSE');
296}
297
298 - 302
=item C<$cond-E<gt>true>

Return 1 iff this condition is always true.

=cut
303
304sub true ($ )
305{
306
114048
1
157499
  my ($self) = @_;
307
114048
114048
114936
357406
  return 0 == keys %{$self->{'hash'}};
308}
309
310 - 317
=item C<$cond-E<gt>string>

Build a string which denotes the condition.

For instance using the C<$cond> definition from L<SYNOPSYS>,
C<$cond-E<gt>string> will return C<"COND1_TRUE COND2_FALSE">.

=cut
318
319sub string ($ )
320{
321
656099
1
940660
  my ($self) = @_;
322
323
656099
2531730
  return $self->{'string'} if defined $self->{'string'};
324
325
205946
223717
  my $res = '';
326
205946
334349
  if ($self->false)
327    {
328
34313
42547
      $res = 'FALSE';
329    }
330  else
331    {
332
171633
305094
      $res = join (' ', $self->conds);
333    }
334
205946
381209
  $self->{'string'} = $res;
335
205946
322206
  return $res;
336}
337
338 - 345
=item C<$cond-E<gt>human>

Build a human readable string which denotes the condition.

For instance using the C<$cond> definition from L<SYNOPSYS>,
C<$cond-E<gt>string> will return C<"COND1 and !COND2">.

=cut
346
347sub _to_human ($ )
348{
349
197
398
  my ($s) = @_;
350
197
850
  if ($s =~ /^(.*)_(TRUE|FALSE)$/)
351    {
352
83
443
      return (($2 eq 'FALSE') ? '!' : '') . $1;
353    }
354  else
355    {
356
114
399
      return $s;
357    }
358}
359
360sub human ($ )
361{
362
329
1
623
  my ($self) = @_;
363
364
329
1367
  return $self->{'human'} if defined $self->{'human'};
365
366
183
302
  my $res = '';
367
183
371
  if ($self->false)
368    {
369
4
6
      $res = 'FALSE';
370    }
371  else
372    {
373
179
197
466
592
      $res = join (' and ', map { _to_human $_ } $self->conds);
374    }
375
183
509
  $self->{'human'} = $res;
376
183
723
  return $res;
377}
378
379 - 386
=item C<$cond-E<gt>subst_string>

Build a C<AC_SUBST>-style string for output in F<Makefile.in>.

For instance using the C<$cond> definition from L<SYNOPSYS>,
C<$cond-E<gt>subst_string> will return C<"@COND1_TRUE@@COND2_FALSE@">.

=cut
387
388sub subst_string ($ )
389{
390
608036
1
813015
  my ($self) = @_;
391
392
608036
2728624
  return $self->{'subst_string'} if defined $self->{'subst_string'};
393
394
2896
4825
  my $res = '';
395
2896
5538
  if ($self->false)
396    {
397
4
5
      $res = '#';
398    }
399  elsif (! $self->true)
400    {
401
1769
4279
      $res = '@' . join ('@@', sort $self->conds) . '@';
402    }
403
2896
7211
  $self->{'subst_string'} = $res;
404
2896
12070
  return $res;
405}
406
407 - 415
=item C<$cond-E<gt>true_when ($when)>

Return 1 iff C<$cond> is true when C<$when> is true.
Return 0 otherwise.

Using the definitions from L<SYNOPSYS>, C<$cond> is true
when C<$both> is true, but the converse is wrong.

=cut
416
417sub true_when ($$)
418{
419
110915
1
164453
  my ($self, $when) = @_;
420
421  # Nothing is true when FALSE (not even FALSE itself, but it
422  # shouldn't hurt if you decide to change that).
423
110915
164452
  return 0 if $self->false || $when->false;
424
425  # If we are true, we stay true when $when is true :)
426
108652
192541
  return 1 if $self->true;
427
428  # $SELF is true under $WHEN if each conditional component of $SELF
429  # exists in $WHEN.
430
100585
173578
  foreach my $cond ($self->conds)
431    {
432
114883
193471
      return 0 unless $when->_has ($cond);
433    }
434
4465
21953
  return 1;
435}
436
437 - 443
=item C<$cond-E<gt>redundant_wrt (@conds)>

Return 1 iff C<$cond> is true for any condition in C<@conds>.
If @conds is empty, return 1 iff C<$cond> is C<FALSE>.
Return 0 otherwise.

=cut
444
445sub redundant_wrt ($@)
446{
447
292
1
468
  my ($self, @conds) = @_;
448
449
292
384
  foreach my $cond (@conds)
450    {
451
304
487
      return 1 if $self->true_when ($cond);
452    }
453
140
245
  return $self->false;
454}
455
456 - 461
=item C<$cond-E<gt>implies_any (@conds)>

Return 1 iff C<$cond> implies any of the conditions in C<@conds>.
Return 0 otherwise.

=cut
462
463sub implies_any ($@)
464{
465
7441
1
17601
  my ($self, @conds) = @_;
466
467
7441
9818
  foreach my $cond (@conds)
468    {
469
97031
158869
      return 1 if $cond->true_when ($self);
470    }
471
3879
16762
  return 0;
472}
473
474 - 481
=item C<$cond-E<gt>not>

Return a negation of C<$cond> as a list of C<Condition>s.
This list should be used to construct a C<DisjConditions>
(we cannot return a C<DisjConditions> from C<Automake::Condition>,
because that would make these two packages interdependent).

=cut
482
483sub not ($ )
484{
485
1978
1
4034
  my ($self) = @_;
486
1978
410
6367
1544
  return @{$self->{'not'}} if defined $self->{'not'};
487
2867
9418
  my @res =
488
1568
4954
    map { new Automake::Condition &conditional_negate ($_) } $self->conds;
489
1568
4783
  $self->{'not'} = [@res];
490
1568
8379
  return @res;
491}
492
493 - 501
=item C<$cond-E<gt>multiply (@conds)>

Assumption: C<@conds> represent a disjunction of conditions.

Return the result of multiplying C<$cond> with that disjunction.
The result will be a list of conditions suitable to construct a
C<DisjConditions>.

=cut
502
503sub multiply ($@)
504{
505
5078
1
10876
  my ($self, @set) = @_;
506
5078
7279
  my %res = ();
507
5078
9139
  for my $cond (@set)
508    {
509
35499
65321
      my $ans = $self->merge ($cond);
510
35499
98009
      $res{$ans} = $ans;
511    }
512
513  # FALSE can always be removed from a disjunction.
514
5078
7769
  delete $res{FALSE};
515
516  # Now, $self is a common factor of the remaining conditions.
517  # If one of the conditions is $self, we can discard the rest.
518
5078
21495
  return ($self, ())
519    if exists $res{$self};
520
521
2524
12136
  return (values %res);
522}
523
524=back
525
526 - 538
=head2 Other helper functions

=over 4

=item C<TRUE>

The C<"TRUE"> conditional.

=item C<FALSE>

The C<"FALSE"> conditional.

=cut
539
540
1183
1183
1183
6063
1563
5520
use constant TRUE => new Automake::Condition "TRUE";
541
1183
1183
1183
4809
1316
3224
use constant FALSE => new Automake::Condition "FALSE";
542
543 - 550
=item C<reduce_and (@conds)>

Return a subset of @conds with the property that the conjunction of
the subset is the same as the conjunction of @conds.  For example, if
both C<COND1_TRUE COND2_TRUE> and C<COND1_TRUE> are in the list,
discard the latter.  If the input list is empty, return C<(TRUE)>.

=cut
551
552sub reduce_and (@)
553{
554
124
1
181
  my (@conds) = @_;
555
124
133
  my @ret = ();
556
124
101
  my $cond;
557
124
248
  while (@conds > 0)
558    {
559
300
347
      $cond = shift @conds;
560
561      # FALSE is absorbent.
562
300
543
      return FALSE
563        if $cond == FALSE;
564
565
292
494
      if (! $cond->redundant_wrt (@ret, @conds))
566        {
567
140
367
          push (@ret, $cond);
568        }
569    }
570
571
116
224
  return TRUE if @ret == 0;
572
112
243
  return @ret;
573}
574
575 - 582
=item C<reduce_or (@conds)>

Return a subset of @conds with the property that the disjunction of
the subset is equivalent to the disjunction of @conds.  For example,
if both C<COND1_TRUE COND2_TRUE> and C<COND1_TRUE> are in the list,
discard the former.  If the input list is empty, return C<(FALSE)>.

=cut
583
584sub reduce_or (@)
585{
586
2585
1
5347
  my (@conds) = @_;
587
2585
3536
  my @ret = ();
588
2585
2906
  my $cond;
589
2585
7500
  while (@conds > 0)
590    {
591
9207
11854
      $cond = shift @conds;
592
593      next
594
9207
20209
       if $cond == FALSE;
595
7453
12403
      return TRUE
596       if $cond == TRUE;
597
598
7441
15049
      push (@ret, $cond)
599       unless $cond->implies_any (@ret, @conds);
600    }
601
602
2573
10754
  return FALSE if @ret == 0;
603
1021
3374
  return @ret;
604}
605
606 - 610
=item C<conditional_negate ($condstr)>

Negate a conditional string.

=cut
611
612sub conditional_negate ($)
613{
614
43613
1
66370
  my ($cond) = @_;
615
616
43613
91063
  $cond =~ s/TRUE$/TRUEO/;
617
43613
77509
  $cond =~ s/FALSE$/TRUE/;
618
43613
56288
  $cond =~ s/TRUEO$/FALSE/;
619
620
43613
118448
  return $cond;
621}
622
623=back
624
625 - 637
=head1 SEE ALSO

L<Automake::DisjConditions>.

=head1 HISTORY

C<AM_CONDITIONAL>s and supporting code were added to Automake 1.1o by
Ian Lance Taylor <ian@cygnus.org> in 1997.  Since then it has been
improved by Tom Tromey <tromey@redhat.com>, Richard Boulton
<richard@tartarus.org>, Raja R Harinath <harinath@cs.umn.edu>,
Akim Demaille <akim@epita.fr>, and  Alexandre Duret-Lutz <adl@gnu.org>.

=cut
638
6391;
640
641### Setup "GNU" style for perl-mode and cperl-mode.
642## Local Variables:
643## perl-indent-level: 2
644## perl-continued-statement-offset: 2
645## perl-continued-brace-offset: 0
646## perl-brace-offset: 0
647## perl-brace-imaginary-offset: 0
648## perl-label-offset: -2
649## cperl-indent-level: 2
650## cperl-brace-offset: 0
651## cperl-continued-brace-offset: 0
652## cperl-label-offset: -2
653## cperl-extra-newline-before-brace: t
654## cperl-merge-trailing-else: nil
655## cperl-continued-statement-offset: 2
656## End: