UpstreamSource: implement prefix guessing
authorMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Thu, 22 Aug 2013 13:24:13 +0000 (16:24 +0300)
committerMarkus Lehtonen <markus.lehtonen@linux.intel.com>
Tue, 3 Mar 2015 08:07:46 +0000 (10:07 +0200)
Add a new attribure 'prefix', i.e. the "leading directory name" in an
archive. For example, this usually is '<name>-<version>' in release
tarballs.

Signed-off-by: Markus Lehtonen <markus.lehtonen@linux.intel.com>
Signed-off-by: Ɓukasz Stelmach <l.stelmach@samsung.com>
gbp/deb/upstreamsource.py
gbp/pkg/__init__.py
tests/06_test_upstream_source.py

index 7eb555ae317fbf72d97491e184cf42fd84b02262..6d1bc60da81e29169003be9c8264d88251a146f5 100644 (file)
@@ -22,7 +22,8 @@ from gbp.deb.policy import DebianPkgPolicy
 
 class DebianUpstreamSource(UpstreamSource):
     """Upstream source class for Debian"""
-    def __init__(self, name, unpacked=None):
+    def __init__(self, name, unpacked=None, **kwargs):
         super(DebianUpstreamSource, self).__init__(name,
                                                    unpacked,
-                                                   DebianPkgPolicy)
+                                                   DebianPkgPolicy,
+                                                   **kwargs)
index df7da9942c43b3288bdfd3d5c60f46e3beee47b2..2318a84e844a5f56bb9e66f027a63795ee10c399 100644 (file)
@@ -20,6 +20,9 @@
 import os
 import re
 import glob
+import stat
+import subprocess
+import zipfile
 
 import six
 
