1 """SCons.Script.SConscript
2
3 This module defines the Python API provided to SConscript and SConstruct
4 files.
5
6 """
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 __revision__ = "src/engine/SCons/Script/SConscript.py 3842 2008/12/20 22:59:52 scons"
32
33 import SCons
34 import SCons.Action
35 import SCons.Builder
36 import SCons.Defaults
37 import SCons.Environment
38 import SCons.Errors
39 import SCons.Node
40 import SCons.Node.Alias
41 import SCons.Node.FS
42 import SCons.Platform
43 import SCons.SConf
44 import SCons.Script.Main
45 import SCons.Tool
46 import SCons.Util
47
48 import os
49 import os.path
50 import re
51 import string
52 import sys
53 import traceback
54 import types
55 import UserList
56
57
58
59
60
61
62
63
64
65
66
69
70 launch_dir = os.path.abspath(os.curdir)
71
72 GlobalDict = None
73
74
75 global_exports = {}
76
77
78 sconscript_chdir = 1
79
81 """Return the locals and globals for the function that called
82 into this module in the current call stack."""
83 try: 1/0
84 except ZeroDivisionError:
85
86
87 frame = sys.exc_info()[2].tb_frame.f_back
88
89
90
91
92
93
94
95
96 while frame.f_globals.get("__name__") == __name__:
97 frame = frame.f_back
98
99 return frame.f_locals, frame.f_globals
100
101
103 """Compute a dictionary of exports given one of the parameters
104 to the Export() function or the exports argument to SConscript()."""
105
106 loc, glob = get_calling_namespaces()
107
108 retval = {}
109 try:
110 for export in exports:
111 if SCons.Util.is_Dict(export):
112 retval.update(export)
113 else:
114 try:
115 retval[export] = loc[export]
116 except KeyError:
117 retval[export] = glob[export]
118 except KeyError, x:
119 raise SCons.Errors.UserError, "Export of non-existent variable '%s'"%x
120
121 return retval
122
124 """A frame on the SConstruct/SConscript call stack"""
125 - def __init__(self, fs, exports, sconscript):
126 self.globals = BuildDefaultGlobals()
127 self.retval = None
128 self.prev_dir = fs.getcwd()
129 self.exports = compute_exports(exports)
130
131 if isinstance(sconscript, SCons.Node.Node):
132 self.sconscript = sconscript
133 elif sconscript == '-':
134 self.sconscript = None
135 else:
136 self.sconscript = fs.File(str(sconscript))
137
138
139 call_stack = []
140
141
142
162
163
164 stack_bottom = '% Stack boTTom %'
165
167 top = fs.Top
168 sd = fs.SConstruct_dir.rdir()
169 exports = kw.get('exports', [])
170
171
172 results = []
173 for fn in files:
174 call_stack.append(Frame(fs, exports, fn))
175 old_sys_path = sys.path
176 try:
177 SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1
178 if fn == "-":
179 exec sys.stdin in call_stack[-1].globals
180 else:
181 if isinstance(fn, SCons.Node.Node):
182 f = fn
183 else:
184 f = fs.File(str(fn))
185 _file_ = None
186
187
188
189
190 fs.chdir(top, change_os_dir=1)
191 if f.rexists():
192 _file_ = open(f.rfile().get_abspath(), "r")
193 elif f.has_src_builder():
194
195
196
197
198 f.build()
199 f.built()
200 f.builder_set(None)
201 if f.exists():
202 _file_ = open(f.get_abspath(), "r")
203 if _file_:
204
205
206
207
208
209
210
211
212
213
214 try:
215 src_dir = kw['src_dir']
216 except KeyError:
217 ldir = fs.Dir(f.dir.get_path(sd))
218 else:
219 ldir = fs.Dir(src_dir)
220 if not ldir.is_under(f.dir):
221
222
223
224
225 ldir = fs.Dir(f.dir.get_path(sd))
226 try:
227 fs.chdir(ldir, change_os_dir=sconscript_chdir)
228 except OSError:
229
230
231
232
233
234
235 fs.chdir(ldir, change_os_dir=0)
236
237 os.chdir(f.rfile().dir.get_abspath())
238
239
240
241
242 sys.path = [ f.dir.get_abspath() ] + sys.path
243
244
245
246
247
248
249
250
251 call_stack[-1].globals.update({stack_bottom:1})
252 old_file = call_stack[-1].globals.get('__file__')
253 try:
254 del call_stack[-1].globals['__file__']
255 except KeyError:
256 pass
257 try:
258 try:
259 exec _file_ in call_stack[-1].globals
260 except SConscriptReturn:
261 pass
262 finally:
263 if old_file is not None:
264 call_stack[-1].globals.update({__file__:old_file})
265 else:
266 SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
267 "Ignoring missing SConscript '%s'" % f.path)
268
269 finally:
270 SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1
271 sys.path = old_sys_path
272 frame = call_stack.pop()
273 try:
274 fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir)
275 except OSError:
276
277
278
279 fs.chdir(frame.prev_dir, change_os_dir=0)
280 rdir = frame.prev_dir.rdir()
281 rdir._create()
282 try:
283 os.chdir(rdir.get_abspath())
284 except OSError, e:
285
286
287
288
289
290
291
292
293
294 if SCons.Action.execute_actions:
295 raise e
296
297 results.append(frame.retval)
298
299
300 if len(results) == 1:
301 return results[0]
302 else:
303 return tuple(results)
304
306 """Print an exception stack trace just for the SConscript file(s).
307 This will show users who have Python errors where the problem is,
308 without cluttering the output with all of the internal calls leading
309 up to where we exec the SConscript."""
310 exc_type, exc_value, exc_tb = sys.exc_info()
311 tb = exc_tb
312 while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
313 tb = tb.tb_next
314 if not tb:
315
316
317 tb = exc_tb
318 stack = traceback.extract_tb(tb)
319 try:
320 type = exc_type.__name__
321 except AttributeError:
322 type = str(exc_type)
323 if type[:11] == "exceptions.":
324 type = type[11:]
325 file.write('%s: %s:\n' % (type, exc_value))
326 for fname, line, func, text in stack:
327 file.write(' File "%s", line %d:\n' % (fname, line))
328 file.write(' %s\n' % text)
329
331 """Annotate a node with the stack frame describing the
332 SConscript file and line number that created it."""
333 tb = sys.exc_info()[2]
334 while tb and not tb.tb_frame.f_locals.has_key(stack_bottom):
335 tb = tb.tb_next
336 if not tb:
337
338 raise SCons.Errors.InternalError, "could not find SConscript stack frame"
339 node.creator = traceback.extract_stack(tb)[0]
340
341
342
343
344
345
347 """An Environment subclass that contains all of the methods that
348 are particular to the wrapper SCons interface and which aren't
349 (or shouldn't be) part of the build engine itself.
350
351 Note that not all of the methods of this class have corresponding
352 global functions, there are some private methods.
353 """
354
355
356
357
359 """Return 1 if 'major' and 'minor' are greater than the version
360 in 'v_major' and 'v_minor', and 0 otherwise."""
361 return (major > v_major or (major == v_major and minor > v_minor))
362
364 """Split a version string into major, minor and (optionally)
365 revision parts.
366
367 This is complicated by the fact that a version string can be
368 something like 3.2b1."""
369 version = string.split(string.split(version_string, ' ')[0], '.')
370 v_major = int(version[0])
371 v_minor = int(re.match('\d+', version[1]).group())
372 if len(version) >= 3:
373 v_revision = int(re.match('\d+', version[2]).group())
374 else:
375 v_revision = 0
376 return v_major, v_minor, v_revision
377
379 """
380 Convert the parameters passed to # SConscript() calls into a list
381 of files and export variables. If the parameters are invalid,
382 throws SCons.Errors.UserError. Returns a tuple (l, e) where l
383 is a list of SConscript filenames and e is a list of exports.
384 """
385 exports = []
386
387 if len(ls) == 0:
388 try:
389 dirs = kw["dirs"]
390 except KeyError:
391 raise SCons.Errors.UserError, \
392 "Invalid SConscript usage - no parameters"
393
394 if not SCons.Util.is_List(dirs):
395 dirs = [ dirs ]
396 dirs = map(str, dirs)
397
398 name = kw.get('name', 'SConscript')
399
400 files = map(lambda n, name = name: os.path.join(n, name), dirs)
401
402 elif len(ls) == 1:
403
404 files = ls[0]
405
406 elif len(ls) == 2:
407
408 files = ls[0]
409 exports = self.Split(ls[1])
410
411 else:
412
413 raise SCons.Errors.UserError, \
414 "Invalid SConscript() usage - too many arguments"
415
416 if not SCons.Util.is_List(files):
417 files = [ files ]
418
419 if kw.get('exports'):
420 exports.extend(self.Split(kw['exports']))
421
422 variant_dir = kw.get('variant_dir') or kw.get('build_dir')
423 if variant_dir:
424 if len(files) != 1:
425 raise SCons.Errors.UserError, \
426 "Invalid SConscript() usage - can only specify one SConscript with a variant_dir"
427 duplicate = kw.get('duplicate', 1)
428 src_dir = kw.get('src_dir')
429 if not src_dir:
430 src_dir, fname = os.path.split(str(files[0]))
431 files = [os.path.join(str(variant_dir), fname)]
432 else:
433 if not isinstance(src_dir, SCons.Node.Node):
434 src_dir = self.fs.Dir(src_dir)
435 fn = files[0]
436 if not isinstance(fn, SCons.Node.Node):
437 fn = self.fs.File(fn)
438 if fn.is_under(src_dir):
439
440 fname = fn.get_path(src_dir)
441 files = [os.path.join(str(variant_dir), fname)]
442 else:
443 files = [fn.abspath]
444 kw['src_dir'] = variant_dir
445 self.fs.VariantDir(variant_dir, src_dir, duplicate)
446
447 return (files, exports)
448
449
450
451
452
453
454
460
463
465 """Exit abnormally if the SCons version is not late enough."""
466 scons_ver = self._get_major_minor_revision(SCons.__version__)
467 if scons_ver < (major, minor, revision):
468 if revision:
469 scons_ver_string = '%d.%d.%d' % (major, minor, revision)
470 else:
471 scons_ver_string = '%d.%d' % (major, minor)
472 print "SCons %s or greater required, but you have SCons %s" % \
473 (scons_ver_string, SCons.__version__)
474 sys.exit(2)
475
477 """Exit abnormally if the Python version is not late enough."""
478 try:
479 v_major, v_minor, v_micro, release, serial = sys.version_info
480 python_ver = (v_major, v_minor)
481 except AttributeError:
482 python_ver = self._get_major_minor_revision(sys.version)[:2]
483 if python_ver < (major, minor):
484 v = string.split(sys.version, " ", 1)[0]
485 print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v)
486 sys.exit(2)
487
488 - def Exit(self, value=0):
490
494
498
502
503 - def Help(self, text):
506
508 try:
509 frame = call_stack[-1]
510 globals = frame.globals
511 exports = frame.exports
512 for var in vars:
513 var = self.Split(var)
514 for v in var:
515 if v == '*':
516 globals.update(global_exports)
517 globals.update(exports)
518 else:
519 if exports.has_key(v):
520 globals[v] = exports[v]
521 else:
522 globals[v] = global_exports[v]
523 except KeyError,x:
524 raise SCons.Errors.UserError, "Import of non-existent variable '%s'"%x
525
533 ls = map(subst_element, ls)
534 subst_kw = {}
535 for key, val in kw.items():
536 if SCons.Util.is_String(val):
537 val = self.subst(val)
538 elif SCons.Util.is_List(val):
539 result = []
540 for v in val:
541 if SCons.Util.is_String(v):
542 v = self.subst(v)
543 result.append(v)
544 val = result
545 subst_kw[key] = val
546
547 files, exports = self._get_SConscript_filenames(ls, subst_kw)
548 subst_kw['exports'] = exports
549 return apply(_SConscript, [self.fs,] + files, subst_kw)
550
554
558
559
560
561
562 SCons.Environment.Environment = SConsEnvironment
563
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585 _DefaultEnvironmentProxy = None
586
593
595 """A class that implements "global function" calls of
596 Environment methods by fetching the specified method from the
597 DefaultEnvironment's class. Note that this uses an intermediate
598 proxy class instead of calling the DefaultEnvironment method
599 directly so that the proxy can override the subst() method and
600 thereby prevent expansion of construction variables (since from
601 the user's point of view this was called as a global function,
602 with no associated construction environment)."""
603 - def __init__(self, method_name, subst=0):
610 env = self.factory()
611 method = getattr(env, self.method_name)
612 return apply(method, args, kw)
613
614
616 """
617 Create a dictionary containing all the default globals for
618 SConstruct and SConscript files.
619 """
620
621 global GlobalDict
622 if GlobalDict is None:
623 GlobalDict = {}
624
625 import SCons.Script
626 d = SCons.Script.__dict__
627 def not_a_module(m, d=d, mtype=type(SCons.Script)):
628 return type(d[m]) != mtype
629 for m in filter(not_a_module, dir(SCons.Script)):
630 GlobalDict[m] = d[m]
631
632 return GlobalDict.copy()
633