public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
* [gentoo-commits] proj/R_overlay:gsoc13/next commit in: roverlay/overlay/pkgdir/manifest/, roverlay/tools/, roverlay/config/, roverlay/, ...
  2013-06-13 16:34 [gentoo-commits] proj/R_overlay:master commit in: roverlay/overlay/pkgdir/manifest/, roverlay/tools/, roverlay/config/, roverlay/, André Erdmann
@ 2013-06-06 21:15 ` André Erdmann
  0 siblings, 0 replies; 2+ messages in thread
From: André Erdmann @ 2013-06-06 21:15 UTC (permalink / raw
  To: gentoo-commits

commit:     47d9b844a010d8b4334facc784d2db1ab08f80c5
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Thu Jun  6 21:13:22 2013 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Thu Jun  6 21:13:22 2013 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=47d9b844

roverlay/overlay: additions dir

---
 roverlay/argutil.py                                | 30 ++++++-
 roverlay/config/const.py                           | 15 ++++
 roverlay/config/entrymap.py                        | 16 ++--
 roverlay/overlay/additionsdir.py                   | 98 ++++++++++++++++++++++
 roverlay/overlay/category.py                       | 13 ++-
 .../overlay/pkgdir/manifest/manifest_ebuild.py     |  4 +-
 roverlay/overlay/pkgdir/packagedir_base.py         | 65 +++++++++++---
 roverlay/overlay/root.py                           | 27 +++++-
 roverlay/tools/__init__.py                         |  0
 roverlay/tools/patch.py                            | 45 ++++++++++
 10 files changed, 286 insertions(+), 27 deletions(-)

diff --git a/roverlay/argutil.py b/roverlay/argutil.py
index 90d95df..99842fd 100644
--- a/roverlay/argutil.py
+++ b/roverlay/argutil.py
@@ -67,6 +67,12 @@ def get_parser ( command_map, default_config_file, default_command='create' ):
          )
       return d
 
+   def is_fs_dir_or_void ( value ):
+      if value:
+         return is_fs_dir ( value )
+      else:
+         return ''
+
    def is_fs_file_or_void ( value ):
       if value:
          return is_fs_file ( value )
@@ -180,6 +186,14 @@ def get_parser ( command_map, default_config_file, default_command='create' ):
    )
 
    arg (
+      '--additions-dir', '-A', # '--patch-dir',  '--ebuild-dir',
+      default=argparse.SUPPRESS,
+      help="directory containing patches and hand-written ebuilds",
+      metavar="<dir>",
+      type=is_fs_dir_or_void
+   )
+
+   arg (
       '--overlay-name', '-N', default=argparse.SUPPRESS,
       help="overlay name",
       metavar="<name>",
@@ -353,6 +367,8 @@ def parse_argv ( command_map, **kw ):
    All args/keywords are passed to get_parser().
    Passes all exceptions.
    """
+   p = get_parser ( command_map=command_map, **kw ).parse_args()
+
    def doconf ( value, path ):
       pos = conf
       if isinstance ( path, str ):
@@ -367,11 +383,17 @@ def parse_argv ( command_map, **kw ):
                pos [k] = dict()
 
             pos = pos [k]
+   # --- end of doconf (...) ---
 
+   def given ( attr_name ):
+      return hasattr ( p, attr_name )
+   # --- end of given (...) ---
 
-   p = get_parser ( command_map=command_map, **kw ).parse_args()
-
-   given = lambda kw : hasattr ( p, kw )
+   def doconf_simple ( attr_name, config_path ) :
+      value = getattr ( p, attr_name, None )
+      if value is not None:
+         doconf ( value, config_path )
+   # --- end of doconf_simple (...) ---
 
    commands = ( p.commands, ) if isinstance ( p.commands, str ) else p.commands
    conf  = dict()
@@ -397,6 +419,8 @@ def parse_argv ( command_map, **kw ):
       doconf ( p.overlay, 'OVERLAY.dir' )
       #extra ['write_overlay'] = True
 
+   doconf_simple ( "additions_dir", "OVERLAY.additions_dir" )
+
    if given ( 'overlay_name' ):
       doconf ( p.overlay_name, 'OVERLAY.name' )
 

diff --git a/roverlay/config/const.py b/roverlay/config/const.py
index 48e0e55..b3e8dc4 100644
--- a/roverlay/config/const.py
+++ b/roverlay/config/const.py
@@ -68,6 +68,21 @@ _CONSTANTS = dict (
       category                = 'sci-R',
       manifest_implementation = 'default',
    ),
+
+   TOOLS = dict (
+      EBUILD = dict (
+         prog   = "/usr/bin/ebuild",
+         target = "manifest",
+      ),
+      PATCH = dict (
+         exe    = "patch",
+         opts   = (
+            '--no-backup-if-mismatch',
+            '--reject-file=-'
+            '--quiet', '--force',
+         )
+      ),
+   ),
 )
 
 def lookup ( key, fallback_value=None ):

diff --git a/roverlay/config/entrymap.py b/roverlay/config/entrymap.py
index 77525a8..c98bc68 100644
--- a/roverlay/config/entrymap.py
+++ b/roverlay/config/entrymap.py
@@ -223,6 +223,11 @@ CONFIG_ENTRY_MAP = dict (
       '''
    ),
 
+   overlay_additions_dir = dict (
+      value_type  = 'fs_abs:fs_dir',
+      description = 'directory containing ebuilds and ebuild patches',
+   ),
+
    overlay_eclass = dict (
       path        = [ 'OVERLAY', 'eclass_files' ],
       value_type  = fs_abslist,
@@ -263,11 +268,11 @@ CONFIG_ENTRY_MAP = dict (
 
    overlay_distdir_root = dict (
       value_type  = 'fs_dir',
-      description = (
-         'DISTDIR which is used for Manifest creation and can, '
-         'depending on the DISTDIR strategy, '
-         'serve as package mirror directory',
-      ),
+      description = '''
+         DISTDIR which is used for Manifest creation and can,
+         depending on the DISTDIR strategy,
+         serve as package mirror directory.
+      '''
    ),
 
    overlay_distdir_strategy = dict (
@@ -293,6 +298,7 @@ CONFIG_ENTRY_MAP = dict (
    eclass                    = 'overlay_eclass',
    keep_nth_latest           = 'overlay_keep_nth_latest',
    manifest_implementation   = 'overlay_manifest_implementation',
+   additions_dir             = 'overlay_additions_dir',
    distdir                   = 'overlay_distdir_root',
    distdir_strategy          = 'overlay_distdir_strategy',
    distdir_flat              = 'overlay_distdir_flat',

diff --git a/roverlay/overlay/additionsdir.py b/roverlay/overlay/additionsdir.py
new file mode 100644
index 0000000..7136bb5
--- /dev/null
+++ b/roverlay/overlay/additionsdir.py
@@ -0,0 +1,98 @@
+# R overlay -- overlay package, overlay additions dir
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 André Erdmann <dywi@mailerd.de>
+# Distributed under the terms of the GNU General Public License;
+# either version 2 of the License, or (at your option) any later version.
+
+import os
+import re
+
+class AdditionsDir ( object ):
+
+   def __init__ ( self, fspath, name=None, parent=None ):
+      self.root   = str ( fspath ) if fspath else None
+      self.parent = parent
+      self.name   = name
+   # --- end of __init__ (...) ---
+
+   def exists ( self ):
+      return self.root is not None and os.path.isdir ( self.root )
+   # --- end of exists (...) ---
+
+   __bool__ = exists
+
+   def get_subdir ( self, relpath ):
+      if self.root:
+         return self.__class__ (
+            fspath = ( self.root + os.sep + relpath ),
+            name   = relpath,
+            parent = self
+         )
+      else:
+         return self
+   # --- end of get_subdir (...) ---
+
+   def get_obj_subdir ( self, obj ):
+      return self.get_subdir ( obj.name )
+   # --- end of get_obj_subdir (...) ---
+
+   def __str__ ( self ):
+      return self.root or ""
+   # --- end of __str__ (...) ---
+
+
+class _AdditionsDirView ( object ):
+
+   def __init__ ( self, additions_dir ):
+      self._additions_dir = additions_dir
+   # --- end of __init__ (...) ---
+
+   def __bool__ ( self ):
+      return bool ( self._additions_dir )
+   # --- end of __bool__ (...) ---
+
+
+class _EbuildAdditionsView ( _AdditionsDirView ):
+
+   def __init__ ( self, additions_dir ):
+      super ( _EbuildAdditionsView, self ).__init__ (
+         additions_dir=additions_dir
+      )
+      if hasattr ( self, 'prepare' ):
+         self.prepare()
+   # --- end of __init__ (...) ---
+
+   def _get_files_with_suffix ( self, suffix ):
+      assert '.' not in self._additions_dir.name
+
+      fre = re.compile (
+         self._additions_dir.name + '[-](?P<pvr>[0-9.]+([-]r[0-9]+)?)'
+         + suffix.replace ( '.', '[.]' )
+      )
+      root = self._additions_dir.root
+      for fname in os.listdir ( root ):
+         fmatch = fre.match ( fname )
+         if fmatch:
+            yield ( fmatch.group ( 'pvr' ), ( root + os.sep + fname ), fname )
+   # --- end of _get_files_with_suffix (...) ---
+
+
+class PatchView ( _EbuildAdditionsView ):
+
+   def has_patches ( self ):
+      return bool ( getattr ( self, '_patches', None ) )
+   # --- end of has_patches (...) ---
+
+   def get_patches ( self, pvr ):
+      patch = self._patches.get ( pvr, None )
+      return ( patch, ) if patch is not None else None
+   # --- end of get_patches (...) ---
+
+   def prepare ( self ):
+      if self._additions_dir.exists():
+         # dict { pvr => patch_file }
+         self._patches = {
+            pvr: fpath
+            for pvr, fpath, fname in self._get_files_with_suffix ( '.patch' )
+         }
+   # --- end of prepare (...) ---

diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py
index baca2d1..7ccd86e 100644
--- a/roverlay/overlay/category.py
+++ b/roverlay/overlay/category.py
@@ -212,7 +212,8 @@ class Category ( object ):
       overwrite_ebuilds,
       keep_n_ebuilds,
       cautious,
-      write_manifest
+      write_manifest,
+      additions_dir
    ):
       """Writes this category to its filesystem location.
 
@@ -230,7 +231,10 @@ class Category ( object ):
                pkg = q.get_nowait()
                # remove manifest writing from threaded writing since it's
                # single-threaded
-               pkg.write ( **write_kw )
+               pkg.write (
+                  additions_dir = additions_dir.get_obj_subdir ( pkg ),
+                  **write_kw
+               )
             except queue.Empty:
                break
             except ( Exception, KeyboardInterrupt ) as err:
@@ -310,7 +314,10 @@ class Category ( object ):
 
       else:
          for package in self._subdirs.values():
-            package.write ( **write_kwargs )
+            package.write (
+               additions_dir = additions_dir.get_obj_subdir ( package ),
+               **write_kwargs
+            )
 
          self.remove_empty()
    # --- end of write (...) ---

diff --git a/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py b/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py
index 898ecb1..1004381 100644
--- a/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py
+++ b/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py
@@ -33,8 +33,8 @@ class ExternalManifestCreation ( object ):
    def _doinit ( self ):
       """Initializes self's data, needs an initialized ConfigTree."""
       self.manifest_env = ManifestEnv.get_new()
-      self.ebuild_tgt   = config.get ( 'TOOLS.EBUILD.target', 'manifest' )
-      self.ebuild_prog  = config.get ( 'TOOLS.EBUILD.prog', '/usr/bin/ebuild' )
+      self.ebuild_tgt   = config.get_or_fail ( 'TOOLS.EBUILD.target' )
+      self.ebuild_prog  = config.get_or_fail ( 'TOOLS.EBUILD.prog' )
 
       # set PORDIR_OVERLAY and DISTDIR
       self.manifest_env ['PORTDIR_OVERLAY'] = config.get_or_fail (

diff --git a/roverlay/overlay/pkgdir/packagedir_base.py b/roverlay/overlay/pkgdir/packagedir_base.py
index 62dd7c7..0e5ee81 100644
--- a/roverlay/overlay/pkgdir/packagedir_base.py
+++ b/roverlay/overlay/pkgdir/packagedir_base.py
@@ -20,6 +20,10 @@ import sys
 import threading
 import shutil
 
+
+import roverlay.overlay.additionsdir
+import roverlay.tools.patch
+
 from roverlay                         import util
 from roverlay.packageinfo             import PackageInfo
 from roverlay.overlay.pkgdir.metadata import MetadataJob
@@ -427,7 +431,9 @@ class PackageDirBase ( object ):
       raises:
       * passes all exceptions (IOError, ..)
       """
-      self.write_ebuilds ( overwrite=True, shared_fh=stream )
+      self.write_ebuilds (
+         overwrite=True, additions_dir=None, shared_fh=stream
+      )
       self.write_metadata ( shared_fh=stream )
       return True
    # --- end of show (...) ---
@@ -445,6 +451,7 @@ class PackageDirBase ( object ):
    # --- end of virtual_cleanup (...) ---
 
    def write ( self,
+      additions_dir,
       overwrite_ebuilds=False,
       write_ebuilds=True, write_manifest=True, write_metadata=True,
       cleanup=True, keep_n_ebuilds=None, cautious=True
@@ -452,6 +459,7 @@ class PackageDirBase ( object ):
       """Writes this directory to its (existent!) filesystem location.
 
       arguments:
+      * additions_dir     -- AdditionsDir object for this package
       * write_ebuilds     -- if set and False: don't write ebuilds
       * write_manifest    -- if set and False: don't write the Manifest file
       * write_metadata    -- if set and False: don't write the metadata file
@@ -490,16 +498,16 @@ class PackageDirBase ( object ):
 
             # write ebuilds
             if self.modified and write_ebuilds:
+               # ( overwrite == None ) <= not modified, which is not
+               # possible in this if-branch
                success = self.write_ebuilds (
-                  # ( overwrite == None ) <= not modified, which is not
-                  # possible in this if-branch
-
-                  overwrite = bool ( overwrite_ebuilds )
-
+                  additions_dir = additions_dir,
+                  overwrite     = bool ( overwrite_ebuilds )
+               )
 #                  overwrite = overwrite_ebuilds \
 #                     if overwrite_ebuilds is not None \
 #                     else not self.modified
-               )
+
 
             # cautious: remove ebuilds after writing them
             if cautious and keep_n_ebuilds is not None:
@@ -525,13 +533,14 @@ class PackageDirBase ( object ):
       return success
    # --- end of write (...) ---
 
-   def write_ebuilds ( self, overwrite, shared_fh=None ):
+   def write_ebuilds ( self, overwrite, additions_dir, shared_fh=None ):
       """Writes all ebuilds.
 
       arguments:
+      * overwrite      -- write ebuilds that have been written before
+      * additions_dir  --
       * shared_fh      -- if set and not None: don't use own file handles
                            (i.e. write files), write everything into shared_fh
-      * overwrite      -- write ebuilds that have been written before
       """
       ebuild_header = self.get_header()
 
@@ -562,6 +571,29 @@ class PackageDirBase ( object ):
          return _success
       # --- end of write_ebuild (...) ---
 
+      def patch_ebuild ( efile, patches ):
+         if patches:
+            self.logger.info ( "Patching " + str ( efile ) )
+            self.logger.debug (
+               "Patches for {} (in that order): {}".format ( efile, patches )
+            )
+
+            patch_ret = None
+
+            for patch in patches:
+               patch_ret = roverlay.tools.patch.dopatch (
+                  filepath=efile, patch=patch, logger=self.logger
+               )
+
+               if patch_ret != os.EX_OK:
+                  # TODO
+                  raise Exception ( patch )
+
+            return True
+         else:
+            return True
+      # --- end of patch_ebuild (...) ---
+
       def ebuilds_to_write():
          """Yields all ebuilds that are ready to be written."""
 
@@ -570,7 +602,7 @@ class PackageDirBase ( object ):
                efile = self.ebuild_filepath_format.format ( PVR=ver )
 
                if efile != p_info ['ebuild_file'] or overwrite:
-                  yield ( efile, p_info )
+                  yield ( ver, efile, p_info )
                # else efile exists
       # --- end of ebuilds_to_write (...) ---
 
@@ -579,12 +611,21 @@ class PackageDirBase ( object ):
       # don't call dodir if shared_fh is set
       hasdir = bool ( shared_fh is not None )
 
-      for efile, p_info in ebuilds_to_write():
+      patchview  = roverlay.overlay.additionsdir.PatchView ( additions_dir )
+      haspatch   = patchview.has_patches()
+
+      for pvr, efile, p_info in ebuilds_to_write():
          if not hasdir:
             util.dodir ( self.physical_location, mkdir_p=True )
             hasdir = True
 
-         if write_ebuild ( efile, p_info ['ebuild'] ):
+         if (
+            write_ebuild ( efile, p_info ['ebuild'] )
+         ) and (
+            not haspatch
+            or patch_ebuild ( efile, patchview.get_patches ( pvr ) )
+         ):
+
             self._need_manifest = True
 
             # update metadata for each successfully written ebuild

diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py
index b9030d2..ca71b81 100644
--- a/roverlay/overlay/root.py
+++ b/roverlay/overlay/root.py
@@ -27,6 +27,9 @@ from roverlay import config, util
 from roverlay.overlay.category import Category
 from roverlay.overlay.header   import EbuildHeader
 
+import roverlay.overlay.additionsdir
+
+
 class Overlay ( object ):
    DEFAULT_USE_DESC = '\n'.join ( (
       'byte-compile - enable byte compiling',
@@ -63,6 +66,7 @@ class Overlay ( object ):
          incremental         = incremental,
          write_allowed       = write_allowed,
          skip_manifest       = skip_manifest,
+         additions_dir       = config.get_or_fail ( 'OVERLAY.additions_dir' ),
          runtime_incremental = runtime_incremental,
       )
    # --- end of new_configured (...) ---
@@ -78,7 +82,8 @@ class Overlay ( object ):
       write_allowed,
       incremental,
       skip_manifest,
-      runtime_incremental=False,
+      additions_dir,
+      runtime_incremental=False
    ):
       """Initializes an overlay.
 
@@ -98,6 +103,11 @@ class Overlay ( object ):
       * skip_manifest       -- skip Manifest generation to save time
                                !!! The created overlay cannot be used with
                                emerge/portage
+      * additions_dir       -- path to a directory that contains additional
+                               files, e.g. hand-written ebuilds and ebuild
+                               patches. The directory has to exist (it will
+                               be checked here).
+                               A value of None or "" disables additions.
       * runtime_incremental -- see package.py:PackageDir.__init__ (...),
                                 Defaults to False (saves memory but costs time)
 
@@ -120,6 +130,14 @@ class Overlay ( object ):
 
       self.skip_manifest        = skip_manifest
 
+      if additions_dir:
+         if os.path.isdir ( additions_dir ):
+            self.additions_dirpath = os.path.abspath ( additions_dir )
+         else:
+            raise ValueError (
+               "additions dir {} does not exist!".format ( additions_dir )
+            )
+
       # calculating eclass names twice,
       # once here and another time when calling _init_overlay
       self._header.set_eclasses ( frozenset (
@@ -463,12 +481,17 @@ class Overlay ( object ):
       if self._writeable:
          self._init_overlay ( reimport_eclass=True )
 
+         additions_dir = roverlay.overlay.additionsdir.AdditionsDir (
+            getattr ( self, 'additions_dirpath', None )
+         )
+
          for cat in self._categories.values():
             cat.write (
                overwrite_ebuilds = False,
                keep_n_ebuilds    = config.get ( 'OVERLAY.keep_nth_latest', None ),
                cautious          = True,
-               write_manifest    = not self.skip_manifest
+               write_manifest    = not self.skip_manifest,
+               additions_dir     = additions_dir.get_obj_subdir ( cat ),
             )
       else:
          # FIXME debug print

diff --git a/roverlay/tools/__init__.py b/roverlay/tools/__init__.py
new file mode 100644
index 0000000..e69de29

diff --git a/roverlay/tools/patch.py b/roverlay/tools/patch.py
new file mode 100644
index 0000000..119ff80
--- /dev/null
+++ b/roverlay/tools/patch.py
@@ -0,0 +1,45 @@
+# R overlay -- tools, run patch(1)
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 André Erdmann <dywi@mailerd.de>
+# Distributed under the terms of the GNU General Public License;
+# either version 2 of the License, or (at your option) any later version.
+
+import subprocess
+
+import roverlay.config
+import roverlay.strutil
+import roverlay.util
+
+_PATCHENV = roverlay.util.keepenv (
+   ( 'PATH', '' ), 'LANG', 'LC_ALL', 'PWD', 'TMPDIR'
+)
+
+# NOTE:
+#  this module has to be loaded after reading roverlay's config
+#
+
+_PATCHOPTS = (
+   ( roverlay.config.get_or_fail ( "TOOLS.PATCH.exe" ), )
+   + roverlay.config.get_or_fail ( "TOOLS.PATCH.opts" )
+)
+
+def dopatch ( filepath, patch, logger ):
+   print ( "RUN PATCH", filepath, patch )
+   patch_call = subprocess.Popen (
+      _PATCHOPTS + (
+         filepath, patch
+      ),
+      stdin=None,
+      stdout=subprocess.PIPE,
+      stderr=subprocess.PIPE,
+      env=_PATCHENV,
+   )
+
+   output = patch_call.communicate()
+
+   # log stderr
+   for line in roverlay.strutil.pipe_lines ( output [1], use_filter=True ):
+      logger.warning ( line )
+
+   return patch_call.returncode
+# --- end of dopatch (...) ---


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* [gentoo-commits] proj/R_overlay:master commit in: roverlay/overlay/pkgdir/manifest/, roverlay/tools/, roverlay/config/, roverlay/, ...
@ 2013-06-13 16:34 André Erdmann
  2013-06-06 21:15 ` [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
  0 siblings, 1 reply; 2+ messages in thread
From: André Erdmann @ 2013-06-13 16:34 UTC (permalink / raw
  To: gentoo-commits

commit:     47d9b844a010d8b4334facc784d2db1ab08f80c5
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Thu Jun  6 21:13:22 2013 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Thu Jun  6 21:13:22 2013 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=47d9b844

roverlay/overlay: additions dir

---
 roverlay/argutil.py                                | 30 ++++++-
 roverlay/config/const.py                           | 15 ++++
 roverlay/config/entrymap.py                        | 16 ++--
 roverlay/overlay/additionsdir.py                   | 98 ++++++++++++++++++++++
 roverlay/overlay/category.py                       | 13 ++-
 .../overlay/pkgdir/manifest/manifest_ebuild.py     |  4 +-
 roverlay/overlay/pkgdir/packagedir_base.py         | 65 +++++++++++---
 roverlay/overlay/root.py                           | 27 +++++-
 roverlay/tools/__init__.py                         |  0
 roverlay/tools/patch.py                            | 45 ++++++++++
 10 files changed, 286 insertions(+), 27 deletions(-)

diff --git a/roverlay/argutil.py b/roverlay/argutil.py
index 90d95df..99842fd 100644
--- a/roverlay/argutil.py
+++ b/roverlay/argutil.py
@@ -67,6 +67,12 @@ def get_parser ( command_map, default_config_file, default_command='create' ):
          )
       return d
 
+   def is_fs_dir_or_void ( value ):
+      if value:
+         return is_fs_dir ( value )
+      else:
+         return ''
+
    def is_fs_file_or_void ( value ):
       if value:
          return is_fs_file ( value )
@@ -180,6 +186,14 @@ def get_parser ( command_map, default_config_file, default_command='create' ):
    )
 
    arg (
+      '--additions-dir', '-A', # '--patch-dir',  '--ebuild-dir',
+      default=argparse.SUPPRESS,
+      help="directory containing patches and hand-written ebuilds",
+      metavar="<dir>",
+      type=is_fs_dir_or_void
+   )
+
+   arg (
       '--overlay-name', '-N', default=argparse.SUPPRESS,
       help="overlay name",
       metavar="<name>",
@@ -353,6 +367,8 @@ def parse_argv ( command_map, **kw ):
    All args/keywords are passed to get_parser().
    Passes all exceptions.
    """
+   p = get_parser ( command_map=command_map, **kw ).parse_args()
+
    def doconf ( value, path ):
       pos = conf
       if isinstance ( path, str ):
@@ -367,11 +383,17 @@ def parse_argv ( command_map, **kw ):
                pos [k] = dict()
 
             pos = pos [k]
+   # --- end of doconf (...) ---
 
+   def given ( attr_name ):
+      return hasattr ( p, attr_name )
+   # --- end of given (...) ---
 
-   p = get_parser ( command_map=command_map, **kw ).parse_args()
-
-   given = lambda kw : hasattr ( p, kw )
+   def doconf_simple ( attr_name, config_path ) :
+      value = getattr ( p, attr_name, None )
+      if value is not None:
+         doconf ( value, config_path )
+   # --- end of doconf_simple (...) ---
 
    commands = ( p.commands, ) if isinstance ( p.commands, str ) else p.commands
    conf  = dict()
@@ -397,6 +419,8 @@ def parse_argv ( command_map, **kw ):
       doconf ( p.overlay, 'OVERLAY.dir' )
       #extra ['write_overlay'] = True
 
+   doconf_simple ( "additions_dir", "OVERLAY.additions_dir" )
+
    if given ( 'overlay_name' ):
       doconf ( p.overlay_name, 'OVERLAY.name' )
 

diff --git a/roverlay/config/const.py b/roverlay/config/const.py
index 48e0e55..b3e8dc4 100644
--- a/roverlay/config/const.py
+++ b/roverlay/config/const.py
@@ -68,6 +68,21 @@ _CONSTANTS = dict (
       category                = 'sci-R',
       manifest_implementation = 'default',
    ),
+
+   TOOLS = dict (
+      EBUILD = dict (
+         prog   = "/usr/bin/ebuild",
+         target = "manifest",
+      ),
+      PATCH = dict (
+         exe    = "patch",
+         opts   = (
+            '--no-backup-if-mismatch',
+            '--reject-file=-'
+            '--quiet', '--force',
+         )
+      ),
+   ),
 )
 
 def lookup ( key, fallback_value=None ):

diff --git a/roverlay/config/entrymap.py b/roverlay/config/entrymap.py
index 77525a8..c98bc68 100644
--- a/roverlay/config/entrymap.py
+++ b/roverlay/config/entrymap.py
@@ -223,6 +223,11 @@ CONFIG_ENTRY_MAP = dict (
       '''
    ),
 
+   overlay_additions_dir = dict (
+      value_type  = 'fs_abs:fs_dir',
+      description = 'directory containing ebuilds and ebuild patches',
+   ),
+
    overlay_eclass = dict (
       path        = [ 'OVERLAY', 'eclass_files' ],
       value_type  = fs_abslist,
@@ -263,11 +268,11 @@ CONFIG_ENTRY_MAP = dict (
 
    overlay_distdir_root = dict (
       value_type  = 'fs_dir',
-      description = (
-         'DISTDIR which is used for Manifest creation and can, '
-         'depending on the DISTDIR strategy, '
-         'serve as package mirror directory',
-      ),
+      description = '''
+         DISTDIR which is used for Manifest creation and can,
+         depending on the DISTDIR strategy,
+         serve as package mirror directory.
+      '''
    ),
 
    overlay_distdir_strategy = dict (
@@ -293,6 +298,7 @@ CONFIG_ENTRY_MAP = dict (
    eclass                    = 'overlay_eclass',
    keep_nth_latest           = 'overlay_keep_nth_latest',
    manifest_implementation   = 'overlay_manifest_implementation',
+   additions_dir             = 'overlay_additions_dir',
    distdir                   = 'overlay_distdir_root',
    distdir_strategy          = 'overlay_distdir_strategy',
    distdir_flat              = 'overlay_distdir_flat',

diff --git a/roverlay/overlay/additionsdir.py b/roverlay/overlay/additionsdir.py
new file mode 100644
index 0000000..7136bb5
--- /dev/null
+++ b/roverlay/overlay/additionsdir.py
@@ -0,0 +1,98 @@
+# R overlay -- overlay package, overlay additions dir
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 André Erdmann <dywi@mailerd.de>
+# Distributed under the terms of the GNU General Public License;
+# either version 2 of the License, or (at your option) any later version.
+
+import os
+import re
+
+class AdditionsDir ( object ):
+
+   def __init__ ( self, fspath, name=None, parent=None ):
+      self.root   = str ( fspath ) if fspath else None
+      self.parent = parent
+      self.name   = name
+   # --- end of __init__ (...) ---
+
+   def exists ( self ):
+      return self.root is not None and os.path.isdir ( self.root )
+   # --- end of exists (...) ---
+
+   __bool__ = exists
+
+   def get_subdir ( self, relpath ):
+      if self.root:
+         return self.__class__ (
+            fspath = ( self.root + os.sep + relpath ),
+            name   = relpath,
+            parent = self
+         )
+      else:
+         return self
+   # --- end of get_subdir (...) ---
+
+   def get_obj_subdir ( self, obj ):
+      return self.get_subdir ( obj.name )
+   # --- end of get_obj_subdir (...) ---
+
+   def __str__ ( self ):
+      return self.root or ""
+   # --- end of __str__ (...) ---
+
+
+class _AdditionsDirView ( object ):
+
+   def __init__ ( self, additions_dir ):
+      self._additions_dir = additions_dir
+   # --- end of __init__ (...) ---
+
+   def __bool__ ( self ):
+      return bool ( self._additions_dir )
+   # --- end of __bool__ (...) ---
+
+
+class _EbuildAdditionsView ( _AdditionsDirView ):
+
+   def __init__ ( self, additions_dir ):
+      super ( _EbuildAdditionsView, self ).__init__ (
+         additions_dir=additions_dir
+      )
+      if hasattr ( self, 'prepare' ):
+         self.prepare()
+   # --- end of __init__ (...) ---
+
+   def _get_files_with_suffix ( self, suffix ):
+      assert '.' not in self._additions_dir.name
+
+      fre = re.compile (
+         self._additions_dir.name + '[-](?P<pvr>[0-9.]+([-]r[0-9]+)?)'
+         + suffix.replace ( '.', '[.]' )
+      )
+      root = self._additions_dir.root
+      for fname in os.listdir ( root ):
+         fmatch = fre.match ( fname )
+         if fmatch:
+            yield ( fmatch.group ( 'pvr' ), ( root + os.sep + fname ), fname )
+   # --- end of _get_files_with_suffix (...) ---
+
+
+class PatchView ( _EbuildAdditionsView ):
+
+   def has_patches ( self ):
+      return bool ( getattr ( self, '_patches', None ) )
+   # --- end of has_patches (...) ---
+
+   def get_patches ( self, pvr ):
+      patch = self._patches.get ( pvr, None )
+      return ( patch, ) if patch is not None else None
+   # --- end of get_patches (...) ---
+
+   def prepare ( self ):
+      if self._additions_dir.exists():
+         # dict { pvr => patch_file }
+         self._patches = {
+            pvr: fpath
+            for pvr, fpath, fname in self._get_files_with_suffix ( '.patch' )
+         }
+   # --- end of prepare (...) ---

diff --git a/roverlay/overlay/category.py b/roverlay/overlay/category.py
index baca2d1..7ccd86e 100644
--- a/roverlay/overlay/category.py
+++ b/roverlay/overlay/category.py
@@ -212,7 +212,8 @@ class Category ( object ):
       overwrite_ebuilds,
       keep_n_ebuilds,
       cautious,
-      write_manifest
+      write_manifest,
+      additions_dir
    ):
       """Writes this category to its filesystem location.
 
@@ -230,7 +231,10 @@ class Category ( object ):
                pkg = q.get_nowait()
                # remove manifest writing from threaded writing since it's
                # single-threaded
-               pkg.write ( **write_kw )
+               pkg.write (
+                  additions_dir = additions_dir.get_obj_subdir ( pkg ),
+                  **write_kw
+               )
             except queue.Empty:
                break
             except ( Exception, KeyboardInterrupt ) as err:
@@ -310,7 +314,10 @@ class Category ( object ):
 
       else:
          for package in self._subdirs.values():
-            package.write ( **write_kwargs )
+            package.write (
+               additions_dir = additions_dir.get_obj_subdir ( package ),
+               **write_kwargs
+            )
 
          self.remove_empty()
    # --- end of write (...) ---

diff --git a/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py b/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py
index 898ecb1..1004381 100644
--- a/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py
+++ b/roverlay/overlay/pkgdir/manifest/manifest_ebuild.py
@@ -33,8 +33,8 @@ class ExternalManifestCreation ( object ):
    def _doinit ( self ):
       """Initializes self's data, needs an initialized ConfigTree."""
       self.manifest_env = ManifestEnv.get_new()
-      self.ebuild_tgt   = config.get ( 'TOOLS.EBUILD.target', 'manifest' )
-      self.ebuild_prog  = config.get ( 'TOOLS.EBUILD.prog', '/usr/bin/ebuild' )
+      self.ebuild_tgt   = config.get_or_fail ( 'TOOLS.EBUILD.target' )
+      self.ebuild_prog  = config.get_or_fail ( 'TOOLS.EBUILD.prog' )
 
       # set PORDIR_OVERLAY and DISTDIR
       self.manifest_env ['PORTDIR_OVERLAY'] = config.get_or_fail (

diff --git a/roverlay/overlay/pkgdir/packagedir_base.py b/roverlay/overlay/pkgdir/packagedir_base.py
index 62dd7c7..0e5ee81 100644
--- a/roverlay/overlay/pkgdir/packagedir_base.py
+++ b/roverlay/overlay/pkgdir/packagedir_base.py
@@ -20,6 +20,10 @@ import sys
 import threading
 import shutil
 
+
+import roverlay.overlay.additionsdir
+import roverlay.tools.patch
+
 from roverlay                         import util
 from roverlay.packageinfo             import PackageInfo
 from roverlay.overlay.pkgdir.metadata import MetadataJob
@@ -427,7 +431,9 @@ class PackageDirBase ( object ):
       raises:
       * passes all exceptions (IOError, ..)
       """
-      self.write_ebuilds ( overwrite=True, shared_fh=stream )
+      self.write_ebuilds (
+         overwrite=True, additions_dir=None, shared_fh=stream
+      )
       self.write_metadata ( shared_fh=stream )
       return True
    # --- end of show (...) ---
@@ -445,6 +451,7 @@ class PackageDirBase ( object ):
    # --- end of virtual_cleanup (...) ---
 
    def write ( self,
+      additions_dir,
       overwrite_ebuilds=False,
       write_ebuilds=True, write_manifest=True, write_metadata=True,
       cleanup=True, keep_n_ebuilds=None, cautious=True
@@ -452,6 +459,7 @@ class PackageDirBase ( object ):
       """Writes this directory to its (existent!) filesystem location.
 
       arguments:
+      * additions_dir     -- AdditionsDir object for this package
       * write_ebuilds     -- if set and False: don't write ebuilds
       * write_manifest    -- if set and False: don't write the Manifest file
       * write_metadata    -- if set and False: don't write the metadata file
@@ -490,16 +498,16 @@ class PackageDirBase ( object ):
 
             # write ebuilds
             if self.modified and write_ebuilds:
+               # ( overwrite == None ) <= not modified, which is not
+               # possible in this if-branch
                success = self.write_ebuilds (
-                  # ( overwrite == None ) <= not modified, which is not
-                  # possible in this if-branch
-
-                  overwrite = bool ( overwrite_ebuilds )
-
+                  additions_dir = additions_dir,
+                  overwrite     = bool ( overwrite_ebuilds )
+               )
 #                  overwrite = overwrite_ebuilds \
 #                     if overwrite_ebuilds is not None \
 #                     else not self.modified
-               )
+
 
             # cautious: remove ebuilds after writing them
             if cautious and keep_n_ebuilds is not None:
@@ -525,13 +533,14 @@ class PackageDirBase ( object ):
       return success
    # --- end of write (...) ---
 
-   def write_ebuilds ( self, overwrite, shared_fh=None ):
+   def write_ebuilds ( self, overwrite, additions_dir, shared_fh=None ):
       """Writes all ebuilds.
 
       arguments:
+      * overwrite      -- write ebuilds that have been written before
+      * additions_dir  --
       * shared_fh      -- if set and not None: don't use own file handles
                            (i.e. write files), write everything into shared_fh
-      * overwrite      -- write ebuilds that have been written before
       """
       ebuild_header = self.get_header()
 
@@ -562,6 +571,29 @@ class PackageDirBase ( object ):
          return _success
       # --- end of write_ebuild (...) ---
 
+      def patch_ebuild ( efile, patches ):
+         if patches:
+            self.logger.info ( "Patching " + str ( efile ) )
+            self.logger.debug (
+               "Patches for {} (in that order): {}".format ( efile, patches )
+            )
+
+            patch_ret = None
+
+            for patch in patches:
+               patch_ret = roverlay.tools.patch.dopatch (
+                  filepath=efile, patch=patch, logger=self.logger
+               )
+
+               if patch_ret != os.EX_OK:
+                  # TODO
+                  raise Exception ( patch )
+
+            return True
+         else:
+            return True
+      # --- end of patch_ebuild (...) ---
+
       def ebuilds_to_write():
          """Yields all ebuilds that are ready to be written."""
 
@@ -570,7 +602,7 @@ class PackageDirBase ( object ):
                efile = self.ebuild_filepath_format.format ( PVR=ver )
 
                if efile != p_info ['ebuild_file'] or overwrite:
-                  yield ( efile, p_info )
+                  yield ( ver, efile, p_info )
                # else efile exists
       # --- end of ebuilds_to_write (...) ---
 
@@ -579,12 +611,21 @@ class PackageDirBase ( object ):
       # don't call dodir if shared_fh is set
       hasdir = bool ( shared_fh is not None )
 
-      for efile, p_info in ebuilds_to_write():
+      patchview  = roverlay.overlay.additionsdir.PatchView ( additions_dir )
+      haspatch   = patchview.has_patches()
+
+      for pvr, efile, p_info in ebuilds_to_write():
          if not hasdir:
             util.dodir ( self.physical_location, mkdir_p=True )
             hasdir = True
 
-         if write_ebuild ( efile, p_info ['ebuild'] ):
+         if (
+            write_ebuild ( efile, p_info ['ebuild'] )
+         ) and (
+            not haspatch
+            or patch_ebuild ( efile, patchview.get_patches ( pvr ) )
+         ):
+
             self._need_manifest = True
 
             # update metadata for each successfully written ebuild

diff --git a/roverlay/overlay/root.py b/roverlay/overlay/root.py
index b9030d2..ca71b81 100644
--- a/roverlay/overlay/root.py
+++ b/roverlay/overlay/root.py
@@ -27,6 +27,9 @@ from roverlay import config, util
 from roverlay.overlay.category import Category
 from roverlay.overlay.header   import EbuildHeader
 
+import roverlay.overlay.additionsdir
+
+
 class Overlay ( object ):
    DEFAULT_USE_DESC = '\n'.join ( (
       'byte-compile - enable byte compiling',
@@ -63,6 +66,7 @@ class Overlay ( object ):
          incremental         = incremental,
          write_allowed       = write_allowed,
          skip_manifest       = skip_manifest,
+         additions_dir       = config.get_or_fail ( 'OVERLAY.additions_dir' ),
          runtime_incremental = runtime_incremental,
       )
    # --- end of new_configured (...) ---
@@ -78,7 +82,8 @@ class Overlay ( object ):
       write_allowed,
       incremental,
       skip_manifest,
-      runtime_incremental=False,
+      additions_dir,
+      runtime_incremental=False
    ):
       """Initializes an overlay.
 
@@ -98,6 +103,11 @@ class Overlay ( object ):
       * skip_manifest       -- skip Manifest generation to save time
                                !!! The created overlay cannot be used with
                                emerge/portage
+      * additions_dir       -- path to a directory that contains additional
+                               files, e.g. hand-written ebuilds and ebuild
+                               patches. The directory has to exist (it will
+                               be checked here).
+                               A value of None or "" disables additions.
       * runtime_incremental -- see package.py:PackageDir.__init__ (...),
                                 Defaults to False (saves memory but costs time)
 
@@ -120,6 +130,14 @@ class Overlay ( object ):
 
       self.skip_manifest        = skip_manifest
 
+      if additions_dir:
+         if os.path.isdir ( additions_dir ):
+            self.additions_dirpath = os.path.abspath ( additions_dir )
+         else:
+            raise ValueError (
+               "additions dir {} does not exist!".format ( additions_dir )
+            )
+
       # calculating eclass names twice,
       # once here and another time when calling _init_overlay
       self._header.set_eclasses ( frozenset (
@@ -463,12 +481,17 @@ class Overlay ( object ):
       if self._writeable:
          self._init_overlay ( reimport_eclass=True )
 
+         additions_dir = roverlay.overlay.additionsdir.AdditionsDir (
+            getattr ( self, 'additions_dirpath', None )
+         )
+
          for cat in self._categories.values():
             cat.write (
                overwrite_ebuilds = False,
                keep_n_ebuilds    = config.get ( 'OVERLAY.keep_nth_latest', None ),
                cautious          = True,
-               write_manifest    = not self.skip_manifest
+               write_manifest    = not self.skip_manifest,
+               additions_dir     = additions_dir.get_obj_subdir ( cat ),
             )
       else:
          # FIXME debug print

diff --git a/roverlay/tools/__init__.py b/roverlay/tools/__init__.py
new file mode 100644
index 0000000..e69de29

diff --git a/roverlay/tools/patch.py b/roverlay/tools/patch.py
new file mode 100644
index 0000000..119ff80
--- /dev/null
+++ b/roverlay/tools/patch.py
@@ -0,0 +1,45 @@
+# R overlay -- tools, run patch(1)
+# -*- coding: utf-8 -*-
+# Copyright (C) 2013 André Erdmann <dywi@mailerd.de>
+# Distributed under the terms of the GNU General Public License;
+# either version 2 of the License, or (at your option) any later version.
+
+import subprocess
+
+import roverlay.config
+import roverlay.strutil
+import roverlay.util
+
+_PATCHENV = roverlay.util.keepenv (
+   ( 'PATH', '' ), 'LANG', 'LC_ALL', 'PWD', 'TMPDIR'
+)
+
+# NOTE:
+#  this module has to be loaded after reading roverlay's config
+#
+
+_PATCHOPTS = (
+   ( roverlay.config.get_or_fail ( "TOOLS.PATCH.exe" ), )
+   + roverlay.config.get_or_fail ( "TOOLS.PATCH.opts" )
+)
+
+def dopatch ( filepath, patch, logger ):
+   print ( "RUN PATCH", filepath, patch )
+   patch_call = subprocess.Popen (
+      _PATCHOPTS + (
+         filepath, patch
+      ),
+      stdin=None,
+      stdout=subprocess.PIPE,
+      stderr=subprocess.PIPE,
+      env=_PATCHENV,
+   )
+
+   output = patch_call.communicate()
+
+   # log stderr
+   for line in roverlay.strutil.pipe_lines ( output [1], use_filter=True ):
+      logger.warning ( line )
+
+   return patch_call.returncode
+# --- end of dopatch (...) ---


^ permalink raw reply related	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2013-06-13 16:34 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2013-06-13 16:34 [gentoo-commits] proj/R_overlay:master commit in: roverlay/overlay/pkgdir/manifest/, roverlay/tools/, roverlay/config/, roverlay/, André Erdmann
2013-06-06 21:15 ` [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox