aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'roverlay/overlay/package.py')
-rw-r--r--roverlay/overlay/package.py290
1 files changed, 290 insertions, 0 deletions
diff --git a/roverlay/overlay/package.py b/roverlay/overlay/package.py
new file mode 100644
index 0000000..d43ec1f
--- /dev/null
+++ b/roverlay/overlay/package.py
@@ -0,0 +1,290 @@
+# R Overlay -- <comment TODO>
+# Copyright 2006-2012 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+import threading
+import os.path
+import sys
+
+from roverlay.metadata import MetadataJob
+
+SUPPRESS_EXCEPTIONS = True
+
+class PackageDir ( object ):
+
+ def __init__ ( self, name, logger, directory ):
+ """Initializes a PackageDir which contains ebuilds, metadata and
+ a Manifest file.
+
+ arguments:
+ * name -- name of the directory (${PN} in ebuilds)
+ * logger -- parent logger
+ * directory -- filesystem location of this PackageDir
+ """
+ self.logger = logger.getChild ( name )
+ self._lock = threading.RLock()
+ self._packages = dict()
+ self._metadata = None
+ self.physical_location = directory
+ # --- end of __init__ (...) ---
+
+ def empty ( self ):
+ """Returns True if no ebuilds stored, else False."""
+ return len ( self._packages ) == 0
+ # --- end of empty (...) ---
+
+ def _get_metadata_filepath ( self ):
+ """Returns the path to the metadata file."""
+ return os.path.join (
+ '??' if self.physical_location is None else self.physical_location,
+ self._metadata.filename
+ )
+ # --- end of _get_metadata_filepath (...) ---
+
+ def _get_ebuild_filepath ( self, pvr ):
+ """Returns the path to the ebuild file.
+
+ arguments:
+ * pvr -- version number with the revision (${PVR} in ebuilds)
+ """
+ filename = "%s-%s.ebuild" % ( self.name, pvr )
+ return os.path.join (
+ '??' if self.physical_location is None else self.physical_location,
+ filename
+ )
+ # --- end of _get_ebuild_filepath (...) ---
+
+ def write ( self ):
+ """Writes this directory to its (existent!) filesystem location.
+
+ returns: None (implicit)
+
+ raises: !! TODO
+ """
+ if self.physical_location is None:
+ raise Exception ( "cannot write - no directory assigned!" )
+
+ self._lock.acquire()
+ self._regen_metadata()
+
+ # mkdir not required here, overlay.Category does this
+
+ # write ebuilds
+ for ver, p_info in self._packages.items():
+ fh = None
+ try:
+ efile = self._get_ebuild_filepath ( ver )
+ ebuild = p_info.ebuild
+
+ fh = open ( efile, 'w' )
+ ebuild.write ( fh )
+ if fh: fh.close()
+
+ # adjust owner/perm? TODO
+ # chmod 0644 or 0444
+ # chown 250.250
+
+ # this marks the package as 'written to fs'
+ # TODO update PackageInfo
+ p_info ['ebuild_filepath'] = efile
+
+ self.logger.info ( "Wrote ebuild %s." % efile )
+ except IOError as e:
+ if fh: fh.close()
+ self.logger.error ( "Couldn't write ebuild %s." % efile )
+ self.logger.exception ( e )
+
+ # write metadata
+ fh = None
+ try:
+ mfile = self._get_metadata_filepath()
+
+ fh = open ( mfile, 'w' )
+ self._metadata.write ( fh )
+ if fh: fh.close()
+
+ except IOError as e:
+ if fh: fh.close()
+ self.logger.error ( "Failed to write metadata at %s." % mfile )
+ self.logger.exception ( e )
+
+ self.generate_manifest()
+
+ self._lock.release()
+ # --- end of write (...) ---
+
+ def show ( self, stream=sys.stderr ):
+ """Prints this dir (the ebuilds and the metadata) into a stream.
+
+ arguments:
+ * stream -- stream to use, defaults to sys.stderr
+
+ returns: None (implicit)
+
+ raises: !! TODO
+ """
+ self._lock.acquire()
+ self._regen_metadata()
+
+
+ for ver, p_info in self._packages.items():
+ efile = self._get_ebuild_filepath ( ver )
+ ebuild = p_info.ebuild
+
+ stream.write ( "[BEGIN ebuild %s]\n" % efile )
+ ebuild.write ( stream )
+ stream.write ( "[END ebuild %s]\n" % efile )
+
+ mfile = self._get_metadata_filepath()
+
+ stream.write ( "[BEGIN %s]\n" % mfile )
+ self._metadata.write ( stream )
+ stream.write ( "[END %s]\n" % mfile )
+
+
+ self._lock.release()
+ # --- end of show (...) ---
+
+ def _latest_package ( self, pkg_filter=None, use_lock=False ):
+ """Returns the package info with the highest version number.
+
+ arguments:
+ * pkg_filter -- either None or a callable,
+ None: do not filter packages
+ else: ignore package if it does not pass the filter
+ * use_lock -- if True: hold lock while searching
+ """
+ first = True
+ retver = None
+ retpkg = None
+
+ if use_lock: self._lock.acquire()
+ for p in self._packages.values():
+ if pkg_filter is None or pkg_filter ( p ):
+ newver = p ['version']
+ if first or newver > retver:
+ retver = newver
+ retpkg = p
+ first = False
+
+ if use_lock: self._lock.release()
+ return retpkg
+ # --- end of _latest_package (...) ---
+
+ def add ( self, package_info ):
+ """Adds a package to this PackageDir.
+
+ arguments:
+ * package_info --
+
+ returns: success as bool
+
+ raises: Exception when ebuild already exists.
+ """
+ # !! p info key TODO
+ shortver = package_info ['ebuild_verstr']
+
+ def already_exists ( release=False ):
+ if filename in self._packages:
+
+ if release: self._lock.release()
+
+ msg = "'%s-%s.ebuild' already exists, cannot add it!" % (
+ self.name, shortver
+ )
+ if SUPPRESS_EXCEPTIONS:
+ logger.warning ( msg )
+ else:
+ raise Exception ( msg )
+
+ return True
+ else:
+ return False
+ # --- end of already_exists (...) ---
+
+ if already_exists ( release=False ): return False
+ self._lock.acquire()
+ if already_exists ( release=True ): return False
+
+ self._packages [shortver] = package_info
+
+ self._lock.release()
+ return True
+ # --- end of add (...) ---
+
+ def _regen_metadata ( self ):
+ """Regenerates the metadata."""
+ self.generate_metadata (
+ skip_if_existent=True,
+ use_all_packages=False,
+ use_old_metadata=False
+ )
+ # --- end of _regen_metadata (...) ---
+
+ def generate_metadata (
+ self,
+ skip_if_existent=False, use_all_packages=False, use_old_metadata=False
+ ):
+ """Creates metadata for this package.
+
+ arguments:
+ * skip_if_existent -- do not create if metadata already exist
+ * use_all_packages -- TODO
+ * use_old_metadata -- TODO
+ """
+ if use_old_metadata or use_all_packages:
+ raise Exception ( "using >1 package for metadata.xml is TODO!" )
+
+ if skip_if_existent and not self._metadata is None:
+ return
+
+ self._lock.acquire()
+
+ if self._metadata is None or not use_old_metadata:
+ del self._metadata
+ self._metadata = MetadataJob ( self.logger )
+
+ if use_all_packages:
+ for p_info in self._packages:
+ self._metadata.update ( p_info )
+ else:
+ self._metadata.update ( _latest_package() )
+
+ self._lock.release()
+ # --- end of generate_metadata (...) ---
+
+ def generate_manifest ( self ):
+ """Generates the Manifest file for this package.
+
+ expects: called in self.write(), after writing metadata/ebuilds
+
+ returns: None (implicit)
+
+ raises: !! TODO
+ * Exception if not physical
+ """
+ if self.physical_location is None:
+ raise Exception ( "no directory assigned." )
+
+ # it should be sufficient to call create_manifest for one ebuild,
+ # choosing the latest one here that exists in self.physical_location.
+ #
+ # metadata.xml's full path cannot be used for manifest creation here
+ # 'cause DISTDIR would be unknown
+ #
+ pkg_info_for_manifest = _latest_package (
+ pkg_filter=lambda pkg : not pkg ['ebuild_filepath'] is None,
+ use_lock=True
+ )
+
+ if pkg_info_for_manifest is None:
+ # ? FIXME
+ raise Exception (
+ "No ebuild written so far! I really don't know what do to!"
+ )
+ else:
+ # TODO: manifest creation interface is single threaded,
+ # may want to 'fix' this later
+ manifest.create_manifest ( pkg_info_for_manifest, nofail=False )
+
+ # --- end of generate_manifest (...) ---