]> git.decadent.org.uk Git - dak.git/blobdiff - daklib/queue.py
Merge remote-tracking branch 'ansgar/package-set' into merge
[dak.git] / daklib / queue.py
index b3e26d7bed50945dc1a47b4cf525c0fb7f309a64..84ac6c984e5389f253bd58c68b86097a901ca7e6 100755 (executable)
@@ -51,15 +51,16 @@ from holding import Holding
 from urgencylog import UrgencyLog
 from dbconn import *
 from summarystats import SummaryStats
-from utils import parse_changes, check_dsc_files
+from utils import parse_changes, check_dsc_files, build_package_set
 from textutils import fix_maintainer
 from lintian import parse_lintian_output, generate_reject_messages
+from contents import UnpackedSource
 
 # suppress some deprecation warnings in squeeze related to apt_pkg
 # module
 import warnings
 warnings.filterwarnings('ignore', \
-    "apt_pkg-.ParseSection\(\) is deprecated. Please see apt_pkg\.TagSection\(\) for the replacement\.", \
+    "apt_pkg.ParseSection\(\) is deprecated. Please see apt_pkg\.TagSection\(\) for the replacement\.", \
     DeprecationWarning)
 warnings.filterwarnings('ignore', \
     "Attribute 'Find' of the 'apt_pkg\.TagSection' object is deprecated, use 'find' instead\.", \
@@ -101,7 +102,7 @@ def get_type(f, session):
 
 # Determine what parts in a .changes are NEW
 
-def determine_new(filename, changes, files, warn=1, session = None):
+def determine_new(filename, changes, files, warn=1, session = None, dsc = None, new = {}):
     """
     Determine what parts in a C{changes} file are NEW.
 
@@ -117,19 +118,30 @@ def determine_new(filename, changes, files, warn=1, session = None):
     @type warn: bool
     @param warn: Warn if overrides are added for (old)stable
 
+    @type dsc: Upload.Pkg.dsc dict
+    @param dsc: (optional); Dsc dictionary
+
+    @type new: dict
+    @param new: new packages as returned by a previous call to this function, but override information may have changed
+
     @rtype: dict
     @return: dictionary of NEW components.
 
     """
     # TODO: This should all use the database instead of parsing the changes
     # file again
-    new = {}
     byhand = {}
 
     dbchg = get_dbchange(filename, session)
     if dbchg is None:
         print "Warning: cannot find changes file in database; won't check byhand"
 
+    # Try to get the Package-Set field from an included .dsc file (if possible).
+    if dsc:
+        for package, entry in build_package_set(dsc, session).items():
+            if not new.has_key(package):
+                new[package] = entry
+
     # Build up a list of potentially new things
     for name, f in files.items():
         # Keep a record of byhand elements
@@ -772,6 +784,30 @@ class Upload(object):
                 if not re_valid_pkg_name.match(prov):
                     self.rejects.append("%s: Invalid Provides field content %s." % (f, prov))
 
+        # If there is a Built-Using field, we need to check we can find the
+        # exact source version
+        built_using = control.Find("Built-Using")
+        if built_using:
+            try:
+                entry["built-using"] = []
+                for dep in apt_pkg.parse_depends(built_using):
+                    bu_s, bu_v, bu_e = dep[0]
+                    # Check that it's an exact match dependency and we have
+                    # some form of version
+                    if bu_e != "=" or len(bu_v) < 1:
+                        self.rejects.append("%s: Built-Using contains non strict dependency (%s %s %s)" % (f, bu_s, bu_e, bu_v))
+                    else:
+                        # Find the source id for this version
+                        bu_so = get_sources_from_name(bu_s, version=bu_v, session = session)
+                        if len(bu_so) != 1:
+                            self.rejects.append("%s: Built-Using (%s = %s): Cannot find source package" % (f, bu_s, bu_v))
+                        else:
+                            entry["built-using"].append( (bu_so[0].source, bu_so[0].version, ) )
+
+            except ValueError, e:
+                self.rejects.append("%s: Cannot parse Built-Using field: %s" % (f, str(e)))
+
+
         # Check the section & priority match those given in the .changes (non-fatal)
         if     control.Find("Section") and entry["section"] != "" \
            and entry["section"] != control.Find("Section"):
@@ -1080,40 +1116,76 @@ class Upload(object):
                 self.rejects.append("source only uploads are not supported.")
 
     ###########################################################################
-    def check_dsc(self, action=True, session=None):
-        """Returns bool indicating whether or not the source changes are valid"""
-        # Ensure there is source to check
-        if not self.pkg.changes["architecture"].has_key("source"):
-            return True
 
-        # Find the .dsc
+    def __dsc_filename(self):
+        """
+        Returns: (Status, Dsc_Filename)
+        where
+          Status: Boolean; True when there was no error, False otherwise
+          Dsc_Filename: String; name of the dsc file if Status is True, reason for the error otherwise
+        """
         dsc_filename = None
-        for f, entry in self.pkg.files.items():
-            if entry["type"] == "dsc":
+
+        # find the dsc
+        for name, entry in self.pkg.files.items():
+            if entry.has_key("type") and entry["type"] == "dsc":
                 if dsc_filename:
-                    self.rejects.append("can not process a .changes file with multiple .dsc's.")
-                    return False
+                    return False, "cannot process a .changes file with multiple .dsc's."
                 else:
-                    dsc_filename = f
+                    dsc_filename = name
 
-        # If there isn't one, we have nothing to do. (We have reject()ed the upload already)
         if not dsc_filename:
-            self.rejects.append("source uploads must contain a dsc file")
-            return False
+            return False, "source uploads must contain a dsc file"
+
+        return True, dsc_filename
+
+    def load_dsc(self, action=True, signing_rules=1):
+        """
+        Find and load the dsc from self.pkg.files into self.dsc
+
+        Returns: (Status, Reason)
+        where
+          Status: Boolean; True when there was no error, False otherwise
+          Reason: String; When Status is False this describes the error
+        """
+
+        # find the dsc
+        (status, dsc_filename) = self.__dsc_filename()
+        if not status:
+            # If status is false, dsc_filename has the reason
+            return False, dsc_filename
 
-        # Parse the .dsc file
         try:
-            self.pkg.dsc.update(utils.parse_changes(dsc_filename, signing_rules=1, dsc_file=1))
+            self.pkg.dsc.update(utils.parse_changes(dsc_filename, signing_rules=signing_rules, dsc_file=1))
         except CantOpenError:
-            # if not -n copy_to_holding() will have done this for us...
             if not action:
-                self.rejects.append("%s: can't read file." % (dsc_filename))
+                return False, "%s: can't read file." % (dsc_filename)
         except ParseChangesError, line:
-            self.rejects.append("%s: parse error, can't grok: %s." % (dsc_filename, line))
+            return False, "%s: parse error, can't grok: %s." % (dsc_filename, line)
         except InvalidDscError, line:
-            self.rejects.append("%s: syntax error on line %s." % (dsc_filename, line))
+            return False, "%s: syntax error on line %s." % (dsc_filename, line)
         except ChangesUnicodeError:
-            self.rejects.append("%s: dsc file not proper utf-8." % (dsc_filename))
+            return False, "%s: dsc file not proper utf-8." % (dsc_filename)
+
+        return True, None
+
+    ###########################################################################
+
+    def check_dsc(self, action=True, session=None):
+        """Returns bool indicating whether or not the source changes are valid"""
+        # Ensure there is source to check
+        if not self.pkg.changes["architecture"].has_key("source"):
+            return True
+
+        (status, reason) = self.load_dsc(action=action)
+        if not status:
+            self.rejects.append(reason)
+            return False
+        (status, dsc_filename) = self.__dsc_filename()
+        if not status:
+            # If status is false, dsc_filename has the reason
+            self.rejects.append(dsc_filename)
+            return False
 
         # Build up the file list of files mentioned by the .dsc
         try:
@@ -1241,11 +1313,10 @@ class Upload(object):
             os.symlink(self.pkg.orig_files[orig_file]["path"], dest)
 
         # Extract the source
-        cmd = "dpkg-source -sn -x %s" % (dsc_filename)
-        (result, output) = commands.getstatusoutput(cmd)
-        if (result != 0):
-            self.rejects.append("'dpkg-source -x' failed for %s [return code: %s]." % (dsc_filename, result))
-            self.rejects.append(utils.prefix_multi_line_string(output, " [dpkg-source output:] "))
+        try:
+            unpacked = UnpackedSource(dsc_filename)
+        except:
+            self.rejects.append("'dpkg-source -x' failed for %s." % dsc_filename)
             return
 
         if not cnf.Find("Dir::Queue::BTSVersionTrack"):
@@ -1257,19 +1328,19 @@ class Upload(object):
             upstr_version = re_strip_revision.sub('', upstr_version)
 
         # Ensure the changelog file exists
-        changelog_filename = "%s-%s/debian/changelog" % (self.pkg.dsc["source"], upstr_version)
-        if not os.path.exists(changelog_filename):
+        changelog_file = unpacked.get_changelog_file()
+        if changelog_file is None:
             self.rejects.append("%s: debian/changelog not found in extracted source." % (dsc_filename))
             return
 
         # Parse the changelog
         self.pkg.dsc["bts changelog"] = ""
-        changelog_file = utils.open_file(changelog_filename)
         for line in changelog_file.readlines():
             m = re_changelog_versions.match(line)
             if m:
                 self.pkg.dsc["bts changelog"] += line
         changelog_file.close()
+        unpacked.cleanup()
 
         # Check we found at least one revision in the changelog
         if not self.pkg.dsc["bts changelog"]:
@@ -1471,7 +1542,7 @@ class Upload(object):
 
         # If we do not have a tagfile, don't do anything
         tagfile = cnf.get("Dinstall::LintianTags")
-        if tagfile is None:
+        if not tagfile:
             return
 
         # Parse the yaml file
@@ -2001,6 +2072,7 @@ distribution."""
         print "Installing."
         self.logger.log(["installing changes", self.pkg.changes_file])
 
+        binaries = []
         poolfiles = []
 
         # Add the .dsc file to the DB first
@@ -2013,7 +2085,9 @@ distribution."""
         # Add .deb / .udeb files to the DB (type is always deb, dbtype is udeb/deb)
         for newfile, entry in self.pkg.files.items():
             if entry["type"] == "deb":
-                poolfiles.append(add_deb_to_db(self, newfile, session))
+                b, pf = add_deb_to_db(self, newfile, session)
+                binaries.append(b)
+                poolfiles.append(pf)
 
         # If this is a sourceful diff only upload that is moving
         # cross-component we need to copy the .orig files into the new
@@ -2098,6 +2172,18 @@ distribution."""
         # Our SQL session will automatically start a new transaction after
         # the last commit
 
+        # Now ensure that the metadata has been added
+        # This has to be done after we copy the files into the pool
+        # For source if we have it:
+        if self.pkg.changes["architecture"].has_key("source"):
+            import_metadata_into_db(source, session)
+
+        # Now for any of our binaries
+        for b in binaries:
+            import_metadata_into_db(b, session)
+
+        session.commit()
+
         # Move the .changes into the 'done' directory
         utils.move(self.pkg.changes_file,
                    os.path.join(cnf["Dir::Queue::Done"], os.path.basename(self.pkg.changes_file)))