Add BuildDir(), Export(), and Install() functionality (courtesy Charles Crain).
[scons.git] / src / engine / SCons / Node / __init__.py
1 """SCons.Node
2
3 The Node package for the SCons software construction utility.
4
5 """
6
7 #
8 # Copyright (c) 2001 Steven Knight
9 #
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:
17 #
18 # The above copyright notice and this permission notice shall be included
19 # in all copies or substantial portions of the Software.
20 #
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.
28 #
29
30 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
31
32
33
34 from SCons.Errors import BuildError
35 import string
36 import types
37 import copy
38
39 # Node states
40 #
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
45 # were up to date.
46 pending = 1
47 executing = 2
48 up_to_date = 3
49 executed = 4
50 failed = 5
51
52 class Node:
53     """The base Node class, for entities that we know how to
54     build, or use to build other Nodes.
55     """
56
57     def __init__(self):
58         self.sources = []       # source files used to build node
59         self.depends = []       # explicit dependencies (from Depends)
60         self.implicit = {}      # implicit (scanned) dependencies
61         self.parents = []
62         self.builder = None
63         self.scanners = []
64         self.scanned = {}
65         self.env = None
66         self.state = None
67         self.bsig = None
68         self.csig = None
69         self.use_signature = 1
70
71     def build(self):
72         """Actually build the node.   Return the status from the build."""
73         if not self.builder:
74             return None
75         stat = self.builder.execute(env = self.env.Dictionary(),
76                                     target = self, source = self.sources)
77         if stat != 0:
78             raise BuildError(node = self, stat = stat)
79
80         # If we succesfully build a node, then we need to rescan for
81         # implicit dependencies, since it might have changed on us.
82
83         # XXX Modify this so we only rescan using the scanner(s) relevant
84         # to this build.
85         for scn in self.scanners:
86             try:
87                 del self.scanned[scn]
88             except KeyError:
89                 pass
90         
91         self.scan()
92
93         for scn in self.scanners:
94             try:
95                 for dep in self.implicit[scn]:
96                     w=Walker(dep)
97                     while not w.is_done():
98                         w.next().scan()
99             except KeyError:
100                 pass
101         return stat
102
103     def builder_set(self, builder):
104         self.builder = builder
105
106     def builder_sig_adapter(self):
107         """Create an adapter for calculating a builder's signature.
108
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.
114         """
115         class Adapter:
116             def __init__(self, node):
117                 self.node = node
118             def get_contents(self):
119                 env = self.node.env.Dictionary()
120                 try:
121                     dir = self.node.cwd
122                 except AttributeError:
123                     dir = None
124                 return self.node.builder.get_contents(env = env, dir = dir)
125         return Adapter(self)
126
127     def scanner_set(self, scanner):
128         if not scanner in self.scanners:
129             self.scanners.append(scanner)
130
131     def scan(self):
132         for scn in self.scanners:
133             self.scanned[scn] = 1
134
135     def env_set(self, env, safe=0):
136         if safe and self.env:
137             return
138         self.env = env
139
140     def get_bsig(self):
141         """Get the node's build signature (based on the signatures
142         of its dependency files and build information)."""
143         return self.bsig
144
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)."""
148         self.bsig = bsig
149
150     def get_csig(self):
151         """Get the signature of the node's content."""
152         return self.csig
153
154     def set_csig(self, csig):
155         """Set the signature of the node's content."""
156         self.csig = csig
157
158     def add_dependency(self, depend):
159         """Adds dependencies. The depend argument must be a list."""
160         self._add_child(self.depends, depend)
161
162     def add_source(self, source):
163         """Adds sources. The source argument must be a list."""
164         self._add_child(self.sources, source)
165
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)
172
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)
178         if child:
179             collection.extend(child)
180
181         for c in child:
182             c._add_parent(self)
183
184     def _add_parent(self, parent):
185         """Adds 'parent' to the list of parents of this node"""
186
187         if parent not in self.parents: self.parents.append(parent)
188
189     def children(self):
190         #XXX Need to remove duplicates from this
191         return self.sources \
192                + self.depends \
193                + reduce(lambda x, y: x + y, self.implicit.values(), [])
194
195     def get_parents(self):
196         return self.parents
197
198     def set_state(self, state):
199         self.state = state
200
201     def get_state(self):
202         return self.state
203
204     def current(self):
205         return None
206
207     def children_are_executed(self):
208         return reduce(lambda x,y: ((y.get_state() == executed
209                                    or y.get_state() == up_to_date)
210                                    and x),
211                       self.children(),
212                       1)
213
214 def get_children(node): return node.children()
215 def ignore_cycle(node, stack): pass
216
217 class Wrapper:
218     def __init__(self, node, kids_func):
219         self.node = node
220         self.kids = copy.copy(kids_func(node))
221
222         # XXX randomize kids here, if requested
223
224     def __str__(self):
225         return str(self.node)
226
227 class Walker:
228     """An iterator for walking a Node tree.
229
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.
237     
238     This class does not get caught in node cycles caused, for example,
239     by C header file include loops.
240     """
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
247
248     def next(self):
249         """Return the next node for this walk of the tree.
250
251         This function is intentionally iterative, not recursive,
252         to sidestep any issues of stack size limitations.
253         """
254
255         while self.stack:
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)
260                 else:
261                     self.stack.append(Wrapper(node, self.kids_func))
262                     self.history[node] = None
263             else:
264                 node = self.stack.pop().node
265                 del self.history[node]
266                 return node
267
268     def is_done(self):
269         return not self.stack