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__"
37 #XXX Get rid of the global array so this becomes re-entrant.
42 for sig_file in sig_files:
47 Encapsulates reading and writing a .sconsign file.
50 def __init__(self, dir, module):
52 dir - the directory for the file
53 module - the signature module being used
58 self.sconsign = os.path.join(dir.path, '.sconsign')
63 file = open(self.sconsign, 'rt')
67 for line in file.readlines():
68 filename, rest = map(string.strip, string.split(line, ":"))
69 self.entries[filename] = rest
72 sig_files.append(self)
74 def get(self, filename):
76 Get the .sconsign entry for a file
78 filename - the filename whose signature will be returned
79 returns - (timestamp, bsig, csig)
83 arr = map(string.strip, string.split(self.entries[filename], " "))
85 return (None, None, None)
87 if arr[1] == '-': bsig = None
88 else: bsig = self.module.from_string(arr[1])
92 if arr[2] == '-': csig = None
93 else: csig = self.module.from_string(arr[2])
96 return (int(arr[0]), bsig, csig)
98 def set(self, filename, timestamp, bsig = None, csig = None):
100 Set the .sconsign entry for a file
102 filename - the filename whose signature will be set
103 timestamp - the timestamp
104 module - the signature module being used
105 bsig - the file's build signature
106 csig - the file's content signature
108 if bsig is None: bsig = '-'
109 else: bsig = self.module.to_string(bsig)
110 if csig is None: csig = ''
111 else: csig = ' ' + self.module.to_string(csig)
112 self.entries[filename] = "%d %s%s" % (timestamp, bsig, csig)
117 Write the .sconsign file to disk.
119 Try to write to a temporary file first, and rename it if we
120 succeed. If we can't write to the temporary file, it's
121 probably because the directory isn't writable (and if so,
122 how did we build anything in this directory, anyway?), so
123 try to write directly to the .sconsign file as a backup.
124 If we can't rename, try to copy the temporary contents back
125 to the .sconsign file. Either way, always try to remove
126 the temporary file at the end.
129 temp = os.path.join(self.dir.path, '.scons%d' % os.getpid())
131 file = open(temp, 'wt')
134 file = open(self.sconsign, 'wt')
135 fname = self.sconsign
136 keys = self.entries.keys()
139 file.write("%s: %s\n" % (name, self.entries[name]))
141 if fname != self.sconsign:
143 os.rename(fname, self.sconsign)
145 open(self.sconsign, 'wb').write(open(fname, 'rb').read())
154 Encapsulates signature calculations and .sconsign file generating
155 for the build engine.
158 def __init__(self, module):
160 Initialize the calculator.
162 module - the signature module to use for signature calculations
166 def bsig(self, node):
168 Generate a node's build signature, the digested signatures
169 of its dependency files and build information.
171 node - the node whose sources will be collected
172 returns - the build signature
174 This no longer handles the recursive descent of the
175 node's children's signatures. We expect that they're
176 already built and updated by someone else, if that's
179 if not node.use_signature:
181 #XXX If configured, use the content signatures from the
182 #XXX .sconsign file if the timestamps match.
184 bsig = node.get_bsig()
188 # Collect the signatures for ALL the nodes that this
189 # node depends on. Just collecting the direct
190 # dependants is not good enough, because
191 # the signature of a non-derived file does
192 # not include the signatures of its psuedo-sources
193 # (e.g. the signature for a .c file does not include
194 # the signatures of the .h files that it includes).
196 # However, we do NOT want to walk dependencies of non-
197 # derived files, because calling get_signature() on the
198 # derived nodes will in turn call bsig() again and do that
200 def walk_non_derived(n, myself=node):
201 if not n.builder or n is myself:
202 return filter(lambda x, i=myself.ignore: x not in i,
205 walker = SCons.Node.Walker(node, walk_non_derived)
208 child = walker.next()
209 if child is None: break
210 if child is node: continue # skip the node itself
211 sigs.append(self.get_signature(child))
214 sigs.append(self.module.signature(node.builder_sig_adapter()))
215 return self.module.collect(filter(lambda x: not x is None, sigs))
217 def csig(self, node):
219 Generate a node's content signature, the digested signature
223 returns - the content signature
225 if not node.use_signature:
227 #XXX If configured, use the content signatures from the
228 #XXX .sconsign file if the timestamps match.
229 csig = node.get_csig()
233 return self.module.signature(node)
235 def get_signature(self, node):
237 Get the appropriate signature for a node.
240 returns - the signature or None if the signature could not
243 This method does not store the signature in the node and
244 in the .sconsign file.
247 if not node.use_signature:
248 # This node type doesn't use a signature (e.g. a
249 # directory) so bail right away.
252 return self.bsig(node)
253 elif not node.exists():
256 return self.csig(node)
258 def current(self, node, newsig):
260 Check if a signature is up to date with respect to a node.
262 node - the node whose signature will be checked
263 newsig - the (presumably current) signature of the file
265 returns - 1 if the file is current with the specified signature,
271 # The node itself has told us whether or not it's
272 # current without checking the signature. The
273 # canonical uses here are a "0" return for a file
274 # that doesn't exist, or a directory.
277 oldtime, oldbsig, oldcsig = node.get_prevsiginfo()
279 if not node.builder and node.get_timestamp() == oldtime:
282 return self.module.current(newsig, oldbsig)