aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'cvs2svn_lib/artifact_manager.py')
-rw-r--r--cvs2svn_lib/artifact_manager.py256
1 files changed, 256 insertions, 0 deletions
diff --git a/cvs2svn_lib/artifact_manager.py b/cvs2svn_lib/artifact_manager.py
new file mode 100644
index 0000000..08f0ec7
--- /dev/null
+++ b/cvs2svn_lib/artifact_manager.py
@@ -0,0 +1,256 @@
+# (Be in -*- python -*- mode.)
+#
+# ====================================================================
+# Copyright (c) 2000-2008 CollabNet. All rights reserved.
+#
+# This software is licensed as described in the file COPYING, which
+# you should have received as part of this distribution. The terms
+# are also available at http://subversion.tigris.org/license-1.html.
+# If newer versions of this license are posted there, you may use a
+# newer version instead, at your option.
+#
+# This software consists of voluntary contributions made by many
+# individuals. For exact contribution history, see the revision
+# history and logs, available at http://cvs2svn.tigris.org/.
+# ====================================================================
+
+"""This module manages the artifacts produced by conversion passes."""
+
+
+from cvs2svn_lib.log import Log
+from cvs2svn_lib.artifact import TempFile
+
+
+class ArtifactNotActiveError(Exception):
+ """An artifact was requested when no passes that have registered
+ that they need it are active."""
+
+ def __init__(self, artifact_name):
+ Exception.__init__(
+ self, 'Artifact %s is not currently active' % artifact_name)
+
+
+class ArtifactManager:
+ """Manage artifacts that are created by one pass but needed by others.
+
+ This class is responsible for cleaning up artifacts once they are no
+ longer needed. The trick is that cvs2svn can be run pass by pass,
+ so not all passes might be executed during a specific program run.
+
+ To use this class:
+
+ - Call artifact_manager.set_artifact(name, artifact) once for each
+ known artifact.
+
+ - Call artifact_manager.creates(which_pass, artifact) to indicate
+ that WHICH_PASS is the pass that creates ARTIFACT.
+
+ - Call artifact_manager.uses(which_pass, artifact) to indicate that
+ WHICH_PASS needs to use ARTIFACT.
+
+ There are also helper methods register_temp_file(),
+ register_artifact_needed(), and register_temp_file_needed() which
+ combine some useful operations.
+
+ Then, in pass order:
+
+ - Call pass_skipped() for any passes that were already executed
+ during a previous cvs2svn run.
+
+ - Call pass_started() when a pass is about to start execution.
+
+ - If a pass that has been started will be continued during the next
+ program run, then call pass_continued().
+
+ - If a pass that has been started finishes execution, call
+ pass_done(), to allow any artifacts that won't be needed anymore
+ to be cleaned up.
+
+ - Call pass_deferred() for any passes that have been deferred to a
+ future cvs2svn run.
+
+ Finally:
+
+ - Call check_clean() to verify that all artifacts have been
+ accounted for."""
+
+ def __init__(self):
+ # A map { artifact_name : artifact } of known artifacts.
+ self._artifacts = { }
+
+ # A map { pass : set_of_artifacts }, where set_of_artifacts is a
+ # set of artifacts needed by the pass.
+ self._pass_needs = { }
+
+ # A set of passes that are currently being executed.
+ self._active_passes = set()
+
+ def set_artifact(self, name, artifact):
+ """Add ARTIFACT to the list of artifacts that we manage.
+
+ Store it under NAME."""
+
+ assert name not in self._artifacts
+ self._artifacts[name] = artifact
+
+ def get_artifact(self, name):
+ """Return the artifact with the specified name.
+
+ If the artifact does not currently exist, raise a KeyError. If it
+ is not registered as being needed by one of the active passes,
+ raise an ArtifactNotActiveError."""
+
+ artifact = self._artifacts[name]
+ for active_pass in self._active_passes:
+ if artifact in self._pass_needs[active_pass]:
+ # OK
+ return artifact
+ else:
+ raise ArtifactNotActiveError(name)
+
+ def creates(self, which_pass, artifact):
+ """Register that WHICH_PASS creates ARTIFACT.
+
+ ARTIFACT must already have been registered."""
+
+ # An artifact is automatically "needed" in the pass in which it is
+ # created:
+ self.uses(which_pass, artifact)
+
+ def uses(self, which_pass, artifact):
+ """Register that WHICH_PASS uses ARTIFACT.
+
+ ARTIFACT must already have been registered."""
+
+ artifact._passes_needed.add(which_pass)
+ if which_pass in self._pass_needs:
+ self._pass_needs[which_pass].add(artifact)
+ else:
+ self._pass_needs[which_pass] = set([artifact])
+
+ def register_temp_file(self, basename, which_pass):
+ """Register a temporary file with base name BASENAME as an artifact.
+
+ Return the filename of the temporary file."""
+
+ artifact = TempFile(basename)
+ self.set_artifact(basename, artifact)
+ self.creates(which_pass, artifact)
+
+ def get_temp_file(self, basename):
+ """Return the filename of the temporary file with the specified BASENAME.
+
+ If the temporary file is not an existing, registered TempFile,
+ raise a KeyError."""
+
+ return self.get_artifact(basename).filename
+
+ def register_artifact_needed(self, artifact_name, which_pass):
+ """Register that WHICH_PASS uses the artifact named ARTIFACT_NAME.
+
+ An artifact with this name must already have been registered."""
+
+ artifact = self._artifacts[artifact_name]
+ artifact._passes_needed.add(which_pass)
+ if which_pass in self._pass_needs:
+ self._pass_needs[which_pass].add(artifact)
+ else:
+ self._pass_needs[which_pass] = set([artifact,])
+
+ def register_temp_file_needed(self, basename, which_pass):
+ """Register that a temporary file is needed by WHICH_PASS.
+
+ Register that the temporary file with base name BASENAME is needed
+ by WHICH_PASS."""
+
+ self.register_artifact_needed(basename, which_pass)
+
+ def _unregister_artifacts(self, which_pass):
+ """Unregister any artifacts that were needed for WHICH_PASS.
+
+ Return a list of artifacts that are no longer needed at all."""
+
+ try:
+ artifacts = list(self._pass_needs[which_pass])
+ except KeyError:
+ # No artifacts were needed for that pass:
+ return []
+
+ del self._pass_needs[which_pass]
+
+ unneeded_artifacts = []
+ for artifact in artifacts:
+ artifact._passes_needed.remove(which_pass)
+ if not artifact._passes_needed:
+ unneeded_artifacts.append(artifact)
+
+ return unneeded_artifacts
+
+ def pass_skipped(self, which_pass):
+ """WHICH_PASS was executed during a previous cvs2svn run.
+
+ Its artifacts were created then, and any artifacts that would
+ normally be cleaned up after this pass have already been cleaned
+ up."""
+
+ self._unregister_artifacts(which_pass)
+
+ def pass_started(self, which_pass):
+ """WHICH_PASS is starting."""
+
+ self._active_passes.add(which_pass)
+
+ def pass_continued(self, which_pass):
+ """WHICH_PASS will be continued during the next program run.
+
+ WHICH_PASS, which has already been started, will be continued
+ during the next program run. Unregister any artifacts that would
+ be cleaned up at the end of WHICH_PASS without actually cleaning
+ them up."""
+
+ self._active_passes.remove(which_pass)
+ self._unregister_artifacts(which_pass)
+
+ def pass_done(self, which_pass, skip_cleanup):
+ """WHICH_PASS is done.
+
+ Clean up all artifacts that are no longer needed. If SKIP_CLEANUP
+ is True, then just do the bookkeeping without actually calling
+ artifact.cleanup()."""
+
+ self._active_passes.remove(which_pass)
+ artifacts = self._unregister_artifacts(which_pass)
+ if not skip_cleanup:
+ for artifact in artifacts:
+ artifact.cleanup()
+
+ def pass_deferred(self, which_pass):
+ """WHICH_PASS is being deferred until a future cvs2svn run.
+
+ Unregister any artifacts that would be cleaned up during
+ WHICH_PASS."""
+
+ self._unregister_artifacts(which_pass)
+
+ def check_clean(self):
+ """All passes have been processed.
+
+ Output a warning messages if all artifacts have not been accounted
+ for. (This is mainly a consistency check, that no artifacts were
+ registered under nonexistent passes.)"""
+
+ unclean_artifacts = [
+ str(artifact)
+ for artifact in self._artifacts.values()
+ if artifact._passes_needed]
+
+ if unclean_artifacts:
+ Log().warn(
+ 'INTERNAL: The following artifacts were not cleaned up:\n %s\n'
+ % ('\n '.join(unclean_artifacts)))
+
+
+# The default ArtifactManager instance:
+artifact_manager = ArtifactManager()
+
+