summaryrefslogtreecommitdiffhomepage
path: root/gbp/deb/source.py
blob: 2952afe6149221cebd95264eae5a763f1ffa4b9f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# vim: set fileencoding=utf-8 :
#
# (C) 2013 Guido Günther <agx@sigxcpu.org>
#    This program is free software; you can redistribute it and/or modify
#    it under the terms of the GNU General Public License as published by
#    the Free Software Foundation; either version 2 of the License, or
#    (at your option) any later version.
#
#    This program is distributed in the hope that it will be useful,
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#    GNU General Public License for more details.
#
#    You should have received a copy of the GNU General Public License
#    along with this program; if not, please see
#    <http://www.gnu.org/licenses/>
"""provides some debian source package related helpers"""

import os
from gbp.deb import DebianPkgPolicy as Policy
from gbp.deb.format import DebianSourceFormat
from gbp.deb.changelog import ChangeLog
from gbp.deb.control import Control


class FileVfs(object):
    def __init__(self, dir):
        """
        Access files in an unpacked Debian source package.

        @param dir: the toplevel of the source tree
        @type dir: C{str}
        """
        self._dir = dir

    def open(self, path, flags=None):
        flags = flags or 'r'
        return open(os.path.join(self._dir, path), flags)


class DebianSourceError(Exception):
    pass


class DebianSource(object):
    """
    A debianized source tree

    Querying/setting information in a debianized source tree
    involves several files. This class provides a common interface.
    """
    def __init__(self, vfs):
        """
        @param vfs: a class that implements I{GitVfs} interface or
             a directory (which will use the I{FileVfs} class. The
             directory must be the toplevel of a Debian source
             package.
        """
        self._changelog = None
        self._control = None

        if isinstance(vfs, str):
            self._vfs = FileVfs(vfs)
        else:
            self._vfs = vfs

    def is_native(self):
        """
        Whether this is a native Debian package
        """
        try:
            with self._vfs.open('debian/source/format') as ff:
                f = DebianSourceFormat(ff.read())
            if f.type:
                return f.type == 'native'
        except IOError as e:
            pass  # Fall back to changelog parsing

        try:
            return '-' not in self.changelog.version
        except IOError as e:
            raise DebianSourceError("Failed to determine source format: %s" % e)

    def is_releasable(self):
        """
        Check if package is releasable

        Debian's current practive is to check for UNRELEASED in the distribution.
        """
        return self.changelog.distribution != 'UNRELEASED'

    @property
    def changelog(self):
        """
        Return the L{gbp.deb.ChangeLog}
        """
        if not self._changelog:
            try:
                with self._vfs.open('debian/changelog', 'rb') as clf:
                    self._changelog = ChangeLog(clf.read().decode('utf-8'))
            except IOError as err:
                raise DebianSourceError('Failed to read changelog: %s' % err)
        return self._changelog

    @property
    def control(self):
        """
        Return the L{gbp.deb.Control}
        """
        if not self._control:
            try:
                with self._vfs.open('debian/control', 'rb') as cf:
                    self._control = Control(cf.read().decode('utf-8'))
            except IOError as err:
                raise DebianSourceError('Failed to read control file: %s' % err)
        return self._control

    @property
    def sourcepkg(self):
        """
        The source package's name
        """
        return self.changelog['Source']

    @property
    def name(self):
        return self.sourcepkg

    @property
    def version(self):
        return self.changelog.version

    @property
    def upstream_version(self):
        return self.changelog.upstream_version

    @property
    def debian_version(self):
        return self.changelog.debian_version

    def upstream_tarball_name(self, compression, component=None):
        """
        Possible upstream tarball name for this source package

        Gives the name of the main tarball if component is None
        """
        if self.is_native():
            return None
        return Policy.build_tarball_name(self.name,
                                         self.upstream_version,
                                         compression=compression,
                                         component=component)

    def upstream_tarball_names(self, comp_type, components=None):
        """
        Possible upstream tarballs names for this source package

        This includes component tarballs names.  with the given
        component names
        """
        names = [self.upstream_tarball_name(comp_type)]
        for component in (components or []):
            names += [self.upstream_tarball_name(comp_type, c) for c in components]
        return names