diff options
Diffstat (limited to 'roverlay/overlay/package.py')
-rw-r--r-- | roverlay/overlay/package.py | 290 |
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 (...) --- |