You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@gump.apache.org by le...@apache.org on 2005/05/01 16:30:51 UTC

svn commit: r165511 - in /gump/branches/Gump3/pygump/python/gump: config.py engine/algorithm.py plugins/__init__.py plugins/buildintelligence.py

Author: leosimons
Date: Sun May  1 07:30:50 2005
New Revision: 165511

URL: http://svn.apache.org/viewcvs?rev=165511&view=rev
Log:
Refactor build algorithms into their own location (GUMP-124).

* pygump/python/gump/plugins/__init__.py: move MultiCastPlugin and support code to gump.engine.algorithm.

* pygump/python/gump/plugins/buildintelligence.py: merge intelligent multicast plugins to gump.engine.algorithm.

* pygump/python/gump/engine/algorithm.py: merge multicast plugins and support code from pygump/python/gump/plugins/__init__.py and pygump/python/gump/plugins/buildintelligence.py into pygump/python/gump/engine/algorithm.py. Rename them to better reflect their purpose, and remove the dependency on AbstractPlugin. Also update to use UPDATE_xxx constants from pygump/python/gump/model/util.py.

Added:
    gump/branches/Gump3/pygump/python/gump/engine/algorithm.py
      - copied, changed from r165027, gump/branches/Gump3/pygump/python/gump/plugins/__init__.py
Removed:
    gump/branches/Gump3/pygump/python/gump/plugins/buildintelligence.py
Modified:
    gump/branches/Gump3/pygump/python/gump/config.py
    gump/branches/Gump3/pygump/python/gump/plugins/__init__.py

Modified: gump/branches/Gump3/pygump/python/gump/config.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/config.py?rev=165511&r1=165510&r2=165511&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/config.py (original)
+++ gump/branches/Gump3/pygump/python/gump/config.py Sun May  1 07:30:50 2005
@@ -174,7 +174,7 @@
     # TODO: implement an error handler that does actual recovery...
     
     #from gump.plugins import LoggingErrorHandler
-    from gump.plugins import OptimisticLoggingErrorHandler
+    from gump.engine.algorithm import OptimisticLoggingErrorHandler
     log = get_logger(config, "plugin.error-handler")
     return OptimisticLoggingErrorHandler(log)
 
@@ -287,16 +287,16 @@
 
 def get_plugin(config):
     """Provide a Plugin implementation."""
-    from gump.plugins import MulticastPlugin
-    #from gump.plugins.buildintelligence import MoreEfficientMulticastPlugin
+    from gump.engine.algorithm import DumbAlgorithm
+    #from gump.engine.algorithm import MoreEfficientAlgorithm
     
     (pre_process_plugins, plugins, post_process_plugins) = get_plugins(config)
     error_handler = get_error_handler(config)
     
-    return (MulticastPlugin(pre_process_plugins, error_handler),
-            #MoreEfficientMulticastPlugin(plugins, error_handler),
-            MulticastPlugin(plugins, error_handler),
-            MulticastPlugin(post_process_plugins, error_handler))
+    return (DumbAlgorithm(pre_process_plugins, error_handler),
+            #MoreEfficientAlgorithm(plugins, error_handler),
+            DumbAlgorithm(plugins, error_handler),
+            DumbAlgorithm(post_process_plugins, error_handler))
 
 #
 # Miscellaneous configuration bits

Copied: gump/branches/Gump3/pygump/python/gump/engine/algorithm.py (from r165027, gump/branches/Gump3/pygump/python/gump/plugins/__init__.py)
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/engine/algorithm.py?p2=gump/branches/Gump3/pygump/python/gump/engine/algorithm.py&p1=gump/branches/Gump3/pygump/python/gump/plugins/__init__.py&r1=165027&r2=165511&rev=165511&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/plugins/__init__.py (original)
+++ gump/branches/Gump3/pygump/python/gump/engine/algorithm.py Sun May  1 07:30:50 2005
@@ -14,11 +14,19 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-"""Contains several modules which can be plugged into the pygump engine.
+"""Contains several algorithms which can be plugged into the Walker.
 
