public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "André Erdmann" <dywi@mailerd.de>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/
Date: Thu, 12 Sep 2013 16:36:40 +0000 (UTC)	[thread overview]
Message-ID: <1379003610.1eb711c0e3f609ef0d0845921f9086dc39da2f5c.dywi@gentoo> (raw)

commit:     1eb711c0e3f609ef0d0845921f9086dc39da2f5c
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Thu Sep 12 16:33:30 2013 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Thu Sep 12 16:33:30 2013 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=1eb711c0

roverlay/setupscript: "hooks" command

Added a "hooks" command for managing hooks. Currently, it is able to print
information about hooks enabled/available. The underlying code (HookScript*,
UserHookScript* classes) supports more, e.g. linking hooks.

Also added a "--overwrite-hooks [<when>]" option to roverlay-setup, which can be
used to control which hooks will be overwritten, e.g. when running the init
command.

---
 roverlay/setupscript/hookenv.py | 641 ++++++++++++++++++++++++++++++++++------
 roverlay/setupscript/initenv.py |   3 +
 roverlay/setupscript/runtime.py |  38 ++-
 3 files changed, 595 insertions(+), 87 deletions(-)

diff --git a/roverlay/setupscript/hookenv.py b/roverlay/setupscript/hookenv.py
index e07b4e7..ebfe0d1 100644
--- a/roverlay/setupscript/hookenv.py
+++ b/roverlay/setupscript/hookenv.py
@@ -6,40 +6,130 @@
 
 import collections
 import errno
+import fnmatch
+import itertools
 import os
 
 
+import roverlay.fsutil
 import roverlay.static.hookinfo
 import roverlay.util.counter
+import roverlay.util.dictwalk
+import roverlay.util.objects
+import roverlay.setupscript.baseenv
+
+
+class HookOverwriteControl ( object ):
+   OV_NONE      = 0
+   OV_SYM_DEAD  = 2**0
+   OV_SYM_EXIST = 2**1
+   OV_SYM       = OV_SYM_DEAD|OV_SYM_EXIST
+   OV_FILE      = 2**2
+   OV_ALL       = ( 2**3 ) - 1
+
+   OV_KEYWORDS = {
+      'none'  : OV_NONE,
+      'dead'  : OV_SYM_DEAD,
+      'links' : OV_SYM,
+      'all'   : OV_ALL,
+   }
+
+   @classmethod
+   def from_str ( cls, vstr ):
+      return cls ( cls.OV_KEYWORDS[vstr] )
+   # --- end of from_str (...) ---
+
+   def __init__ ( self, value ):
+      super ( HookOverwriteControl, self ).__init__()
+      assert isinstance ( value, int ) and value >= 0
+      self.value = value
+   # --- end of __init__ (...) ---
 
+   def can_overwrite ( self, mask=None ):
+      if mask is None:
+         return self.value == self.OV_NONE
+      else:
+         return self.value & mask
+   # --- end of can_overwrite (...) ---
 
-class HookScript ( object ):
+   def overwrite_dead_symlinks ( self ):
+      return self.value & self.OV_SYM_DEAD
+
+   def overwrite_symlinks ( self ):
+      return self.value & self.OV_SYM
+
+   def overwrite_all ( self ):
+      return self.value == self.OV_ALL
+
+   def get_str ( self ):
+      value = self.value
+      def gen_words():
+         if value == self.OV_NONE:
+            yield "none"
+         else:
+            if value & self.OV_SYM_EXIST:
+               if value & self.OV_SYM_DEAD:
+                  yield "symlinks"
+               else:
+                  yield "symlinks to existing files"
+            elif value & self.OV_SYM_DEAD:
+               yield "broken symlinks"
+
+            if value & self.OV_FILE:
+               yield "files"
+      # --- end of gen_words (...) ---
+
+      return ', '.join ( gen_words() ) + " (0x{:x})".format ( value )
+   # --- end of get_str (...) ---
+
+   __str__ = get_str
+
+# --- end of HookOverwriteControl ---
+
+
+class HookScriptBase ( roverlay.util.objects.Referenceable ):
+
+   CACHE_REF = True
+
+   def __init__ ( self,
+      fspath, filename=None, priority=None, is_hidden=False
+   ):
+      """HookScriptBase constructor.
+
+      arguments:
+      * fspath    -- absolute path to the hook script
+      * filename  -- name of the hook script
+                      Defaults to os.path.basename(fspath).
+      * priority  -- priority of the hook script. Defaults to auto-detect.
+      * is_hidden -- whether the script is "hidden" or not. Defaults to False.
+      """
+      super ( HookScriptBase, self ).__init__()
 