@@ -312,7 +315,7 @@ class UpstreamSource(object):
     @cvar _unpacked: path to the unpacked source tree
     @type _unpacked: string
     """
-    def __init__(self, name, unpacked=None, pkg_policy=PkgPolicy):
+    def __init__(self, name, unpacked=None, pkg_policy=PkgPolicy, prefix=None):
         self._orig = False
         self._tarball = False
         self._pkg_policy = pkg_policy
@@ -323,6 +326,9 @@ class UpstreamSource(object):
         self._filename_base, \
         self._archive_fmt, \
         self._compression = parse_archive_filename(os.path.basename(self.path))
+        self._prefix = prefix
+        if self._prefix is None:
+            self._determine_prefix()
 
         self._check_orig()
         if self.is_dir():
@@ -372,6 +378,68 @@ class UpstreamSource(object):
     def path(self):
         return self._path.rstrip('/')
 
+
+    @staticmethod
+    def _get_topdir_files(file_list):
+        """Parse content of the top directory from a file list
+
+        >>> UpstreamSource._get_topdir_files([])
+        set([])
+        >>> UpstreamSource._get_topdir_files([('-', 'foo/bar')])
+        set([('d', 'foo')])
+        >>> UpstreamSource._get_topdir_files([('d', 'foo/'), ('-', 'foo/bar')])
+        set([('d', 'foo')])
+        >>> UpstreamSource._get_topdir_files([('d', 'foo'), ('-', 'foo/bar')])
+        set([('d', 'foo')])
+        >>> UpstreamSource._get_topdir_files([('-', 'fob'), ('d', 'foo'), ('d', 'foo/bar'), ('-', 'foo/bar/baz')])
+        set([('-', 'fob'), ('d', 'foo')])
+        >>> UpstreamSource._get_topdir_files([('-', './foo/bar')])
+        set([('d', 'foo')])
+        >>> UpstreamSource._get_topdir_files([('-', 'foo/bar'), ('-', '.foo/bar')])
+        set([('d', '.foo'), ('d', 'foo')])
+        """
+        topdir_files = set()
+        for typ, path in file_list:
+            split = re.sub('^(?:./|../)*', '', path).split('/')
+            if len(split) == 1:
+                topdir_files.add((typ, path))
+            else:
+                topdir_files.add(('d', split[0]))
+        return topdir_files
+
+    def _determine_prefix(self):
+        """Determine the prefix, i.e. the "leading directory name"""
+        self._prefix = ''
+        if self.is_dir():
+            # For directories we presume that the prefix is just the dirname
+            self._prefix = os.path.basename(self.path.rstrip('/'))
+        else:
+            files = []
+            if self._archive_fmt == 'zip':
+                archive = zipfile.ZipFile(self.path)
+                for info in archive.infolist():
+                    typ = 'd' if stat.S_ISDIR(info.external_attr >> 16) else '?'
+                    files.append((typ, info.filename))
+            elif self._archive_fmt == 'tar':
+                popen = subprocess.Popen(['tar', '-t', '-v', '-f', self.path],
+                                stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+                out, _err = popen.communicate()
+                if popen.returncode:
+                    raise GbpError("Listing tar archive content failed")
+                for line in out.splitlines():
+                    fields = line.split(None, 5)
+                    files.append((fields[0][0], fields[-1]))
+            else:
+                raise GbpError("Unsupported archive format %s, unable to "
+                               "determine prefix for '%s'" %
+                               (self._archive_fmt, self.path))
+            # Determine prefix from the archive content
+            topdir_files = self._get_topdir_files(files)
+            if len(topdir_files) == 1:
+                typ, name = topdir_files.pop()
+                if typ == 'd':
+                    self._prefix = name
+
     @property
     def archive_fmt(self):
         """Archive format of the sources, e.g. 'tar'"""
@@ -382,6 +450,11 @@ class UpstreamSource(object):
         """Compression format of the sources, e.g. 'gzip'"""
         return self._compression
 
+    @property
+    def prefix(self):
+        """Prefix, i.e. the 'leading directory name' of the sources"""
+        return self._prefix
+
     def unpack(self, dir, filters=[]):
         """
         Unpack packed upstream sources into a given directory
@@ -397,7 +470,8 @@ class UpstreamSource(object):
             raise GbpError("Filters must be a list")
 
         self._unpack_archive(dir, filters)
-        self.unpacked = self._unpacked_toplevel(dir)
+        src_dir = os.path.join(dir, self._prefix)
+        self.unpacked = src_dir if os.path.isdir(src_dir) else dir
 
     def _unpack_archive(self, dir, filters):
         """
@@ -415,17 +489,6 @@ class UpstreamSource(object):
         except gbpc.CommandExecFailed:
             raise GbpError("Unpacking of %s failed" % self.path)
 
-    def _unpacked_toplevel(self, dir):
-        """unpacked archives can contain a leading directory or not"""
-        unpacked = glob.glob('%s/*' % dir)
-        unpacked.extend(glob.glob("%s/.*" % dir)) # include hidden files and folders
-        # Check that dir contains nothing but a single folder:
-        if len(unpacked) == 1 and os.path.isdir(unpacked[0]):
-            return unpacked[0]
-        else:
-            # We can determine "no prefix" from this
-            return os.path.join(dir, ".")
-
     def _unpack_tar(self, dir, filters):
         """
         Unpack a tarball to I{dir} applying a list of I{filters}. Leave the
index b5989725f01ce1de1075202518a24525a69f832e..c6df6f14f6eaae3bdf4e14a745e3e662e2766995 100644 (file)
@@ -28,6 +28,7 @@ class TestDir(unittest.TestCase):
         self.assertEqual(source.path, self.upstream_dir)
         self.assertEqual(source.unpacked, self.upstream_dir)
         self.assertEqual(source.guess_version(), ('test', '1.0'))
+        self.assertEqual(source.prefix, 'test-1.0')
 
     def tearDown(self):
         context.teardown()
@@ -65,6 +66,7 @@ class TestTar(unittest.TestCase):
         self.assertEqual(repacked.guess_version(), ('gbp', '0.1'))
         self.assertEqual(repacked.archive_fmt, 'tar')
         self.assertEqual(repacked.compression, 'bzip2')
+        self.assertEqual(repacked.prefix, 'gbp')
         self._check_tar(repacked, ["gbp/errors.py", "gbp/__init__.py"])
 
     def test_pack_filtered(self):
@@ -94,7 +96,8 @@ class TestZip(unittest.TestCase):
         self.zipfile = self.tmpdir.join("gbp-0.1.zip")
         z = zipfile.ZipFile(self.zipfile, "w")
         for f in glob.glob(os.path.join(context.projectdir, "gbp/*.py")):
-            z.write(f, f, zipfile.ZIP_DEFLATED)
+            arcname = os.path.relpath(f, context.projectdir)
+            z.write(f, arcname, zipfile.ZIP_DEFLATED)
         z.close()
 
     def tearDown(self):
@@ -109,6 +112,7 @@ class TestZip(unittest.TestCase):
         self.assertEqual(source.guess_version(), ('gbp', '0.1'))
         self.assertEqual(source.archive_fmt, 'zip')
         self.assertEqual(source.compression, None)
+        self.assertEqual(source.prefix, 'gbp')
         source.unpack(str(self.tmpdir))
         self.assertNotEqual(source.unpacked, None)