-A plugin is an instance of a class that extends AbstractPlugin. See the
-documentation for AbstractPlugin to learn more about the contracts 
-surrounding plugins.
+Algorithms are a sort of "super-plugins" which are responsible for
+
+ a) actually firing up other plugins
+ b) implementing the "build algorithm"
+
+The walker handles dependency ordering and a depth-first traversal,
+after that its up to the algorithm implementation to provide all other
+build intelligence.
+
+The algorithm code is very similar to that of a plugin; make sure to
+read the plugin documentation for some additional details.
 """
 
 __copyright__ = "Copyright (c) 2004-2005 The Apache Software Foundation"
@@ -26,7 +34,11 @@
 
 import sys
 
-from gump.model import ModelObject
+from gump.model import ModelObject, CvsModule
+from gump.model.util import mark_failure, check_failure, mark_skip, check_skip
+from gump.model.util import mark_whether_module_was_updated
+
+from gump.model.util import UPDATE_TYPE_CHECKOUT, UPDATE_TYPE_UPDATE
 
 class BaseErrorHandler:
     """Base error handler for use with the MulticastPlugin.
@@ -67,164 +79,115 @@
                 visited_model_object.exceptions = []
             visited_model_object.exceptions.append( (type, value, traceback) )
 
-class AbstractPlugin:
-    """Base class for all plugins.
-    
-    To create a concrete plugin, implement one or more of these methods:
-        
-        - initialize()
-        - visit_workspace(workspace)
-        - visit_repository(repository)
-        - visit_module(module)
-        - visit_project(project)
-        - finalize()
-    
-    Ordering
-    --------
-    Each of these methods will be called in a "topologically sorted" order.
-    Concretely, this means that:
-        
-        * initialize will be called first;
-        * visit_workspace will be called first;
-        * visit_repository will be called before any contained module or
-          project is visited;
-        * visit_module will be called before any contained project is
-          visited;
-        * visit_project will be called only after all dependencies of that
-          project have already been visited;
-        * finalize will be called last.
-    
-    More concretely, this means plugins usually do not have to worry about
-    the correct ordering of events, since this is usually what you want.
-    
-    Error handling
-    --------------
-    Plugins are expected to raise an exception (usually gump.model.Error)
-    if there is a problem with the model that prevents them from functioning
-    as intended. If they are able to proceed but their execution results in
-    a failure (which is not uncommon in the gump world at all), the plugin
-    should *not* raise an exception but rather annotate the model with the
-    information about that exception.
-    
-    Model annotation
-    ----------------
-    Plugins are expected to communicate with their environment through reading
-    properties from the gump object model or setting properties on that model.
-    
-    In general, plugins that execute commands are expected to set the return
-    code for that command as a "$action_exit_status" property on whatever part
-    of the model makes most sense, for example a plugin that executes a Script
-    model element should set the "build_exit_status" property on that element.
-    
-    Also in general, plugins that execute commands are expected to place the
-    output (both stdout and stderr) of their commands as a "$action_log"
-    property on the relevant part of the model, where "$action" is the
-    conceptual task they are performing (update,build,...).
-    """
-    def __init__(self, log):
-        self.log = log
-    
-    def _initialize(self):
-        if not hasattr(self,'initialize'): return        
-        if not callable(self.initialize): return        
-        self.initialize()
-    
-    def _visit_workspace(self, workspace):
-        if not hasattr(self,'visit_workspace'): return        
-        if not callable(self.visit_workspace): return        
-        self.visit_workspace(workspace)
-    
-    def _visit_repository(self, repository):
-        if not hasattr(self,'visit_repository'): return        
-        if not callable(self.visit_repository): return        
-        self.visit_repository(repository)
-    
-    def _visit_module(self, module):
-        if not hasattr(self,'visit_module'): return        
-        if not callable(self.visit_module): return        
-        self.visit_module(module)
-    
-    def _visit_project(self, project):
-        if not hasattr(self,'visit_project'): return        
-        if not callable(self.visit_project): return        
-        self.visit_project(project)
-
-    def _finalize(self):
-        if not hasattr(self,'finalize'): return        
-        if not callable(self.finalize): return        
-        self.finalize()
-    
-    def __str__(self):
-        return self.__class__.__name__
-    
-class MulticastPlugin(AbstractPlugin):
+class DumbAlgorithm:
     """Core plugin that redirects visit_XXX calls to other plugins."""
     def __init__(self, plugin_list, error_handler=BaseErrorHandler()):
         self.list = plugin_list
         self.error_handler = error_handler
     
