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__"
37 from SCons.Util import PathList, scons_str2nodes, scons_subst
40 from UserList import UserList
41 from UserDict import UserDict
42 from Errors import UserError
45 from UserString import UserString
52 if os.name == 'posix':
54 def spawn(cmd, args, env):
58 os.execvpe(cmd, args, env)
61 pid, stat = os.waitpid(pid, 0)
67 def pathsearch(cmd, env):
68 # In order to deal with the fact that 1.5.2 doesn't have
69 # os.spawnvpe(), roll our own PATH search.
70 if os.path.isabs(cmd):
71 if not os.path.exists(cmd):
73 if type(exts) != type([]):
74 exts = string.split(exts, os.pathsep)
81 if type(path) != type([]):
82 path = string.split(path, os.pathsep)
84 if type(exts) != type([]):
85 exts = string.split(exts, os.pathsep)
88 for e in [None] + exts:
90 for dir, ext in pairs:
91 f = os.path.join(dir, cmd)
98 def spawn(cmd, args, env):
100 ret = os.spawnvpe(os.P_WAIT, cmd, args, env)
101 except AttributeError:
102 cmd = pathsearch(cmd, env)
103 ret = os.spawnve(os.P_WAIT, cmd, args, env)
109 """A factory for builder objects."""
110 if kw.has_key('src_builder'):
111 return apply(MultiStepBuilder, (), kw)
112 elif kw.has_key('action') and (type(kw['action']) is types.DictType or
113 isinstance(kw['action'], UserDict)):
114 action_dict = kw['action']
116 for suffix, action in action_dict.items():
118 bld_kw['action'] = action
119 bld_kw['src_suffix'] = suffix
120 builders.append(apply(BuilderBase, (), bld_kw))
122 kw['builders'] = builders
123 return apply(CompositeBuilder, (), kw)
125 return apply(BuilderBase, (), kw)
130 """Base class for Builders, objects that create output
131 nodes (files) from input nodes (files).
134 def __init__(self, name = None,
139 node_factory = SCons.Node.FS.default_fs.File):
141 self.action = Action(action)
145 self.src_suffix = src_suffix
146 self.node_factory = node_factory
147 if self.suffix and self.suffix[0] not in '.$':
148 self.suffix = '.' + self.suffix
149 if self.src_suffix and self.src_suffix[0] not in '.$':
150 self.src_suffix = '.' + self.src_suffix
152 def __cmp__(self, other):
153 return cmp(self.__dict__, other.__dict__)
155 def __call__(self, env, target = None, source = None):
156 def adjustixes(files, pre, suf):
158 if not type(files) is type([]):
161 if type(f) == type(""):
162 if pre and f[:len(pre)] != pre:
165 if f[-len(suf):] != suf:
170 tlist = scons_str2nodes(adjustixes(target,
171 env.subst(self.prefix),
172 env.subst(self.suffix)),
174 slist = scons_str2nodes(adjustixes(source, None,
175 env.subst(self.src_suffix)),
186 def execute(self, **kw):
187 """Execute a builder's action to create an output object.
189 return apply(self.action.execute, (), kw)
191 class MultiStepBuilder(BuilderBase):
192 """This is a builder subclass that can build targets in
193 multiple steps. The src_builder parameter to the constructor
194 accepts a builder that is called to build sources supplied to
195 this builder. The targets of that first build then become
196 the sources of this builder.
198 If this builder has a src_suffix supplied, then the src_builder
199 builder is NOT invoked if the suffix of a source file matches
202 def __init__(self, src_builder,
208 node_factory = SCons.Node.FS.default_fs.File):
209 BuilderBase.__init__(self, name, action, prefix, suffix, src_suffix,
211 self.src_builder = src_builder
213 def __call__(self, env, target = None, source = None):
214 slist = scons_str2nodes(source, self.node_factory)
216 src_suffix = env.subst(self.src_suffix)
218 path, ext = os.path.splitext(snode.path)
219 if not src_suffix or ext != src_suffix:
220 tgt = self.src_builder(env, target = [ path ],
222 if not type(tgt) is types.ListType:
223 final_sources.append(tgt)
225 final_sources.extend(tgt)
227 final_sources.append(snode)
228 return BuilderBase.__call__(self, env, target=target,
229 source=final_sources)
231 class CompositeBuilder(BuilderBase):
232 """This is a convenient Builder subclass that can build different
233 files based on their suffixes. For each target, this builder
234 will examine the target's sources. If they are all the same
235 suffix, and that suffix is equal to one of the child builders'
236 src_suffix, then that child builder will be used. Otherwise,
239 Child builders are supplied via the builders arg to the
240 constructor. Each must have its src_suffix set."""
241 def __init__(self, name = None,
245 BuilderBase.__init__(self, name=name, prefix=prefix,
247 self.builder_dict = {}
249 if not bld.src_suffix:
250 raise InternalError, "All builders supplied to CompositeBuilder class must have a src_suffix."
251 self.builder_dict[bld.src_suffix] = bld
253 def __call__(self, env, target = None, source = None):
254 ret = BuilderBase.__call__(self, env, target=target, source=source)
257 for suffix, bld in self.builder_dict.items():
258 builder_dict[env.subst(bld.src_suffix)] = bld
260 if type(ret) is types.ListType:
265 suflist = map(lambda x: os.path.splitext(x.path)[1],
268 for suffix in suflist:
269 if last_suffix and last_suffix != suffix:
270 raise UserError, "The builder for %s is only capable of building source files of identical suffixes." % tnode.path
274 tnode.builder_set(builder_dict[last_suffix])
276 raise UserError, "Builder not capable of building files with suffix: %s" % suffix
283 """A factory for action objects."""
285 return FunctionAction(act)
286 elif type(act) == types.StringType or isinstance(act, UserString):
287 return CommandAction(act)
288 elif type(act) == types.ListType or isinstance(act, UserList):
289 return ListAction(act)
294 """Base class for actions that create output objects.
296 We currently expect Actions will only be accessible through
297 Builder objects, so they don't yet merit their own module."""
298 def __cmp__(self, other):
299 return cmp(self.__dict__, other.__dict__)
301 def show(self, string):
304 class CommandAction(ActionBase):
305 """Class for command-execution actions."""
306 def __init__(self, string):
307 self.command = string
309 def execute(self, **kw):
311 if kw.has_key('target'):
313 if type(t) is type(""):
315 loc['targets'] = PathList(map(os.path.normpath, t))
316 loc['target'] = loc['targets'][0]
317 if kw.has_key('source'):
319 if type(s) is type(""):
321 loc['sources'] = PathList(map(os.path.normpath, s))
324 if kw.has_key('env'):
327 cmd_str = scons_subst(self.command, loc, glob)
328 for cmd in string.split(cmd_str, '\n'):
332 args = string.split(cmd)
336 import SCons.Defaults
337 ENV = SCons.Defaults.ConstructionEnvironment['ENV']
338 ret = spawn(args[0], args, ENV)
340 #XXX This doesn't account for ignoring errors (-i)
346 class FunctionAction(ActionBase):
347 """Class for Python function actions."""
348 def __init__(self, function):
349 self.function = function
351 def execute(self, **kw):
353 # XXX: WHAT SHOULD WE PRINT HERE?
355 return self.function(kw)
357 class ListAction(ActionBase):
358 """Class for lists of other actions."""
359 def __init__(self, list):
360 self.list = map(lambda x: Action(x), list)
362 def execute(self, **kw):
364 r = apply(l.execute, (), kw)