3 The Signature package for the scons software construction utility.
10 # Permission is hereby granted, free of charge, to any person obtaining
11 # a copy of this software and associated documentation files (the
12 # "Software"), to deal in the Software without restriction, including
13 # without limitation the rights to use, copy, modify, merge, publish,
14 # distribute, sublicense, and/or sell copies of the Software, and to
15 # permit persons to whom the Software is furnished to do so, subject to
16 # the following conditions:
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
21 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
22 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
23 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
45 default_module = TimeStamp
47 default_max_drift = 2*24*60*60
49 #XXX Get rid of the global array so this becomes re-entrant.
52 # 1 means use build signature for derived source files
53 # 0 means use content signature for derived source files
58 for sig_file in sig_files:
63 """Objects of this type are pickled to the .sconsign file, so it
64 should only contain simple builtin Python datatypes and no methods.
66 This class is used to store cache information about nodes between
67 scons runs for efficiency, and to store the build signature for
68 nodes so that scons can determine if they are out of date. """
70 # setup the default value for various attributes:
71 # (We make the class variables so the default values won't get pickled
72 # with the instances, which would waste a lot of space)
80 Encapsulates reading and writing a .sconsign file.
83 def __init__(self, dir, module=None):
85 dir - the directory for the file
86 module - the signature module being used
92 self.module = default_calc.module
95 self.sconsign = os.path.join(dir.path, '.sconsign')
100 file = open(self.sconsign, 'rb')
105 self.entries = cPickle.load(file)
106 if type(self.entries) is not type({}):
110 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
111 "Ignoring corrupt .sconsign file: %s"%self.sconsign)
114 sig_files.append(self)
116 def get(self, filename):
118 Get the .sconsign entry for a file
120 filename - the filename whose signature will be returned
121 returns - (timestamp, bsig, csig)
123 entry = self.get_entry(filename)
124 return (entry.timestamp, entry.bsig, entry.csig)
126 def get_entry(self, filename):
128 Create an entry for the filename and return it, or if one already exists,
132 return self.entries[filename]
134 return SConsignEntry()
136 def set_entry(self, filename, entry):
140 self.entries[filename] = entry
143 def set_csig(self, filename, csig):
145 Set the csig .sconsign entry for a file
147 filename - the filename whose signature will be set
148 csig - the file's content signature
151 entry = self.get_entry(filename)
153 self.set_entry(filename, entry)
155 def set_bsig(self, filename, bsig):
157 Set the csig .sconsign entry for a file
159 filename - the filename whose signature will be set
160 bsig - the file's built signature
163 entry = self.get_entry(filename)
165 self.set_entry(filename, entry)
167 def set_timestamp(self, filename, timestamp):
169 Set the csig .sconsign entry for a file
171 filename - the filename whose signature will be set
172 timestamp - the file's timestamp
175 entry = self.get_entry(filename)
176 entry.timestamp = timestamp
177 self.set_entry(filename, entry)
179 def get_implicit(self, filename):
180 """Fetch the cached implicit dependencies for 'filename'"""
181 entry = self.get_entry(filename)
182 return entry.implicit
184 def set_implicit(self, filename, implicit):
185 """Cache the implicit dependencies for 'filename'."""
186 entry = self.get_entry(filename)
187 if SCons.Util.is_String(implicit):
188 implicit = [implicit]
189 implicit = map(str, implicit)
190 entry.implicit = implicit
191 self.set_entry(filename, entry)
195 Write the .sconsign file to disk.
197 Try to write to a temporary file first, and rename it if we
198 succeed. If we can't write to the temporary file, it's
199 probably because the directory isn't writable (and if so,
200 how did we build anything in this directory, anyway?), so
201 try to write directly to the .sconsign file as a backup.
202 If we can't rename, try to copy the temporary contents back
203 to the .sconsign file. Either way, always try to remove
204 the temporary file at the end.
207 temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
209 file = open(temp, 'wb')
213 file = open(self.sconsign, 'wb')
214 fname = self.sconsign
217 cPickle.dump(self.entries, file, 1)
219 if fname != self.sconsign:
221 mode = os.stat(self.sconsign)[0]
222 os.chmod(self.sconsign, 0666)
223 os.unlink(self.sconsign)
227 os.rename(fname, self.sconsign)
229 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
230 os.chmod(self.sconsign, mode)
238 Encapsulates signature calculations and .sconsign file generating
239 for the build engine.
242 def __init__(self, module=default_module, max_drift=default_max_drift):
244 Initialize the calculator.
246 module - the signature module to use for signature calculations
247 max_drift - the maximum system clock drift used to determine when to
248 cache content signatures. A negative value means to never cache
249 content signatures. (defaults to 2 days)
252 self.max_drift = max_drift
254 def bsig(self, node, cache=None):
256 Generate a node's build signature, the digested signatures
257 of its dependency files and build information.
259 node - the node whose sources will be collected
260 cache - alternate node to use for the signature cache
261 returns - the build signature
263 This no longer handles the recursive descent of the
264 node's children's signatures. We expect that they're
265 already built and updated by someone else, if that's
269 if cache is None: cache = node
271 bsig = cache.get_bsig()
275 children = node.children()
277 # double check bsig, because the call to childre() above may
279 bsig = cache.get_bsig()
283 sigs = map(lambda n, c=self: n.calc_signature(c), children)
284 if node.has_builder():
285 sigs.append(self.module.signature(node.get_executor()))
287 bsig = self.module.collect(filter(lambda x: not x is None, sigs))
291 # don't store the bsig here, because it isn't accurate until
292 # the node is actually built.
296 def csig(self, node, cache=None):
298 Generate a node's content signature, the digested signature
302 cache - alternate node to use for the signature cache
303 returns - the content signature
306 if cache is None: cache = node
308 csig = cache.get_csig()
312 if self.max_drift >= 0:
313 info = node.get_prevsiginfo()
317 mtime = node.get_timestamp()
319 if (info and info[0] and info[2] and info[0] == mtime):
320 # use the signature stored in the .sconsign file
322 # Set the csig here so it doesn't get recalculated unnecessarily
323 # and so it's set when the .sconsign file gets written
326 csig = self.module.signature(node)
327 # Set the csig here so it doesn't get recalculated unnecessarily
328 # and so it's set when the .sconsign file gets written
331 if self.max_drift >= 0 and (time.time() - mtime) > self.max_drift:
333 node.store_timestamp()
337 def current(self, node, newsig):
339 Check if a signature is up to date with respect to a node.
341 node - the node whose signature will be checked
342 newsig - the (presumably current) signature of the file
344 returns - 1 if the file is current with the specified signature,
347 oldtime, oldbsig, oldcsig = node.get_prevsiginfo()
349 if not node.has_builder() and node.get_timestamp() == oldtime:
352 return self.module.current(newsig, oldbsig)
355 default_calc = Calculator()