557 lines
14 KiB
Perl
557 lines
14 KiB
Perl
# Copyright (C) 1997, 2001, 2002, 2003, 2004, 2006 Free Software Foundation, Inc.
|
|
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 2, or (at your option)
|
|
# any later version.
|
|
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
package Automake::DisjConditions;
|
|
|
|
use Carp;
|
|
use strict;
|
|
use Automake::Condition qw/TRUE FALSE/;
|
|
|
|
=head1 NAME
|
|
|
|
Automake::DisjConditions - record a disjunction of Conditions
|
|
|
|
=head1 SYNOPSIS
|
|
|
|
use Automake::Condition;
|
|
use Automake::DisjConditions;
|
|
|
|
# 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 DisjConditions to represent
|
|
# "(COND1 and not COND2) or (not COND3)"
|
|
my $set = new Automake::DisjConditions $cond, $other;
|
|
|
|
# Return the list of Conditions involved in $set.
|
|
my @conds = $set->conds;
|
|
|
|
# Return one of the Condition involved in $set.
|
|
my $cond = $set->one_cond;
|
|
|
|
# Return true iff $set is always true (i.e. its subconditions
|
|
# cover all cases).
|
|
if ($set->true) { ... }
|
|
|
|
# Return false iff $set is always false (i.e. is empty, or contains
|
|
# only false conditions).
|
|
if ($set->false) { ... }
|
|
|
|
# Return a string representing the DisjConditions.
|
|
# "COND1_TRUE COND2_FALSE | COND3_FALSE"
|
|
my $str = $set->string;
|
|
|
|
# Return a human readable string representing the DisjConditions.
|
|
# "(COND1 and !COND2) or (!COND3)"
|
|
my $str = $set->human;
|
|
|
|
# Merge (OR) several DisjConditions.
|
|
my $all = $set->merge($set2, $set3, ...)
|
|
|
|
# Invert a DisjConditions, i.e., create a new DisjConditions
|
|
# that complements $set.
|
|
my $inv = $set->invert;
|
|
|
|
# Multiply two DisjConditions.
|
|
my $prod = $set1->multiply ($set2);
|
|
|
|
# Return the subconditions of a DisjConditions with respect to
|
|
# a Condition. See the description for a real example.
|
|
my $subconds = $set->sub_conditions ($cond);
|
|
|
|
# Check whether a new definition in condition $cond would be
|
|
# ambiguous w.r.t. existing definitions in $set.
|
|
($msg, $ambig_cond) = $set->ambiguous_p ($what, $cond);
|
|
|
|
=head1 DESCRIPTION
|
|
|
|
A C<DisjConditions> is a disjunction of C<Condition>s. In Automake
|
|
they are used to represent the conditions into which Makefile
|
|
variables and Makefile rules are defined.
|
|
|
|
If the variable C<VAR> is defined as
|
|
|
|
if COND1
|
|
if COND2
|
|
VAR = value1
|
|
endif
|
|
endif
|
|
if !COND3
|
|
if COND4
|
|
VAR = value2
|
|
endif
|
|
endif
|
|
|
|
then it will be associated a C<DisjConditions> created with
|
|
the following statement.
|
|
|
|
new Automake::DisjConditions
|
|
(new Automake::Condition ("COND1_TRUE", "COND2_TRUE"),
|
|
new Automake::Condition ("COND3_FALSE", "COND4_TRUE"));
|
|
|
|
As you can see, a C<DisjConditions> is made from a list of
|
|
C<Condition>s. Since C<DisjConditions> is a disjunction, and
|
|
C<Condition> is a conjunction, the above can be read as
|
|
follows.
|
|
|
|
(COND1 and COND2) or ((not COND3) and COND4)
|
|
|
|
That's indeed the condition in which C<VAR> has a value.
|
|
|
|
Like C<Condition> objects, a C<DisjConditions> object is unique
|
|
with respect to its conditions. Two C<DisjConditions> objects created
|
|
for the same set of conditions will have the same address. This makes
|
|
it easy to compare C<DisjConditions>s: just compare the references.
|
|
|
|
=head2 Methods
|
|
|
|
=over 4
|
|
|
|
=item C<$set = new Automake::DisjConditions [@conds]>
|
|
|
|
Create a C<DisjConditions> object from the list of C<Condition>
|
|
objects passed in arguments.
|
|
|
|
If the C<@conds> list is empty, the C<DisjConditions> is assumed to be
|
|
false.
|
|
|
|
As explained previously, the reference (object) returned is unique
|
|
with respect to C<@conds>. For this purpose, duplicate elements are
|
|
ignored.
|
|
|
|
=cut
|
|
|
|
# Keys in this hash are DisjConditions strings. Values are the
|
|
# associated object DisjConditions. This is used by `new' to reuse
|
|
# DisjConditions objects with identical conditions.
|
|
use vars '%_disjcondition_singletons';
|
|
|
|
sub new ($;@)
|
|
{
|
|
my ($class, @conds) = @_;
|
|
my @filtered_conds = ();
|
|
for my $cond (@conds)
|
|
{
|
|
confess "`$cond' isn't a reference" unless ref $cond;
|
|
confess "`$cond' isn't an Automake::Condition"
|
|
unless $cond->isa ("Automake::Condition");
|
|
|
|
# This is a disjunction of conditions, so we drop
|
|
# false conditions. We'll always treat an "empty"
|
|
# DisjConditions as false for this reason.
|
|
next if $cond->false;
|
|
|
|
push @filtered_conds, $cond;
|
|
}
|
|
|
|
my $string;
|
|
if (@filtered_conds)
|
|
{
|
|
@filtered_conds = sort { $a->string cmp $b->string } @filtered_conds;
|
|
$string = join (' | ', map { $_->string } @filtered_conds);
|
|
}
|
|
else
|
|
{
|
|
$string = 'FALSE';
|
|
}
|
|
|
|
# Return any existing identical DisjConditions.
|
|
my $me = $_disjcondition_singletons{$string};
|
|
return $me if $me;
|
|
|
|
# Else, create a new DisjConditions.
|
|
|
|
# Store conditions as keys AND as values, because blessed
|
|
# objects are converted to strings when used as keys (so
|
|
# at least we still have the value when we need to call
|
|
# a method).
|
|
my %h = map {$_ => $_} @filtered_conds;
|
|
|
|
my $self = {
|
|
hash => \%h,
|
|
string => $string,
|
|
conds => \@filtered_conds,
|
|
};
|
|
bless $self, $class;
|
|
|
|
$_disjcondition_singletons{$string} = $self;
|
|
return $self;
|
|
}
|
|
|
|
|
|
=item C<CLONE>
|
|
|
|
Internal special subroutine to fix up the self hashes in
|
|
C<%_disjcondition_singletons> upon thread creation. C<CLONE> is invoked
|
|
automatically with ithreads from Perl 5.7.2 or later, so if you use this
|
|
module with earlier versions of Perl, it is not thread-safe.
|
|
|
|
=cut
|
|
|
|
sub CLONE
|
|
{
|
|
foreach my $self (values %_disjcondition_singletons)
|
|
{
|
|
my %h = map { $_ => $_ } @{$self->{'conds'}};
|
|
$self->{'hash'} = \%h;
|
|
}
|
|
}
|
|
|
|
|
|
=item C<@conds = $set-E<gt>conds>
|
|
|
|
Return the list of C<Condition> objects involved in C<$set>.
|
|
|
|
=cut
|
|
|
|
sub conds ($ )
|
|
{
|
|
my ($self) = @_;
|
|
return @{$self->{'conds'}};
|
|
}
|
|
|
|
=item C<$cond = $set-E<gt>one_cond>
|
|
|
|
Return one C<Condition> object involved in C<$set>.
|
|
|
|
=cut
|
|
|
|
sub one_cond ($)
|
|
{
|
|
my ($self) = @_;
|
|
return (%{$self->{'hash'}},)[1];
|
|
}
|
|
|
|
=item C<$et = $set-E<gt>false>
|
|
|
|
Return 1 iff the C<DisjConditions> object is always false (i.e., if it
|
|
is empty, or if it contains only false C<Condition>s). Return 0
|
|
otherwise.
|
|
|
|
=cut
|
|
|
|
sub false ($ )
|
|
{
|
|
my ($self) = @_;
|
|
return 0 == keys %{$self->{'hash'}};
|
|
}
|
|
|
|
=item C<$et = $set-E<gt>true>
|
|
|
|
Return 1 iff the C<DisjConditions> object is always true (i.e. covers all
|
|
conditions). Return 0 otherwise.
|
|
|
|
=cut
|
|
|
|
sub true ($ )
|
|
{
|
|
my ($self) = @_;
|
|
return $self->invert->false;
|
|
}
|
|
|
|
=item C<$str = $set-E<gt>string>
|
|
|
|
Build a string which denotes the C<DisjConditions>.
|
|
|
|
=cut
|
|
|
|
sub string ($ )
|
|
{
|
|
my ($self) = @_;
|
|
return $self->{'string'};
|
|
}
|
|
|
|
=item C<$cond-E<gt>human>
|
|
|
|
Build a human readable string which denotes the C<DisjConditions>.
|
|
|
|
=cut
|
|
|
|
sub human ($ )
|
|
{
|
|
my ($self) = @_;
|
|
|
|
return $self->{'human'} if defined $self->{'human'};
|
|
|
|
my $res = '';
|
|
if ($self->false)
|
|
{
|
|
$res = 'FALSE';
|
|
}
|
|
else
|
|
{
|
|
my @c = $self->conds;
|
|
if (1 == @c)
|
|
{
|
|
$res = $c[0]->human;
|
|
}
|
|
else
|
|
{
|
|
$res = '(' . join (') or (', map { $_->human } $self->conds) . ')';
|
|
}
|
|
}
|
|
$self->{'human'} = $res;
|
|
return $res;
|
|
}
|
|
|
|
|
|
=item C<$newcond = $cond-E<gt>merge (@otherconds)>
|
|
|
|
Return a new C<DisjConditions> which is the disjunction of
|
|
C<$cond> and C<@otherconds>. Items in C<@otherconds> can be
|
|
@C<Condition>s or C<DisjConditions>.
|
|
|
|
=cut
|
|
|
|
sub merge ($@)
|
|
{
|
|
my ($self, @otherconds) = @_;
|
|
new Automake::DisjConditions (
|
|
map { $_->isa ("Automake::DisjConditions") ? $_->conds : $_ }
|
|
($self, @otherconds));
|
|
}
|
|
|
|
|
|
=item C<$prod = $set1-E<gt>multiply ($set2)>
|
|
|
|
Multiply two conditional sets.
|
|
|
|
my $set1 = new Automake::DisjConditions
|
|
(new Automake::Condition ("A_TRUE"),
|
|
new Automake::Condition ("B_TRUE"));
|
|
my $set2 = new Automake::DisjConditions
|
|
(new Automake::Condition ("C_FALSE"),
|
|
new Automake::Condition ("D_FALSE"));
|
|
|
|
C<$set1-E<gt>multiply ($set2)> will return
|
|
|
|
new Automake::DisjConditions
|
|
(new Automake::Condition ("A_TRUE", "C_FALSE"),
|
|
new Automake::Condition ("B_TRUE", "C_FALSE"),;
|
|
new Automake::Condition ("A_TRUE", "D_FALSE"),
|
|
new Automake::Condition ("B_TRUE", "D_FALSE"));
|
|
|
|
The argument can also be a C<Condition>.
|
|
|
|
=cut
|
|
|
|
# Same as multiply() but take a list of Conditionals as second argument.
|
|
# We use this in invert().
|
|
sub _multiply ($@)
|
|
{
|
|
my ($self, @set) = @_;
|
|
my @res = map { $_->multiply (@set) } $self->conds;
|
|
return new Automake::DisjConditions (Automake::Condition::reduce_or @res);
|
|
}
|
|
|
|
sub multiply ($$)
|
|
{
|
|
my ($self, $set) = @_;
|
|
return $self->_multiply ($set) if $set->isa('Automake::Condition');
|
|
return $self->_multiply ($set->conds);
|
|
}
|
|
|
|
=item C<$inv = $set-E<gt>invert>
|
|
|
|
Invert a C<DisjConditions>. Return a C<DisjConditions> which is true
|
|
when C<$set> is false, and vice-versa.
|
|
|
|
my $set = new Automake::DisjConditions
|
|
(new Automake::Condition ("A_TRUE", "B_TRUE"),
|
|
new Automake::Condition ("A_FALSE", "B_FALSE"));
|
|
|
|
Calling C<$set-E<gt>invert> will return the following C<DisjConditions>.
|
|
|
|
new Automake::DisjConditions
|
|
(new Automake::Condition ("A_TRUE", "B_FALSE"),
|
|
new Automake::Condition ("A_FALSE", "B_TRUE"));
|
|
|
|
We implement the inversion by a product-of-sums to sum-of-products
|
|
conversion using repeated multiplications. Because of the way we
|
|
implement multiplication, the result of inversion is in canonical
|
|
prime implicant form.
|
|
|
|
=cut
|
|
|
|
sub invert($ )
|
|
{
|
|
my ($self) = @_;
|
|
|
|
return $self->{'invert'} if defined $self->{'invert'};
|
|
|
|
# The invert of an empty DisjConditions is TRUE.
|
|
my $res = new Automake::DisjConditions TRUE;
|
|
|
|
# !((a.b)+(c.d)+(e.f))
|
|
# = (!a+!b).(!c+!d).(!e+!f)
|
|
# We develop this into a sum of product iteratively, starting from TRUE:
|
|
# 1) TRUE
|
|
# 2) TRUE.!a + TRUE.!b
|
|
# 3) TRUE.!a.!c + TRUE.!b.!c + TRUE.!a.!d + TRUE.!b.!d
|
|
# 4) TRUE.!a.!c.!e + TRUE.!b.!c.!e + TRUE.!a.!d.!e + TRUE.!b.!d.!e
|
|
# + TRUE.!a.!c.!f + TRUE.!b.!c.!f + TRUE.!a.!d.!f + TRUE.!b.!d.!f
|
|
foreach my $cond ($self->conds)
|
|
{
|
|
$res = $res->_multiply ($cond->not);
|
|
}
|
|
|
|
# Cache result.
|
|
$self->{'invert'} = $res;
|
|
# It's tempting to also set $res->{'invert'} to $self, but that
|
|
# is a bad idea as $self hasn't been normalized in any way.
|
|
# (Different inputs can produce the same inverted set.)
|
|
return $res;
|
|
}
|
|
|
|
=item C<$self-E<gt>simplify>
|
|
|
|
Return a C<Disjunction> which is a simplified canonical form of C<$self>.
|
|
This canonical form contains only prime implicants, but it can contain
|
|
non-essential prime implicants.
|
|
|
|
=cut
|
|
|
|
sub simplify ($)
|
|
{
|
|
my ($self) = @_;
|
|
return $self->invert->invert;
|
|
}
|
|
|
|
=item C<$self-E<gt>sub_conditions ($cond)>
|
|
|
|
Return the subconditions of C<$self> that contains C<$cond>, with
|
|
C<$cond> stripped. More formally, return C<$res> such that
|
|
C<$res-E<gt>multiply ($cond) == $self-E<gt>multiply ($cond)> and
|
|
C<$res> does not mention any of the variables in C<$cond>.
|
|
|
|
For instance, consider:
|
|
|
|
my $a = new Automake::DisjConditions
|
|
(new Automake::Condition ("A_TRUE", "B_TRUE"),
|
|
new Automake::Condition ("A_TRUE", "C_FALSE"),
|
|
new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
|
|
new Automake::Condition ("A_FALSE"));
|
|
my $b = new Automake::DisjConditions
|
|
(new Automake::Condition ("A_TRUE", "B_FALSE"));
|
|
|
|
Calling C<$a-E<gt>sub_conditions ($b)> will return the following
|
|
C<DisjConditions>.
|
|
|
|
new Automake::DisjConditions
|
|
(new Automake::Condition ("C_FALSE"), # From A_TRUE C_FALSE
|
|
new Automake::Condition ("C_TRUE")); # From A_TRUE B_FALSE C_TRUE"
|
|
|
|
=cut
|
|
|
|
sub sub_conditions ($$)
|
|
{
|
|
my ($self, $subcond) = @_;
|
|
|
|
# Make $subcond blindingly apparent in the DisjConditions.
|
|
# For instance `$b->multiply($a->conds)' (from the POD example) is:
|
|
# (new Automake::Condition ("FALSE"),
|
|
# new Automake::Condition ("A_TRUE", "B_FALSE", "C_FALSE"),
|
|
# new Automake::Condition ("A_TRUE", "B_FALSE", "C_TRUE"),
|
|
# new Automake::Condition ("FALSE"))
|
|
my @prodconds = $subcond->multiply ($self->conds);
|
|
|
|
# Now, strip $subcond from the remaining (i.e., non-false) Conditions.
|
|
my @res = map { $_->false ? () : $_->strip ($subcond) } @prodconds;
|
|
|
|
return new Automake::DisjConditions @res;
|
|
}
|
|
|
|
=item C<($string, $ambig_cond) = $condset-E<gt>ambiguous_p ($what, $cond)>
|
|
|
|
Check for an ambiguous condition. Return an error message and the
|
|
other condition involved if we have an ambiguity. Return an empty
|
|
string and FALSE otherwise.
|
|
|
|
C<$what> is the name of the thing being defined, to use in the error
|
|
message. C<$cond> is the C<Condition> under which it is being
|
|
defined. C<$condset> is the C<DisjConditions> under which it had
|
|
already been defined.
|
|
|
|
=cut
|
|
|
|
sub ambiguous_p ($$$)
|
|
{
|
|
my ($self, $var, $cond) = @_;
|
|
|
|
# Note that these rules don't consider the following
|
|
# example as ambiguous.
|
|
#
|
|
# if COND1
|
|
# FOO = foo
|
|
# endif
|
|
# if COND2
|
|
# FOO = bar
|
|
# endif
|
|
#
|
|
# It's up to the user to not define COND1 and COND2
|
|
# simultaneously.
|
|
|
|
return ("$var multiply defined in condition " . $cond->human, $cond)
|
|
if exists $self->{'hash'}{$cond};
|
|
|
|
foreach my $vcond ($self->conds)
|
|
{
|
|
return ("$var was already defined in condition " . $vcond->human
|
|
. ", which includes condition ". $cond->human, $vcond)
|
|
if $vcond->true_when ($cond);
|
|
|
|
return ("$var was already defined in condition " . $vcond->human
|
|
. ", which is included in condition " . $cond->human, $vcond)
|
|
if $cond->true_when ($vcond);
|
|
}
|
|
return ('', FALSE);
|
|
}
|
|
|
|
=head1 SEE ALSO
|
|
|
|
L<Automake::Condition>.
|
|
|
|
=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>, Pavel Roskin <proski@gnu.org>, and
|
|
Alexandre Duret-Lutz <adl@gnu.org>.
|
|
|
|
=cut
|
|
|
|
1;
|
|
|
|
### Setup "GNU" style for perl-mode and cperl-mode.
|
|
## Local Variables:
|
|
## perl-indent-level: 2
|
|
## perl-continued-statement-offset: 2
|
|
## perl-continued-brace-offset: 0
|
|
## perl-brace-offset: 0
|
|
## perl-brace-imaginary-offset: 0
|
|
## perl-label-offset: -2
|
|
## cperl-indent-level: 2
|
|
## cperl-brace-offset: 0
|
|
## cperl-continued-brace-offset: 0
|
|
## cperl-label-offset: -2
|
|
## cperl-extra-newline-before-brace: t
|
|
## cperl-merge-trailing-else: nil
|
|
## cperl-continued-statement-offset: 2
|
|
## End:
|