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 from __future__ import division
30
31 __revision__ = "src/engine/SCons/Script/SConscript.py rel_2.5.1:3735:9dc6cee5c168 2016/11/03 14:02:02 bdbaddog"
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 collections
49 import os
50 import os.path
51 import re
52 import sys
53 import traceback
54
57
58 launch_dir = os.path.abspath(os.curdir)
59
60 GlobalDict = None
61
62
63 global_exports = {}
64
65
66 sconscript_chdir = 1
67
69 """Return the locals and globals for the function that called
70 into this module in the current call stack."""
71 try: 1//0
72 except ZeroDivisionError:
73
74
75 frame = sys.exc_info()[2].tb_frame.f_back
76
77
78
79
80
81
82
83
84 while frame.f_globals.get("__name__") == __name__:
85 frame = frame.f_back
86
87 return frame.f_locals, frame.f_globals
88
89
91 """Compute a dictionary of exports given one of the parameters
92 to the Export() function or the exports argument to SConscript()."""
93
94 loc, glob = get_calling_namespaces()
95
96 retval = {}
97 try:
98 for export in exports:
99 if SCons.Util.is_Dict(export):
100 retval.update(export)
101 else:
102 try:
103 retval[export] = loc[export]
104 except KeyError:
105 retval[export] = glob[export]
106 except KeyError, x:
107 raise SCons.Errors.UserError("Export of non-existent variable '%s'"%x)
108
109 return retval
110
112 """A frame on the SConstruct/SConscript call stack"""
113 - def __init__(self, fs, exports, sconscript):
114 self.globals = BuildDefaultGlobals()
115 self.retval = None
116 self.prev_dir = fs.getcwd()
117 self.exports = compute_exports(exports)
118
119 if isinstance(sconscript, SCons.Node.Node):
120 self.sconscript = sconscript
121 elif sconscript == '-':
122 self.sconscript = None
123 else:
124 self.sconscript = fs.File(str(sconscript))
125
126
127 call_stack = []
128
129
130
150
151
152 stack_bottom = '% Stack boTTom %'
153
155 top = fs.Top
156 sd = fs.SConstruct_dir.rdir()
157 exports = kw.get('exports', [])
158
159
160 results = []
161 for fn in files:
162 call_stack.append(Frame(fs, exports, fn))
163 old_sys_path = sys.path
164 try:
165 SCons.Script.sconscript_reading = SCons.Script.sconscript_reading + 1
166 if fn == "-":
167 exec sys.stdin in call_stack[-1].globals
168 else:
169 if isinstance(fn, SCons.Node.Node):
170 f = fn
171 else:
172 f = fs.File(str(fn))
173 _file_ = None
174
175
176
177
178 fs.chdir(top, change_os_dir=1)
179 if f.rexists():
180 actual = f.rfile()
181 _file_ = open(actual.get_abspath(), "r")
182 elif f.srcnode().rexists():
183 actual = f.srcnode().rfile()
184 _file_ = open(actual.get_abspath(), "r")
185 elif f.has_src_builder():
186
187
188
189
190 f.build()
191 f.built()
192 f.builder_set(None)
193 if f.exists():
194 _file_ = open(f.get_abspath(), "r")
195 if _file_:
196
197
198
199
200
201
202
203
204
205
206 try:
207 src_dir = kw['src_dir']
208 except KeyError:
209 ldir = fs.Dir(f.dir.get_path(sd))
210 else:
211 ldir = fs.Dir(src_dir)
212 if not ldir.is_under(f.dir):
213
214
215
216
217 ldir = fs.Dir(f.dir.get_path(sd))
218 try:
219 fs.chdir(ldir, change_os_dir=sconscript_chdir)
220 except OSError:
221
222
223
224
225
226
227 fs.chdir(ldir, change_os_dir=0)
228 os.chdir(actual.dir.get_abspath())
229
230
231
232
233 sys.path = [ f.dir.get_abspath() ] + sys.path
234
235
236
237
238
239
240
241
242 call_stack[-1].globals.update({stack_bottom:1})
243 old_file = call_stack[-1].globals.get('__file__')
244 try:
245 del call_stack[-1].globals['__file__']
246 except KeyError:
247 pass
248 try:
249 try:
250 exec _file_ in call_stack[-1].globals
251 except SConscriptReturn:
252 pass
253 finally:
254 if old_file is not None:
255 call_stack[-1].globals.update({__file__:old_file})
256 else:
257 SCons.Warnings.warn(SCons.Warnings.MissingSConscriptWarning,
258 "Ignoring missing SConscript '%s'" % f.get_internal_path())
259
260 finally:
261 SCons.Script.sconscript_reading = SCons.Script.sconscript_reading - 1
262 sys.path = old_sys_path
263 frame = call_stack.pop()
264 try:
265 fs.chdir(frame.prev_dir, change_os_dir=sconscript_chdir)
266 except OSError:
267
268
269
270 fs.chdir(frame.prev_dir, change_os_dir=0)
271 rdir = frame.prev_dir.rdir()
272 rdir._create()
273 try:
274 os.chdir(rdir.get_abspath())
275 except OSError, e:
276
277
278
279
280
281
282
283
284
285 if SCons.Action.execute_actions:
286 raise e
287
288 results.append(frame.retval)
289
290
291 if len(results) == 1:
292 return results[0]
293 else:
294 return tuple(results)
295
297 """Print an exception stack trace just for the SConscript file(s).
298 This will show users who have Python errors where the problem is,
299 without cluttering the output with all of the internal calls leading
300 up to where we exec the SConscript."""
301 exc_type, exc_value, exc_tb = sys.exc_info()
302 tb = exc_tb
303 while tb and stack_bottom not in tb.tb_frame.f_locals:
304 tb = tb.tb_next
305 if not tb:
306
307
308 tb = exc_tb
309 stack = traceback.extract_tb(tb)
310 try:
311 type = exc_type.__name__
312 except AttributeError:
313 type = str(exc_type)
314 if type[:11] == "exceptions.":
315 type = type[11:]
316 file.write('%s: %s:\n' % (type, exc_value))
317 for fname, line, func, text in stack:
318 file.write(' File "%s", line %d:\n' % (fname, line))
319 file.write(' %s\n' % text)
320
322 """Annotate a node with the stack frame describing the
323 SConscript file and line number that created it."""
324 tb = sys.exc_info()[2]
325 while tb and stack_bottom not in tb.tb_frame.f_locals:
326 tb = tb.tb_next
327 if not tb:
328
329 raise SCons.Errors.InternalError("could not find SConscript stack frame")
330 node.creator = traceback.extract_stack(tb)[0]
331
332
333
334
335
336
338 """An Environment subclass that contains all of the methods that
339 are particular to the wrapper SCons interface and which aren't
340 (or shouldn't be) part of the build engine itself.
341
342 Note that not all of the methods of this class have corresponding
343 global functions, there are some private methods.
344 """
345
346
347
348
350 """Return 1 if 'major' and 'minor' are greater than the version
351 in 'v_major' and 'v_minor', and 0 otherwise."""
352 return (major > v_major or (major == v_major and minor > v_minor))
353
355 """Split a version string into major, minor and (optionally)
356 revision parts.
357
358 This is complicated by the fact that a version string can be
359 something like 3.2b1."""
360 version = version_string.split(' ')[0].split('.')
361 v_major = int(version[0])
362 v_minor = int(re.match('\d+', version[1]).group())
363 if len(version) >= 3:
364 v_revision = int(re.match('\d+', version[2]).group())
365 else:
366 v_revision = 0
367 return v_major, v_minor, v_revision
368
370 """
371 Convert the parameters passed to SConscript() calls into a list
372 of files and export variables. If the parameters are invalid,
373 throws SCons.Errors.UserError. Returns a tuple (l, e) where l
374 is a list of SConscript filenames and e is a list of exports.
375 """
376 exports = []
377
378 if len(ls) == 0:
379 try:
380 dirs = kw["dirs"]
381 except KeyError:
382 raise SCons.Errors.UserError("Invalid SConscript usage - no parameters")
383
384 if not SCons.Util.is_List(dirs):
385 dirs = [ dirs ]
386 dirs = list(map(str, dirs))
387
388 name = kw.get('name', 'SConscript')
389
390 files = [os.path.join(n, name) for n in dirs]
391
392 elif len(ls) == 1:
393
394 files = ls[0]
395
396 elif len(ls) == 2:
397
398 files = ls[0]
399 exports = self.Split(ls[1])
400
401 else:
402
403 raise SCons.Errors.UserError("Invalid SConscript() usage - too many arguments")
404
405 if not SCons.Util.is_List(files):
406 files = [ files ]
407
408 if kw.get('exports'):
409 exports.extend(self.Split(kw['exports']))
410
411 variant_dir = kw.get('variant_dir') or kw.get('build_dir')
412 if variant_dir:
413 if len(files) != 1:
414 raise SCons.Errors.UserError("Invalid SConscript() usage - can only specify one SConscript with a variant_dir")
415 duplicate = kw.get('duplicate', 1)
416 src_dir = kw.get('src_dir')
417 if not src_dir:
418 src_dir, fname = os.path.split(str(files[0]))
419 files = [os.path.join(str(variant_dir), fname)]
420 else:
421 if not isinstance(src_dir, SCons.Node.Node):
422 src_dir = self.fs.Dir(src_dir)
423 fn = files[0]
424 if not isinstance(fn, SCons.Node.Node):
425 fn = self.fs.File(fn)
426 if fn.is_under(src_dir):
427
428 fname = fn.get_path(src_dir)
429 files = [os.path.join(str(variant_dir), fname)]
430 else:
431 files = [fn.get_abspath()]
432 kw['src_dir'] = variant_dir
433 self.fs.VariantDir(variant_dir, src_dir, duplicate)
434
435 return (files, exports)
436
437
438
439
440
441
442
448
451
453 """Exit abnormally if the SCons version is not late enough."""
454
455 if SCons.__version__ == '__' + 'VERSION__':
456 SCons.Warnings.warn(SCons.Warnings.DevelopmentVersionWarning,
457 "EnsureSConsVersion is ignored for development version")
458 return
459 scons_ver = self._get_major_minor_revision(SCons.__version__)
460 if scons_ver < (major, minor, revision):
461 if revision:
462 scons_ver_string = '%d.%d.%d' % (major, minor, revision)
463 else:
464 scons_ver_string = '%d.%d' % (major, minor)
465 print "SCons %s or greater required, but you have SCons %s" % \
466 (scons_ver_string, SCons.__version__)
467 sys.exit(2)
468
470 """Exit abnormally if the Python version is not late enough."""
471 if sys.version_info < (major, minor):
472 v = sys.version.split()[0]
473 print "Python %d.%d or greater required, but you have Python %s" %(major,minor,v)
474 sys.exit(2)
475
476 - def Exit(self, value=0):
478
479 - def Export(self, *vars, **kw):
483
487
491
492 - def Help(self, text, append=False):
495
497 try:
498 frame = call_stack[-1]
499 globals = frame.globals
500 exports = frame.exports
501 for var in vars:
502 var = self.Split(var)
503 for v in var:
504 if v == '*':
505 globals.update(global_exports)
506 globals.update(exports)
507 else:
508 if v in exports:
509 globals[v] = exports[v]
510 else:
511 globals[v] = global_exports[v]
512 except KeyError,x:
513 raise SCons.Errors.UserError("Import of non-existent variable '%s'"%x)
514
525 ls = list(map(subst_element, ls))
526 subst_kw = {}
527 for key, val in kw.items():
528 if SCons.Util.is_String(val):
529 val = self.subst(val)
530 elif SCons.Util.is_List(val):
531 result = []
532 for v in val:
533 if SCons.Util.is_String(v):
534 v = self.subst(v)
535 result.append(v)
536 val = result
537 subst_kw[key] = val
538
539 files, exports = self._get_SConscript_filenames(ls, subst_kw)
540 subst_kw['exports'] = exports
541 return _SConscript(self.fs, *files, **subst_kw)
542
546
550
551
552
553
554 SCons.Environment.Environment = SConsEnvironment
555
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577 _DefaultEnvironmentProxy = None
578
585
587 """A class that implements "global function" calls of
588 Environment methods by fetching the specified method from the
589 DefaultEnvironment's class. Note that this uses an intermediate
590 proxy class instead of calling the DefaultEnvironment method
591 directly so that the proxy can override the subst() method and
592 thereby prevent expansion of construction variables (since from
593 the user's point of view this was called as a global function,
594 with no associated construction environment)."""
595 - def __init__(self, method_name, subst=0):
602 env = self.factory()
603 method = getattr(env, self.method_name)
604 return method(*args, **kw)
605
606
608 """
609 Create a dictionary containing all the default globals for
610 SConstruct and SConscript files.
611 """
612
613 global GlobalDict
614 if GlobalDict is None:
615 GlobalDict = {}
616
617 import SCons.Script
618 d = SCons.Script.__dict__
619 def not_a_module(m, d=d, mtype=type(SCons.Script)):
620 return not isinstance(d[m], mtype)
621 for m in filter(not_a_module, dir(SCons.Script)):
622 GlobalDict[m] = d[m]
623
624 return GlobalDict.copy()
625
626
627
628
629
630
631