2 # Maemian::Relation -- operations on dependencies and relationships
4 # Copyright (C) 1998 Christian Schwarz and Richard Braakman
5 # Copyright (C) 2004-2009 Russ Allbery <rra@debian.org>
7 # This program is free software; you can redistribute it and/or modify it
8 # under the terms of the GNU General Public License as published by the Free
9 # Software Foundation; either version 2 of the License, or (at your option)
12 # This program is distributed in the hope that it will be useful, but WITHOUT
13 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
14 # FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 # You should have received a copy of the GNU General Public License along with
18 # this program. If not, see <http://www.gnu.org/licenses/>.
20 package Maemian::Relation;
25 use Maemian::Relation::Version;
29 Maemian::Relation - Maemian operations on dependencies and relationships
33 my $depends = Maemian::Relation->new('foo | bar, baz');
34 print "yes\n" if $depends->implies('baz');
35 print "no\n" if $depends->implies('foo');
39 This module provides functions for parsing and evaluating package
40 relationship fields such as Depends and Recommends for binary packages and
41 Build-Depends for source packages. It parses a relationship into an
42 internal format and can then answer questions such as "does this
43 dependency require that a given package be installed" or "is this
44 relationship a superset of another relationship."
46 A dependency line is viewed as a predicate formula. The comma separator
47 means "and", and the alternatives separator means "or". A bare package
48 name is the predicate "a package of this name is available". A package
49 name with a version clause is the predicate "a package of this name that
50 satisfies this version clause is available." Architecture restrictions,
51 as specified in Policy for build dependencies, are supported and also
52 checked in the implication logic unless the new_noarch() constructor is
53 used. With that constructor, architecture restrictions are ignored.
61 Creates a new Maemian::Relation object corresponding to the parsed
62 relationship RELATION. This object can then be used to ask questions
63 about that relationship. RELATION may be C<undef> or the empty string, in
64 which case the returned Maemian::Relation object is empty (always
69 # The internal parser which converts a single package element of a
70 # relationship into the parsed form used for later processing. We permit
71 # substvars to be used as package names so that we can use these routines with
72 # the unparsed debian/control file.
74 my ($class, $element) = @_;
76 ^\s* # skip leading whitespace
77 ( # package name or substvar (1)
78 [a-zA-Z0-9][a-zA-Z0-9+.-]+ # package name
80 \$\{[a-zA-Z0-9:-]+\} # substvar
81 ) # end of package name or substvar
82 (?: # start of optional version
83 \s* \( # open parenthesis for version part
84 \s* (<<|<=|=|>=|>>|<|>) # relation part (2)
85 \s* (.*?) # version (3)
86 \s* \) # closing parenthesis
87 )? # end of optional version
88 (?: # start of optional architecture
89 \s* \[ # open bracket for architecture
90 \s* (.*?) # architectures (4)
91 \s* \] # closing bracket
92 )? # end of optional architecture
95 # If there's no version, we don't need to do any further processing.
96 # Otherwise, convert the legacy < and > relations to the current ones.
97 return ['PRED', $1, undef, undef, $4] if not defined $2;
101 } elsif ($two eq '>') {
104 return ['PRED', $1, $two, $3, $4];
108 # Create a new Maemian::Relation object, parsing the argument into our
111 my ($class, $relation) = @_;
112 $relation = '' unless defined($relation);
114 for my $element (split(/\s*,\s*/, $relation)) {
115 next if $element =~ /^$/;
117 for my $alternative (split(/\s*\|\s*/, $element)) {
118 push(@alternatives, $class->parse_element($alternative));
120 if (@alternatives == 1) {
121 push(@result, @alternatives);
123 push(@result, ['OR', @alternatives]);
130 $self = ['AND', @result];
132 bless($self, $class);
136 =item new_noarch(RELATION)
138 Creates a new Maemian::Relation object corresponding to the parsed
139 relationship RELATION, ignoring architecture restrictions. This should be
140 used in cases where we only care if a dependency is present in some cases
141 and we don't want to require that the architectures match (such as when
142 checking for proper build dependencies, since if there are architecture
143 constraints the maintainer is doing something beyond Maemian's ability to
144 analyze). RELATION may be C<undef> or the empty string, in which case the
145 returned Maemian::Relation object is empty (always satisfied).
150 my ($class, $relation) = @_;
151 $relation = '' unless defined($relation);
152 $relation =~ s/\[[^\]]*\]//g;
153 return $class->new($relation);
158 =head1 INSTANCE METHODS
164 Returns a list of duplicated elements within the relation object. Each
165 element of the returned list will be a reference to an anonymous array
166 holding a set of relations considered duplicates of each other. Two
167 relations are considered duplicates if one implies the other, meaning that
168 if one relationship is satisfied, the other is necessarily satisfied.
169 This relationship does not have to be commutative: the opposite
170 implication may not hold.
177 # There are no duplicates unless the top-level relationship is AND.
178 if ($self->[0] ne 'AND') {
182 # The logic here is a bit complex in order to merge sets of duplicate
183 # dependencies. We want foo (<< 2), foo (>> 1), foo (= 1.5) to end up as
184 # one set of duplicates, even though the first doesn't imply the second.
186 # $dups holds a hash, where the key is the earliest dependency in a set
187 # and the value is a hash whose keys are the other dependencies in the
188 # set. $seen holds a map from package names to the duplicate sets that
189 # they're part of, if they're not the earliest package in a set. If
190 # either of the dependencies in a duplicate pair were already seen, add
191 # the missing one of the pair to the existing set rather than creating a
194 for (my $i = 1; $i < @$self; $i++) {
195 for (my $j = $i + 1; $j < @$self; $j++) {
196 my $forward = $self->implies_array($self->[$i], $self->[$j]);
197 my $reverse = $self->implies_array($self->[$j], $self->[$i]);
198 if ($forward or $reverse) {
199 my $first = unparse($self->[$i]);
200 my $second = unparse($self->[$j]);
202 $dups{$seen{$first}}->{$second} = $j;
203 $seen{$second} = $seen{$first};
204 } elsif ($seen{$second}) {
205 $dups{$seen{$second}}->{$first} = $i;
206 $seen{$first} = $seen{$second};
208 $dups{$first} ||= {};
209 $dups{$first}->{$second} = $j;
210 $seen{$second} = $first;
216 # The sort maintains the original order in which we encountered the
217 # dependencies, just in case that helps the user find the problems,
218 # despite the fact we're using a hash.
221 sort { $dups{$_}->{$a} <=> $dups{$_}->{$b} } keys %{ $dups{$_} }
226 =item implies(RELATION)
228 Returns true if the relationship implies RELATION, meaning that if the
229 Maemian::Relation object is satisfied, RELATION will always be satisfied.
230 RELATION may be either a string or another Maemian::Relation object.
232 By default, architecture restrictions are honored in RELATION if it is a
233 string. If architecture restrictions should be ignored in RELATION,
234 create a Maemian::Relation object with new_noarch() and pass that in as
235 RELATION instead of the string.
239 # This internal function does the heavily lifting of comparing two
242 # Takes two elements and returns true iff the second can be deduced from the
243 # first. If the second is falsified by the first (in other words, if p
244 # actually implies not q), return 0. Otherwise, return undef. The 0 return
245 # is used by implies_element_inverse.
246 sub implies_element {
247 my ($self, $p, $q) = @_;
249 # If the names don't match, there is no relationship between them.
250 $$p[1] = '' unless defined $$p[1];
251 $$q[1] = '' unless defined $$q[1];
252 return undef if $$p[1] ne $$q[1];
254 # If the names match, then the only difference is in the architecture or
255 # version clauses. First, check architecture. The architectures for p
256 # must be a superset of the architectures for q.
257 my @p_arches = split(' ', defined($$p[4]) ? $$p[4] : '');
258 my @q_arches = split(' ', defined($$q[4]) ? $$q[4] : '');
259 if (@p_arches || @q_arches) {
260 my $p_arch_neg = @p_arches && $p_arches[0] =~ /^!/;
261 my $q_arch_neg = @q_arches && $q_arches[0] =~ /^!/;
263 # If p has no arches, it is a superset of q and we should fall through
264 # to the version check.
269 # If q has no arches, it is a superset of p and there are no useful
271 elsif (not @q_arches) {
275 # Both have arches. If neither are negated, we know nothing useful
276 # unless q is a subset of p.
277 elsif (not $p_arch_neg and not $q_arch_neg) {
278 my %p_arches = map { $_ => 1 } @p_arches;
280 for my $arch (@q_arches) {
281 $subset = 0 unless $p_arches{$arch};
283 return undef unless $subset;
286 # If both are negated, we know nothing useful unless p is a subset of
287 # q (and therefore has fewer things excluded, and therefore is more
289 elsif ($p_arch_neg and $q_arch_neg) {
290 my %q_arches = map { $_ => 1 } @q_arches;
292 for my $arch (@p_arches) {
293 $subset = 0 unless $q_arches{$arch};
295 return undef unless $subset;
298 # If q is negated and p isn't, we'd need to know the full list of
299 # arches to know if there's any relationship, so bail.
300 elsif (not $p_arch_neg and $q_arch_neg) {
304 # If p is negated and q isn't, q is a subset of p iff none of the
305 # negated arches in p are present in q.
306 elsif ($p_arch_neg and not $q_arch_neg) {
307 my %q_arches = map { $_ => 1 } @q_arches;
309 for my $arch (@p_arches) {
310 $subset = 0 if $q_arches{substr($arch, 1)};
312 return undef unless $subset;
316 # Now, down to version. The implication is true if p's clause is stronger
317 # than q's, or is equivalent.
319 # If q has no version clause, then p's clause is always stronger.
320 return 1 if not defined $$q[2];
322 # If q does have a version clause, then p must also have one to have any
323 # useful relationship.
324 return undef if not defined $$p[2];
326 # q wants an exact version, so p must provide that exact version. p
327 # disproves q if q's version is outside the range enforced by p.
329 if ($$p[2] eq '<<') {
330 return versions_lte($$p[3], $$q[3]) ? 0 : undef;
331 } elsif ($$p[2] eq '<=') {
332 return versions_lt($$p[3], $$q[3]) ? 0 : undef;
333 } elsif ($$p[2] eq '>>') {
334 return versions_gte($$p[3], $$q[3]) ? 0 : undef;
335 } elsif ($$p[2] eq '>=') {
336 return versions_gt($$p[3], $$q[3]) ? 0 : undef;
337 } elsif ($$p[2] eq '=') {
338 return versions_equal($$p[3], $$q[3]);
342 # A greater than clause may disprove a less than clause. Otherwise, if
343 # p's clause is <<, <=, or =, the version must be <= q's to imply q.
344 if ($$q[2] eq '<=') {
345 if ($$p[2] eq '>>') {
346 return versions_gte($$p[3], $$q[3]) ? 0 : undef;
347 } elsif ($$p[2] eq '>=') {
348 return versions_gt($$p[3], $$q[3]) ? 0 : undef;
349 } elsif ($$p[2] eq '=') {
350 return versions_lte($$p[3], $$q[3]);
352 return versions_lte($$p[3], $$q[3]) ? 1 : undef;
356 # Similar, but << is stronger than <= so p's version must be << q's
357 # version if the p relation is <= or =.
358 if ($$q[2] eq '<<') {
359 if ($$p[2] eq '>>' or $$p[2] eq '>=') {
360 return versions_gte($$p[3], $$p[3]) ? 0 : undef;
361 } elsif ($$p[2] eq '<<') {
362 return versions_lte($$p[3], $$q[3]);
363 } elsif ($$p[2] eq '=') {
364 return versions_lt($$p[3], $$q[3]);
366 return versions_lt($$p[3], $$q[3]) ? 1 : undef;
370 # Same logic as above, only inverted.
371 if ($$q[2] eq '>=') {
372 if ($$p[2] eq '<<') {
373 return versions_lte($$p[3], $$q[3]) ? 0 : undef;
374 } elsif ($$p[2] eq '<=') {
375 return versions_lt($$p[3], $$q[3]) ? 0 : undef;
376 } elsif ($$p[2] eq '=') {
377 return versions_gte($$p[3], $$q[3]);
379 return versions_gte($$p[3], $$q[3]) ? 1 : undef;
382 if ($$q[2] eq '>>') {
383 if ($$p[2] eq '<<' or $$p[2] eq '<=') {
384 return versions_lte($$p[3], $$q[3]) ? 0 : undef;
385 } elsif ($$p[2] eq '>>') {
386 return versions_gte($$p[3], $$q[3]);
387 } elsif ($$p[2] eq '=') {
388 return versions_gt($$p[3], $$q[3]);
390 return versions_gt($$p[3], $$q[3]) ? 1 : undef;
397 # This internal function does the heavy of AND, OR, and NOT logic. It expects
398 # two references to arrays instead of an object and a relation.
400 my ($self, $p, $q) = @_;
402 if ($q->[0] eq 'PRED') {
403 if ($p->[0] eq 'PRED') {
404 return $self->implies_element($p, $q);
405 } elsif ($p->[0] eq 'AND') {
408 return 1 if $self->implies_array($p->[$i++], $q);
411 } elsif ($p->[0] eq 'OR') {
414 return 0 if not $self->implies_array($p->[$i++], $q);
417 } elsif ($p->[0] eq 'NOT') {
418 return $self->implies_array_inverse($p->[1], $q);
420 } elsif ($q->[0] eq 'AND') {
421 # Each of q's clauses must be deduced from p.
424 return 0 if not $self->implies_array($p, $q->[$i++]);
427 } elsif ($q->[0] eq 'OR') {
428 # If p is something other than OR, p needs to satisfy one of the
429 # clauses of q. If p is an AND clause, q is satisfied if any of the
430 # clauses of p satisfy it.
432 # The interesting case is OR. In this case, do an OR to OR comparison
433 # to determine if q's clause is a superset of p's clause as follows:
434 # take each branch of p and see if it satisfies a branch of q. If
435 # each branch of p satisfies some branch of q, return 1. Otherwise,
438 # Simple logic that requires that p satisfy at least one of the
439 # clauses of q considered in isolation will miss that a|b satisfies
440 # a|b|c, since a|b doesn't satisfy any of a, b, or c in isolation.
441 if ($p->[0] eq 'PRED') {
444 return 1 if $self->implies_array($p, $q->[$i++]);
447 } elsif ($p->[0] eq 'AND') {
450 return 1 if $self->implies_array($p->[$i++], $q);
453 } elsif ($p->[0] eq 'OR') {
454 for ($i = 1; $i < @$p; $i++) {
458 if ($self->implies_array($p->[$i], $q->[$j++])) {
463 return 0 unless $satisfies;
466 } elsif ($p->[0] eq 'NOT') {
467 return $self->implies_array_inverse($p->[1], $q);
469 } elsif ($q->[0] eq 'NOT') {
470 if ($p->[0] eq 'NOT') {
471 return $self->implies_array($q->[1], $p->[1]);
473 return $self->implies_array_inverse($p, $q->[1]);
477 # The public interface.
479 my ($self, $relation) = @_;
480 if (ref($relation) ne 'Maemian::Relation') {
481 $relation = Maemian::Relation->new($relation);
483 return $self->implies_array($self, $relation);
486 =item implies_inverse(RELATION)
488 Returns true if the relationship implies that RELATION is certainly false,
489 meaning that if the Maemian::Relation object is satisfied, RELATION cannot
490 be satisfied. RELATION may be either a string or another
491 Maemian::Relation object.
493 As with implies(), by default, architecture restrictions are honored in
494 RELATION if it is a string. If architecture restrictions should be
495 ignored in RELATION, create a Maemian::Relation object with new_noarch()
496 and pass that in as RELATION instead of the string.
500 # This internal function does the heavy lifting of inverse implication between
501 # two elements. Takes two elements and returns true iff the falsehood of
502 # the second can be deduced from the truth of the first. In other words, p
503 # implies not q, or resstated, q implies not p. (Since if a implies b, not b
504 # implies not a.) Due to the return value of implies_element(), we can let it
505 # do most of the work.
506 sub implies_element_inverse {
507 my ($self, $p, $q) = @_;
508 my $result = $self->implies_element($q, $p);
510 return not $result if defined $result;
514 # This internal function does the heavily lifting for AND, OR, and NOT
515 # handling for inverse implications. It takes two references to arrays and
516 # returns true iff the falsehood of the second can be deduced from the truth
518 sub implies_array_inverse {
519 my ($self, $p, $q) = @_;
521 if ($$q[0] eq 'PRED') {
522 if ($$p[0] eq 'PRED') {
523 return $self->implies_element_inverse($p, $q);
524 } elsif ($$p[0] eq 'AND') {
525 # q's falsehood can be deduced from any of p's clauses
528 return 1 if $self->implies_array_inverse($$p[$i++], $q);
531 } elsif ($$p[0] eq 'OR') {
532 # q's falsehood must be deduced from each of p's clauses
535 return 0 if not $self->implies_array_inverse($$p[$i++], $q);
538 } elsif ($$p[0] eq 'NOT') {
539 return $self->implies_array($q, $$p[1]);
541 } elsif ($$q[0] eq 'AND') {
542 # Any of q's clauses must be falsified by p.
545 return 1 if $self->implies_array_inverse($p, $$q[$i++]);
548 } elsif ($$q[0] eq 'OR') {
549 # Each of q's clauses must be falsified by p.
552 return 0 if not $self->implies_array_inverse($p, $$q[$i++]);
555 } elsif ($$q[0] eq 'NOT') {
556 return $self->implies_array($p, $$q[1]);
560 # The public interface.
561 sub implies_inverse {
562 my ($self, $relation) = @_;
563 if (ref($relation) ne 'Maemian::Relation') {
564 $relation = Maemian::Relation->new($relation);
566 return $self->implies_array_inverse($self, $relation);
571 Returns the textual form of a relationship. This converts the internal
572 form back into the textual representation and returns that, not the
573 original argument, so the spacing is standardized. Returns undef on
574 internal faliures (such as an object in an unexpected format).
578 # The second argument isn't part of the public API. It's a partial relation
579 # that's not a blessed object and is used by unparse() internally so that it
582 # We also support a NOT predicate. This currently isn't ever generated by a
583 # regular relation, but it may someday be useful.
585 my ($self, $partial) = @_;
586 my $relation = defined($partial) ? $partial : $self;
587 if ($relation->[0] eq 'PRED') {
588 my $text = $relation->[1];
589 if (defined $relation->[2]) {
590 $text .= " ($relation->[2] $relation->[3])";
592 if (defined $relation->[4]) {
593 $text .= " [$relation->[4]]";
596 } elsif ($relation->[0] eq 'AND' || $relation->[0] eq 'OR') {
597 my $seperator = ($relation->[0] eq 'AND') ? ', ' : ' | ';
599 for my $element (@$relation) {
600 $text .= $seperator if $text;
601 my $result = $self->unparse($element);
602 return unless defined($result);
606 } elsif ($relation->[0] eq 'NOT') {
607 return '! ' . $self->unparse($relation->[1]);
617 Originally written by Russ Allbery <rra@debian.org> for Maemian.
628 # indent-tabs-mode: nil
629 # cperl-indent-level: 4
631 # vim: syntax=perl sw=4 ts=8