-
Notifications
You must be signed in to change notification settings - Fork 31
/
repo_ops.py
173 lines (142 loc) · 6.2 KB
/
repo_ops.py
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
165
166
167
168
169
170
171
172
173
__all__ = ("install", "uninstall", "replace", "operations")
from itertools import chain
import os
import shutil
import time
from snakeoil import compression
from snakeoil.data_source import local_source
from snakeoil.osutils import ensure_dirs, pjoin, normpath
from snakeoil.version import get_version
from pkgcore import __title__
from pkgcore.ebuild import conditionals
from pkgcore.log import logger
from pkgcore.operations import repo as repo_ops
from pkgcore.vdb.contents import ContentsFile
def update_mtime(path, timestamp=None):
if timestamp is None:
timestamp = time.time()
logger.debug(f"updating vdb timestamp for {path!r}")
try:
os.utime(path, (timestamp, timestamp))
except EnvironmentError as e:
logger.error(f"failed updated vdb timestamp for {path!r}: {e}")
class install(repo_ops.install):
def __init__(self, repo, newpkg, observer):
base = pjoin(repo.location, newpkg.category)
dirname = f"{newpkg.package}-{newpkg.fullver}"
self.install_path = pjoin(base, dirname)
self.tmp_write_path = pjoin(base, f".tmp.{dirname}")
super().__init__(repo, newpkg, observer)
def add_data(self, domain):
# error checking?
dirpath = self.tmp_write_path
ensure_dirs(dirpath, mode=0o755, minimal=True)
update_mtime(self.repo.location)
rewrite = self.repo._metadata_rewrites
for k in self.new_pkg.tracked_attributes:
if k == "contents":
v = ContentsFile(pjoin(dirpath, "CONTENTS"),
mutable=True, create=True)
v.update(self.new_pkg.contents)
v.flush()
elif k == "environment":
data = compression.compress_data('bzip2',
self.new_pkg.environment.bytes_fileobj().read())
with open(pjoin(dirpath, "environment.bz2"), "wb") as f:
f.write(data)
del data
else:
v = getattr(self.new_pkg, k)
if k in ('bdepend', 'depend', 'rdepend'):
s = v.slotdep_str(domain)
elif k == 'user_patches':
s = '\n'.join(chain.from_iterable(files for _, files in v))
elif not isinstance(v, str):
try:
s = ' '.join(v)
except TypeError:
s = str(v)
else:
s = v
with open(pjoin(dirpath, rewrite.get(k, k.upper())), "w", 32768) as f:
if s:
s += '\n'
f.write(s)
# ebuild_data is the actual ebuild- no point in holding onto
# it for built ebuilds, but if it's there, we store it.
o = getattr(self.new_pkg, "ebuild", None)
if o is None:
logger.warning(
"doing install/replace op, "
"but source package doesn't provide the actual ebuild data. "
"Creating an empty file")
o = ''
else:
o = o.bytes_fileobj().read()
# XXX lil hackish accessing PF
with open(pjoin(dirpath, self.new_pkg.PF + ".ebuild"), "wb") as f:
f.write(o)
# install NEEDED and NEEDED.ELF.2 files from tmpdir if they exist
pkg_tmpdir = normpath(pjoin(domain.pm_tmpdir, self.new_pkg.category,
self.new_pkg.PF, 'temp'))
for f in ['NEEDED', 'NEEDED.ELF.2']:
fp = pjoin(pkg_tmpdir, f)
if os.path.exists(fp):
local_source(fp).transfer_to_path(pjoin(dirpath, f))
# XXX finally, hack to keep portage from doing stupid shit.
# relies on counter to discern what to punt during
# merging/removal, we don't need that crutch however. problem?
# No counter file, portage wipes all of our merges (friendly
# bugger).
# need to get zmedico to localize the counter
# creation/counting to per CP for this trick to behave
# perfectly.
with open(pjoin(dirpath, "COUNTER"), "w") as f:
f.write(str(int(time.time())))
# finally, we mark who made this.
with open(pjoin(dirpath, "PKGMANAGER"), "w") as f:
f.write(get_version(__title__, __file__))
return True
def finalize_data(self):
os.rename(self.tmp_write_path, self.install_path)
update_mtime(self.repo.location)
return True
class uninstall(repo_ops.uninstall):
def __init__(self, repo, pkg, observer):
self.remove_path = pjoin(
repo.location, pkg.category, pkg.package+"-"+pkg.fullver)
super().__init__(repo, pkg, observer)
def remove_data(self):
return True
def finalize_data(self):
update_mtime(self.repo.location)
shutil.rmtree(self.remove_path)
update_mtime(self.repo.location)
return True
# should convert these to mixins.
class replace(repo_ops.replace, install, uninstall):
def __init__(self, repo, pkg, newpkg, observer):
uninstall.__init__(self, repo, pkg, observer)
install.__init__(self, repo, newpkg, observer)
remove_data = uninstall.remove_data
def add_data(self, domain):
return install.add_data(self, domain)
def finalize_data(self):
# XXX: should really restructure this into
# a rename of the unmerge dir, rename merge into it's place (for
# literal same fullver replacements), then wipe the unmerge
# that minimizes the window for races, and gets the data in place
# should unmerge somehow die.
uninstall.finalize_data(self)
install.finalize_data(self)
return True
class operations(repo_ops.operations):
def _cmd_implementation_install(self, pkg, observer):
return install(self.repo, pkg, observer)
def _cmd_implementation_uninstall(self, pkg, observer):
return uninstall(self.repo, pkg, observer)
def _cmd_implementation_replace(self, oldpkg, newpkg, observer):
return replace(self.repo, oldpkg, newpkg, observer)
def _cmd_api_regen_cache(self, *args, **kwargs):
# disable threaded cache updates
super()._cmd_api_regen_cache(*args, threads=1, **kwargs)