3 The Node 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__"
34 from SCons.Errors import BuildError
41 # These are in "priority" order, so that the maximum value for any
42 # child/dependency of a node represents the state of that node if
43 # it has no builder of its own. The canonical example is a file
44 # system directory, which is only up to date if all of its children
53 """The base Node class, for entities that we know how to
54 build, or use to build other Nodes.
58 self.sources = [] # source files used to build node
59 self.depends = [] # explicit dependencies (from Depends)
60 self.implicit = {} # implicit (scanned) dependencies
69 self.use_signature = 1
72 """Actually build the node. Return the status from the build."""
75 stat = self.builder.execute(env = self.env.Dictionary(),
76 target = self, source = self.sources)
78 raise BuildError(node = self, stat = stat)
80 # If we succesfully build a node, then we need to rescan for
81 # implicit dependencies, since it might have changed on us.
83 # XXX Modify this so we only rescan using the scanner(s) relevant
85 for scn in self.scanners:
93 for scn in self.scanners:
95 for dep in self.implicit[scn]:
97 while not w.is_done():
103 def builder_set(self, builder):
104 self.builder = builder
106 def builder_sig_adapter(self):
107 """Create an adapter for calculating a builder's signature.
109 The underlying signature class will call get_contents()
110 to fetch the signature of a builder, but the actual
111 content of that signature depends on the node and the
112 environment (for construction variable substitution),
113 so this adapter provides the right glue between the two.
116 def __init__(self, node):
118 def get_contents(self):
119 env = self.node.env.Dictionary()
122 except AttributeError:
124 return self.node.builder.get_contents(env = env, dir = dir)
127 def scanner_set(self, scanner):
128 if not scanner in self.scanners:
129 self.scanners.append(scanner)
132 for scn in self.scanners:
133 self.scanned[scn] = 1
135 def env_set(self, env, safe=0):
136 if safe and self.env:
141 """Get the node's build signature (based on the signatures
142 of its dependency files and build information)."""
145 def set_bsig(self, bsig):
146 """Set the node's build signature (based on the signatures
147 of its dependency files and build information)."""
151 """Get the signature of the node's content."""
154 def set_csig(self, csig):
155 """Set the signature of the node's content."""
158 def add_dependency(self, depend):
159 """Adds dependencies. The depend argument must be a list."""
160 self._add_child(self.depends, depend)
162 def add_source(self, source):
163 """Adds sources. The source argument must be a list."""
164 self._add_child(self.sources, source)
166 def add_implicit(self, implicit, key):
167 """Adds implicit (scanned) dependencies. The implicit
168 argument must be a list."""
169 if not self.implicit.has_key(key):
170 self.implicit[key] = []
171 self._add_child(self.implicit[key], implicit)
173 def _add_child(self, collection, child):
174 """Adds 'child' to 'collection'. The 'child' argument must be a list"""
175 if type(child) is not type([]):
176 raise TypeError("child must be a list")
177 child = filter(lambda x, s=collection: x not in s, child)
179 collection.extend(child)
184 def _add_parent(self, parent):
185 """Adds 'parent' to the list of parents of this node"""
187 if parent not in self.parents: self.parents.append(parent)
190 #XXX Need to remove duplicates from this
191 return self.sources \
193 + reduce(lambda x, y: x + y, self.implicit.values(), [])
195 def get_parents(self):
198 def set_state(self, state):
207 def children_are_executed(self):
208 return reduce(lambda x,y: ((y.get_state() == executed
209 or y.get_state() == up_to_date)
214 def get_children(node): return node.children()
215 def ignore_cycle(node, stack): pass
218 def __init__(self, node, kids_func):
220 self.kids = copy.copy(kids_func(node))
222 # XXX randomize kids here, if requested
225 return str(self.node)
228 """An iterator for walking a Node tree.
230 This is depth-first, children are visited before the parent.
231 The Walker object can be initialized with any node, and
232 returns the next node on the descent with each next() call.
233 'kids_func' is an optional function that will be called to
234 get the children of a node instead of calling 'children'.
235 'cycle_func' is an optional function that will be called
236 when a cycle is detected.
238 This class does not get caught in node cycles caused, for example,
239 by C header file include loops.
241 def __init__(self, node, kids_func=get_children, cycle_func=ignore_cycle):
242 self.kids_func = kids_func
243 self.cycle_func = cycle_func
244 self.stack = [Wrapper(node, self.kids_func)]
245 self.history = {} # used to efficiently detect and avoid cycles
246 self.history[node] = None
249 """Return the next node for this walk of the tree.
251 This function is intentionally iterative, not recursive,
252 to sidestep any issues of stack size limitations.
256 if self.stack[-1].kids:
257 node = self.stack[-1].kids.pop(0)
258 if self.history.has_key(node):
259 self.cycle_func(node, self.stack)
261 self.stack.append(Wrapper(node, self.kids_func))
262 self.history[node] = None
264 node = self.stack.pop().node
265 del self.history[node]
269 return not self.stack