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;

Reply via email to