-   def __init__ ( self, fspath, filename=None ):
-      super ( HookScript, self ).__init__()
       fname = (
          filename if filename is not None else os.path.basename ( fspath )
       )
 
-      self.fspath  = fspath
-      self.name    = os.path.splitext ( fname )[0] or fname
-      static_entry = roverlay.static.hookinfo.get ( self.name, None )
-
-      if static_entry is not None:
-         self.default_events = static_entry[0]
-         self.priority       = static_entry[1]
-         self.is_hidden      = static_entry[2]
+      self.filename = fname
+
+      if priority is True:
+         prio_str, dash, remainder = fname.partition ( '-' )
+         if prio_str and dash and remainder:
+            try:
+               prio = int ( prio_str, 10 )
+            except ValueError:
+               self.priority = None
+            else:
+               self.priority = prio
+               fname         = remainder
+         else:
+            self.priority = None
       else:
-         self.default_events = False
-         self.priority       = None
-         self.is_hidden      = False
-   # --- end of __init__ (...) ---
+         self.priority = priority
 
-   def is_visible ( self ):
-      return not self.is_hidden and (
-         self.priority is None or self.priority >= 0
-      )
-   # --- end of is_visible (...) ---
+      self.name      = os.path.splitext ( fname )[0]
+      self.fspath    = fspath
+      self.is_hidden = is_hidden
+   # --- end of __init__ (...) ---
 
    def __str__ ( self ):
       yesno = lambda k: 'y' if k else 'n'
@@ -54,6 +144,118 @@ class HookScript ( object ):
       )
    # --- end of __str__ (...) ---
 
+   def get_static_info ( self ):
+      return roverlay.static.hookinfo.get ( self.name, None )
+   # --- end of get_static_info (...) ---
+
+   def is_visible ( self ):
+      return not self.is_hidden and (
+         self.priority is None or self.priority >= 0
+      )
+   # --- end of is_visible (...) ---
+
+   @roverlay.util.objects.abstractmethod
+   def get_hookscript ( self ):
+      pass
+   # --- end of get_hookscript (...) ---
+
+   @roverlay.util.objects.abstractmethod
+   def get_hookscript_path ( self ):
+      pass
+   # --- end of get_hookscript_path (...) ---
+
+   @roverlay.util.objects.abstractmethod
+   def get_dest_name ( self, *args, **kwargs ):
+      pass
+   # --- end of get_dest_name (...) ---
+
+# --- end of HookScriptBase ---
+
+
+class UserHookScript ( HookScriptBase ):
+
+   def __init__ ( self, fspath, filename=None, event=None ):
+      super ( UserHookScript, self ).__init__ (
+         fspath, filename=filename, priority=True
+      )
+
+      self.hook_script_fspath = os.path.realpath ( self.fspath )
+      self.hook_script_ref    = (
+         False if not os.path.islink ( self.fspath ) else None
+      )
+
+      self.event = event
+   # --- end of __init__ (...) ---
+
+   def set_hookscript ( self, script_obj, strict=True ):
+      if script_obj and self.hook_script_ref is False:
+         raise Exception (
+            "user hook script {} is not a link!".format ( self.fspath )
+         )
+      elif script_obj is None or script_obj is False:
+         self.hook_script_ref = False
+
+      else:
+         self.hook_script_ref = script_obj.get_ref()
+         script_obj.add_user_script ( self )
+   # --- end of set_hookscript (...) ---
+
+   def has_hookscript ( self ):
+      return self.hook_script_ref is not None
+   # --- end of has_hookscript (...) ---
+
+   def get_hookscript ( self ):
+      ref = self.hook_script_ref
+      if ref is False:
+         return None
+      elif ref is None:
+         raise roverlay.util.objects.ObjectDisappeared()
+      else:
+         return ref.deref_safe()
+   # --- end of get_hookscript (...) ---
+
+   def get_hookscript_path ( self ):
+      return self.hook_script_fspath
+   # --- end of get_hookscript_path (...) ---
+
+   def get_dest_name ( self ):
+      return self.filename
+   # --- end of get_dest_name (...) ---
+
+# --- end of UserHookScript ---
+
+
+class HookScript ( HookScriptBase ):
+
+   def __init__ ( self, fspath, filename=None ):
+      super ( HookScript, self ).__init__ ( fspath, filename=filename )
+
+      static_entry = self.get_static_info()
+      if static_entry is not None:
+         self.default_events = static_entry[0]
+         self.priority       = static_entry[1]
+         self.is_hidden      = static_entry[2]
+      else:
+         self.default_events = False
+
+      self.user_script_refs = set()
+   # --- end of __init__ (...) ---
+
+   def add_user_script ( self, user_script ):
+      self.user_script_refs.add ( user_script.get_ref() )
+   # --- end of add_user_script (...) ---
+
+   def iter_user_scripts ( self, ignore_missing=True ):
+      if ignore_missing:
+         for ref in self.user_script_refs:
+            obj = ref.deref_unsafe()
+            if obj is not None:
+               yield obj
+      else:
+         for ref in self.user_script_refs:
+            yield obj.deref_safe()
+   # --- end of iter_user_scripts (...) ---
+
    def set_priority_from_generator ( self, number_gen, only_if_unset=True ):
       if self.priority is None:
          self.priority = next ( number_gen )
@@ -78,53 +280,47 @@ class HookScript ( object ):
       )
    # --- end of get_dest_name (...) ---
 
+   def get_hookscript ( self ):
+      return self
+   # --- end of get_hookscript (...) ---
+
+   def get_hookscript_path ( self ):
+      return self.fspath
+   # --- end of get_hookscript_path (...) ---
 
 # --- end of HookScript ---
 
 
-class HookScriptDir ( object ):
+class HookScriptDirBase ( roverlay.util.objects.Referenceable ):
+
+   HOOK_SCRIPT_CLS = None
 
    def __init__ ( self, root ):
-      super ( HookScriptDir, self ).__init__()
+      super ( HookScriptDirBase, self ).__init__()
 
-      self.root      = root
-      self._scripts  = collections.OrderedDict()
+      self.root     = root
+      self.scripts  = collections.OrderedDict()
+      self.writable = None
    # --- end of __init__ (...) ---
 
    def __bool__ ( self ):
-      return bool ( self._scripts )
+      return bool ( self.scripts )
    # --- end of __bool__ (...) ---
 
+   def get_fspath ( self, relpath=None ):
+      if relpath:
+         return self.root + os.sep + str ( relpath )
+      else:
+         return self.root
+   # --- end of get_fspath (...) ---
+
    def get_script ( self, name ):
-      script = self._scripts [name]
+      script = self.scripts [name]
       return script if script.is_visible() else None
    # --- end of get_scripts (...) ---
 
-   def iter_default_scripts ( self, unpack=False ):
-      if unpack:
-         for script in self._scripts.values():
-            if script.default_events:
-               for event in script.default_events:
-                  yield ( event, script )
-      else:
-         for script in self._scripts.values():
-            if script.default_events:
-               yield script
-   # --- end of iter_default_scripts (...) ---
-
-   def get_default_scripts ( self ):
-      scripts = dict()
-      for event, script in self.iter_default_scripts ( unpack=True ):
-         if event not in scripts:
-            scripts [event] = [ script ]
-         else:
-            scripts [event].append ( script )
-
-      return scripts
-   # --- end of get_default_scripts (...) ---
-
    def iter_scripts ( self ):
-      for script in self._scripts.values():
+      for script in self.scripts.values():
          if script.is_visible():
             yield script
    # --- end of iter_scripts (...) ---
@@ -136,80 +332,354 @@ class HookScriptDir ( object ):
       except OSError as oserr:
          if oserr.errno != errno.ENOENT:
             raise
-
       else:
+         HOOK_CLS = self.HOOK_SCRIPT_CLS
          for fname in filenames:
             fspath = root + os.sep + fname
             if os.path.isfile ( fspath ):
-               script_obj = HookScript ( fspath, fname )
-               self._scripts [script_obj.name] = script_obj
+               script_obj = HOOK_CLS ( fspath, filename=fname )
+               self.scripts [script_obj.name] = script_obj
    # --- end of scan (...) ---
 