-    def initialize(self):
+    def _initialize(self):
         for visitor in self.list:
             try: visitor._initialize()
             except:
                 (type, value, traceback) = sys.exc_info()
                 self.error_handler.handle(visitor, "{{{initialization stage}}}", type, value, traceback)
 
-    def visit_workspace(self, workspace):
+    def _visit_workspace(self, workspace):
         for visitor in self.list:
             try: visitor._visit_workspace(workspace)
             except:
                 (type, value, traceback) = sys.exc_info()
                 self.error_handler.handle(visitor, workspace, type, value, traceback)
 
-    def visit_repository(self, repository):
+    def _visit_repository(self, repository):
         for visitor in self.list:
             try: visitor._visit_repository(repository)
             except:
                 (type, value, traceback) = sys.exc_info()
                 self.error_handler.handle(visitor, repository, type, value, traceback)
 
-    def visit_module(self, module):
+    def _visit_module(self, module):
         for visitor in self.list:
             try: visitor._visit_module(module)
             except:
                 (type, value, traceback) = sys.exc_info()
                 self.error_handler.handle(visitor, module, type, value, traceback)
 
-    def visit_project(self, project):
+    def _visit_project(self, project):
         for visitor in self.list:
             try: visitor._visit_project(project)
             except:
                 (type, value, traceback) = sys.exc_info()
                 self.error_handler.handle(visitor, project, type, value, traceback)
 
-    def finalize(self):
+    def _finalize(self):
         for visitor in self.list:
             try: visitor._finalize()
             except:
                 (type, value, traceback) = sys.exc_info()
                 self.error_handler.handle(visitor, "{{{finalization stage}}}", type, value, traceback)
 
-class LoggingPlugin(AbstractPlugin):
-    """Plugin that prints debug messages as it visits model objects."""
-    def __init__(self, log):
-        self.log = log
-    
-    def initialize(self):
-        self.log.debug("Initializing...")
-    
-    def visit_workspace(self, workspace):
-        self.log.debug("Visiting workspace.")
+class MoreEfficientAlgorithm(DumbAlgorithm):
+    """MulticastPlugin that implements a more efficient build algorithm.
+
+    This plugin detects "failure" for a particular step in the build, and when
+    it does, it sometimes skips a few steps.
     
-    def visit_repository(self, repository):
-        self.log.debug("Visiting repository '%s'." % repository.name)
+    The following rules are implemented:
+      - if a module fails to update, the projects it contains are not built
+      - if there were no changes to the module since the previous run, the projects
+        it contains are not built
+      - if a project fails to build, none of its dependees are built
+    
+    If an element "fails", its "failed" property will be set to "True" and an
+    array named "failure_cause" will be created pointing to the elements that
+    "caused" them to fail.
+    """
+    #def __init__(self, plugin_list):
+    #    MulticastPlugin.__init__(self, plugin_list, OptimisticLoggingErrorHandler())
     
     def visit_module(self, module):
-        self.log.debug("Visiting module '%s'." % module.name)
-    
+        # run the delegates
+        try:
+            for visitor in self.list:
+                visitor._visit_module(module)
+        except:
+            (type, value, traceback) = sys.exc_info()
+            self.error_handler.handle(visitor, module, type, value, traceback)
+            mark_failure(module, module.exceptions[len(module.exceptions)-1])
+        
+        # check for update errors
+        if getattr(module, "update_exit_status", False):
+            mark_failure(module, module)
+            # if module update failed, don't try and attempt to build contained
+            # projects either.
+            for project in module.projects.values():
+                mark_failure(project, module)
+        
+        # check for changes
+        mark_whether_module_was_updated(module)
+        if not getattr(module, "was_updated", False):
+            for project in module.projects.values():
+                mark_skip(project)
+            
     def visit_project(self, project):
-        self.log.debug("Visiting project '%s'." % project.name)
-
-    def finalize(self):
-        self.log.debug("Finishing up...")
-    
+        # check for dependencies that failed to build
+        for relationship in project.dependencies:
+            if check_failure(relationship.dependency):
+                mark_failure(project, relationship)
+
+        # don't build if its not going to do any good
+        if not check_failure(project) and not check_skip(project):
+            try:
+                for visitor in self.list:
+                    visitor._visit_project(project)
+            except:
+                (type, value, traceback) = sys.exc_info()
+                self.error_handler.handle(visitor, project, type, value, traceback)
+                mark_failure(project, project.exceptions[len(project.exceptions)-1])
+            
+            # blame commands that went awry
+            for command in project.commands:
+                if getattr(command, "build_exit_status", False):
+                    mark_failure(command, command)
+                    mark_failure(project, command)

