1 # (Be in -*- python -*- mode.)
3 # ====================================================================
4 # Copyright (c) 2000-2008 CollabNet. All rights reserved.
6 # This software is licensed as described in the file COPYING, which
7 # you should have received as part of this distribution. The terms
8 # are also available at http://subversion.tigris.org/license-1.html.
9 # If newer versions of this license are posted there, you may use a
10 # newer version instead, at your option.
12 # This software consists of voluntary contributions made by many
13 # individuals. For exact contribution history, see the revision
14 # history and logs, available at http://cvs2svn.tigris.org/.
15 # ====================================================================
17 """This module manages the artifacts produced by conversion passes."""
20 from cvs2svn_lib
.log
import logger
21 from cvs2svn_lib
.artifact
import TempFile
24 class ArtifactNotActiveError(Exception):
25 """An artifact was requested when no passes that have registered
26 that they need it are active."""
28 def __init__(self
, artifact_name
):
30 self
, 'Artifact %s is not currently active' % artifact_name
)
33 class ArtifactManager
:
34 """Manage artifacts that are created by one pass but needed by others.
36 This class is responsible for cleaning up artifacts once they are no
37 longer needed. The trick is that cvs2svn can be run pass by pass,
38 so not all passes might be executed during a specific program run.
42 - Call artifact_manager.set_artifact(name, artifact) once for each
45 - Call artifact_manager.creates(which_pass, artifact) to indicate
46 that WHICH_PASS is the pass that creates ARTIFACT.
48 - Call artifact_manager.uses(which_pass, artifact) to indicate that
49 WHICH_PASS needs to use ARTIFACT.
51 There are also helper methods register_temp_file(),
52 register_artifact_needed(), and register_temp_file_needed() which
53 combine some useful operations.
57 - Call pass_skipped() for any passes that were already executed
58 during a previous cvs2svn run.
60 - Call pass_started() when a pass is about to start execution.
62 - If a pass that has been started will be continued during the next
63 program run, then call pass_continued().
65 - If a pass that has been started finishes execution, call
66 pass_done(), to allow any artifacts that won't be needed anymore
69 - Call pass_deferred() for any passes that have been deferred to a
74 - Call check_clean() to verify that all artifacts have been
78 # A map { artifact_name : artifact } of known artifacts.
81 # A map { pass : set_of_artifacts }, where set_of_artifacts is a
82 # set of artifacts needed by the pass.
83 self
._pass
_needs
= { }
85 # A set of passes that are currently being executed.
86 self
._active
_passes
= set()
88 def set_artifact(self
, name
, artifact
):
89 """Add ARTIFACT to the list of artifacts that we manage.
91 Store it under NAME."""
93 assert name
not in self
._artifacts
94 self
._artifacts
[name
] = artifact
96 def get_artifact(self
, name
):
97 """Return the artifact with the specified name.
99 If the artifact does not currently exist, raise a KeyError. If it
100 is not registered as being needed by one of the active passes,
101 raise an ArtifactNotActiveError."""
103 artifact
= self
._artifacts
[name
]
104 for active_pass
in self
._active
_passes
:
105 if artifact
in self
._pass
_needs
[active_pass
]:
109 raise ArtifactNotActiveError(name
)
111 def creates(self
, which_pass
, artifact
):
112 """Register that WHICH_PASS creates ARTIFACT.
114 ARTIFACT must already have been registered."""
116 # An artifact is automatically "needed" in the pass in which it is
118 self
.uses(which_pass
, artifact
)
120 def uses(self
, which_pass
, artifact
):
121 """Register that WHICH_PASS uses ARTIFACT.
123 ARTIFACT must already have been registered."""
125 artifact
._passes
_needed
.add(which_pass
)
126 if which_pass
in self
._pass
_needs
:
127 self
._pass
_needs
[which_pass
].add(artifact
)
129 self
._pass
_needs
[which_pass
] = set([artifact
])
131 def register_temp_file(self
, basename
, which_pass
):
132 """Register a temporary file with base name BASENAME as an artifact.
134 Return the filename of the temporary file."""
136 artifact
= TempFile(basename
)
137 self
.set_artifact(basename
, artifact
)
138 self
.creates(which_pass
, artifact
)
140 def get_temp_file(self
, basename
):
141 """Return the filename of the temporary file with the specified BASENAME.
143 If the temporary file is not an existing, registered TempFile,
146 return self
.get_artifact(basename
).filename
148 def register_artifact_needed(self
, artifact_name
, which_pass
):
149 """Register that WHICH_PASS uses the artifact named ARTIFACT_NAME.
151 An artifact with this name must already have been registered."""
153 artifact
= self
._artifacts
[artifact_name
]
154 artifact
._passes
_needed
.add(which_pass
)
155 if which_pass
in self
._pass
_needs
:
156 self
._pass
_needs
[which_pass
].add(artifact
)
158 self
._pass
_needs
[which_pass
] = set([artifact
,])
160 def register_temp_file_needed(self
, basename
, which_pass
):
161 """Register that a temporary file is needed by WHICH_PASS.
163 Register that the temporary file with base name BASENAME is needed
166 self
.register_artifact_needed(basename
, which_pass
)
168 def _unregister_artifacts(self
, which_pass
):
169 """Unregister any artifacts that were needed for WHICH_PASS.
171 Return a list of artifacts that are no longer needed at all."""
174 artifacts
= list(self
._pass
_needs
[which_pass
])
176 # No artifacts were needed for that pass:
179 del self
._pass
_needs
[which_pass
]
181 unneeded_artifacts
= []
182 for artifact
in artifacts
:
183 artifact
._passes
_needed
.remove(which_pass
)
184 if not artifact
._passes
_needed
:
185 unneeded_artifacts
.append(artifact
)
187 return unneeded_artifacts
189 def pass_skipped(self
, which_pass
):
190 """WHICH_PASS was executed during a previous cvs2svn run.
192 Its artifacts were created then, and any artifacts that would
193 normally be cleaned up after this pass have already been cleaned
196 self
._unregister
_artifacts
(which_pass
)
198 def pass_started(self
, which_pass
):
199 """WHICH_PASS is starting."""
201 self
._active
_passes
.add(which_pass
)
203 def pass_continued(self
, which_pass
):
204 """WHICH_PASS will be continued during the next program run.
206 WHICH_PASS, which has already been started, will be continued
207 during the next program run. Unregister any artifacts that would
208 be cleaned up at the end of WHICH_PASS without actually cleaning
211 self
._active
_passes
.remove(which_pass
)
212 self
._unregister
_artifacts
(which_pass
)
214 def pass_done(self
, which_pass
, skip_cleanup
):
215 """WHICH_PASS is done.
217 Clean up all artifacts that are no longer needed. If SKIP_CLEANUP
218 is True, then just do the bookkeeping without actually calling
219 artifact.cleanup()."""
221 self
._active
_passes
.remove(which_pass
)
222 artifacts
= self
._unregister
_artifacts
(which_pass
)
224 for artifact
in artifacts
:
227 def pass_deferred(self
, which_pass
):
228 """WHICH_PASS is being deferred until a future cvs2svn run.
230 Unregister any artifacts that would be cleaned up during
233 self
._unregister
_artifacts
(which_pass
)
235 def check_clean(self
):
236 """All passes have been processed.
238 Output a warning messages if all artifacts have not been accounted
239 for. (This is mainly a consistency check, that no artifacts were
240 registered under nonexistent passes.)"""
242 unclean_artifacts
= [
244 for artifact
in self
._artifacts
.values()
245 if artifact
._passes
_needed
]
247 if unclean_artifacts
:
249 'INTERNAL: The following artifacts were not cleaned up:\n %s\n'
250 % ('\n '.join(unclean_artifacts
)))
253 # The default ArtifactManager instance:
254 artifact_manager
= ArtifactManager()