+# --- end of HookScriptDirBase ---
+
+
+class NestedHookScriptDirBase ( HookScriptDirBase ):
+   SUBDIR_CLS       = collections.OrderedDict
+   DIRNAMES_IGNORE  = frozenset({ '.*', })
+   FILENAMES_IGNORE = frozenset({ '.*', })
+
+   def dirname_filter ( self, dirname, _fnmatch=fnmatch.fnmatch ):
+      for pattern in self.DIRNAMES_IGNORE:
+         if _fnmatch ( dirname, pattern ):
+            return False
+      return True
+   # --- end of dirname_filter (...) ---
+
+   def get_script ( self, name ):
+      return [
+         script for script in self.iter_scripts() if script.name == name
+      ]
+   # --- end of get_script (...) ---
+
+   def filename_filter ( self, filename, _fnmatch=fnmatch.fnmatch ):
+      for pattern in self.FILENAMES_IGNORE:
+         if _fnmatch ( filename, pattern ):
+            return False
+      return True
+   # --- end of filename_filter (...) ---
+
+   def create_hookscript ( self, fspath, filename, root ):
+      return self.HOOK_SCRIPT_CLS ( fspath, filename=filename )
+   # --- end of create_hookscript (...) ---
+
+   def scan ( self, prune_empty=True ):
+      SUBDIR_CLS = self.SUBDIR_CLS
+
+      self.scripts = roverlay.fsutil.get_fs_dict (
+         self.root, create_item=self.create_hookscript,
+         dict_cls=self.SUBDIR_CLS, dirname_filter=self.dirname_filter,
+         filename_filter=self.filename_filter, include_root=False,
+         prune_empty=True,
+      )
+
+      for event, hook in self.iter_scripts():
+         if hook.event is None:
+            hook.event = event
+   # --- end of scan (...) ---
+
+   def iter_scripts ( self ):
+      # roverlay uses per-event subdirs containing hook files
+      SUBDIR_CLS = self.SUBDIR_CLS
+      for event, subdir in self.scripts.items():
+         if isinstance ( subdir, SUBDIR_CLS ):
+            for hook in subdir.values():
+               if isinstance ( hook, HookScriptBase ) and hook.is_visible():
+               #if not isinstance ( hook, SUBDIR_CLS ):
+                  yield ( event, hook )
+   # --- end of iter_scripts (...) ---
+
+# --- end of NestedHookScriptDirBase ---
+
+
+class UserHookScriptDir ( NestedHookScriptDirBase ):
+
+   HOOK_SCRIPT_CLS = UserHookScript
+
+   def create_hookdir_refs ( self,
+      hook_dir, overwrite=False, compare_fspath=True
+   ):
+      for event, user_script in self.iter_scripts():
+         if overwrite or not user_script.has_hookscript():
+            try:
+               hook = hook_dir.get_script ( user_script.name )
+            except KeyError:
+               pass
+            else:
+               if hook is not None and (
+                  not compare_fspath or user_script.fspath == hook.fspath
+               ):
+                  user_script.set_hookscript ( hook )
+   # --- end of create_hookdir_refs (...) ---
+
+   def make_hookdir_refs ( self, hook_dir, overwrite=False ):
+      # try exact fs path matches first, then use name-based ones
+      self.create_hookdir_refs ( hook_dir, compare_fspath=True )
+      self.create_hookdir_refs ( hook_dir, compare_fspath=False )
+   # --- end of make_hookdir_refs (...) ---
+
+   def register_hook_link_unsafe ( self, event_name, hook, link, link_name ):
+      subdir = self.scripts.get ( event_name, None )
+      if subdir is None or link_name not in subdir:
+         if subdir is None:
+            subdir = self.SUBDIR_CLS()
+            self.scripts [event_name] = subdir
+         # -- end if
+
+         entry = self.HOOKDIR_CLS ( link, filename=link_name )
+         subdir [link_name] = entry
+      else:
+         entry = subdir [link_name]
+      # -- end if
+
+      entry.set_hookscript ( hook, strict=False )
+      if entry.event is None:
+         entry.event = event_name
+      elif entry.event != event_name:
+         raise AssertionError ( "entry.event != event_name" )
+      return True
+   # --- end of register_hook_link_unsafe (...) ---
+
+   def iter_nonlinked ( self ):
+      for event, script in self.iter_scripts():
+         if not script.has_hookscript():
+            yield script
+   # --- end of iter_nonlinked (...) ---
+
+# --- end of UserHookScriptDir ---
+
+
+class HookScriptDir ( HookScriptDirBase ):
+
+   HOOK_SCRIPT_CLS = HookScript
+
+   def iter_linked ( self ):
+      # 2-tuple ( hook_script, list ( linked_user_scripts ) )
+      for script in self.iter_scripts():
+         yield ( script, list ( script.iter_user_scripts() ) )
+   # --- end of iter_linked (...) ---
+
+   def iter_default_scripts ( self, unpack=False ):
+      if unpack:
+         for script in self.iter_scripts():
+            if script.default_events:
+               for event in script.default_events:
+                  yield ( event, script )
+      else:
+         for script in self.iter_scripts():
+            if script.default_events:
+               yield script
+   # --- end of iter_default_scripts (...) ---
+
+   def get_default_scripts ( self ):
+      return roverlay.util.dictwalk.dictmerge (
+         self.iter_default_scripts ( unpack=True ),
+         get_value=lambda kv:kv[1]
+      )
+   # --- end of get_default_scripts (...) ---
+
 # --- end of HookScriptDir ---
 
 
