On Sun, Apr 16, 2006 at 09:34:43PM +0200, [EMAIL PROTECTED] wrote:
> On Sun, Apr 16, 2006 at 01:28:21AM +0200, Nicolas François wrote:
> > It only simplifies the Depends field, but I think it should be sufficient.
> > (I assumed there is no automatic variables for the other dependency
> > fields; this can be changed easily)
>
> Please do. There is no reason to handle the different fields
> differently.
So here is the new patch.
It differentiates 3 categories of fields:
* Depends|Pre-Depends
Compute an intersection
* Replaces|Conflicts|Provides
Compute an union
* Recommends|Suggests|Enhances
Only merge the dependencies if they are identical
Kind Regards,
--
Nekral
Index: controllib.pl
===================================================================
--- controllib.pl (révision 239)
+++ controllib.pl (copie de travail)
@@ -168,6 +168,7 @@
$v =~ s/\s*,\s*$//;
}
$v =~ s/\$\{\}/\$/g;
+ $v = simplify_deps($f, $v);
print("$f: $v\n") || &syserr("write error on control data");
}
@@ -409,4 +410,288 @@
}
}
+# Simplify a dependency line by merging the dependencies on the same
+# package.
+sub simplify_deps {
+ my $dep_field=shift;
+ my $dep_line=shift;
+ my $dep_merger;
+ if ($dep_field =~ m/^(Depends|Pre-Depends)$/) {
+ $dep_merger = \&merge_depends;
+ } elsif ($dep_field =~ m/^(Replaces|Conflicts|Provides)$/) {
+ $dep_merger = \&merge_conflicts;
+ } elsif ($dep_field =~ m/^(Recommends|Suggests|Enhances)$/) {
+ $dep_merger = \&merge_identical;
+ } else {
+ return $dep_line;
+ }
+
+ my $dep_list = parsedep($dep_line, 1, 0);
+
+ # %pkg_dep keeps the versionned dependency associated with each package
+ my %pkg_dep;
+ # When the dependency can't be merged, it is pushed in @new_dep_list.
+ # Dependencies with a list of archs or an OR list are also push in
+ # this array.
+ my @new_dep_list;
+ foreach my $dep_and (@$dep_list) {
+ my $new_dep_and;
+ if (scalar(@$dep_and) > 1) {
+ # ORed list
+ push @new_dep_list, $dep_and;
+ } else {
+ my $alternate = [EMAIL PROTECTED];
+ my ($package, $relation, $version, $arch_list)= @{$alternate};
+ if (scalar(@$arch_list)) {
+ # Dependency with a list of archs
+ push @new_dep_list, $dep_and;
+ } else {
+ # Merge this dependency with the previous dependency of
+ # this package
+ my @versioned_deps = ($relation, $version);
+ my @new_deps;
+ @new_deps= &$dep_merger($pkg_dep{$package},
+ [EMAIL PROTECTED]);
+ if ((scalar @new_deps)>1) {
+ # The dependency could not be merged. Push both
+ # dependencies in @new_dep_list.
+ delete $pkg_dep{$package};
+ my @arch_list = ();
+ foreach (@new_deps) {
+ my @new_alternate = ($package, [EMAIL PROTECTED],
+ [EMAIL PROTECTED], [EMAIL
PROTECTED]);
+ my @or_list = ([EMAIL PROTECTED]);
+ push @new_dep_list, [EMAIL PROTECTED];
+ }
+ } else {
+ $pkg_dep{$package} = $new_deps[0];
+ }
+ }
+ }
+ }
+ # Push the merged dependencies
+ foreach my $pkg (keys %pkg_dep) {
+ my @versioned_deps = @{$pkg_dep{$pkg}};
+ my @arch_list = ();
+ my @new_alternate = ($pkg, $versioned_deps[0],
+ $versioned_deps[1], [EMAIL PROTECTED]);
+ my @or_list = ([EMAIL PROTECTED]);
+ push @new_dep_list, [EMAIL PROTECTED];
+ }
+
+ return showdep([EMAIL PROTECTED], 1);
+}
+
+# Merge two Depends dependencies on the same package.
+# (intersection of intervals)
+# The dependencies are provided as a reference to a (relation, version)
+# array.
+#
+# Return a reference to an array.
+# If the dependencies can be merged, this array will have one element: a
+# reference to an (relation, version) array.
+sub merge_depends {
+ my $versioned_dep1 = shift;
+ my $versioned_dep2 = shift;
+ my ($relation1, $version1) = @$versioned_dep1;
+ my ($relation2, $version2) = @$versioned_dep2;
+
+ my @result = ();
+ return $versioned_dep2 if ( not defined $versioned_dep1
+ or not defined $relation1);
+ return $versioned_dep1 if ( not defined $versioned_dep2
+ or not defined $relation2);
+
+ my @default = ($versioned_dep1, $versioned_dep2);
+
+ if ($relation1 =~ m/</ and $relation2 =~ m/</) {
+ if (cmp_version($version1, $version2) < 0) {
+ @result = ($relation1, $version1);
+ } elsif ($version1 eq $version2) {
+ if ($relation1 eq "<<" or $relation2 eq "<<") {
+ @result = ("<<", $version1);
+ } else {
+ @result = ("<=", $version1);
+ }
+ } else {
+ @result = ($relation2, $version2);
+ }
+ return ([EMAIL PROTECTED]);
+ } elsif ($relation1 eq "=" or $relation2 eq "=") {
+ # Make sure the "=" relation is the first dependency
+ if ($relation2 eq "=") {
+ ($relation1, $relation2) = ($relation2, $relation1);
+ ($version1, $version2) = ($version2, $version1);
+ }
+
+ my $cmp = cmp_version($version1, $version2);
+ # Detect unsatifiable conditions
+ if ($relation2 eq "<<") {
+ return @default if ($cmp >= 0);
+ } elsif ($relation2 eq "<=" or $relation2 eq "<") {
+ return @default if ($cmp > 0);
+ } elsif ($relation2 eq "=") {
+ return @default if ($cmp != 0);
+ } elsif ($relation2 eq ">=" or $relation2 eq ">") {
+ return @default if ($cmp < 0);
+ } elsif ($relation2 eq ">>") {
+ return @default if ($cmp <= 0);
+ } else {
+ return @default;
+ }
+ # Otherwise, just return the dependency on a fixed version
+ @result = ($relation1, $version1);
+ return ([EMAIL PROTECTED]);
+ } elsif ($relation1 =~ m/>/ and $relation2 =~ m/>/) {
+ if (cmp_version($version1, $version2) > 0) {
+ @result = ($relation1, $version1);
+ } elsif ($version1 eq $version2) {
+ if ($relation1 eq ">>" or $relation2 eq ">>") {
+ @result = (">>", $version1);
+ } else {
+ @result = (">=", $version1);
+ }
+ } else {
+ @result = ($relation2, $version2);
+ }
+ return ([EMAIL PROTECTED]);
+ } else {
+ # Either unsatisfiable or a bounded interval
+ return @default;
+ }
+}
+
+# Merge two Conflicts dependencies on the same package.
+# (union of intervals)
+# The dependencies are provided as a reference to a (relation, version)
+# array.
+#
+# Return a reference to an array.
+# If the dependencies can be merged, this array will have one element: a
+# reference to an (relation, version) array.
+sub merge_conflicts {
+ my $versioned_dep1 = shift;
+ my $versioned_dep2 = shift;
+ my ($relation1, $version1) = @$versioned_dep1;
+ my ($relation2, $version2) = @$versioned_dep2;
+
+ my @result = ();
+ return $versioned_dep2 if (not defined $versioned_dep1);
+ return $versioned_dep1 if (not defined $versioned_dep2);
+ return $versioned_dep1 if (not defined $relation1);
+ return $versioned_dep2 if (not defined $relation2);
+
+ my @default = ($versioned_dep1, $versioned_dep2);
+
+ if ($relation1 =~ m/</ and $relation2 =~ m/</) {
+ if (cmp_version($version1, $version2) < 0) {
+ @result = ($relation2, $version2);
+ } elsif ($version1 eq $version2) {
+ if ($relation1 eq "<<" and $relation2 eq "<<") {
+ @result = ("<<", $version1);
+ } else {
+ @result = ("<=", $version1);
+ }
+ } else {
+ @result = ($relation1, $version1);
+ }
+ return ([EMAIL PROTECTED]);
+ } elsif ($relation1 eq "=" or $relation2 eq "=") {
+ # Make sure the "=" relation is the first dependency
+ if ($relation2 eq "=") {
+ ($relation1, $relation2) = ($relation2, $relation1);
+ ($version1, $version2) = ($version2, $version1);
+ }
+
+ my $cmp = cmp_version($version1, $version2);
+ # Detect unsatifiable conditions
+ if ($relation2 eq "<<") {
+ return @default if ($cmp > 0);
+ $relation2="<=" if ($cmp == 0);
+ } elsif ($relation2 eq "<=" or $relation2 eq "<") {
+ return @default if ($cmp > 0);
+ } elsif ($relation2 eq "=") {
+ return @default if ($cmp != 0);
+ } elsif ($relation2 eq ">=" or $relation2 eq ">") {
+ return @default if ($cmp < 0);
+ } elsif ($relation2 eq ">>") {
+ return @default if ($cmp < 0);
+ $relation2=">=" if ($cmp == 0);
+ } else {
+ return @default;
+ }
+ # Otherwise, just return the dependency on a fixed version
+ @result = ($relation2, $version2);
+ return ([EMAIL PROTECTED]);
+ } elsif ($relation1 =~ m/>/ and $relation2 =~ m/>/) {
+ if (cmp_version($version1, $version2) > 0) {
+ @result = ($relation2, $version2);
+ } elsif ($version1 eq $version2) {
+ if ($relation1 eq ">>" and $relation2 eq ">>") {
+ @result = (">>", $version1);
+ } else {
+ @result = (">=", $version1);
+ }
+ } else {
+ @result = ($relation1, $version1);
+ }
+ return ([EMAIL PROTECTED]);
+ } else {
+ # Make sure the >, >= or >> relation is the first dependency
+ if ($relation2 =~ m/>/) {
+ ($relation1, $relation2) = ($relation2, $relation1);
+ ($version1, $version2) = ($version2, $version1);
+ }
+
+ # In some cases we can't conclude (unconnected intervals)
+ if (cmp_version($version1, $version2) > 0) {
+ return @default;
+ } elsif ( ($version1 eq $version2)
+ and ($relation1 eq ">>" and $relation2 eq "<<")) {
+ return @default;
+ }
+
+ # Otherwise, it's an unversionned conflict
+ undef @result;
+ return ([EMAIL PROTECTED]);
+ }
+
+ return @default; # not reached
+}
+
+# Only merge two dependencies if they are identical.
+sub merge_identical {
+ my $versioned_dep1 = shift;
+ my $versioned_dep2 = shift;
+ my ($relation1, $version1) = @$versioned_dep1;
+ my ($relation2, $version2) = @$versioned_dep2;
+
+ my @result = ();
+ return $versioned_dep2 if (not defined $versioned_dep1);
+ return $versioned_dep1 if (not defined $versioned_dep2);
+
+ my @default = ($versioned_dep1, $versioned_dep2);
+
+ if ( ($version1 eq $version2)
+ and ($relation1 eq $relation2)) {
+ my @result = ($relation1, $version1);
+ return ([EMAIL PROTECTED]);
+ }
+
+ return @default;
+}
+
+
+# Compare two package versions.
+# Return an integer less than, equal to, or greater than zero if the first
+# version is found, respectively, to be less than, to match, or be greater
+# than the second one.
+sub cmp_version {
+ my ($v1, $v2) = (shift, shift);
+
+ return 0 if ($v1 eq $v2);
+ system("dpkg", "--compare-versions", $v1, "lt", $v2);
+ return (($? >> 8) != 0)?1:-1;
+}
+
1;