3 The Signature package for the scons software construction utility.
8 # Copyright (c) 2001 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__"
36 #XXX Get rid of the global array so this becomes re-entrant.
41 for sig_file in sig_files:
46 Encapsulates reading and writing a .sconsign file.
49 def __init__(self, dir, module):
51 dir - the directory for the file
52 module - the signature module being used
57 self.sconsign = os.path.join(dir.path, '.sconsign')
62 file = open(self.sconsign, 'rt')
66 for line in file.readlines():
67 filename, rest = map(string.strip, string.split(line, ":"))
68 self.entries[filename] = rest
71 sig_files.append(self)
73 def get(self, filename):
75 Get the .sconsign entry for a file
77 filename - the filename whose signature will be returned
78 returns - (timestamp, bsig, csig)
82 arr = map(string.strip, string.split(self.entries[filename], " "))
84 return (None, None, None)
86 if arr[1] == '-': bsig = None
87 else: bsig = self.module.from_string(arr[1])
91 if arr[2] == '-': csig = None
92 else: csig = self.module.from_string(arr[2])
95 return (int(arr[0]), bsig, csig)
97 def set(self, filename, timestamp, bsig = None, csig = None):
99 Set the .sconsign entry for a file
101 filename - the filename whose signature will be set
102 timestamp - the timestamp
103 module - the signature module being used
104 bsig - the file's build signature
105 csig - the file's content signature
107 if bsig is None: bsig = '-'
108 else: bsig = self.module.to_string(bsig)
109 if csig is None: csig = ''
110 else: csig = ' ' + self.module.to_string(csig)
111 self.entries[filename] = "%d %s%s" % (timestamp, bsig, csig)
116 Write the .sconsign file to disk.
120 file = open(self.sconsign, 'wt')
124 keys = self.entries.keys()
127 file.write("%s: %s\n" % (name, self.entries[name]))
132 Encapsulates signature calculations and .sconsign file generating
133 for the build engine.
136 def __init__(self, module):
138 Initialize the calculator.
140 module - the signature module to use for signature calculations
144 def bsig(self, node):
146 Generate a node's build signature, the digested signatures
147 of its dependency files and build information.
149 node - the node whose sources will be collected
150 returns - the build signature
152 This no longer handles the recursive descent of the
153 node's children's signatures. We expect that they're
154 already built and updated by someone else, if that's
157 if not node.use_signature:
159 #XXX If configured, use the content signatures from the
160 #XXX .sconsign file if the timestamps match.
162 bsig = node.get_bsig()
166 # Collect the signatures for ALL the nodes that this
167 # node depends on. Just collecting the direct
168 # dependants is not good enough, because
169 # the signature of a non-derived file does
170 # not include the signatures of its psuedo-sources
171 # (e.g. the signature for a .c file does not include
172 # the signatures of the .h files that it includes).
174 # However, we do NOT want to walk dependencies of non-
175 # derived files, because calling get_signature() on the
176 # derived nodes will in turn call bsig() again and do that
178 def walk_non_derived(n, myself=node):
179 if not n.builder or n is myself:
182 walker = SCons.Node.Walker(node, walk_non_derived)
185 child = walker.next()
186 if child is None: break
187 if child is node: continue # skip the node itself
188 sigs.append(self.get_signature(child))
191 sigs.append(self.module.signature(node.builder_sig_adapter()))
192 return self.module.collect(filter(lambda x: not x is None, sigs))
194 def csig(self, node):
196 Generate a node's content signature, the digested signature
200 returns - the content signature
202 if not node.use_signature:
204 #XXX If configured, use the content signatures from the
205 #XXX .sconsign file if the timestamps match.
206 csig = node.get_csig()
210 return self.module.signature(node)
212 def get_signature(self, node):
214 Get the appropriate signature for a node.
217 returns - the signature or None if the signature could not
220 This method does not store the signature in the node and
221 in the .sconsign file.
224 if not node.use_signature:
225 # This node type doesn't use a signature (e.g. a
226 # directory) so bail right away.
229 return self.bsig(node)
230 elif not node.exists():
233 return self.csig(node)
235 def current(self, node, newsig):
237 Check if a signature is up to date with respect to a node.
239 node - the node whose signature will be checked
240 newsig - the (presumably current) signature of the file
242 returns - 1 if the file is current with the specified signature,
248 # The node itself has told us whether or not it's
249 # current without checking the signature. The
250 # canonical uses here are a "0" return for a file
251 # that doesn't exist, or a directory.
254 oldtime, oldbsig, oldcsig = node.get_prevsiginfo()
256 if not node.builder and node.get_timestamp() == oldtime:
259 return self.module.current(newsig, oldbsig)