3 The Signature package for the scons software construction utility.
8 # Copyright (c) 2001, 2002 Steven Knight
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 #XXX Get rid of the global array so this becomes re-entrant.
50 # 1 means use build signature for derived source files
51 # 0 means use content signature for derived source files
56 for sig_file in sig_files:
61 """Objects of this type are pickled to the .sconsign file, so it
62 should only contain simple builtin Python datatypes and no methods.
64 This class is used to store cache information about nodes between
65 scons runs for efficiency, and to store the build signature for
66 nodes so that scons can determine if they are out of date. """
68 # setup the default value for various attributes:
69 # (We make the class variables so the default values won't get pickled
70 # with the instances, which would waste a lot of space)
78 Encapsulates reading and writing a .sconsign file.
81 def __init__(self, dir, module=None):
83 dir - the directory for the file
84 module - the signature module being used
90 self.module = default_calc.module
93 self.sconsign = os.path.join(dir.path, '.sconsign')
98 file = open(self.sconsign, 'rb')
103 self.entries = cPickle.load(file)
104 if type(self.entries) is not type({}):
108 SCons.Warnings.warn(SCons.Warnings.CorruptSConsignWarning,
109 "Ignoring corrupt .sconsign file: %s"%self.sconsign)
112 sig_files.append(self)
114 def get(self, filename):
116 Get the .sconsign entry for a file
118 filename - the filename whose signature will be returned
119 returns - (timestamp, bsig, csig)
121 entry = self.get_entry(filename)
122 return (entry.timestamp, entry.bsig, entry.csig)
124 def get_entry(self, filename):
126 Create an entry for the filename and return it, or if one already exists,
130 return self.entries[filename]
132 return SConsignEntry()
134 def set_entry(self, filename, entry):
138 self.entries[filename] = entry
141 def set_csig(self, filename, csig):
143 Set the csig .sconsign entry for a file
145 filename - the filename whose signature will be set
146 csig - the file's content signature
149 entry = self.get_entry(filename)
151 self.set_entry(filename, entry)
153 def set_bsig(self, filename, bsig):
155 Set the csig .sconsign entry for a file
157 filename - the filename whose signature will be set
158 bsig - the file's built signature
161 entry = self.get_entry(filename)
163 self.set_entry(filename, entry)
165 def set_timestamp(self, filename, timestamp):
167 Set the csig .sconsign entry for a file
169 filename - the filename whose signature will be set
170 timestamp - the file's timestamp
173 entry = self.get_entry(filename)
174 entry.timestamp = timestamp
175 self.set_entry(filename, entry)
177 def get_implicit(self, filename):
178 """Fetch the cached implicit dependencies for 'filename'"""
179 entry = self.get_entry(filename)
180 return entry.implicit
182 def set_implicit(self, filename, implicit):
183 """Cache the implicit dependencies for 'filename'."""
184 entry = self.get_entry(filename)
185 if SCons.Util.is_String(implicit):
186 implicit = [implicit]
187 implicit = map(str, implicit)
188 entry.implicit = implicit
189 self.set_entry(filename, entry)
193 Write the .sconsign file to disk.
195 Try to write to a temporary file first, and rename it if we
196 succeed. If we can't write to the temporary file, it's
197 probably because the directory isn't writable (and if so,
198 how did we build anything in this directory, anyway?), so
199 try to write directly to the .sconsign file as a backup.
200 If we can't rename, try to copy the temporary contents back
201 to the .sconsign file. Either way, always try to remove
202 the temporary file at the end.
205 temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
207 file = open(temp, 'wb')
211 file = open(self.sconsign, 'wb')
212 fname = self.sconsign
215 cPickle.dump(self.entries, file, 1)
217 if fname != self.sconsign:
219 mode = os.stat(self.sconsign)[0]
220 os.chmod(self.sconsign, 0666)
221 os.unlink(self.sconsign)
225 os.rename(fname, self.sconsign)
227 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
228 os.chmod(self.sconsign, mode)
236 Encapsulates signature calculations and .sconsign file generating
237 for the build engine.
240 def __init__(self, module=default_module, max_drift=2*24*60*60):
242 Initialize the calculator.
244 module - the signature module to use for signature calculations
245 max_drift - the maximum system clock drift used to determine when to
246 cache content signatures. A negative value means to never cache
247 content signatures. (defaults to 2 days)
250 self.max_drift = max_drift
252 def bsig(self, node):
254 Generate a node's build signature, the digested signatures
255 of its dependency files and build information.
257 node - the node whose sources will be collected
258 returns - the build signature
260 This no longer handles the recursive descent of the
261 node's children's signatures. We expect that they're
262 already built and updated by someone else, if that's
265 sigs = map(lambda n, c=self: n.calc_signature(c), node.children())
267 sigs.append(self.module.signature(node.builder_sig_adapter()))
269 bsig = self.module.collect(filter(lambda x: not x is None, sigs))
273 # don't store the bsig here, because it isn't accurate until
274 # the node is actually built.
278 def csig(self, node):
280 Generate a node's content signature, the digested signature
284 returns - the content signature
286 if self.max_drift >= 0:
287 info = node.get_prevsiginfo()
291 mtime = node.get_timestamp()
293 if (info and info[0] and info[2] and info[0] == mtime):
294 # use the signature stored in the .sconsign file
296 # Set the csig here so it doesn't get recalculated unnecessarily
297 # and so it's set when the .sconsign file gets written
300 csig = self.module.signature(node)
301 # Set the csig here so it doesn't get recalculated unnecessarily
302 # and so it's set when the .sconsign file gets written
305 if self.max_drift >= 0 and (time.time() - mtime) > self.max_drift:
307 node.store_timestamp()
311 def current(self, node, newsig):
313 Check if a signature is up to date with respect to a node.
315 node - the node whose signature will be checked
316 newsig - the (presumably current) signature of the file
318 returns - 1 if the file is current with the specified signature,
321 oldtime, oldbsig, oldcsig = node.get_prevsiginfo()
323 if not node.builder and node.get_timestamp() == oldtime:
326 return self.module.current(newsig, oldbsig)
329 default_calc = Calculator()