~barry/bzr-builddeb/609186-urls

327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
1
#    upstream.py -- Providers of upstream source
2
#    Copyright (C) 2009 Canonical Ltd.
334.4.12 by Jelmer Vernooij
Add UScanSource tests.
3
#    Copyright (C) 2009 Jelmer Vernooij <jelmer@debian.org>
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
4
#
5
#    This file is part of bzr-builddeb.
6
#
7
#    bzr-builddeb is free software; you can redistribute it and/or modify
8
#    it under the terms of the GNU General Public License as published by
9
#    the Free Software Foundation; either version 2 of the License, or
10
#    (at your option) any later version.
11
#
12
#    bzr-builddeb is distributed in the hope that it will be useful,
13
#    but WITHOUT ANY WARRANTY; without even the implied warranty of
14
#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15
#    GNU General Public License for more details.
16
#
17
#    You should have received a copy of the GNU General Public License
18
#    along with bzr-builddeb; if not, write to the Free Software
19
#    Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20
21
import os
22
import shutil
23
import subprocess
327.2.10 by James Westby
Re-instate split mode.
24
import tarfile
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
25
import tempfile
26
451.1.1 by Jelmer Vernooij
Attempt to import from "debian" first and try "debian_bundle" as fallback,
27
try:
28
    from debian.changelog import Version
29
except ImportError:
30
    # Prior to 0.1.15 the debian module was called debian_bundle
31
    from debian_bundle.changelog import Version
327.2.6 by James Westby
Improve MergeModeDistiller.
32
334.4.7 by Jelmer Vernooij
Fix merging of upstream branch in merge-upstream.
33
from bzrlib.revisionspec import RevisionSpec
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
34
from bzrlib.trace import note
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
35
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
36
from bzrlib.plugins.builddeb.errors import (
37
    MissingUpstreamTarball,
38
    PackageVersionNotPresent,
443.3.3 by Jelmer Vernooij
Allow exporting upstream revisions without a pristine tar delta if bzr's export supports --use-file-timestamps.
39
    PerFileTimestampsNotSupported,
369.2.4 by Muharem Hrnjadovic
"pristine-tar gentar" failures are now detected and an appropriate exception (PackageVersionNotPresent) is raised to the caller so it can try to obtain the required package from the next package source.
40
    PristineTarError,
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
41
    )
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
42
from bzrlib.plugins.builddeb.import_dsc import DistributionBranch
43
from bzrlib.plugins.builddeb.repack_tarball import repack_tarball
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
44
from bzrlib.plugins.builddeb.util import (
432.1.2 by Jelmer Vernooij
Add convenience function for using export.
45
    export,
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
46
    get_snapshot_revision,
47
    tarball_name,
48
    )
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
49
327.1.33 by James Westby
Add tests for UpstreamProvider.
50
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
51
class UpstreamSource(object):
52
    """A source for upstream versions (uscan, get-orig-source, etc)."""
53
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
54
    def get_latest_version(self, package, version, target_dir):
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
55
        """Fetch the source tarball for the latest available version.
56
57
        :param package: Name of the package
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
58
        :param version: The current version of the package.
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
59
        :param target_dir: Directory in which to store the tarball
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
60
        :return: The version number of the new version, or None if no newer
61
                version is available.
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
62
        """
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
63
        raise NotImplementedError(self.get_latest_version)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
64
65
    def get_specific_version(self, package, version, target_dir):
66
        """Fetch the source tarball for a particular version.
67
68
        :param package: Name of the package
69
        :param version: Version string of the version to fetch
70
        :param target_dir: Directory in which to store the tarball
71
        """
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
72
        raise NotImplementedError(self.get_specific_version)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
73
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
74
    def _tarball_path(self, package, version, target_dir, format=None):
75
        return os.path.join(target_dir, tarball_name(package, version,
76
                    format=format))
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
77
78
79
class PristineTarSource(UpstreamSource):
80
    """Source that uses the pristine-tar revisions in the packaging branch."""
81
334.4.3 by Jelmer Vernooij
Move apt-related functions onto AptSource.
82
    def __init__(self, tree, branch):
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
83
        self.branch = branch
84
        self.tree = tree
85
360 by James Westby
Complete merging of UpstreamSource code.
86
    def get_specific_version(self, package, version, target_dir):
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
87
        db = DistributionBranch(self.branch, None, tree=self.tree)
