aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'lib/_emerge/EbuildBuildDir.py')
-rw-r--r--lib/_emerge/EbuildBuildDir.py161
1 files changed, 161 insertions, 0 deletions
diff --git a/lib/_emerge/EbuildBuildDir.py b/lib/_emerge/EbuildBuildDir.py
new file mode 100644
index 000000000..477113db8
--- /dev/null
+++ b/lib/_emerge/EbuildBuildDir.py
@@ -0,0 +1,161 @@
+# 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
+