Modified: gump/branches/Gump3/pygump/python/gump/plugins/__init__.py
URL: http://svn.apache.org/viewcvs/gump/branches/Gump3/pygump/python/gump/plugins/__init__.py?rev=165511&r1=165510&r2=165511&view=diff
==============================================================================
--- gump/branches/Gump3/pygump/python/gump/plugins/__init__.py (original)
+++ gump/branches/Gump3/pygump/python/gump/plugins/__init__.py Sun May  1 07:30:50 2005
@@ -28,45 +28,6 @@
 
 from gump.model import ModelObject
 
-class BaseErrorHandler:
-    """Base error handler for use with the MulticastPlugin.
-    
-    This handler just re-raises a caught error.
-    """
-    def handle(self, visitor, visited_model_object, type, value, traceback):
-        """Override this method to be able to swallow exceptions."""
-        # TODO this is not properly saving the traceback stack. Highly annoying. Fix it!
-        raise type, value
-
-class LoggingErrorHandler:
-    """Logging error handler for use with the MulticastPlugin.
-    
-    This handler logs then just re-raises a caught error.
-    """
-    def __init__(self, log):
-        self.log = log
-
-    def handle(self, visitor, visited_model_object, type, value, traceback):
-        """Override this method to be able to swallow exceptions."""
-        self.log.exception("%s threw an exception while visiting %s!" % (visitor, visited_model_object))
-        raise type, value
-
-class OptimisticLoggingErrorHandler:
-    """Logging error handler for use with the MulticastPlugin.
-    
-    This handler logs a caught error then stores it on the model.
-    """
-    def __init__(self, log):
-        self.log = log
-
-    def handle(self, visitor, visited_model_object, type, value, traceback):
-        """Override this method to be able to swallow exceptions."""
-        self.log.exception("%s threw an exception while visiting %s!" % (visitor, visited_model_object))
-        if isinstance(visited_model_object, ModelObject):
-            if not hasattr(visited_model_object, 'exceptions'):
-                visited_model_object.exceptions = []
-            visited_model_object.exceptions.append( (type, value, traceback) )
-
 class AbstractPlugin:
     """Base class for all plugins.
     
@@ -157,54 +118,6 @@
     def __str__(self):
         return self.__class__.__name__
     
-class MulticastPlugin(AbstractPlugin):
-    """Core plugin that redirects visit_XXX calls to other plugins."""
-    def __init__(self, plugin_list, error_handler=BaseErrorHandler()):
-        self.list = plugin_list
-        self.error_handler = error_handler
-    
-    def initialize(self):
-        for visitor in self.list:
-            try: visitor._initialize()
-            except:
-                (type, value, traceback) = sys.exc_info()
-                self.error_handler.handle(visitor, "{{{initialization stage}}}", type, value, traceback)
-
-    def visit_workspace(self, workspace):
-        for visitor in self.list:
-            try: visitor._visit_workspace(workspace)
-            except:
-                (type, value, traceback) = sys.exc_info()
-                self.error_handler.handle(visitor, workspace, type, value, traceback)
-
-    def visit_repository(self, repository):
-        for visitor in self.list:
-            try: visitor._visit_repository(repository)
-            except:
-                (type, value, traceback) = sys.exc_info()
-                self.error_handler.handle(visitor, repository, type, value, traceback)
-
-    def visit_module(self, module):
-        for visitor in self.list:
-            try: visitor._visit_module(module)
-            except:
-                (type, value, traceback) = sys.exc_info()
-                self.error_handler.handle(visitor, module, type, value, traceback)
-
-    def visit_project(self, project):
-        for visitor in self.list:
-            try: visitor._visit_project(project)
-            except:
-                (type, value, traceback) = sys.exc_info()
-                self.error_handler.handle(visitor, project, type, value, traceback)
-
-    def finalize(self):
-        for visitor in self.list:
-            try: visitor._finalize()
-            except:
-                (type, value, traceback) = sys.exc_info()
-                self.error_handler.handle(visitor, "{{{finalization stage}}}", type, value, traceback)
-
 class LoggingPlugin(AbstractPlugin):
     """Plugin that prints debug messages as it visits model objects."""
     def __init__(self, log):