88
        if not db.has_upstream_version_in_packaging_branch(version):
360 by James Westby
Complete merging of UpstreamSource code.
89
            raise PackageVersionNotPresent(package, version, self)
360.1.2 by Muharem Hrnjadovic
Made revid_of_upstream_version_from_branch() a public method.
90
        revid = db.revid_of_upstream_version_from_branch(version)
443.3.1 by Jelmer Vernooij
Pass revision object to pristine_tar_delta functions rather than revid.
91
        rev = self.branch.repository.get_revision(revid)
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
92
        note("Using pristine-tar to reconstruct the needed tarball.")
443.3.4 by Jelmer Vernooij
Some fixes related to format picking.
93
        if db.has_pristine_tar_delta(rev):
94
            format = db.pristine_tar_format(rev)
95
        else:
96
            format = 'gz'
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
97
        target_filename = self._tarball_path(package, version,
98
                                             target_dir, format=format)
369.2.4 by Muharem Hrnjadovic
"pristine-tar gentar" failures are now detected and an appropriate exception (PackageVersionNotPresent) is raised to the caller so it can try to obtain the required package from the next package source.
99
        try:
100
            db.reconstruct_pristine_tar(revid, package, version, target_filename)
101
        except PristineTarError:
102
            raise PackageVersionNotPresent(package, version, self)
443.3.3 by Jelmer Vernooij
Allow exporting upstream revisions without a pristine tar delta if bzr's export supports --use-file-timestamps.
103
        except PerFileTimestampsNotSupported:
104
            raise PackageVersionNotPresent(package, version, self)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
105
106
107
class AptSource(UpstreamSource):
108
    """Upstream source that uses apt-source."""
109
334.4.3 by Jelmer Vernooij
Move apt-related functions onto AptSource.
110
    def get_specific_version(self, package, upstream_version, target_dir, 
334.4.13 by Jelmer Vernooij
Fix apt tests.
111
            _apt_pkg=None):
334.4.3 by Jelmer Vernooij
Move apt-related functions onto AptSource.
112
        if _apt_pkg is None:
113
            import apt_pkg
114
        else:
115
            apt_pkg = _apt_pkg
116
        apt_pkg.init()
370.2.1 by Muharem Hrnjadovic
Handle the case where the apt.sources file contains no source URIs (LP:375897)
117
451.4.2 by Jelmer Vernooij
Use helper function.
118
        def get_fn(obj, new_name, old_name):
119
            try:
120
                return getattr(obj, new_name)
121
            except AttributeError:
122
                return getattr(obj, old_name)
123
370.2.1 by Muharem Hrnjadovic
Handle the case where the apt.sources file contains no source URIs (LP:375897)
124
        # Handle the case where the apt.sources file contains no source
125
        # URIs (LP:375897)
126
        try:
451.4.2 by Jelmer Vernooij
Use helper function.
127
            get_sources = get_fn(apt_pkg, 'SourceRecords',
128
                "GetPkgSrcRecords")
430 by James Westby
Adapt to the python-apt 0.8 API. Thanks to Julian Andres Klode.
129
            sources = get_sources()
370.2.1 by Muharem Hrnjadovic
Handle the case where the apt.sources file contains no source URIs (LP:375897)
130
        except SystemError:
131
            raise PackageVersionNotPresent(package, upstream_version, self)
132
451.4.2 by Jelmer Vernooij
Use helper function.
133
        restart = get_fn(sources, 'restart', 'Restart')
430 by James Westby
Adapt to the python-apt 0.8 API. Thanks to Julian Andres Klode.
134
        restart()
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
135
        note("Using apt to look for the upstream tarball.")
451.4.2 by Jelmer Vernooij
Use helper function.
136
        lookup = get_fn(sources, 'lookup', 'Lookup')
430 by James Westby
Adapt to the python-apt 0.8 API. Thanks to Julian Andres Klode.
137
        while lookup(package):
451.4.2 by Jelmer Vernooij
Use helper function.
138
            version = get_fn(sources, 'version', 'Version')
430 by James Westby
Adapt to the python-apt 0.8 API. Thanks to Julian Andres Klode.
139
            if upstream_version == Version(version).upstream_version:
140
                if self._run_apt_source(package, version, target_dir):
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
141
                    return
334.4.3 by Jelmer Vernooij
Move apt-related functions onto AptSource.
142
                break
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
143
        note("apt could not find the needed tarball.")
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
144
        raise PackageVersionNotPresent(package, upstream_version, self)
334.4.3 by Jelmer Vernooij
Move apt-related functions onto AptSource.
145
146
    def _get_command(self, package, version_str):
147
        return 'apt-get source -y --only-source --tar-only %s=%s' % \
148
            (package, version_str)
149
150
    def _run_apt_source(self, package, version_str, target_dir):
151
        command = self._get_command(package, version_str)
152
        proc = subprocess.Popen(command, shell=True, cwd=target_dir)
153
        proc.wait()
154
        if proc.returncode != 0:
155
            return False
156
        return True
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
157
158
159
class UpstreamBranchSource(UpstreamSource):
443.1.1 by Jelmer Vernooij
Pass version -> revision map into UpstreamBranchSource.
160
    """Upstream source that uses the upstream branch.
161
    
162
    :ivar upstream_branch: Branch with upstream sources
163
    :ivar upstream_version_map: Map from version strings to revids
164
    """
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
165
443.1.2 by Jelmer Vernooij
Inline get_upstream_sources.
166
    def __init__(self, upstream_branch, upstream_revision_map=None):
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
167
        self.upstream_branch = upstream_branch
443.1.1 by Jelmer Vernooij
Pass version -> revision map into UpstreamBranchSource.
168
        if upstream_revision_map is None:
169
            self.upstream_revision_map = {}
170
        else:
171
            self.upstream_revision_map = upstream_revision_map
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
172
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
173
    def _get_revision_id(self, version):
443.1.1 by Jelmer Vernooij
Pass version -> revision map into UpstreamBranchSource.
174
        if version in self.upstream_revision_map:
175
             return self.upstream_revision_map[version]
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
176
        revspec = get_snapshot_revision(version)
177
        if revspec is not None:
178
            return RevisionSpec.from_string(
179
                revspec).as_revision_id(self.upstream_branch)
443.1.1 by Jelmer Vernooij
Pass version -> revision map into UpstreamBranchSource.
180
        return None
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
181
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
182
    def get_specific_version(self, package, version, target_dir):
334.4.7 by Jelmer Vernooij
Fix merging of upstream branch in merge-upstream.
183
        self.upstream_branch.lock_read()
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
184
        try:
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
185
            revid = self._get_revision_id(version)
443.1.1 by Jelmer Vernooij
Pass version -> revision map into UpstreamBranchSource.
186
            if revid is None:
187
                raise PackageVersionNotPresent(package, version, self)
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
188
            note("Exporting upstream branch revision %s to create the tarball",
334.4.7 by Jelmer Vernooij
Fix merging of upstream branch in merge-upstream.
189
                 revid)
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
190
            target_filename = self._tarball_path(package, version, target_dir)
191
            tarball_base = "%s-%s" % (package, version)
334.4.5 by Jelmer Vernooij
Support parsing revision id from version string.
192
            rev_tree = self.upstream_branch.repository.revision_tree(revid)
432.1.2 by Jelmer Vernooij
Add convenience function for using export.
193
            export(rev_tree, target_filename, 'tgz', tarball_base)
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
194
        finally:
334.4.7 by Jelmer Vernooij
Fix merging of upstream branch in merge-upstream.
195
            self.upstream_branch.unlock()
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
196
197
198
class GetOrigSourceSource(UpstreamSource):
199
    """Upstream source that uses the get-orig-source rule in debian/rules."""
200
201
    def __init__(self, tree, larstiq):
202
        self.tree = tree
203
        self.larstiq = larstiq
204
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
205
    def _get_orig_source(self, source_dir, desired_tarball_names,
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
206
                        target_dir):
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
207
        note("Trying to use get-orig-source to retrieve needed tarball.")
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
208
        command = ["make", "-f", "debian/rules", "get-orig-source"]
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
209
        proc = subprocess.Popen(command, cwd=source_dir)
210
        ret = proc.wait()
211
        if ret != 0:
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
212
            note("Trying to run get-orig-source rule failed")
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
213
            return False
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
214
        for desired_tarball_name in desired_tarball_names:
215
            fetched_tarball = os.path.join(source_dir, desired_tarball_name)
216
            if os.path.exists(fetched_tarball):
217
                repack_tarball(fetched_tarball, desired_tarball_name,
405.1.3 by James Westby
Allow merge-upstream to not repack .bz2 tarballs.
218
                               target_dir=target_dir, force_gz=False)
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
219
                return True
412 by James Westby
Remove use of deprecated trace.info. Thanks John.
220
        note("get-orig-source did not create %s", desired_tarball_name)
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
221
        return False
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
222
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
223
    def get_specific_version(self, package, version, target_dir):
224
        if self.larstiq:
225
            rules_name = 'rules'
226
        else:
227
            rules_name = 'debian/rules'
228
        rules_id = self.tree.path2id(rules_name)
229
        if rules_id is not None:
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
230
            desired_tarball_names = [tarball_name(package, version),
231
                    tarball_name(package, version, 'bz2'),
232
                    tarball_name(package, version, 'lzma')]
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
233
            tmpdir = tempfile.mkdtemp(prefix="builddeb-get-orig-source-")
234
            try:
235
                base_export_dir = os.path.join(tmpdir, "export")
236
                export_dir = base_export_dir
237
                if self.larstiq:
238
                    os.mkdir(export_dir)
239
                    export_dir = os.path.join(export_dir, "debian")
432.1.2 by Jelmer Vernooij
Add convenience function for using export.
240
                export(self.tree, export_dir, format="dir")
360 by James Westby
Complete merging of UpstreamSource code.
241
                if not self._get_orig_source(base_export_dir,
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
242
                        desired_tarball_names, target_dir):
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
243
                    raise PackageVersionNotPresent(package, version, self)
244
                return
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
245
            finally:
246
                shutil.rmtree(tmpdir)
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
247
        note("No debian/rules file to try and use for a get-orig-source rule")
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
248
        raise PackageVersionNotPresent(package, version, self)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
249
250
251
class UScanSource(UpstreamSource):
252
    """Upstream source that uses uscan."""
253
254
    def __init__(self, tree, larstiq):
255
        self.tree = tree
256
        self.larstiq = larstiq
257
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
258
    def _uscan(self, package, upstream_version, watch_file, target_dir):
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
259
        note("Using uscan to look for the upstream tarball.")
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
260
        r = os.system("uscan --upstream-version %s --force-download --rename "
261
                      "--package %s --watchfile %s --check-dirname-level 0 " 
356 by James Westby
Add UpstreamSource that abstracts methods more.
262
                      "--download --repack --destdir %s --download-version %s" %
263
                      (upstream_version, package, watch_file, target_dir,
264
                       upstream_version))
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
265
        if r != 0:
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
266
            note("uscan could not find the needed tarball.")
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
267
            return False
268
        return True
269
334.4.12 by Jelmer Vernooij
Add UScanSource tests.
270
    def _export_watchfile(self):
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
271
        if self.larstiq:
272
            watchfile = 'watch'
273
        else:
274
            watchfile = 'debian/watch'
275
        watch_id = self.tree.path2id(watchfile)
276
        if watch_id is None:
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
277
            note("No watch file to use to retrieve upstream tarball.")
334.4.12 by Jelmer Vernooij
Add UScanSource tests.
278
            return None
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
279
        (tmp, tempfilename) = tempfile.mkstemp()
280
        try:
281
            tmp = os.fdopen(tmp, 'wb')
282
            watch = self.tree.get_file_text(watch_id)
283
            tmp.write(watch)
284
        finally:
285
            tmp.close()
334.4.12 by Jelmer Vernooij
Add UScanSource tests.
286
        return tempfilename
287
288
    def get_specific_version(self, package, version, target_dir):
289
        tempfilename = self._export_watchfile()
290
        if tempfilename is None:
291
            raise PackageVersionNotPresent(package, version, self)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
292
        try:
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
293
            if not self._uscan(package, version, tempfilename,
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
294
                    target_dir):
295
                raise PackageVersionNotPresent(package, version, self)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
296
        finally:
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
297
            os.unlink(tempfilename)
298
299
    def get_latest_version(self, package, version, target_dir):
300
        pass
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
301
302
303
class SelfSplitSource(UpstreamSource):
304
305
    def __init__(self, tree):
306
        self.tree = tree
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
307
308
    def _split(self, package, upstream_version, target_filename):
309
        tmpdir = tempfile.mkdtemp(prefix="builddeb-get-orig-source-")
310
        try:
311
            export_dir = os.path.join(tmpdir,
312
                    "%s-%s" % (package, upstream_version))
432.1.2 by Jelmer Vernooij
Add convenience function for using export.
313
            export(self.tree, export_dir, format="dir")
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
314
            shutil.rmtree(os.path.join(export_dir, "debian"))
315
            tar = tarfile.open(target_filename, "w:gz")
316
            try:
317
                tar.add(export_dir, "%s-%s" % (package, upstream_version))
318
            finally:
319
                tar.close()
320
        finally:
321
            shutil.rmtree(tmpdir)
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
322
323
    def get_specific_version(self, package, version, target_dir):
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
324
        note("Using the current branch without the 'debian' directory "
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
325
                "to create the tarball")
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
326
        self._split(package, version, 
327
                    self._tarball_path(package, version, target_dir))
334.4.2 by Jelmer Vernooij
Move all upstream sources to separate objects.
328
329
334.4.8 by Jelmer Vernooij
Add a stacked upstream source object.
330
class StackedUpstreamSource(UpstreamSource):
331
    """An upstream source that checks a list of other upstream sources.
332
    
333
    The first source that can provide a tarball, wins. 
334
    """
335
336
    def __init__(self, sources):
337
        self._sources = sources
338
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
339
    def __repr__(self):
340
        return "%s(%r)" % (self.__class__.__name__, self._sources)
341
334.4.8 by Jelmer Vernooij
Add a stacked upstream source object.
342
    def get_specific_version(self, package, version, target_dir):
343
        for source in self._sources:
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
344
            try:
345
                return source.get_specific_version(package, version, target_dir)
346
            except PackageVersionNotPresent:
347
                pass
348
        raise PackageVersionNotPresent(package, version, self)
349
393.2.8 by James Westby
Avoid explicit paths with subprocess to avoid issues such as 507270.
350
    def get_latest_version(self, package, version, target_dir):
351
        for source in self._sources:
352
            try:
353
                new_version = source.get_latest_version(package, version, target_dir)
354
                if new_version is not None:
355
                    return new_version
356
            except NotImplementedError:
357
                pass
358
        return None
359
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
360
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
361
class UpstreamProvider(object):
362
    """An upstream provider can provide the upstream source for a package.
363
364
    Different implementations can provide it in different ways, for
365
    instance using pristine-tar, or using apt.
366
    """
367
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
368
    def __init__(self, package, version, store_dir, sources):
327.2.6 by James Westby
Improve MergeModeDistiller.
369
        """Create an UpstreamProvider.
370
371
        :param package: the name of the source package that is being built.
372
        :param version: the Version of the package that is being built.
373
        :param store_dir: A directory to cache the tarballs.
374
        """
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
375
        self.package = package
327.1.26 by James Westby
Make sure that we use a Version object in UpstreamProvider.
376
        self.version = Version(version)
327.2.6 by James Westby
Improve MergeModeDistiller.
377
        self.store_dir = store_dir
334.4.8 by Jelmer Vernooij
Add a stacked upstream source object.
378
        self.source = StackedUpstreamSource(sources)
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
379
380
    def provide(self, target_dir):
327.2.6 by James Westby
Improve MergeModeDistiller.
381
        """Provide the upstream tarball any way possible.
382
383
        Call this to place the correctly named tarball in to target_dir,
384
        through means possible.
385
386
        If the tarball is already there then do nothing.
387
        If it is in self.store_dir then copy it over.
388
        Else retrive it and cache it in self.store_dir, then copy it over:
389
           - If pristine-tar metadata is available then that will be used.
390
           - Else if apt knows about a source of that version that will be
391
             retrieved.
392
           - Else if uscan knows about that version it will be downloaded
393
             and repacked as needed.
394
           - Else a call will be made to get-orig-source to try and retrieve
395
             the tarball.
396
397
        If the tarball can't be found at all then MissingUpstreamTarball
398
        will be raised.
399
400
        :param target_dir: The directory to place the tarball in.
401
        :return: The path to the tarball.
402
        """
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
403
        note("Looking for a way to retrieve the upstream tarball")
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
404
        in_target = self.already_exists_in_target(target_dir)
405
        if in_target is not None:
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
406
            note("Upstream tarball already exists in build directory, "
327.1.18 by James Westby
Give more information about where the upstream tarball is being searched for
407
                    "using that")
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
408
            return in_target
405.1.6 by James Westby
Tests for importing bz2.
409
        if self.already_exists_in_store() is None:
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
410
            if not os.path.exists(self.store_dir):
411
                os.makedirs(self.store_dir)
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
412
            try:
405.1.3 by James Westby
Allow merge-upstream to not repack .bz2 tarballs.
413
                self.source.get_specific_version(self.package,
361 by James Westby
Provide to the store dir, and then copy to the target dir.
414
                    self.version.upstream_version, self.store_dir)
334.4.11 by Jelmer Vernooij
use exception to indicate missing package versions rather than boolean return value
415
            except PackageVersionNotPresent:
411 by James Westby
Fix all the upstream tests.
416
                raise MissingUpstreamTarball(self._tarball_names()[0])
327.1.18 by James Westby
Give more information about where the upstream tarball is being searched for
417
        else:
400.1.1 by John Arbash Meinel
trace.info was deprecated, use trace.note instead.
418
             note("Using the upstream tarball that is present in "
327.1.18 by James Westby
Give more information about where the upstream tarball is being searched for
419
                     "%s" % self.store_dir)
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
420
        path = self.provide_from_store_dir(target_dir)
421
        assert path is not None
422
        return path
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
423
424
    def already_exists_in_target(self, target_dir):
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
425
        for tarball_name in self._tarball_names():
426
            path = os.path.join(target_dir, tarball_name)
427
            if os.path.exists(path):
428
                return path
429
        return None
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
430
431
    def already_exists_in_store(self):
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
432
        for tarball_name in self._tarball_names():
433
            path = os.path.join(self.store_dir, tarball_name)
434
            if os.path.exists(path):
435
                return path
436
        return None
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
437
438
    def provide_from_store_dir(self, target_dir):
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
439
        path = self.already_exists_in_store()
440
        if path is not None:
405.1.3 by James Westby
Allow merge-upstream to not repack .bz2 tarballs.
441
            repack_tarball(path, os.path.basename(path),
442
                    target_dir=target_dir, force_gz=False)
405.1.6 by James Westby
Tests for importing bz2.
443
            return path
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
444
        return path
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
445
405.1.1 by James Westby
Support for building using .tar.bz2|lzma.
446
    def _tarball_names(self):
447
        return [tarball_name(self.package, self.version.upstream_version),
448
                tarball_name(self.package, self.version.upstream_version, format='bz2'),
449
                tarball_name(self.package, self.version.upstream_version, format='lzma')]
327.2.10 by James Westby
Re-instate split mode.
450
327.2.1 by James Westby
Create UpstreamProvider to extract out the pristine-tar/uscan/etc. logic
451
452
class _MissingUpstreamProvider(UpstreamProvider):
453
    """For tests"""
454
455
    def __init__(self):
456
        pass
457
458
    def provide(self, target_dir):
459
        raise MissingUpstreamTarball("test_tarball")
460
461
462
class _TouchUpstreamProvider(UpstreamProvider):
463
    """For tests"""
464
465
    def __init__(self, desired_tarball_name):
466
        self.desired_tarball_name = desired_tarball_name
467
468
    def provide(self, target_dir):
469
        f = open(os.path.join(target_dir, self.desired_tarball_name), "wb")
470
        f.write("I am a tarball, honest\n")
471
        f.close()
327.2.6 by James Westby
Improve MergeModeDistiller.
472
334.4.4 by Jelmer Vernooij
Move all source functions onto objects.
473
327.2.6 by James Westby
Improve MergeModeDistiller.
474
class _SimpleUpstreamProvider(UpstreamProvider):
475
    """For tests"""
476
477
    def __init__(self, package, version, store_dir):
478
        self.package = package
327.1.27 by James Westby
Handle the change to Version objects in UpstreamExporter.
479
        self.version = Version(version)
327.2.6 by James Westby
Improve MergeModeDistiller.
480
        self.store_dir = store_dir
481
482
    def provide(self, target_dir):
408 by James Westby
Fix the upstream tests.
483
        path = (self.already_exists_in_target(target_dir)
484
                or self.provide_from_store_dir(target_dir))
485
        if path is not None:
486
            return path
411 by James Westby
Fix all the upstream tests.
487
        raise MissingUpstreamTarball(self._tarball_names()[0])