1
# Copyright (C) 2013 Canonical Ltd.
2
# Author: Colin Watson <cjwatson@ubuntu.com>
4
# This program is free software: you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; version 3 of the License.
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.
13
# You should have received a copy of the GNU General Public License
14
# along with this program. If not, see <http://www.gnu.org/licenses/>.
16
"""Basic support for writing ar archive files.
18
We do things this way so that Click packages can be created with minimal
19
dependencies (e.g. on non-Ubuntu systems). No read support is needed, since
20
Click packages are always installed on systems that have dpkg.
22
Some method names and general approach come from the tarfile module in
23
Python's standard library; details of the format come from dpkg.
26
from __future__ import print_function
39
def __init__(self, name=None, mode="w", fileobj=None):
41
raise ValueError("only mode 'w' is supported")
46
if name is None and hasattr(fileobj, "name"):
48
if hasattr(fileobj, "mode"):
49
if fileobj.mode != "wb":
50
raise ValueError("fileobj must be opened with mode='wb'")
51
self._mode = fileobj.mode
52
self.opened_fileobj = False
54
fileobj = open(name, self.real_mode)
55
self.opened_fileobj = True
57
self.fileobj = fileobj
61
if self.opened_fileobj:
67
raise IOError("ArFile %s is closed" % self.name)
73
def __exit__(self, *args):
77
self.fileobj.write(b"!<arch>\n")
79
def add_header(self, name, size):
81
raise ValueError("ar member name '%s' length too long" % name)
83
raise ValueError("ar member size %d too large" % size)
84
header = ("%-16s%-12u0 0 100644 %-10d`\n" % (
85
name, int(time.time()), size)).encode()
86
assert len(header) == 60 # sizeof(struct ar_hdr)
87
self.fileobj.write(header)
89
def add_data(self, name, data):
91
self.add_header(name, size)
92
self.fileobj.write(data)
94
self.fileobj.write(b"\n") # padding
96
def add_file(self, name, path):
97
with open(path, "rb") as fobj:
98
size = os.fstat(fobj.fileno()).st_size
99
self.add_header(name, size)
100
shutil.copyfileobj(fobj, self.fileobj)
102
self.fileobj.write(b"\n") # padding