Index: third_party/dpkg-dev/scripts/Dpkg/Deps.pm |
diff --git a/third_party/dpkg-dev/scripts/Dpkg/Deps.pm b/third_party/dpkg-dev/scripts/Dpkg/Deps.pm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..7bc18153c11220f986758c28d38f5206802817d1 |
--- /dev/null |
+++ b/third_party/dpkg-dev/scripts/Dpkg/Deps.pm |
@@ -0,0 +1,1468 @@ |
+# Copyright © 2007-2009 Raphaël Hertzog <hertzog@debian.org> |
+# Copyright © 2012 Guillem Jover <guillem@debian.org> |
+# |
+# This program is free software; you may 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 of the License, or |
+# (at your option) any later version. |
+# |
+# This 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 <https://www.gnu.org/licenses/>. |
+######################################################################### |
+# Several parts are inspired by lib/Dep.pm from lintian (same license) |
+# |
+# Copyright © 1998 Richard Braakman |
+# Portions Copyright © 1999 Darren Benham |
+# Portions Copyright © 2000 Sean 'Shaleh' Perry |
+# Portions Copyright © 2004 Frank Lichtenheld |
+# Portions Copyright © 2006 Russ Allbery |
+ |
+package Dpkg::Deps; |
+ |
+=encoding utf8 |
+ |
+=head1 NAME |
+ |
+Dpkg::Deps - parse and manipulate dependencies of Debian packages |
+ |
+=head1 DESCRIPTION |
+ |
+The Dpkg::Deps module provides objects implementing various types of |
+dependencies. |
+ |
+The most important function is deps_parse(), it turns a dependency line in |
+a set of Dpkg::Deps::{Simple,AND,OR,Union} objects depending on the case. |
+ |
+=head1 FUNCTIONS |
+ |
+All the deps_* functions are exported by default. |
+ |
+=over 4 |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+our $VERSION = '1.02'; |
+ |
+use Dpkg::Version; |
+use Dpkg::Arch qw(get_host_arch get_build_arch); |
+use Dpkg::BuildProfiles qw(get_build_profiles); |
+use Dpkg::ErrorHandling; |
+use Dpkg::Gettext; |
+ |
+use Exporter qw(import); |
+our @EXPORT = qw(deps_concat deps_parse deps_eval_implication deps_compare); |
+ |
+=item deps_eval_implication($rel_p, $v_p, $rel_q, $v_q) |
+ |
+($rel_p, $v_p) and ($rel_q, $v_q) express two dependencies as (relation, |
+version). The relation variable can have the following values that are |
+exported by Dpkg::Version: REL_EQ, REL_LT, REL_LE, REL_GT, REL_GT. |
+ |
+This functions returns 1 if the "p" dependency implies the "q" |
+dependency. It returns 0 if the "p" dependency implies that "q" is |
+not satisfied. It returns undef when there's no implication. |
+ |
+The $v_p and $v_q parameter should be Dpkg::Version objects. |
+ |
+=cut |
+ |
+sub deps_eval_implication { |
+ my ($rel_p, $v_p, $rel_q, $v_q) = @_; |
+ |
+ # If versions are not valid, we can't decide of any implication |
+ return unless defined($v_p) and $v_p->is_valid(); |
+ return unless defined($v_q) and $v_q->is_valid(); |
+ |
+ # q wants an exact version, so p must provide that exact version. p |
+ # disproves q if q's version is outside the range enforced by p. |
+ if ($rel_q eq REL_EQ) { |
+ if ($rel_p eq REL_LT) { |
+ return ($v_p <= $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_LE) { |
+ return ($v_p < $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_GT) { |
+ return ($v_p >= $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_GE) { |
+ return ($v_p > $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_EQ) { |
+ return ($v_p == $v_q); |
+ } |
+ } |
+ |
+ # A greater than clause may disprove a less than clause. An equal |
+ # cause might as well. Otherwise, if |
+ # p's clause is <<, <=, or =, the version must be <= q's to imply q. |
+ if ($rel_q eq REL_LE) { |
+ if ($rel_p eq REL_GT) { |
+ return ($v_p >= $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_GE) { |
+ return ($v_p > $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_EQ) { |
+ return ($v_p <= $v_q) ? 1 : 0; |
+ } else { # <<, <= |
+ return ($v_p <= $v_q) ? 1 : undef; |
+ } |
+ } |
+ |
+ # Similar, but << is stronger than <= so p's version must be << q's |
+ # version if the p relation is <= or =. |
+ if ($rel_q eq REL_LT) { |
+ if ($rel_p eq REL_GT or $rel_p eq REL_GE) { |
+ return ($v_p >= $v_p) ? 0 : undef; |
+ } elsif ($rel_p eq REL_LT) { |
+ return ($v_p <= $v_q) ? 1 : undef; |
+ } elsif ($rel_p eq REL_EQ) { |
+ return ($v_p < $v_q) ? 1 : 0; |
+ } else { # <<, <= |
+ return ($v_p < $v_q) ? 1 : undef; |
+ } |
+ } |
+ |
+ # Same logic as above, only inverted. |
+ if ($rel_q eq REL_GE) { |
+ if ($rel_p eq REL_LT) { |
+ return ($v_p <= $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_LE) { |
+ return ($v_p < $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_EQ) { |
+ return ($v_p >= $v_q) ? 1 : 0; |
+ } else { # >>, >= |
+ return ($v_p >= $v_q) ? 1 : undef; |
+ } |
+ } |
+ if ($rel_q eq REL_GT) { |
+ if ($rel_p eq REL_LT or $rel_p eq REL_LE) { |
+ return ($v_p <= $v_q) ? 0 : undef; |
+ } elsif ($rel_p eq REL_GT) { |
+ return ($v_p >= $v_q) ? 1 : undef; |
+ } elsif ($rel_p eq REL_EQ) { |
+ return ($v_p > $v_q) ? 1 : 0; |
+ } else { |
+ return ($v_p > $v_q) ? 1 : undef; |
+ } |
+ } |
+ |
+ return; |
+} |
+ |
+=item my $dep = deps_concat(@dep_list) |
+ |
+This function concatenates multiple dependency lines into a single line, |
+joining them with ", " if appropriate, and always returning a valid string. |
+ |
+=cut |
+ |
+sub deps_concat { |
+ my (@dep_list) = @_; |
+ |
+ return join(', ', grep { defined $_ } @dep_list); |
+} |
+ |
+=item my $dep = deps_parse($line, %options) |
+ |
+This function parses the dependency line and returns an object, either a |
+Dpkg::Deps::AND or a Dpkg::Deps::Union. Various options can alter the |
+behaviour of that function. |
+ |
+=over 4 |
+ |
+=item use_arch (defaults to 1) |
+ |
+Take into account the architecture restriction part of the dependencies. |
+Set to 0 to completely ignore that information. |
+ |
+=item host_arch (defaults to the current architecture) |
+ |
+Define the host architecture. By default it uses |
+Dpkg::Arch::get_host_arch() to identify the proper architecture. |
+ |
+=item build_arch (defaults to the current architecture) |
+ |
+Define the build architecture. By default it uses |
+Dpkg::Arch::get_build_arch() to identify the proper architecture. |
+ |
+=item reduce_arch (defaults to 0) |
+ |
+If set to 1, ignore dependencies that do not concern the current host |
+architecture. This implicitely strips off the architecture restriction |
+list so that the resulting dependencies are directly applicable to the |
+current architecture. |
+ |
+=item use_profiles (defaults to 1) |
+ |
+Take into account the profile restriction part of the dependencies. Set |
+to 0 to completely ignore that information. |
+ |
+=item build_profiles (defaults to no profile) |
+ |
+Define the active build profiles. By default no profile is defined. |
+ |
+=item reduce_profiles (defaults to 0) |
+ |
+If set to 1, ignore dependencies that do not concern the current build |
+profile. This implicitly strips off the profile restriction list so |
+that the resulting dependencies are directly applicable to the current |
+profiles. |
+ |
+=item reduce_restrictions (defaults to 0) |
+ |
+If set to 1, ignore dependencies that do not concern the current set of |
+restrictions. This implicitly strips off any restriction list so that the |
+resulting dependencies are directly applicable to the current restriction. |
+This currently implies C<reduce_arch> and C<reduce_profiles>, and overrides |
+them if set. |
+ |
+=item union (defaults to 0) |
+ |
+If set to 1, returns a Dpkg::Deps::Union instead of a Dpkg::Deps::AND. Use |
+this when parsing non-dependency fields like Conflicts. |
+ |
+=item build_dep (defaults to 0) |
+ |
+If set to 1, allow build-dep only arch qualifiers, that is “:native”. |
+This should be set whenever working with build-deps. |
+ |
+=back |
+ |
+=cut |
+ |
+sub deps_parse { |
+ my $dep_line = shift; |
+ my %options = (@_); |
+ $options{use_arch} = 1 if not exists $options{use_arch}; |
+ $options{reduce_arch} = 0 if not exists $options{reduce_arch}; |
+ $options{host_arch} = get_host_arch() if not exists $options{host_arch}; |
+ $options{build_arch} = get_build_arch() if not exists $options{build_arch}; |
+ $options{use_profiles} = 1 if not exists $options{use_profiles}; |
+ $options{reduce_profiles} = 0 if not exists $options{reduce_profiles}; |
+ $options{build_profiles} = [ get_build_profiles() ] |
+ if not exists $options{build_profiles}; |
+ $options{reduce_restrictions} = 0 if not exists $options{reduce_restrictions}; |
+ $options{union} = 0 if not exists $options{union}; |
+ $options{build_dep} = 0 if not exists $options{build_dep}; |
+ |
+ if ($options{reduce_restrictions}) { |
+ $options{reduce_arch} = 1; |
+ $options{reduce_profiles} = 1; |
+ } |
+ |
+ # Strip trailing/leading spaces |
+ $dep_line =~ s/^\s+//; |
+ $dep_line =~ s/\s+$//; |
+ |
+ my @dep_list; |
+ foreach my $dep_and (split(/\s*,\s*/m, $dep_line)) { |
+ my @or_list = (); |
+ foreach my $dep_or (split(/\s*\|\s*/m, $dep_and)) { |
+ my $dep_simple = Dpkg::Deps::Simple->new($dep_or, host_arch => |
+ $options{host_arch}, |
+ build_arch => |
+ $options{build_arch}, |
+ build_dep => |
+ $options{build_dep}); |
+ if (not defined $dep_simple->{package}) { |
+ warning(_g("can't parse dependency %s"), $dep_or); |
+ return; |
+ } |
+ $dep_simple->{arches} = undef if not $options{use_arch}; |
+ if ($options{reduce_arch}) { |
+ $dep_simple->reduce_arch($options{host_arch}); |
+ next if not $dep_simple->arch_is_concerned($options{host_arch}); |
+ } |
+ $dep_simple->{restrictions} = undef if not $options{use_profiles}; |
+ if ($options{reduce_profiles}) { |
+ $dep_simple->reduce_profiles($options{build_profiles}); |
+ next if not $dep_simple->profile_is_concerned($options{build_profiles}); |
+ } |
+ push @or_list, $dep_simple; |
+ } |
+ next if not @or_list; |
+ if (scalar @or_list == 1) { |
+ push @dep_list, $or_list[0]; |
+ } else { |
+ my $dep_or = Dpkg::Deps::OR->new(); |
+ $dep_or->add($_) foreach (@or_list); |
+ push @dep_list, $dep_or; |
+ } |
+ } |
+ my $dep_and; |
+ if ($options{union}) { |
+ $dep_and = Dpkg::Deps::Union->new(); |
+ } else { |
+ $dep_and = Dpkg::Deps::AND->new(); |
+ } |
+ foreach my $dep (@dep_list) { |
+ if ($options{union} and not $dep->isa('Dpkg::Deps::Simple')) { |
+ warning(_g('an union dependency can only contain simple dependencies')); |
+ return; |
+ } |
+ $dep_and->add($dep); |
+ } |
+ return $dep_and; |
+} |
+ |
+=item deps_compare($a, $b) |
+ |
+Implements a comparison operator between two dependency objects. |
+This function is mainly used to implement the sort() method. |
+ |
+=back |
+ |
+=cut |
+ |
+my %relation_ordering = ( |
+ undef => 0, |
+ REL_GE() => 1, |
+ REL_GT() => 2, |
+ REL_EQ() => 3, |
+ REL_LT() => 4, |
+ REL_LE() => 5, |
+); |
+ |
+sub deps_compare { |
+ my ($a, $b) = @_; |
+ return -1 if $a->is_empty(); |
+ return 1 if $b->is_empty(); |
+ while ($a->isa('Dpkg::Deps::Multiple')) { |
+ return -1 if $a->is_empty(); |
+ my @deps = $a->get_deps(); |
+ $a = $deps[0]; |
+ } |
+ while ($b->isa('Dpkg::Deps::Multiple')) { |
+ return 1 if $b->is_empty(); |
+ my @deps = $b->get_deps(); |
+ $b = $deps[0]; |
+ } |
+ my $ar = defined($a->{relation}) ? $a->{relation} : 'undef'; |
+ my $br = defined($b->{relation}) ? $b->{relation} : 'undef'; |
+ return (($a->{package} cmp $b->{package}) || |
+ ($relation_ordering{$ar} <=> $relation_ordering{$br}) || |
+ ($a->{version} cmp $b->{version})); |
+} |
+ |
+ |
+package Dpkg::Deps::Simple; |
+ |
+=head1 OBJECTS - Dpkg::Deps::* |
+ |
+There are several kind of dependencies. A Dpkg::Deps::Simple dependency |
+represents a single dependency statement (it relates to one package only). |
+Dpkg::Deps::Multiple dependencies are built on top of this object |
+and combine several dependencies in a different manners. Dpkg::Deps::AND |
+represents the logical "AND" between dependencies while Dpkg::Deps::OR |
+represents the logical "OR". Dpkg::Deps::Multiple objects can contain |
+Dpkg::Deps::Simple object as well as other Dpkg::Deps::Multiple objects. |
+ |
+In practice, the code is only meant to handle the realistic cases which, |
+given Debian's dependencies structure, imply those restrictions: AND can |
+contain Simple or OR objects, OR can only contain Simple objects. |
+ |
+Dpkg::Deps::KnownFacts is a special object that is used while evaluating |
+dependencies and while trying to simplify them. It represents a set of |
+installed packages along with the virtual packages that they might |
+provide. |
+ |
+=head2 COMMON FUNCTIONS |
+ |
+=over 4 |
+ |
+=item $dep->is_empty() |
+ |
+Returns true if the dependency is empty and doesn't contain any useful |
+information. This is true when a Dpkg::Deps::Simple object has not yet |
+been initialized or when a (descendant of) Dpkg::Deps::Multiple contains |
+an empty list of dependencies. |
+ |
+=item $dep->get_deps() |
+ |
+Returns a list of sub-dependencies. For Dpkg::Deps::Simple it returns |
+itself. |
+ |
+=item $dep->output([$fh]) |
+ |
+=item "$dep" |
+ |
+Returns a string representing the dependency. If $fh is set, it prints |
+the string to the filehandle. |
+ |
+=item $dep->implies($other_dep) |
+ |
+Returns 1 when $dep implies $other_dep. Returns 0 when $dep implies |
+NOT($other_dep). Returns undef when there's no implication. $dep and |
+$other_dep do not need to be of the same type. |
+ |
+=item $dep->sort() |
+ |
+Sorts alphabetically the internal list of dependencies. It's a no-op for |
+Dpkg::Deps::Simple objects. |
+ |
+=item $dep->arch_is_concerned($arch) |
+ |
+Returns true if the dependency applies to the indicated architecture. For |
+multiple dependencies, it returns true if at least one of the |
+sub-dependencies apply to this architecture. |
+ |
+=item $dep->reduce_arch($arch) |
+ |
+Simplifies the dependency to contain only information relevant to the given |
+architecture. A Dpkg::Deps::Simple object can be left empty after this |
+operation. For Dpkg::Deps::Multiple objects, the non-relevant |
+sub-dependencies are simply removed. |
+ |
+This trims off the architecture restriction list of Dpkg::Deps::Simple |
+objects. |
+ |
+=item $dep->get_evaluation($facts) |
+ |
+Evaluates the dependency given a list of installed packages and a list of |
+virtual packages provided. Those lists are part of the |
+Dpkg::Deps::KnownFacts object given as parameters. |
+ |
+Returns 1 when it's true, 0 when it's false, undef when some information |
+is lacking to conclude. |
+ |
+=item $dep->simplify_deps($facts, @assumed_deps) |
+ |
+Simplifies the dependency as much as possible given the list of facts (see |
+object Dpkg::Deps::KnownFacts) and a list of other dependencies that are |
+known to be true. |
+ |
+=item $dep->has_arch_restriction() |
+ |
+For a simple dependency, returns the package name if the dependency |
+applies only to a subset of architectures. For multiple dependencies, it |
+returns the list of package names that have such a restriction. |
+ |
+=item $dep->reset() |
+ |
+Clears any dependency information stored in $dep so that $dep->is_empty() |
+returns true. |
+ |
+=back |
+ |
+=head2 Dpkg::Deps::Simple |
+ |
+Such an object has four interesting properties: |
+ |
+=over 4 |
+ |
+=item package |
+ |
+The package name (can be undef if the dependency has not been initialized |
+or if the simplification of the dependency lead to its removal). |
+ |
+=item relation |
+ |
+The relational operator: "=", "<<", "<=", ">=" or ">>". It can be |
+undefined if the dependency had no version restriction. In that case the |
+following field is also undefined. |
+ |
+=item version |
+ |
+The version. |
+ |
+=item arches |
+ |
+The list of architectures where this dependency is applicable. It's |
+undefined when there's no restriction, otherwise it's an |
+array ref. It can contain an exclusion list, in that case each |
+architecture is prefixed with an exclamation mark. |
+ |
+=item archqual |
+ |
+The arch qualifier of the dependency (can be undef if there's none). |
+In the dependency "python:any (>= 2.6)", the arch qualifier is "any". |
+ |
+=back |
+ |
+=head3 METHODS |
+ |
+=over 4 |
+ |
+=item $simple_dep->parse_string('dpkg-dev (>= 1.14.8) [!hurd-i386]') |
+ |
+Parses the dependency and modifies internal properties to match the parsed |
+dependency. |
+ |
+=item $simple_dep->merge_union($other_dep) |
+ |
+Returns true if $simple_dep could be modified to represent the union of |
+both dependencies. Otherwise returns false. |
+ |
+=back |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+use Carp; |
+ |
+use Dpkg::Arch qw(debarch_is); |
+use Dpkg::Version; |
+use Dpkg::ErrorHandling; |
+use Dpkg::Gettext; |
+use Dpkg::Util qw(:list); |
+ |
+use parent qw(Dpkg::Interface::Storable); |
+ |
+sub new { |
+ my ($this, $arg, %opts) = @_; |
+ my $class = ref($this) || $this; |
+ my $self = {}; |
+ bless $self, $class; |
+ $self->reset(); |
+ $self->{host_arch} = $opts{host_arch} || Dpkg::Arch::get_host_arch(); |
+ $self->{build_arch} = $opts{build_arch} || Dpkg::Arch::get_build_arch(); |
+ $self->{build_dep} = $opts{build_dep} || 0; |
+ $self->parse_string($arg) if defined($arg); |
+ return $self; |
+} |
+ |
+sub reset { |
+ my ($self) = @_; |
+ $self->{package} = undef; |
+ $self->{relation} = undef; |
+ $self->{version} = undef; |
+ $self->{arches} = undef; |
+ $self->{archqual} = undef; |
+ $self->{restrictions} = undef; |
+} |
+ |
+sub parse { |
+ my ($self, $fh, $desc) = @_; |
+ my $line = <$fh>; |
+ chomp($line); |
+ return $self->parse_string($line); |
+} |
+ |
+sub parse_string { |
+ my ($self, $dep) = @_; |
+ return if not $dep =~ |
+ m{^\s* # skip leading whitespace |
+ ([a-zA-Z0-9][a-zA-Z0-9+.-]*) # package name |
+ (?: # start of optional part |
+ : # colon for architecture |
+ ([a-zA-Z0-9][a-zA-Z0-9-]*) # architecture name |
+ )? # end of optional part |
+ (?: # start of optional part |
+ \s* \( # open parenthesis for version part |
+ \s* (<<|<=|=|>=|>>|<|>) # relation part |
+ \s* (.*?) # do not attempt to parse version |
+ \s* \) # closing parenthesis |
+ )? # end of optional part |
+ (?: # start of optional architecture |
+ \s* \[ # open bracket for architecture |
+ \s* (.*?) # don't parse architectures now |
+ \s* \] # closing bracket |
+ )? # end of optional architecture |
+ (?: # start of optional restriction |
+ \s* < # open bracket for restriction |
+ \s* (.*?) # don't parse restrictions now |
+ \s* > # closing bracket |
+ )? # end of optional restriction |
+ \s*$ # trailing spaces at end |
+ }x; |
+ if (defined($2)) { |
+ return if $2 eq 'native' and not $self->{build_dep}; |
+ $self->{archqual} = $2; |
+ } |
+ $self->{package} = $1; |
+ $self->{relation} = version_normalize_relation($3) if defined($3); |
+ if (defined($4)) { |
+ $self->{version} = Dpkg::Version->new($4); |
+ } |
+ if (defined($5)) { |
+ $self->{arches} = [ split(/\s+/, $5) ]; |
+ } |
+ if (defined($6)) { |
+ $self->{restrictions} = [ map { lc } split /\s+/, $6 ]; |
+ } |
+} |
+ |
+sub output { |
+ my ($self, $fh) = @_; |
+ my $res = $self->{package}; |
+ if (defined($self->{archqual})) { |
+ $res .= ':' . $self->{archqual}; |
+ } |
+ if (defined($self->{relation})) { |
+ $res .= ' (' . $self->{relation} . ' ' . $self->{version} . ')'; |
+ } |
+ if (defined($self->{arches})) { |
+ $res .= ' [' . join(' ', @{$self->{arches}}) . ']'; |
+ } |
+ if (defined($self->{restrictions})) { |
+ $res .= ' <' . join(' ', @{$self->{restrictions}}) . '>'; |
+ } |
+ if (defined($fh)) { |
+ print { $fh } $res; |
+ } |
+ return $res; |
+} |
+ |
+# _arch_is_superset(\@p, \@q) |
+# |
+# Returns true if the arch list @p is a superset of arch list @q. |
+# The arguments can also be undef in case there's no explicit architecture |
+# restriction. |
+sub _arch_is_superset { |
+ my ($p, $q) = @_; |
+ my $p_arch_neg = defined($p) && $p->[0] =~ /^!/; |
+ my $q_arch_neg = defined($q) && $q->[0] =~ /^!/; |
+ |
+ # If "p" has no arches, it is a superset of q and we should fall through |
+ # to the version check. |
+ if (not defined $p) { |
+ return 1; |
+ } |
+ |
+ # If q has no arches, it is a superset of p and there are no useful |
+ # implications. |
+ elsif (not defined $q) { |
+ return 0; |
+ } |
+ |
+ # Both have arches. If neither are negated, we know nothing useful |
+ # unless q is a subset of p. |
+ elsif (not $p_arch_neg and not $q_arch_neg) { |
+ my %p_arches = map { $_ => 1 } @{$p}; |
+ my $subset = 1; |
+ for my $arch (@{$q}) { |
+ $subset = 0 unless $p_arches{$arch}; |
+ } |
+ return 0 unless $subset; |
+ } |
+ |
+ # If both are negated, we know nothing useful unless p is a subset of |
+ # q (and therefore has fewer things excluded, and therefore is more |
+ # general). |
+ elsif ($p_arch_neg and $q_arch_neg) { |
+ my %q_arches = map { $_ => 1 } @{$q}; |
+ my $subset = 1; |
+ for my $arch (@{$p}) { |
+ $subset = 0 unless $q_arches{$arch}; |
+ } |
+ return 0 unless $subset; |
+ } |
+ |
+ # If q is negated and p isn't, we'd need to know the full list of |
+ # arches to know if there's any relationship, so bail. |
+ elsif (not $p_arch_neg and $q_arch_neg) { |
+ return 0; |
+ } |
+ |
+ # If p is negated and q isn't, q is a subset of p if none of the |
+ # negated arches in p are present in q. |
+ elsif ($p_arch_neg and not $q_arch_neg) { |
+ my %q_arches = map { $_ => 1 } @{$q}; |
+ my $subset = 1; |
+ for my $arch (@{$p}) { |
+ $subset = 0 if $q_arches{substr($arch, 1)}; |
+ } |
+ return 0 unless $subset; |
+ } |
+ return 1; |
+} |
+ |
+# _arch_qualifier_allows_implication($p, $q) |
+# |
+# Returns true if the arch qualifier $p and $q are compatible with the |
+# implication $p -> $q, false otherwise. $p/$q can be |
+# undef/"any"/"native" or an architecture string. |
+sub _arch_qualifier_allows_implication { |
+ my ($p, $q) = @_; |
+ if (defined $p and $p eq 'any') { |
+ return 1 if defined $q and $q eq 'any'; |
+ return 0; |
+ } elsif (defined $p and $p eq 'native') { |
+ return 1 if defined $q and ($q eq 'any' or $q eq 'native'); |
+ return 0; |
+ } elsif (defined $p) { |
+ return 1 if defined $q and ($p eq $q or $q eq 'any'); |
+ return 0; |
+ } else { |
+ return 0 if defined $q and $q ne 'any' and $q ne 'native'; |
+ return 1; |
+ } |
+} |
+ |
+# Returns true if the dependency in parameter can deduced from the current |
+# dependency. Returns false if it can be negated. Returns undef if nothing |
+# can be concluded. |
+sub implies { |
+ my ($self, $o) = @_; |
+ if ($o->isa('Dpkg::Deps::Simple')) { |
+ # An implication is only possible on the same package |
+ return if $self->{package} ne $o->{package}; |
+ |
+ # Our architecture set must be a superset of the architectures for |
+ # o, otherwise we can't conclude anything. |
+ return unless _arch_is_superset($self->{arches}, $o->{arches}); |
+ |
+ # The arch qualifier must not forbid an implication |
+ return unless _arch_qualifier_allows_implication($self->{archqual}, |
+ $o->{archqual}); |
+ |
+ # If o has no version clause, then our dependency is stronger |
+ return 1 if not defined $o->{relation}; |
+ # If o has a version clause, we must also have one, otherwise there |
+ # can't be an implication |
+ return if not defined $self->{relation}; |
+ |
+ return Dpkg::Deps::deps_eval_implication($self->{relation}, |
+ $self->{version}, $o->{relation}, $o->{version}); |
+ |
+ } elsif ($o->isa('Dpkg::Deps::AND')) { |
+ # TRUE: Need to imply all individual elements |
+ # FALSE: Need to NOT imply at least one individual element |
+ my $res = 1; |
+ foreach my $dep ($o->get_deps()) { |
+ my $implication = $self->implies($dep); |
+ unless (defined($implication) && $implication == 1) { |
+ $res = $implication; |
+ last if defined $res; |
+ } |
+ } |
+ return $res; |
+ } elsif ($o->isa('Dpkg::Deps::OR')) { |
+ # TRUE: Need to imply at least one individual element |
+ # FALSE: Need to not apply all individual elements |
+ # UNDEF: The rest |
+ my $res = undef; |
+ foreach my $dep ($o->get_deps()) { |
+ my $implication = $self->implies($dep); |
+ if (defined($implication)) { |
+ if (not defined $res) { |
+ $res = $implication; |
+ } else { |
+ if ($implication) { |
+ $res = 1; |
+ } else { |
+ $res = 0; |
+ } |
+ } |
+ last if defined($res) && $res == 1; |
+ } |
+ } |
+ return $res; |
+ } else { |
+ croak 'Dpkg::Deps::Simple cannot evaluate implication with a ' . |
+ ref($o); |
+ } |
+} |
+ |
+sub get_deps { |
+ my $self = shift; |
+ return $self; |
+} |
+ |
+sub sort { |
+ # Nothing to sort |
+} |
+ |
+sub arch_is_concerned { |
+ my ($self, $host_arch) = @_; |
+ |
+ return 0 if not defined $self->{package}; # Empty dep |
+ return 1 if not defined $self->{arches}; # Dep without arch spec |
+ |
+ my $seen_arch = 0; |
+ foreach my $arch (@{$self->{arches}}) { |
+ $arch=lc($arch); |
+ |
+ if ($arch =~ /^!/) { |
+ my $not_arch = $arch; |
+ $not_arch =~ s/^!//; |
+ |
+ if (debarch_is($host_arch, $not_arch)) { |
+ $seen_arch = 0; |
+ last; |
+ } else { |
+ # !arch includes by default all other arches |
+ # unless they also appear in a !otherarch |
+ $seen_arch = 1; |
+ } |
+ } elsif (debarch_is($host_arch, $arch)) { |
+ $seen_arch = 1; |
+ last; |
+ } |
+ } |
+ return $seen_arch; |
+} |
+ |
+sub reduce_arch { |
+ my ($self, $host_arch) = @_; |
+ if (not $self->arch_is_concerned($host_arch)) { |
+ $self->reset(); |
+ } else { |
+ $self->{arches} = undef; |
+ } |
+} |
+ |
+sub has_arch_restriction { |
+ my ($self) = @_; |
+ if (defined $self->{arches}) { |
+ return $self->{package}; |
+ } else { |
+ return (); |
+ } |
+} |
+ |
+sub profile_is_concerned { |
+ my ($self, $build_profiles) = @_; |
+ |
+ return 0 if not defined $self->{package}; # Empty dep |
+ return 1 if not defined $self->{restrictions}; # Dep without restrictions |
+ |
+ my $seen_profile = 0; |
+ foreach my $restriction (@{$self->{restrictions}}) { |
+ # Determine if this restriction is negated, and within the "profile" |
+ # namespace, otherwise it does not concern this check. |
+ next if $restriction !~ m/^(!)?profile\.(.*)/; |
+ |
+ my $negated = defined $1 && $1 eq '!'; |
+ my $profile = $2; |
+ |
+ # Determine if the restriction matches any of the specified profiles. |
+ my $found = any { $_ eq $profile } @{$build_profiles}; |
+ |
+ if ($negated) { |
+ if ($found) { |
+ $seen_profile = 0; |
+ last; |
+ } else { |
+ # "!profile.this" includes by default all other profiles |
+ # unless they also appear in a "!profile.other". |
+ $seen_profile = 1; |
+ } |
+ } elsif ($found) { |
+ $seen_profile = 1; |
+ last; |
+ } |
+ } |
+ return $seen_profile; |
+} |
+ |
+sub reduce_profiles { |
+ my ($self, $build_profiles) = @_; |
+ |
+ if (not $self->profile_is_concerned($build_profiles)) { |
+ $self->reset(); |
+ } else { |
+ $self->{restrictions} = undef; |
+ } |
+} |
+ |
+sub get_evaluation { |
+ my ($self, $facts) = @_; |
+ return if not defined $self->{package}; |
+ return $facts->_evaluate_simple_dep($self); |
+} |
+ |
+sub simplify_deps { |
+ my ($self, $facts) = @_; |
+ my $eval = $self->get_evaluation($facts); |
+ $self->reset() if defined $eval and $eval == 1; |
+} |
+ |
+sub is_empty { |
+ my $self = shift; |
+ return not defined $self->{package}; |
+} |
+ |
+sub merge_union { |
+ my ($self, $o) = @_; |
+ return 0 if not $o->isa('Dpkg::Deps::Simple'); |
+ return 0 if $self->is_empty() or $o->is_empty(); |
+ return 0 if $self->{package} ne $o->{package}; |
+ return 0 if defined $self->{arches} or defined $o->{arches}; |
+ |
+ if (not defined $o->{relation} and defined $self->{relation}) { |
+ # Union is the non-versioned dependency |
+ $self->{relation} = undef; |
+ $self->{version} = undef; |
+ return 1; |
+ } |
+ |
+ my $implication = $self->implies($o); |
+ my $rev_implication = $o->implies($self); |
+ if (defined($implication)) { |
+ if ($implication) { |
+ $self->{relation} = $o->{relation}; |
+ $self->{version} = $o->{version}; |
+ return 1; |
+ } else { |
+ return 0; |
+ } |
+ } |
+ if (defined($rev_implication)) { |
+ if ($rev_implication) { |
+ # Already merged... |
+ return 1; |
+ } else { |
+ return 0; |
+ } |
+ } |
+ return 0; |
+} |
+ |
+package Dpkg::Deps::Multiple; |
+ |
+=head2 Dpkg::Deps::Multiple |
+ |
+This is the base class for Dpkg::Deps::{AND,OR,Union}. It implements |
+the following methods: |
+ |
+=over 4 |
+ |
+=item $mul->add($dep) |
+ |
+Adds a new dependency object at the end of the list. |
+ |
+=back |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+use Carp; |
+ |
+use Dpkg::ErrorHandling; |
+ |
+use parent qw(Dpkg::Interface::Storable); |
+ |
+sub new { |
+ my $this = shift; |
+ my $class = ref($this) || $this; |
+ my $self = { list => [ @_ ] }; |
+ bless $self, $class; |
+ return $self; |
+} |
+ |
+sub reset { |
+ my ($self) = @_; |
+ $self->{list} = []; |
+} |
+ |
+sub add { |
+ my $self = shift; |
+ push @{$self->{list}}, @_; |
+} |
+ |
+sub get_deps { |
+ my $self = shift; |
+ return grep { not $_->is_empty() } @{$self->{list}}; |
+} |
+ |
+sub sort { |
+ my $self = shift; |
+ my @res = (); |
+ @res = sort { Dpkg::Deps::deps_compare($a, $b) } @{$self->{list}}; |
+ $self->{list} = [ @res ]; |
+} |
+ |
+sub arch_is_concerned { |
+ my ($self, $host_arch) = @_; |
+ my $res = 0; |
+ foreach my $dep (@{$self->{list}}) { |
+ $res = 1 if $dep->arch_is_concerned($host_arch); |
+ } |
+ return $res; |
+} |
+ |
+sub reduce_arch { |
+ my ($self, $host_arch) = @_; |
+ my @new; |
+ foreach my $dep (@{$self->{list}}) { |
+ $dep->reduce_arch($host_arch); |
+ push @new, $dep if $dep->arch_is_concerned($host_arch); |
+ } |
+ $self->{list} = [ @new ]; |
+} |
+ |
+sub has_arch_restriction { |
+ my ($self) = @_; |
+ my @res; |
+ foreach my $dep (@{$self->{list}}) { |
+ push @res, $dep->has_arch_restriction(); |
+ } |
+ return @res; |
+} |
+ |
+ |
+sub is_empty { |
+ my $self = shift; |
+ return scalar @{$self->{list}} == 0; |
+} |
+ |
+sub merge_union { |
+ croak 'method merge_union() is only valid for Dpkg::Deps::Simple'; |
+} |
+ |
+package Dpkg::Deps::AND; |
+ |
+=head2 Dpkg::Deps::AND |
+ |
+This object represents a list of dependencies who must be met at the same |
+time. |
+ |
+=over 4 |
+ |
+=item $and->output([$fh]) |
+ |
+The output method uses ", " to join the list of sub-dependencies. |
+ |
+=back |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+use parent -norequire, qw(Dpkg::Deps::Multiple); |
+ |
+sub output { |
+ my ($self, $fh) = @_; |
+ my $res = join(', ', map { $_->output() } grep { not $_->is_empty() } $self->get_deps()); |
+ if (defined($fh)) { |
+ print { $fh } $res; |
+ } |
+ return $res; |
+} |
+ |
+sub implies { |
+ my ($self, $o) = @_; |
+ # If any individual member can imply $o or NOT $o, we're fine |
+ foreach my $dep ($self->get_deps()) { |
+ my $implication = $dep->implies($o); |
+ return 1 if defined($implication) && $implication == 1; |
+ return 0 if defined($implication) && $implication == 0; |
+ } |
+ # If o is an AND, we might have an implication, if we find an |
+ # implication within us for each predicate in o |
+ if ($o->isa('Dpkg::Deps::AND')) { |
+ my $subset = 1; |
+ foreach my $odep ($o->get_deps()) { |
+ my $found = 0; |
+ foreach my $dep ($self->get_deps()) { |
+ $found = 1 if $dep->implies($odep); |
+ } |
+ $subset = 0 if not $found; |
+ } |
+ return 1 if $subset; |
+ } |
+ return; |
+} |
+ |
+sub get_evaluation { |
+ my ($self, $facts) = @_; |
+ # Return 1 only if all members evaluates to true |
+ # Return 0 if at least one member evaluates to false |
+ # Return undef otherwise |
+ my $result = 1; |
+ foreach my $dep ($self->get_deps()) { |
+ my $eval = $dep->get_evaluation($facts); |
+ if (not defined $eval) { |
+ $result = undef; |
+ } elsif ($eval == 0) { |
+ $result = 0; |
+ last; |
+ } elsif ($eval == 1) { |
+ # Still possible |
+ } |
+ } |
+ return $result; |
+} |
+ |
+sub simplify_deps { |
+ my ($self, $facts, @knowndeps) = @_; |
+ my @new; |
+ |
+WHILELOOP: |
+ while (@{$self->{list}}) { |
+ my $dep = shift @{$self->{list}}; |
+ my $eval = $dep->get_evaluation($facts); |
+ next if defined($eval) and $eval == 1; |
+ foreach my $odep (@knowndeps, @new) { |
+ next WHILELOOP if $odep->implies($dep); |
+ } |
+ # When a dependency is implied by another dependency that |
+ # follows, then invert them |
+ # "a | b, c, a" becomes "a, c" and not "c, a" |
+ my $i = 0; |
+ foreach my $odep (@{$self->{list}}) { |
+ if (defined $odep and $odep->implies($dep)) { |
+ splice @{$self->{list}}, $i, 1; |
+ unshift @{$self->{list}}, $odep; |
+ next WHILELOOP; |
+ } |
+ $i++; |
+ } |
+ push @new, $dep; |
+ } |
+ $self->{list} = [ @new ]; |
+} |
+ |
+ |
+package Dpkg::Deps::OR; |
+ |
+=head2 Dpkg::Deps::OR |
+ |
+This object represents a list of dependencies of which only one must be met |
+for the dependency to be true. |
+ |
+=over 4 |
+ |
+=item $or->output([$fh]) |
+ |
+The output method uses " | " to join the list of sub-dependencies. |
+ |
+=back |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+use parent -norequire, qw(Dpkg::Deps::Multiple); |
+ |
+sub output { |
+ my ($self, $fh) = @_; |
+ my $res = join(' | ', map { $_->output() } grep { not $_->is_empty() } $self->get_deps()); |
+ if (defined($fh)) { |
+ print { $fh } $res; |
+ } |
+ return $res; |
+} |
+ |
+sub implies { |
+ my ($self, $o) = @_; |
+ |
+ # Special case for AND with a single member, replace it by its member |
+ if ($o->isa('Dpkg::Deps::AND')) { |
+ my @subdeps = $o->get_deps(); |
+ if (scalar(@subdeps) == 1) { |
+ $o = $subdeps[0]; |
+ } |
+ } |
+ |
+ # In general, an OR dependency can't imply anything except if each |
+ # of its member implies a member in the other OR dependency |
+ if ($o->isa('Dpkg::Deps::OR')) { |
+ my $subset = 1; |
+ foreach my $dep ($self->get_deps()) { |
+ my $found = 0; |
+ foreach my $odep ($o->get_deps()) { |
+ $found = 1 if $dep->implies($odep); |
+ } |
+ $subset = 0 if not $found; |
+ } |
+ return 1 if $subset; |
+ } |
+ return; |
+} |
+ |
+sub get_evaluation { |
+ my ($self, $facts) = @_; |
+ # Returns false if all members evaluates to 0 |
+ # Returns true if at least one member evaluates to true |
+ # Returns undef otherwise |
+ my $result = 0; |
+ foreach my $dep ($self->get_deps()) { |
+ my $eval = $dep->get_evaluation($facts); |
+ if (not defined $eval) { |
+ $result = undef; |
+ } elsif ($eval == 1) { |
+ $result = 1; |
+ last; |
+ } elsif ($eval == 0) { |
+ # Still possible to have a false evaluation |
+ } |
+ } |
+ return $result; |
+} |
+ |
+sub simplify_deps { |
+ my ($self, $facts) = @_; |
+ my @new; |
+ |
+WHILELOOP: |
+ while (@{$self->{list}}) { |
+ my $dep = shift @{$self->{list}}; |
+ my $eval = $dep->get_evaluation($facts); |
+ if (defined($eval) and $eval == 1) { |
+ $self->{list} = []; |
+ return; |
+ } |
+ foreach my $odep (@new, @{$self->{list}}) { |
+ next WHILELOOP if $odep->implies($dep); |
+ } |
+ push @new, $dep; |
+ } |
+ $self->{list} = [ @new ]; |
+} |
+ |
+package Dpkg::Deps::Union; |
+ |
+=head2 Dpkg::Deps::Union |
+ |
+This object represents a list of relationships. |
+ |
+=over 4 |
+ |
+=item $union->output([$fh]) |
+ |
+The output method uses ", " to join the list of relationships. |
+ |
+=item $union->implies($other_dep) |
+ |
+=item $union->get_evaluation($other_dep) |
+ |
+Those methods are not meaningful for this object and always return undef. |
+ |
+=item $union->simplify_deps($facts) |
+ |
+The simplication is done to generate an union of all the relationships. |
+It uses $simple_dep->merge_union($other_dep) to get its job done. |
+ |
+=back |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+use parent -norequire, qw(Dpkg::Deps::Multiple); |
+ |
+sub output { |
+ my ($self, $fh) = @_; |
+ my $res = join(', ', map { $_->output() } grep { not $_->is_empty() } $self->get_deps()); |
+ if (defined($fh)) { |
+ print { $fh } $res; |
+ } |
+ return $res; |
+} |
+ |
+sub implies { |
+ # Implication test are not useful on Union |
+ return; |
+} |
+ |
+sub get_evaluation { |
+ # Evaluation are not useful on Union |
+ return; |
+} |
+ |
+sub simplify_deps { |
+ my ($self, $facts) = @_; |
+ my @new; |
+ |
+WHILELOOP: |
+ while (@{$self->{list}}) { |
+ my $odep = shift @{$self->{list}}; |
+ foreach my $dep (@new) { |
+ next WHILELOOP if $dep->merge_union($odep); |
+ } |
+ push @new, $odep; |
+ } |
+ $self->{list} = [ @new ]; |
+} |
+ |
+package Dpkg::Deps::KnownFacts; |
+ |
+=head2 Dpkg::Deps::KnownFacts |
+ |
+This object represents a list of installed packages and a list of virtual |
+packages provided (by the set of installed packages). |
+ |
+=over 4 |
+ |
+=item my $facts = Dpkg::Deps::KnownFacts->new(); |
+ |
+Creates a new object. |
+ |
+=cut |
+ |
+use strict; |
+use warnings; |
+ |
+use Dpkg::Version; |
+ |
+sub new { |
+ my $this = shift; |
+ my $class = ref($this) || $this; |
+ my $self = { |
+ pkg => {}, |
+ virtualpkg => {}, |
+ }; |
+ bless $self, $class; |
+ return $self; |
+} |
+ |
+=item $facts->add_installed_package($package, $version, $arch, $multiarch) |
+ |
+Records that the given version of the package is installed. If |
+$version/$arch is undefined we know that the package is installed but we |
+don't know which version/architecture it is. $multiarch is the Multi-Arch |
+field of the package. If $multiarch is undef, it will be equivalent to |
+"Multi-Arch: no". |
+ |
+Note that $multiarch is only used if $arch is provided. |
+ |
+=cut |
+ |
+sub add_installed_package { |
+ my ($self, $pkg, $ver, $arch, $multiarch) = @_; |
+ my $p = { |
+ package => $pkg, |
+ version => $ver, |
+ architecture => $arch, |
+ multiarch => $multiarch || 'no', |
+ }; |
+ $self->{pkg}{"$pkg:$arch"} = $p if defined $arch; |
+ push @{$self->{pkg}{$pkg}}, $p; |
+} |
+ |
+=item $facts->add_provided_package($virtual, $relation, $version, $by) |
+ |
+Records that the "$by" package provides the $virtual package. $relation |
+and $version correspond to the associated relation given in the Provides |
+field. This might be used in the future for versioned provides. |
+ |
+=cut |
+ |
+sub add_provided_package { |
+ my ($self, $pkg, $rel, $ver, $by) = @_; |
+ if (not exists $self->{virtualpkg}{$pkg}) { |
+ $self->{virtualpkg}{$pkg} = []; |
+ } |
+ push @{$self->{virtualpkg}{$pkg}}, [ $by, $rel, $ver ]; |
+} |
+ |
+=item my ($check, $param) = $facts->check_package($package) |
+ |
+$check is one when the package is found. For a real package, $param |
+contains the version. For a virtual package, $param contains an array |
+reference containing the list of packages that provide it (each package is |
+listed as [ $provider, $relation, $version ]). |
+ |
+This function is obsolete and should not be used. Dpkg::Deps::KnownFacts |
+is only meant to be filled with data and then passed to Dpkg::Deps |
+methods where appropriate, but it should not be directly queried. |
+ |
+=back |
+ |
+=cut |
+ |
+sub check_package { |
+ my ($self, $pkg) = @_; |
+ if (exists $self->{pkg}{$pkg}) { |
+ return (1, $self->{pkg}{$pkg}[0]{version}); |
+ } |
+ if (exists $self->{virtualpkg}{$pkg}) { |
+ return (1, $self->{virtualpkg}{$pkg}); |
+ } |
+ return (0, undef); |
+} |
+ |
+## The functions below are private to Dpkg::Deps |
+ |
+sub _find_package { |
+ my ($self, $dep, $lackinfos) = @_; |
+ my ($pkg, $archqual) = ($dep->{package}, $dep->{archqual}); |
+ return if not exists $self->{pkg}{$pkg}; |
+ my $host_arch = $dep->{host_arch}; |
+ my $build_arch = $dep->{build_arch}; |
+ foreach my $p (@{$self->{pkg}{$pkg}}) { |
+ my $a = $p->{architecture}; |
+ my $ma = $p->{multiarch}; |
+ if (not defined $a) { |
+ $$lackinfos = 1; |
+ next; |
+ } |
+ if (not defined $archqual) { |
+ return $p if $ma eq 'foreign'; |
+ return $p if $a eq $host_arch or $a eq 'all'; |
+ } elsif ($archqual eq 'any') { |
+ return $p if $ma eq 'allowed'; |
+ } elsif ($archqual eq 'native') { |
+ return $p if $a eq $build_arch and $ma ne 'foreign'; |
+ } else { |
+ return $p if $a eq $archqual; |
+ } |
+ } |
+ return; |
+} |
+ |
+sub _find_virtual_packages { |
+ my ($self, $pkg) = @_; |
+ return () if not exists $self->{virtualpkg}{$pkg}; |
+ return @{$self->{virtualpkg}{$pkg}}; |
+} |
+ |
+sub _evaluate_simple_dep { |
+ my ($self, $dep) = @_; |
+ my ($lackinfos, $pkg) = (0, $dep->{package}); |
+ my $p = $self->_find_package($dep, \$lackinfos); |
+ if ($p) { |
+ if (defined $dep->{relation}) { |
+ if (defined $p->{version}) { |
+ return 1 if version_compare_relation($p->{version}, |
+ $dep->{relation}, $dep->{version}); |
+ } else { |
+ $lackinfos = 1; |
+ } |
+ } else { |
+ return 1; |
+ } |
+ } |
+ foreach my $virtpkg ($self->_find_virtual_packages($pkg)) { |
+ # XXX: Adapt when versioned provides are allowed |
+ next if defined $virtpkg->[1]; |
+ next if defined $dep->{relation}; # Provides don't satisfy versioned deps |
+ return 1; |
+ } |
+ return if $lackinfos; |
+ return 0; |
+} |
+ |
+=head1 CHANGES |
+ |
+=head2 Version 1.02 |
+ |
+=over |
+ |
+=item * Add new Dpkg::deps_concat() function. |
+ |
+=back |
+ |
+=head2 Version 1.01 |
+ |
+=over |
+ |
+=item * Add new $dep->reset() method that all dependency objects support. |
+ |
+=item * Dpkg::Deps::Simple now recognizes the arch qualifier "any" and |
+stores it in the "archqual" property when present. |
+ |
+=item * Dpkg::Deps::KnownFacts->add_installed_package() now accepts 2 |
+supplementary parameters ($arch and $multiarch). |
+ |
+=item * Dpkg::Deps::KnownFacts->check_package() is obsolete, it should |
+not have been part of the public API. |
+ |
+=back |
+ |
+=cut |
+ |
+1; |