+
 class SetupHookEnvironment (
    roverlay.setupscript.baseenv.SetupSubEnvironment
 ):
 
    NEEDS_CONFIG_TREE = True
 
-   def setup ( self ):
-      additions_dir = self.config.get ( 'OVERLAY.additions_dir', None )
-      if additions_dir:
-         self.user_hook_root = os.path.join ( additions_dir, 'hooks' )
-         self.writable       = self.setup_env.private_file.check_writable (
-            self.user_hook_root + os.sep + '.keep'
+   def format_hook_info_lines ( self,
+      info, sort_info=True, append_newline=False
+   ):
+      max_name_len = min ( 30, max ( len(x[0]) for x in info ) )
+
+      event_names  = set()
+      for name, ev_prio in info:
+         event_names.update ( item[0] for item in ev_prio )
+
+      event_words = [
+         ( ev, (4+len(ev)) * ' ' ) for ev in sorted ( event_names )
+      ]
+
+      if sort_info:
+         my_info = sorted (
+            info,
+            key=lambda k: ( not k[1], k[0] )
          )
       else:
-         self.user_hook_root = None
-         self.writable       = None
+         my_info = info
+
+      for name, event_prio_list in my_info:
+         events   = dict ( event_prio_list )
+         get_prio = lambda p: ( "UU" if p is None else p )
+
+         yield "{name:>{nlen}} | {ev}".format (
+            name=name, nlen=max_name_len,
+            ev=' '.join (
+               (
+                  "{name}({prio:0>2})".format (
+                     name=ev, prio=get_prio ( events[ev] )
+                  ) if ev in events else replacement
+                  for ev, replacement in event_words
+               )
+            )
+         ).rstrip()
+      # -- end for
+
+      if append_newline:
+         yield ""
+   # --- end of format_hook_info_lines (...) ---
+
+   def get_hook_root_info ( self, nonlinked_only=False ):
+      if nonlinked_only:
+         return [
+            ( script.name, [] )
+            for script, user_scripts in self.hook_root.iter_linked()
+               if not user_scripts
+         ]
+      else:
+         return [
+            (
+               script.name,
+               [ ( s.event or "undef", s.priority ) for s in user_scripts ]
+            )
+            for script, user_scripts in self.hook_root.iter_linked()
+         ]
+   # --- end of get_hook_root_info (...) ---
+
+   def get_user_hook_info ( self ):
+      return [
+         ( s.name, [ ( s.event or "undef", s.priority ) ] )
+         for event, s in self.user_hooks.iter_scripts()
+      ]
+   # --- end of get_user_hook_info (...) ---
+
+   def gen_hook_info_lines ( self, append_newline=True ):
+      info = (
+         self.get_user_hook_info()
+         + self.get_hook_root_info ( nonlinked_only=True )
+      )
+      for line in self.format_hook_info_lines (
+         info, append_newline=append_newline
+      ):
+         yield line
+   # --- end of gen_hook_info_lines (...) ---
+
+   def setup ( self ):
+      self.hook_overwrite_control = self.setup_env.hook_overwrite
+
+      additions_dir = self.config.get ( 'OVERLAY.additions_dir', None )
 
       self.hook_root = HookScriptDir (
          os.path.join ( self.setup_env.data_root, 'hooks' )
       )
       self.hook_root.scan()
       self._prio_gen = roverlay.util.counter.UnsafeCounter ( 30 )
+
+
+      if additions_dir:
+         self.user_hooks = UserHookScriptDir (
+            os.path.join ( additions_dir, 'hooks' )
+         )
+         self.user_hooks.writable = (
+            self.setup_env.private_file.check_writable (
+               self.user_hooks.get_fspath ( '.keep' )
+            )
+         )
+         self.user_hooks.scan()
+         if self.hook_root:
+            self.user_hooks.make_hookdir_refs ( self.hook_root )
+      else:
+         self.user_hooks = None
    # --- end of setup (...) ---
 
-   def _link_hook ( self, source, link ):
+   def check_link_allowed ( self, source, link, link_name ):
       if os.path.lexists ( link ):
-         linkdest = os.path.realpath ( link )
+         allow_overwrite = False
+
+         skip_message = (
+            'Skipping activation of hook {!r} - '.format ( link_name )
+         )
+         ov_message = 'Overwriting hook {!r} - '.format ( link_name )
+
+
+         if os.path.islink ( link ):
+            linkdest = os.path.realpath ( link )
+
+            if linkdest == source or linkdest == os.path.realpath ( source ):
+               self.info ( skip_message + "already set up.\n" )
+               allow_overwrite = None
 
-         message = 'Skipping activation of hook {!r} - '.format ( link )
+            elif os.path.exists ( link ):
+               if self.hook_overwrite_control.overwrite_symlinks():
+                  self.info ( ov_message + "symlink.\n" )
+                  allow_overwrite = True
+               else:
+                  self.error ( skip_message + "symlink.\n" )
 
-         if linkdest == source or linkdest == os.path.realpath ( source ):
-            self.info ( message + "already set up.\n" )
-            return True
+            elif self.hook_overwrite_control.overwrite_dead_symlinks():
+               self.info ( ov_message + "broken symlink.\n" )
+               allow_overwrite = True
+
+            else:
+               self.error ( skip_message + "broken symlink.\n" )
+
+         elif os.path.isfile ( link ):
+            if self.hook_overwrite_control.overwrite_all():
+               self.error ( ov_message + "file.\n" )
+               allow_overwrite = True
+            else:
+               self.error ( skip_message + "file.\n" )
 
-         elif link != linkdest:
-            # symlink or link was relative
-            self.error ( message + "is a link to another file.\n" )
          else:
-            self.error ( message + "exists, but is not a link.\n" )
+            self.error ( skip_message + "not a file!\n" )
 
-         return None
+         # -- end if
+
+         return allow_overwrite
       else:
-         return self.setup_env.private_file.symlink ( source, link )
-   # --- end of _link_hook (...) ---
+         return True
+   # --- end of check_link_allowed (...) ---
 
    def link_hooks_v ( self, event_name, hooks ):
       success = False
 
-      if self.writable and self.user_hook_root:
-         destdir = self.user_hook_root + os.sep + event_name
+      if self.user_hooks is not None and self.user_hooks.writable:
+
+         destdir = self.user_hooks.get_fspath ( event_name )
          self.setup_env.private_dir.dodir ( destdir )
 
+         # note that there is a race condition when users manipulate their
+         # hook dir while running roverlay-setup
          to_link = []
          for script in hooks:
             script.set_priority_from_generator ( self._prio_gen )
-            to_link.append (
-               ( script.fspath, destdir + os.sep + script.get_dest_name() )
-            )
-
-         success = True
-         for source, link_name in to_link:
-            if self._link_hook ( source, link_name ) is False:
+            dest_name = script.get_dest_name()
+            link = destdir + os.sep + dest_name
+
+            if self.check_link_allowed ( script.fspath, link, dest_name ):
+               to_link.append ( ( script, dest_name, link ) )
+         # -- end for
+
+         register_link = self.user_hooks.register_hook_link_unsafe
+         symlink       = self.setup_env.private_file.symlink
+         success       = True
+
+         for script, dest_name, link in to_link:
+            have_link = symlink ( script.fspath, link )
+            if have_link:
+               register_link ( event_name, script, link, dest_name )
+            elif have_link is not None:
                success = False
       # -- end if
 
@@ -230,4 +700,9 @@ class SetupHookEnvironment (
       return success
    # --- end of enable_defaults (...) ---
 
+   def run ( self ):
+      # TODO
+      self.info ( '\n'.join ( self.gen_hook_info_lines() ) )
+   # --- end of run (...) ---
+
 # --- end of SetupHookEnvironment ---

diff --git a/roverlay/setupscript/initenv.py b/roverlay/setupscript/initenv.py
index febe39e..5e511f8 100644
--- a/roverlay/setupscript/initenv.py
+++ b/roverlay/setupscript/initenv.py
@@ -101,6 +101,9 @@ class SetupInitEnvironment (
          )
 
       yield ( "enable default hooks", get_option ( 'want_default_hooks' ) )
+
+      yield ( "overwrite hooks", self.setup_env.hook_overwrite.get_str() )
+
       yield ( "additional config variables", get_option ( 'config_vars' ) )
    # --- end of gen_pretend_options (...) ---
 

diff --git a/roverlay/setupscript/runtime.py b/roverlay/setupscript/runtime.py
index e094951..5a55515 100644
--- a/roverlay/setupscript/runtime.py
+++ b/roverlay/setupscript/runtime.py
@@ -50,14 +50,18 @@ def arg_stdout_or_fs ( value ):
 class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
    MULTIPLE_COMMANDS = False
    COMMAND_DESCRIPTION = {
-      'init':     'initialize roverlay\'s config and filesystem layout',
-      'mkconfig': 'generate a config file',
+      'init'     : 'initialize roverlay\'s config and filesystem layout',
+      'mkconfig' : 'generate a config file',
+      'hooks'    : 'manage hook files',
    }
    DEFAULT_COMMAND = "init"
 
-   COMMANDS_WITH_PRETEND = frozenset ({ 'init', })
+   COMMANDS_WITH_PRETEND = frozenset ({ 'init', 'hooks', })
 
-   SETUP_TARGETS = ( 'version', 'actions', 'setup', 'config', 'init', )
+   SETUP_TARGETS = (
+      'usage', 'version',
+      'actions', 'setup', 'config', 'init', 'hooks',
+   )
    PARSE_TARGETS = ( 'actions', 'setup', 'config', 'init', )
 
 
@@ -214,6 +218,25 @@ class SetupArgParser ( roverlay.argparser.RoverlayArgumentParser ):
             )
    # --- end of parse_init (...) ---
 
+   def setup_hooks ( self ):
+      arg = self.add_argument_group (
+         "hooks", title="options for managing hooks"
+      )
+
+      arg (
+         "--overwrite-hooks", dest='hook_overwrite',
+         default="dead", const="links", nargs="?", metavar="<when>",
+         choices=[ 'none', 'dead', 'links', 'all', ],
+         flags=self.ARG_WITH_DEFAULT,
+         help=(
+            'control hook overwrite behavior '
+            '(%(choices)s; \'%(const)s\' if specified without an arg)'
+         ),
+      )
+
+      return arg
+   # --- end of setup_hooks (...) ---
+
 # --- end of SetupArgParser ---
 
 
@@ -366,6 +389,11 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
       self.data_root      = expanduser ( options ['data_root'] )
       self.conf_root      = expanduser ( options ['conf_root'] )
       self.user_conf_root = expanduser ( options ['private_conf_root'] )
+      self.hook_overwrite = (
+         roverlay.setupscript.hookenv.HookOverwriteControl.from_str (
+            options ['hook_overwrite']
+         )
+      )
 
 
       self.fs_ops_virtual = {
@@ -470,6 +498,8 @@ class SetupEnvironment ( roverlay.runtime.IndependentRuntimeEnvironment ):
    def default_main ( self ):
       if self.wants_command ( "mkconfig" ):
          self.write_config_file ( self.options ['output'] )
+      elif self.wants_command ( "hooks" ):
+         self.get_hook_env().run()
       elif self.wants_command ( "init" ):
          self.get_init_env().run()
    # --- end of default_main (...) ---


             reply	other threads:[~2013-09-12 16:36 UTC|newest]

Thread overview: 12+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-09-12 16:36 André Erdmann [this message]
  -- strict thread matches above, loose matches on Subject: below --
2014-04-01 16:38 [gentoo-commits] proj/R_overlay:master commit in: roverlay/setupscript/ André Erdmann
2014-02-22 14:56 André Erdmann
2014-02-21 17:36 André Erdmann
2014-02-17 17:22 André Erdmann
2014-02-17 17:10 André Erdmann
2013-09-19 15:00 André Erdmann
2013-09-18 15:24 André Erdmann
2013-09-13 15:20 André Erdmann
2013-09-13 15:10 André Erdmann
2013-09-13 15:10 André Erdmann
2013-09-11 14:50 André Erdmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1379003610.1eb711c0e3f609ef0d0845921f9086dc39da2f5c.dywi@gentoo \
    --to=dywi@mailerd.de \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox