~bzr-pqm/bzr/bzr.dev

5724.1.3 by John Arbash Meinel
Change the exporters to ensure that we are writing the data out in binary mode.
1
# Copyright (C) 2005, 2006, 2008-2011 Canonical Ltd
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
2
#
1185.31.12 by John Arbash Meinel
Refactored the export code to make it easier to add new export formats.
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
7
#
1185.31.12 by John Arbash Meinel
Refactored the export code to make it easier to add new export formats.
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
1773.4.1 by Martin Pool
Add pyflakes makefile target; fix many warnings
12
#
1185.31.12 by John Arbash Meinel
Refactored the export code to make it easier to add new export formats.
13
# You should have received a copy of the GNU General Public License
14
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1185.31.12 by John Arbash Meinel
Refactored the export code to make it easier to add new export formats.
16
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
17
"""Export a tree to a tarball."""
18
6379.6.3 by Jelmer Vernooij
Use absolute_import.
19
from __future__ import absolute_import
20
5718.5.15 by Jelmer Vernooij
Only write out basename of the tarfile to the gzip file.
21
import os
3368.2.32 by Ian Clatworthy
add --filters to export command
22
import StringIO
23
import sys
1185.31.12 by John Arbash Meinel
Refactored the export code to make it easier to add new export formats.
24
import tarfile
3408.7.1 by Martin Pool
Support tarball export to stdout
25
5718.1.1 by Jelmer Vernooij
Fix import of BzrError.
26
from bzrlib import (
27
    errors,
28
    osutils,
29
    )
3613.2.2 by Robert Collins
Refactor exporters to remove obvious duplication to a helper function.
30
from bzrlib.export import _export_iter_entries
5718.5.2 by Jelmer Vernooij
Factor out export_tarball.
31
5967.6.2 by Martin Pool
Delete fairly useless and repetitive per-format export single-call functions.
32
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
33
def prepare_tarball_item(tree, root, final_path, tree_path, entry, force_mtime=None):
5952.1.17 by geoffreyfishing at gmail
Renamed export item function in tar exporter.
34
    """Prepare a tarball item for exporting
5967.6.2 by Martin Pool
Delete fairly useless and repetitive per-format export single-call functions.
35
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
36
    :param tree: Tree to export
5952.1.14 by geoffreyfishing at gmail
Cleaned up code and removed unnecessary arguments.
37
    :param final_path: Final path to place item
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
38
    :param tree_path: Path for the entry in the tree
5952.1.14 by geoffreyfishing at gmail
Cleaned up code and removed unnecessary arguments.
39
    :param entry: Entry to export
5967.6.2 by Martin Pool
Delete fairly useless and repetitive per-format export single-call functions.
40
    :param force_mtime: Option mtime to force, instead of using tree
41
        timestamps.
42
5952.1.17 by geoffreyfishing at gmail
Renamed export item function in tar exporter.
43
    Returns a (tarinfo, fileobj) tuple
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
44
    """
5952.1.14 by geoffreyfishing at gmail
Cleaned up code and removed unnecessary arguments.
45
    filename = osutils.pathjoin(root, final_path).encode('utf8')
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
46
    item = tarfile.TarInfo(filename)
47
    if force_mtime is not None:
48
        item.mtime = force_mtime
49
    else:
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
50
        item.mtime = tree.get_file_mtime(entry.file_id, tree_path)
5952.1.14 by geoffreyfishing at gmail
Cleaned up code and removed unnecessary arguments.
51
    if entry.kind == "file":
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
52
        item.type = tarfile.REGTYPE
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
53
        if tree.is_executable(entry.file_id, tree_path):
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
54
            item.mode = 0755
55
        else:
56
            item.mode = 0644
6006.3.4 by Martin Pool
Support exporting tarballs from ContentFilterTree
57
        # This brings the whole file into memory, but that's almost needed for
58
        # the tarfile contract, which wants the size of the file up front.  We
59
        # want to make sure it doesn't change, and we need to read it in one
60
        # go for content filtering.
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
61
        content = tree.get_file_text(entry.file_id, tree_path)
6006.3.4 by Martin Pool
Support exporting tarballs from ContentFilterTree
62
        item.size = len(content)
63
        fileobj = StringIO.StringIO(content)
5952.1.14 by geoffreyfishing at gmail
Cleaned up code and removed unnecessary arguments.
64
    elif entry.kind == "directory":
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
65
        item.type = tarfile.DIRTYPE
66
        item.name += '/'
67
        item.size = 0
68
        item.mode = 0755
69
        fileobj = None
5952.1.14 by geoffreyfishing at gmail
Cleaned up code and removed unnecessary arguments.
70
    elif entry.kind == "symlink":
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
71
        item.type = tarfile.SYMTYPE
72
        item.size = 0
73
        item.mode = 0755
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
74
        item.linkname = tree.get_symlink_target(entry.file_id, tree_path)
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
75
        fileobj = None
76
    else:
5952.1.26 by Vincent Ladeuil
Fix the test failure, streams should be closed. In the right order.
77
        raise errors.BzrError("don't know how to export {%s} of kind %r"
5952.1.18 by geoffreyfishing at gmail
Fixed line ending problems.
78
                              % (entry.file_id, entry.kind))
5952.1.4 by geoffreyfishing at gmail
Cleaned up export_tarball_item.
79
    return (item, fileobj)
1185.31.12 by John Arbash Meinel
Refactored the export code to make it easier to add new export formats.
80
5967.6.2 by Martin Pool
Delete fairly useless and repetitive per-format export single-call functions.
81
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
82
def export_tarball_generator(tree, ball, root, subdir=None, force_mtime=None):
5967.6.3 by Martin Pool
Further shrink export code
83
    """Export tree contents to a tarball.
84
85
    :returns: A generator that will repeatedly produce None as each file is
86
        emitted.  The entire generator must be consumed to complete writing
87
        the file.
5952.1.3 by geoffreyfishing at gmail
Updated code to requested format.
88
89
    :param tree: Tree to export
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
90
5967.6.3 by Martin Pool
Further shrink export code
91
    :param ball: Tarball to export to; it will be closed when writing is
92
        complete.
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
93
5952.1.3 by geoffreyfishing at gmail
Updated code to requested format.
94
    :param subdir: Sub directory to export
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
95
96
    :param force_mtime: Option mtime to force, instead of using tree
97
        timestamps.
5952.1.3 by geoffreyfishing at gmail
Updated code to requested format.
98
    """
5967.6.4 by Martin Pool
Close tarball from finally block (thanks jam)
99
    try:
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
100
        for final_path, tree_path, entry in _export_iter_entries(tree, subdir):
5967.6.4 by Martin Pool
Close tarball from finally block (thanks jam)
101
            (item, fileobj) = prepare_tarball_item(
6331.2.1 by Jelmer Vernooij
Consistently pass tree path when exporting.
102
                tree, root, final_path, tree_path, entry, force_mtime)
5967.6.4 by Martin Pool
Close tarball from finally block (thanks jam)
103
            ball.addfile(item, fileobj)
104
            yield
105
    finally:
106
        ball.close()
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
107
108
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
109
def tgz_exporter_generator(tree, dest, root, subdir, force_mtime=None,
110
    fileobj=None):
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
111
    """Export this tree to a new tar file.
112
113
    `dest` will be created holding the contents of this tree; if it
114
    already exists, it will be clobbered, like with "tar -c".
115
    """
116
    import gzip
5718.5.16 by Jelmer Vernooij
Use revision tree timestamp if possible.
117
    if force_mtime is not None:
118
        root_mtime = force_mtime
119
    elif (getattr(tree, "repository", None) and
120
          getattr(tree, "get_revision_id", None)):
121
        # If this is a revision tree, use the revisions' timestamp
122
        rev = tree.repository.get_revision(tree.get_revision_id())
123
        root_mtime = rev.timestamp
124
    elif tree.get_root_id() is not None:
5718.5.15 by Jelmer Vernooij
Only write out basename of the tarfile to the gzip file.
125
        root_mtime = tree.get_file_mtime(tree.get_root_id())
5718.5.9 by Jelmer Vernooij
Add test for export zip to stdout.
126
    else:
5718.5.23 by Jelmer Vernooij
Leave default timestamp.
127
        root_mtime = None
5861.2.2 by Wouter van Heyst
Heavy handed approach to ensuring all streams are closed
128
129
    is_stdout = False
6018.1.1 by Martin Pool
Avoid unset local variable
130
    basename = None
5952.1.11 by geoffreyfishing at gmail
Implemented fileobj on tgz_exporter.
131
    if fileobj is not None:
132
        stream = fileobj
133
    elif dest == '-':
5718.5.22 by Jelmer Vernooij
Cope with mtime not always being available.
134
        stream = sys.stdout
5861.2.2 by Wouter van Heyst
Heavy handed approach to ensuring all streams are closed
135
        is_stdout = True
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
136
    else:
5724.1.4 by John Arbash Meinel
actually make the plain_tar export work again.
137
        stream = open(dest, 'wb')
5718.5.15 by Jelmer Vernooij
Only write out basename of the tarfile to the gzip file.
138
        # gzip file is used with an explicit fileobj so that
139
        # the basename can be stored in the gzip file rather than
140
        # dest. (bug 102234)
5718.5.22 by Jelmer Vernooij
Cope with mtime not always being available.
141
        basename = os.path.basename(dest)
142
    try:
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
143
        zipstream = gzip.GzipFile(basename, 'w', fileobj=stream,
5952.1.18 by geoffreyfishing at gmail
Fixed line ending problems.
144
                                  mtime=root_mtime)
5718.5.22 by Jelmer Vernooij
Cope with mtime not always being available.
145
    except TypeError:
146
        # Python < 2.7 doesn't support the mtime argument
5861.2.2 by Wouter van Heyst
Heavy handed approach to ensuring all streams are closed
147
        zipstream = gzip.GzipFile(basename, 'w', fileobj=stream)
148
    ball = tarfile.open(None, 'w|', fileobj=zipstream)
5967.6.3 by Martin Pool
Further shrink export code
149
    for _ in export_tarball_generator(
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
150
        tree, ball, root, subdir, force_mtime):
5952.1.15 by geoffreyfishing at gmail
Major code cleanup.
151
        yield
5952.1.26 by Vincent Ladeuil
Fix the test failure, streams should be closed. In the right order.
152
    # Closing zipstream may trigger writes to stream
5952.1.15 by geoffreyfishing at gmail
Major code cleanup.
153
    zipstream.close()
154
    if not is_stdout:
5952.1.26 by Vincent Ladeuil
Fix the test failure, streams should be closed. In the right order.
155
        # Now we can safely close the stream
5952.1.15 by geoffreyfishing at gmail
Major code cleanup.
156
        stream.close()
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
157
158
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
159
def tbz_exporter_generator(tree, dest, root, subdir,
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
160
                           force_mtime=None, fileobj=None):
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
161
    """Export this tree to a new tar file.
162
163
    `dest` will be created holding the contents of this tree; if it
164
    already exists, it will be clobbered, like with "tar -c".
165
    """
5952.1.12 by geoffreyfishing at gmail
Implemented fileobj in tbz_exporter.
166
    if fileobj is not None:
167
        ball = tarfile.open(None, 'w|bz2', fileobj)
168
    elif dest == '-':
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
169
        ball = tarfile.open(None, 'w|bz2', sys.stdout)
170
    else:
5967.6.3 by Martin Pool
Further shrink export code
171
        # tarfile.open goes on to do 'os.getcwd() + dest' for opening the
172
        # tar file. With dest being unicode, this throws UnicodeDecodeError
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
173
        # unless we encode dest before passing it on. This works around
5967.6.3 by Martin Pool
Further shrink export code
174
        # upstream python bug http://bugs.python.org/issue8396 (fixed in
175
        # Python 2.6.5 and 2.7b1)
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
176
        ball = tarfile.open(dest.encode(osutils._fs_enc), 'w:bz2')
5967.6.3 by Martin Pool
Further shrink export code
177
    return export_tarball_generator(
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
178
        tree, ball, root, subdir, force_mtime)
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
179
180
181
def plain_tar_exporter_generator(tree, dest, root, subdir, compression=None,
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
182
    force_mtime=None, fileobj=None):
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
183
    """Export this tree to a new tar file.
184
185
    `dest` will be created holding the contents of this tree; if it
186
    already exists, it will be clobbered, like with "tar -c".
187
    """
5952.1.13 by geoffreyfishing at gmail
Adjusted .tar.lzma
188
    if fileobj is not None:
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
189
        stream = fileobj
5952.1.13 by geoffreyfishing at gmail
Adjusted .tar.lzma
190
    elif dest == '-':
5718.5.15 by Jelmer Vernooij
Only write out basename of the tarfile to the gzip file.
191
        stream = sys.stdout
5718.5.4 by Jelmer Vernooij
fix timestamp in tgz files.
192
    else:
5724.1.3 by John Arbash Meinel
Change the exporters to ensure that we are writing the data out in binary mode.
193
        stream = open(dest, 'wb')
5718.5.15 by Jelmer Vernooij
Only write out basename of the tarfile to the gzip file.
194
    ball = tarfile.open(None, 'w|', stream)
5967.6.3 by Martin Pool
Further shrink export code
195
    return export_tarball_generator(
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
196
        tree, ball, root, subdir, force_mtime)
197
198
199
def tar_xz_exporter_generator(tree, dest, root, subdir,
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
200
                              force_mtime=None, fileobj=None):
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
201
    return tar_lzma_exporter_generator(tree, dest, root, subdir,
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
202
                                       force_mtime, fileobj, "xz")
203
204
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
205
def tar_lzma_exporter_generator(tree, dest, root, subdir,
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
206
                      force_mtime=None, fileobj=None,
207
                                compression_format="alone"):
5718.5.17 by Jelmer Vernooij
Support tar.lzma.
208
    """Export this tree to a new .tar.lzma file.
5718.5.10 by Jelmer Vernooij
Support creating .tar.xz files.
209
210
    `dest` will be created holding the contents of this tree; if it
211
    already exists, it will be clobbered, like with "tar -c".
212
    """
5718.5.11 by Jelmer Vernooij
Tests, tests, tests.
213
    if dest == '-':
5718.5.17 by Jelmer Vernooij
Support tar.lzma.
214
        raise errors.BzrError("Writing to stdout not supported for .tar.lzma")
5718.5.11 by Jelmer Vernooij
Tests, tests, tests.
215
5952.1.13 by geoffreyfishing at gmail
Adjusted .tar.lzma
216
    if fileobj is not None:
5952.2.1 by Vincent Ladeuil
PEP8 tweaks, lines too long, spaces at end of lines.
217
        raise errors.BzrError(
218
            "Writing to fileobject not supported for .tar.lzma")
5718.5.10 by Jelmer Vernooij
Support creating .tar.xz files.
219
    try:
220
        import lzma
221
    except ImportError, e:
222
        raise errors.DependencyNotPresent('lzma', e)
223
5718.5.17 by Jelmer Vernooij
Support tar.lzma.
224
    stream = lzma.LZMAFile(dest.encode(osutils._fs_enc), 'w',
5967.6.3 by Martin Pool
Further shrink export code
225
        options={"format": compression_format})
5718.5.15 by Jelmer Vernooij
Only write out basename of the tarfile to the gzip file.
226
    ball = tarfile.open(None, 'w:', fileobj=stream)
5967.6.3 by Martin Pool
Further shrink export code
227
    return export_tarball_generator(
6006.3.7 by Martin Pool
Remove duplicated content-filtering code from exporters
228
        tree, ball, root, subdir, force_mtime=force_mtime)