[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

[PATCH] Support :any architecture qualifiers for multiarch



(CCing debian-python in case anyone is wondering what those spurious
entries for Python packages in update_excuses.html are about.)

Multiarch adds a Depends: foo:any syntax, permitted only if the target
of the dependency is "Multi-Arch: allowed" [1].  This has been supported
by dpkg and apt for some time and is now safe to use in unstable.
However, britney does not yet understand it.  dh-python recently started
using this syntax [2], and as a result we're seeing lots of this kind of
thing in excuses:

  i18nspector/i386 unsatisfiable Depends: python3:any (>= 3.2.3-3~)

I've fixed this for Ubuntu's britney instance with the following patch.
I'm afraid I haven't had a chance to rebase it on top of Debian or to
test it there, but that should hopefully not be very much work, and
certainly easier than recreating all this from scratch ...

[1] https://wiki.ubuntu.com/MultiarchSpec#Extended_semantics_of_per-architecture_package_relationships
[2] http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=722045

=== modified file 'britney.py'
--- britney.py	2013-09-09 11:37:52 +0000
+++ britney.py	2013-09-16 13:25:37 +0000
@@ -230,14 +230,15 @@ FAKESRC = 4
 SOURCE = 2
 SOURCEVER = 3
 ARCHITECTURE = 4
-# PREDEPENDS = 5 - No longer used by the python code
+MULTIARCH = 5
+# PREDEPENDS = 6 - No longer used by the python code
 #  - The C-code needs it for alignment reasons and still check it
 #    but ignore it if it is None (so keep it None).
-DEPENDS = 6
-CONFLICTS = 7
-PROVIDES = 8
-RDEPENDS = 9
-RCONFLICTS = 10
+DEPENDS = 7
+CONFLICTS = 8
+PROVIDES = 9
+RDEPENDS = 10
+RCONFLICTS = 11
 
 
 class Britney(object):
@@ -546,6 +547,7 @@ class Britney(object):
                     pkg, 
                     version,
                     get_field('Architecture'),
+                    get_field('Multi-Arch'),
                     None, # Pre-depends - leave as None for the C-code
                     deps,
                     ', '.join(final_conflicts_list) or None,
@@ -1042,7 +1044,7 @@ class Britney(object):
             binaries = self.binaries[suite][arch][0]
             for pkg in binaries:
                 output = "Package: %s\n" % pkg
-                for key, k in ((SECTION, 'Section'), (ARCHITECTURE, 'Architecture'), (SOURCE, 'Source'), (VERSION, 'Version'), 
+                for key, k in ((SECTION, 'Section'), (ARCHITECTURE, 'Architecture'), (MULTIARCH, 'Multi-Arch'), (SOURCE, 'Source'), (VERSION, 'Version'), 
                           (DEPENDS, 'Depends'), (PROVIDES, 'Provides'), (CONFLICTS, 'Conflicts')):
                     if not binaries[pkg][key]: continue
                     if key == SOURCE:
@@ -1138,12 +1140,19 @@ class Britney(object):
 
         # for every package, version and operation in the block
         for name, version, op in block:
+            if ":" in name:
+                name, archqual = name.split(":", 1)
+            else:
+                archqual = None
+
             # look for the package in unstable
             if name in binaries[0]:
                 package = binaries[0][name]
-                # check the versioned dependency (if present)
+                # check the versioned dependency and architecture qualifier
+                # (if present)
                 if op == '' and version == '' or apt_pkg.check_dep(package[VERSION], op, version):
-                    packages.append(name)
+                    if archqual is None or (archqual == 'any' and package[MULTIARCH] == 'allowed'):
+                        packages.append(name)
 
             # look for the package in the virtual packages list and loop on them
             for prov in binaries[1].get(name, []):
@@ -1151,7 +1160,9 @@ class Britney(object):
                 package = binaries[0][prov]
                 # A provides only satisfies an unversioned dependency
                 # (per Policy Manual §7.5)
-                if op == '' and version == '':
+                # A provides only satisfies a dependency without an
+                # architecture qualifier (per analysis of apt code)
+                if op == '' and version == '' and archqual is None:
                     packages.append(prov)
 
         return (len(packages) > 0, packages)

=== modified file 'lib/britney-py.c'
--- lib/britney-py.c	2012-10-22 12:43:51 +0000
+++ lib/britney-py.c	2013-09-16 12:28:15 +0000
@@ -107,22 +107,28 @@ static PyObject *dpkgpackages_add_binary
     pyString = PyList_GetItem(value, 5);
     if (pyString == NULL) return NULL;
     if (pyString != Py_None) {
+        pkg->multiarch = PyString_AsString(pyString);
+    } else pkg->multiarch = NULL;
+
+    pyString = PyList_GetItem(value, 6);
+    if (pyString == NULL) return NULL;
+    if (pyString != Py_None) {
         pkg->depends[0] = read_dep_andor(PyString_AsString(pyString));
     } else pkg->depends[0] = NULL;
 
-    pyString = PyList_GetItem(value, 6);
+    pyString = PyList_GetItem(value, 7);
     if (pyString == NULL) return NULL;
     if (pyString != Py_None) {
         pkg->depends[1] = read_dep_andor(PyString_AsString(pyString));
     } else pkg->depends[1] = NULL;
 
-    pyString = PyList_GetItem(value, 7);
+    pyString = PyList_GetItem(value, 8);
     if (pyString == NULL) return NULL;
     if (pyString != Py_None) {
         pkg->conflicts = read_dep_and(PyString_AsString(pyString));
     } else pkg->conflicts = NULL;
 
-    pyString = PyList_GetItem(value, 8);
+    pyString = PyList_GetItem(value, 9);
     if (pyString == NULL) return NULL;
     if (pyString != Py_None) {
         pkg->provides = read_packagenames(PyString_AsString(pyString));
@@ -204,12 +210,13 @@ static PyObject *build_system(PyObject *
        # SOURCE = 2
        # SOURCEVER = 3
        # ARCHITECTURE = 4
-       # PREDEPENDS = 5
-       # DEPENDS = 6
-       # CONFLICTS = 7
-       # PROVIDES = 8
-       # RDEPENDS = 9
-       # RCONFLICTS = 10
+       # MULTIARCH = 5
+       # PREDEPENDS = 6
+       # DEPENDS = 7
+       # CONFLICTS = 8
+       # PROVIDES = 9
+       # RDEPENDS = 10
+       # RCONFLICTS = 11
     */
 
     dpkg_packages *dpkg_pkgs = new_packages(arch);
@@ -245,22 +252,28 @@ static PyObject *build_system(PyObject *
         pyString = PyList_GetItem(value, 5);
         if (pyString == NULL) continue;
         if (pyString != Py_None) {
+            pkg->multiarch = PyString_AsString(pyString);
+        } else pkg->multiarch = NULL;
+
+        pyString = PyList_GetItem(value, 6);
+        if (pyString == NULL) continue;
+        if (pyString != Py_None) {
             pkg->depends[0] = read_dep_andor(PyString_AsString(pyString));
         } else pkg->depends[0] = NULL;
 
-        pyString = PyList_GetItem(value, 6);
+        pyString = PyList_GetItem(value, 7);
         if (pyString == NULL) continue;
         if (pyString != Py_None) {
             pkg->depends[1] = read_dep_andor(PyString_AsString(pyString));
         } else pkg->depends[1] = NULL;
 
-        pyString = PyList_GetItem(value, 7);
+        pyString = PyList_GetItem(value, 8);
         if (pyString == NULL) continue;
         if (pyString != Py_None) {
             pkg->conflicts = read_dep_and(PyString_AsString(pyString));
         } else pkg->conflicts = NULL;
 
-        pyString = PyList_GetItem(value, 8);
+        pyString = PyList_GetItem(value, 9);
         if (pyString == NULL) continue;
         if (pyString != Py_None) {
             pkg->provides = read_packagenames(PyString_AsString(pyString));

=== modified file 'lib/dpkg.c'
--- lib/dpkg.c	2012-03-05 12:13:21 +0000
+++ lib/dpkg.c	2013-09-16 12:22:59 +0000
@@ -24,7 +24,8 @@ static collpackagelist *get_matching(dpk
 static deplist *read_deplist(char **buf, char sep, char end);
 static dependency *read_dependency(char **buf, char *end);
 static void add_virtualpackage(virtualpkgtbl *vpkgs, char *package, 
-                               char *version, dpkg_collected_package *cpkg);
+                               char *version, char *multiarch,
+                               dpkg_collected_package *cpkg);
 static void remove_virtualpackage(virtualpkgtbl *vpkgs, char *pkgname,
 			          dpkg_collected_package *cpkg);
 static char *read_packagename(char **buf, char *end);
@@ -177,9 +178,9 @@ void add_package(dpkg_packages *pkgs, dp
     add_packagetbl(pkgs->packages, cpkg->pkg->package, cpkg);
 	
     add_virtualpackage(pkgs->virtualpkgs, cpkg->pkg->package, 
-		       cpkg->pkg->version, cpkg);
+		       cpkg->pkg->version, cpkg->pkg->multiarch, cpkg);
     for (v = cpkg->pkg->provides; v != NULL; v = v->next) {
-	add_virtualpackage(pkgs->virtualpkgs, v->value, NULL, cpkg);
+	add_virtualpackage(pkgs->virtualpkgs, v->value, NULL, NULL, cpkg);
     }
 }
 
@@ -246,7 +247,8 @@ static void remove_virtualpackage(virtua
 }
 
 static void add_virtualpackage(virtualpkgtbl *vpkgs, char *package, 
-                               char *version, dpkg_collected_package *cpkg)
+                               char *version, char *multiarch,
+                               dpkg_collected_package *cpkg)
 {
     dpkg_provision value;
     virtualpkg *list, **addto;
@@ -254,6 +256,7 @@ static void add_virtualpackage(virtualpk
    
     value.pkg = cpkg;
     value.version = version;
+    value.multiarch = multiarch;
     
     list = lookup_virtualpkgtbl(vpkgs, package);
     shouldreplace = (list != NULL);
@@ -398,11 +401,11 @@ deplistlist *read_dep_andor(char *buf) {
 static dependency *read_dependency(char **buf, char *end) {
     dependency *dep;
     char *name;
-    char newend[10];
+    char newend[11];
     DEBUG_ONLY( char *strend = *buf + strlen(*buf); )
     
     assert(strlen(end) <= 8);
-    newend[0] = '('; strcpy(newend + 1, end);
+    newend[0] = '('; newend[1] = ':'; strcpy(newend + 2, end);
     
     name = my_strdup(read_until_char(buf, newend));
     if (name == NULL) return NULL;
@@ -411,6 +414,13 @@ static dependency *read_dependency(char
     if (dep == NULL) die("read_dependency alloc 1:");
     
     dep->package = name;
+
+    if (**buf == ':') {
+	(*buf)++;
+	dep->archqual = my_strdup(read_until_char(buf, newend));
+	if (dep->archqual == NULL) return NULL;
+    } else
+	dep->archqual = NULL;
     
     while(isspace(**buf)) (*buf)++;
     
@@ -465,7 +475,7 @@ static dependency *read_dependency(char
 	}
 	
 	while (isspace(**buf)) (*buf)++;
-	newend[0] = ')';
+	newend[0] = ')'; strcpy(newend + 1, end);
 	dep->version = my_strdup(read_until_char(buf, newend));
 	while (isspace(**buf)) (*buf)++;
 	
@@ -509,6 +519,14 @@ static collpackagelist **get_matching_lo
 	    }
 	}
 
+	if (dep->archqual != NULL) {
+	    if (strcmp(dep->archqual, "any") == 0) {
+		if (strcmp(vpkg->value.multiarch, "allowed") != 0)
+		    add = 0;
+	    } else
+		add = 0;
+	}
+
 	if (add) {
 	    insert_l_collpackagelist(addto, vpkg->value.pkg, line);
 	    addto = &(*addto)->next;

=== modified file 'lib/dpkg.h'
--- lib/dpkg.h	2011-12-27 10:39:57 +0000
+++ lib/dpkg.h	2013-09-16 10:25:34 +0000
@@ -33,6 +33,7 @@ extern char *dependency_relation_sym[];
 typedef struct dependency dependency;
 struct dependency {
     char *package;
+    char *archqual;
     dependency_relation op;
     char *version;
 };
@@ -48,6 +49,7 @@ typedef struct dpkg_package dpkg_package
 struct dpkg_package {
     char *package;
     char *version;
+    char *multiarch;
     
     char *source;
     char *source_ver;
@@ -102,6 +104,7 @@ LIST(collpackagelist, dpkg_collected_pac
 typedef struct dpkg_provision dpkg_provision;
 struct dpkg_provision {
     char *version;
+    char *multiarch;
     dpkg_collected_package *pkg;
 };
 

=== modified file 'lib/example.py'
--- lib/example.py	2006-08-20 19:25:21 +0000
+++ lib/example.py	2013-09-09 11:37:56 +0000
@@ -8,20 +8,21 @@ import britney
 # SOURCE = 2
 # SOURCEVER = 3
 # ARCHITECTURE = 4
-# PREDEPENDS = 5
-# DEPENDS = 6
-# CONFLICTS = 7
-# PROVIDES = 8
-# RDEPENDS = 9
-# RCONFLICTS = 10
+# MULTIARCH = 5
+# PREDEPENDS = 6
+# DEPENDS = 7
+# CONFLICTS = 8
+# PROVIDES = 9
+# RDEPENDS = 10
+# RCONFLICTS = 11
 
-packages = {'phpldapadmin': ['1.0', 'web', 'phpldapadmin', '1.0', 'all', '', 'apache2 (>= 2.0)', '', '', [], []],
-            'apache2': ['2.0', 'web', 'apache2', '2.0', 'i386', '', '', 'phpldapadmin (<= 1.0~)', '', [], []],
+packages = {'phpldapadmin': ['1.0', 'web', 'phpldapadmin', '1.0', 'all', None, '', 'apache2 (>= 2.0)', '', '', [], []],
+            'apache2': ['2.0', 'web', 'apache2', '2.0', 'i386', None, '', '', 'phpldapadmin (<= 1.0~)', '', [], []],
            }
 
 system = britney.buildSystem('i386', packages)
 print system.is_installable('phpldapadmin'), system.packages
 system.remove_binary('apache2')
 print system.is_installable('phpldapadmin'), system.packages
-system.add_binary('apache2', ['2.0', 'web', 'apache2', '2.0', 'i386', '', '', 'phpldapadmin (<= 1.0~)', '', [], []])
+system.add_binary('apache2', ['2.0', 'web', 'apache2', '2.0', 'i386', None, '', '', 'phpldapadmin (<= 1.0~)', '', [], []])
 print system.is_installable('phpldapadmin'), system.packages


Thanks,

-- 
Colin Watson                                       [cjwatson@ubuntu.com]


Reply to: