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
|
# Copyright 1999-2012 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
import functools
from _emerge.AsynchronousLock import AsynchronousLock
import portage
from portage import os
from portage.exception import PortageException
from portage.util.SlotObject import SlotObject
class EbuildBuildDir(SlotObject):
__slots__ = ("scheduler", "settings",
"locked", "_catdir", "_lock_obj")
def __init__(self, **kwargs):
SlotObject.__init__(self, **kwargs)
self.locked = False
def _assert_lock(self, async_lock):
if async_lock.returncode != os.EX_OK:
# TODO: create a better way to propagate this error to the caller
raise AssertionError("AsynchronousLock failed with returncode %s" \
% (async_lock.returncode,))
def clean_log(self):
"""Discard existing log. The log will not be be discarded
in cases when it would not make sense, like when FEATURES=keepwork
is enabled."""
settings = self.settings
if 'keepwork' in settings.features:
return
log_file = settings.get('PORTAGE_LOG_FILE')
if log_file is not None and os.path.isfile(log_file):
try:
os.unlink(log_file)
except OSError:
pass
def async_lock(self):
"""
Acquire the lock asynchronously. Notification is available
via the add_done_callback method of the returned Future instance.
This raises an AlreadyLocked exception if async_lock() is called
while a lock is already held. In order to avoid this, call
async_unlock() or check whether the "locked" attribute is True
or False before calling async_lock().
@returns: Future, result is None
"""
if self._lock_obj is not None:
raise self.AlreadyLocked((self._lock_obj,))
dir_path = self.settings.get('PORTAGE_BUILDDIR')
if not dir_path:
raise AssertionError('PORTAGE_BUILDDIR is unset')
catdir = os.path.dirname(dir_path)
self._catdir = catdir
catdir_lock = AsynchronousLock(path=catdir, scheduler=self.scheduler)
builddir_lock = AsynchronousLock(path=dir_path, scheduler=self.scheduler)
result = self.scheduler.create_future()
def catdir_locked(catdir_lock):
try:
self._assert_lock(catdir_lock)
except AssertionError as e:
result.set_exception(e)
return
try:
portage.util.ensure_dirs(catdir,
gid=portage.portage_gid,
mode=0o70, mask=0)
except PortageException as e:
if not os.path.isdir(catdir):
result.set_exception(e)
return
builddir_lock.addExitListener(builddir_locked)
builddir_lock.start()
def builddir_locked(builddir_lock):
try:
self._assert_lock(builddir_lock)
except AssertionError as e:
catdir_lock.async_unlock.add_done_callback(
functools.partial(catdir_unlocked, exception=e))
return
self._lock_obj = builddir_lock
self.locked = True
self.settings['PORTAGE_BUILDDIR_LOCKED'] = '1'
catdir_lock.async_unlock().add_done_callback(catdir_unlocked)
def catdir_unlocked(future, exception=None):
if not (exception is None and future.exception() is None):
result.set_exception(exception or future.exception())
else:
result.set_result(None)
try:
portage.util.ensure_dirs(os.path.dirname(catdir),
gid=portage.portage_gid,
mode=0o70, mask=0)
except PortageException:
if not os.path.isdir(os.path.dirname(catdir)):
raise
catdir_lock.addExitListener(catdir_locked)
catdir_lock.start()
return result
def async_unlock(self):
"""
Release the lock asynchronously. Release notification is available
via the add_done_callback method of the returned Future instance.
@returns: Future, result is None
"""
result = self.scheduler.create_future()
def builddir_unlocked(future):
if future.exception() is not None:
result.set_exception(future.exception())
else:
self._lock_obj = None
self.locked = False
self.settings.pop('PORTAGE_BUILDDIR_LOCKED', None)
catdir_lock = AsynchronousLock(
path=self._catdir, scheduler=self.scheduler)
catdir_lock.addExitListener(catdir_locked)
catdir_lock.start()
def catdir_locked(catdir_lock):
if catdir_lock.wait() != os.EX_OK:
result.set_result(None)
else:
try:
os.rmdir(self._catdir)
except OSError:
pass
catdir_lock.async_unlock().add_done_callback(catdir_unlocked)
def catdir_unlocked(future):
if future.exception() is None:
result.set_result(None)
else:
result.set_exception(future.exception())
if self._lock_obj is None:
self.scheduler.call_soon(result.set_result, None)
else:
self._lock_obj.async_unlock().add_done_callback(builddir_unlocked)
return result
class AlreadyLocked(portage.exception.PortageException):
pass
|