You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1427 lines
51 KiB

6 years ago
  1. """SCons.Script
  2. This file implements the main() function used by the scons script.
  3. Architecturally, this *is* the scons script, and will likely only be
  4. called from the external "scons" wrapper. Consequently, anything here
  5. should not be, or be considered, part of the build engine. If it's
  6. something that we expect other software to want to use, it should go in
  7. some other module. If it's specific to the "scons" script invocation,
  8. it goes here.
  9. """
  10. from __future__ import print_function
  11. unsupported_python_version = (2, 6, 0)
  12. deprecated_python_version = (2, 7, 0)
  13. # Copyright (c) 2001 - 2017 The SCons Foundation
  14. #
  15. # Permission is hereby granted, free of charge, to any person obtaining
  16. # a copy of this software and associated documentation files (the
  17. # "Software"), to deal in the Software without restriction, including
  18. # without limitation the rights to use, copy, modify, merge, publish,
  19. # distribute, sublicense, and/or sell copies of the Software, and to
  20. # permit persons to whom the Software is furnished to do so, subject to
  21. # the following conditions:
  22. #
  23. # The above copyright notice and this permission notice shall be included
  24. # in all copies or substantial portions of the Software.
  25. #
  26. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
  27. # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  28. # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  29. # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  30. # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  31. # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  32. # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  33. __revision__ = "src/engine/SCons/Script/Main.py rel_3.0.0:4395:8972f6a2f699 2017/09/18 12:59:24 bdbaddog"
  34. import SCons.compat
  35. import os
  36. import sys
  37. import time
  38. import traceback
  39. import sysconfig
  40. import SCons.CacheDir
  41. import SCons.Debug
  42. import SCons.Defaults
  43. import SCons.Environment
  44. import SCons.Errors
  45. import SCons.Job
  46. import SCons.Node
  47. import SCons.Node.FS
  48. import SCons.Platform
  49. import SCons.SConf
  50. import SCons.Script
  51. import SCons.Taskmaster
  52. import SCons.Util
  53. import SCons.Warnings
  54. import SCons.Script.Interactive
  55. def fetch_win32_parallel_msg():
  56. # A subsidiary function that exists solely to isolate this import
  57. # so we don't have to pull it in on all platforms, and so that an
  58. # in-line "import" statement in the _main() function below doesn't
  59. # cause warnings about local names shadowing use of the 'SCons'
  60. # global in nest scopes and UnboundLocalErrors and the like in some
  61. # versions (2.1) of Python.
  62. import SCons.Platform.win32
  63. return SCons.Platform.win32.parallel_msg
  64. def revert_io():
  65. # This call is added to revert stderr and stdout to the original
  66. # ones just in case some build rule or something else in the system
  67. # has redirected them elsewhere.
  68. sys.stderr = sys.__stderr__
  69. sys.stdout = sys.__stdout__
  70. class SConsPrintHelpException(Exception):
  71. pass
  72. display = SCons.Util.display
  73. progress_display = SCons.Util.DisplayEngine()
  74. first_command_start = None
  75. last_command_end = None
  76. class Progressor(object):
  77. prev = ''
  78. count = 0
  79. target_string = '$TARGET'
  80. def __init__(self, obj, interval=1, file=None, overwrite=False):
  81. if file is None:
  82. file = sys.stdout
  83. self.obj = obj
  84. self.file = file
  85. self.interval = interval
  86. self.overwrite = overwrite
  87. if callable(obj):
  88. self.func = obj
  89. elif SCons.Util.is_List(obj):
  90. self.func = self.spinner
  91. elif obj.find(self.target_string) != -1:
  92. self.func = self.replace_string
  93. else:
  94. self.func = self.string
  95. def write(self, s):
  96. self.file.write(s)
  97. self.file.flush()
  98. self.prev = s
  99. def erase_previous(self):
  100. if self.prev:
  101. length = len(self.prev)
  102. if self.prev[-1] in ('\n', '\r'):
  103. length = length - 1
  104. self.write(' ' * length + '\r')
  105. self.prev = ''
  106. def spinner(self, node):
  107. self.write(self.obj[self.count % len(self.obj)])
  108. def string(self, node):
  109. self.write(self.obj)
  110. def replace_string(self, node):
  111. self.write(self.obj.replace(self.target_string, str(node)))
  112. def __call__(self, node):
  113. self.count = self.count + 1
  114. if (self.count % self.interval) == 0:
  115. if self.overwrite:
  116. self.erase_previous()
  117. self.func(node)
  118. ProgressObject = SCons.Util.Null()
  119. def Progress(*args, **kw):
  120. global ProgressObject
  121. ProgressObject = Progressor(*args, **kw)
  122. # Task control.
  123. #
  124. _BuildFailures = []
  125. def GetBuildFailures():
  126. return _BuildFailures
  127. class BuildTask(SCons.Taskmaster.OutOfDateTask):
  128. """An SCons build task."""
  129. progress = ProgressObject
  130. def display(self, message):
  131. display('scons: ' + message)
  132. def prepare(self):
  133. self.progress(self.targets[0])
  134. return SCons.Taskmaster.OutOfDateTask.prepare(self)
  135. def needs_execute(self):
  136. if SCons.Taskmaster.OutOfDateTask.needs_execute(self):
  137. return True
  138. if self.top and self.targets[0].has_builder():
  139. display("scons: `%s' is up to date." % str(self.node))
  140. return False
  141. def execute(self):
  142. if print_time:
  143. start_time = time.time()
  144. global first_command_start
  145. if first_command_start is None:
  146. first_command_start = start_time
  147. SCons.Taskmaster.OutOfDateTask.execute(self)
  148. if print_time:
  149. global cumulative_command_time
  150. global last_command_end
  151. finish_time = time.time()
  152. last_command_end = finish_time
  153. cumulative_command_time = cumulative_command_time+finish_time-start_time
  154. sys.stdout.write("Command execution time: %s: %f seconds\n"%(str(self.node), finish_time-start_time))
  155. def do_failed(self, status=2):
  156. _BuildFailures.append(self.exception[1])
  157. global exit_status
  158. global this_build_status
  159. if self.options.ignore_errors:
  160. SCons.Taskmaster.OutOfDateTask.executed(self)
  161. elif self.options.keep_going:
  162. SCons.Taskmaster.OutOfDateTask.fail_continue(self)
  163. exit_status = status
  164. this_build_status = status
  165. else:
  166. SCons.Taskmaster.OutOfDateTask.fail_stop(self)
  167. exit_status = status
  168. this_build_status = status
  169. def executed(self):
  170. t = self.targets[0]
  171. if self.top and not t.has_builder() and not t.side_effect:
  172. if not t.exists():
  173. if t.__class__.__name__ in ('File', 'Dir', 'Entry'):
  174. errstr="Do not know how to make %s target `%s' (%s)." % (t.__class__.__name__, t, t.get_abspath())
  175. else: # Alias or Python or ...
  176. errstr="Do not know how to make %s target `%s'." % (t.__class__.__name__, t)
  177. sys.stderr.write("scons: *** " + errstr)
  178. if not self.options.keep_going:
  179. sys.stderr.write(" Stop.")
  180. sys.stderr.write("\n")
  181. try:
  182. raise SCons.Errors.BuildError(t, errstr)
  183. except KeyboardInterrupt:
  184. raise
  185. except:
  186. self.exception_set()
  187. self.do_failed()
  188. else:
  189. print("scons: Nothing to be done for `%s'." % t)
  190. SCons.Taskmaster.OutOfDateTask.executed(self)
  191. else:
  192. SCons.Taskmaster.OutOfDateTask.executed(self)
  193. def failed(self):
  194. # Handle the failure of a build task. The primary purpose here
  195. # is to display the various types of Errors and Exceptions
  196. # appropriately.
  197. exc_info = self.exc_info()
  198. try:
  199. t, e, tb = exc_info
  200. except ValueError:
  201. t, e = exc_info
  202. tb = None
  203. if t is None:
  204. # The Taskmaster didn't record an exception for this Task;
  205. # see if the sys module has one.
  206. try:
  207. t, e, tb = sys.exc_info()[:]
  208. except ValueError:
  209. t, e = exc_info
  210. tb = None
  211. # Deprecated string exceptions will have their string stored
  212. # in the first entry of the tuple.
  213. if e is None:
  214. e = t
  215. buildError = SCons.Errors.convert_to_BuildError(e)
  216. if not buildError.node:
  217. buildError.node = self.node
  218. node = buildError.node
  219. if not SCons.Util.is_List(node):
  220. node = [ node ]
  221. nodename = ', '.join(map(str, node))
  222. errfmt = "scons: *** [%s] %s\n"
  223. sys.stderr.write(errfmt % (nodename, buildError))
  224. if (buildError.exc_info[2] and buildError.exc_info[1] and
  225. not isinstance(
  226. buildError.exc_info[1],
  227. (EnvironmentError, SCons.Errors.StopError,
  228. SCons.Errors.UserError))):
  229. type, value, trace = buildError.exc_info
  230. if tb and print_stacktrace:
  231. sys.stderr.write("scons: internal stack trace:\n")
  232. traceback.print_tb(tb, file=sys.stderr)
  233. traceback.print_exception(type, value, trace)
  234. elif tb and print_stacktrace:
  235. sys.stderr.write("scons: internal stack trace:\n")
  236. traceback.print_tb(tb, file=sys.stderr)
  237. self.exception = (e, buildError, tb) # type, value, traceback
  238. self.do_failed(buildError.exitstatus)
  239. self.exc_clear()
  240. def postprocess(self):
  241. if self.top:
  242. t = self.targets[0]
  243. for tp in self.options.tree_printers:
  244. tp.display(t)
  245. if self.options.debug_includes:
  246. tree = t.render_include_tree()
  247. if tree:
  248. print()
  249. print(tree)
  250. SCons.Taskmaster.OutOfDateTask.postprocess(self)
  251. def make_ready(self):
  252. """Make a task ready for execution"""
  253. SCons.Taskmaster.OutOfDateTask.make_ready(self)
  254. if self.out_of_date and self.options.debug_explain:
  255. explanation = self.out_of_date[0].explain()
  256. if explanation:
  257. sys.stdout.write("scons: " + explanation)
  258. class CleanTask(SCons.Taskmaster.AlwaysTask):
  259. """An SCons clean task."""
  260. def fs_delete(self, path, pathstr, remove=True):
  261. try:
  262. if os.path.lexists(path):
  263. if os.path.isfile(path) or os.path.islink(path):
  264. if remove: os.unlink(path)
  265. display("Removed " + pathstr)
  266. elif os.path.isdir(path) and not os.path.islink(path):
  267. # delete everything in the dir
  268. for e in sorted(os.listdir(path)):
  269. p = os.path.join(path, e)
  270. s = os.path.join(pathstr, e)
  271. if os.path.isfile(p):
  272. if remove: os.unlink(p)
  273. display("Removed " + s)
  274. else:
  275. self.fs_delete(p, s, remove)
  276. # then delete dir itself
  277. if remove: os.rmdir(path)
  278. display("Removed directory " + pathstr)
  279. else:
  280. errstr = "Path '%s' exists but isn't a file or directory."
  281. raise SCons.Errors.UserError(errstr % (pathstr))
  282. except SCons.Errors.UserError as e:
  283. print(e)
  284. except (IOError, OSError) as e:
  285. print("scons: Could not remove '%s':" % pathstr, e.strerror)
  286. def _get_files_to_clean(self):
  287. result = []
  288. target = self.targets[0]
  289. if target.has_builder() or target.side_effect:
  290. result = [t for t in self.targets if not t.noclean]
  291. return result
  292. def _clean_targets(self, remove=True):
  293. target = self.targets[0]
  294. if target in SCons.Environment.CleanTargets:
  295. files = SCons.Environment.CleanTargets[target]
  296. for f in files:
  297. self.fs_delete(f.get_abspath(), str(f), remove)
  298. def show(self):
  299. for t in self._get_files_to_clean():
  300. if not t.isdir():
  301. display("Removed " + str(t))
  302. self._clean_targets(remove=False)
  303. def remove(self):
  304. for t in self._get_files_to_clean():
  305. try:
  306. removed = t.remove()
  307. except OSError as e:
  308. # An OSError may indicate something like a permissions
  309. # issue, an IOError would indicate something like
  310. # the file not existing. In either case, print a
  311. # message and keep going to try to remove as many
  312. # targets as possible.
  313. print("scons: Could not remove '{0}'".format(str(t)), e.strerror)
  314. else:
  315. if removed:
  316. display("Removed " + str(t))
  317. self._clean_targets(remove=True)
  318. execute = remove
  319. # We want the Taskmaster to update the Node states (and therefore
  320. # handle reference counts, etc.), but we don't want to call
  321. # back to the Node's post-build methods, which would do things
  322. # we don't want, like store .sconsign information.
  323. executed = SCons.Taskmaster.Task.executed_without_callbacks
  324. # Have the Taskmaster arrange to "execute" all of the targets, because
  325. # we'll figure out ourselves (in remove() or show() above) whether
  326. # anything really needs to be done.
  327. make_ready = SCons.Taskmaster.Task.make_ready_all
  328. def prepare(self):
  329. pass
  330. class QuestionTask(SCons.Taskmaster.AlwaysTask):
  331. """An SCons task for the -q (question) option."""
  332. def prepare(self):
  333. pass
  334. def execute(self):
  335. if self.targets[0].get_state() != SCons.Node.up_to_date or \
  336. (self.top and not self.targets[0].exists()):
  337. global exit_status
  338. global this_build_status
  339. exit_status = 1
  340. this_build_status = 1
  341. self.tm.stop()
  342. def executed(self):
  343. pass
  344. class TreePrinter(object):
  345. def __init__(self, derived=False, prune=False, status=False):
  346. self.derived = derived
  347. self.prune = prune
  348. self.status = status
  349. def get_all_children(self, node):
  350. return node.all_children()
  351. def get_derived_children(self, node):
  352. children = node.all_children(None)
  353. return [x for x in children if x.has_builder()]
  354. def display(self, t):
  355. if self.derived:
  356. func = self.get_derived_children
  357. else:
  358. func = self.get_all_children
  359. s = self.status and 2 or 0
  360. SCons.Util.print_tree(t, func, prune=self.prune, showtags=s)
  361. def python_version_string():
  362. return sys.version.split()[0]
  363. def python_version_unsupported(version=sys.version_info):
  364. return version < unsupported_python_version
  365. def python_version_deprecated(version=sys.version_info):
  366. return version < deprecated_python_version
  367. # Global variables
  368. print_objects = 0
  369. print_memoizer = 0
  370. print_stacktrace = 0
  371. print_time = 0
  372. sconscript_time = 0
  373. cumulative_command_time = 0
  374. exit_status = 0 # final exit status, assume success by default
  375. this_build_status = 0 # "exit status" of an individual build
  376. num_jobs = None
  377. delayed_warnings = []
  378. class FakeOptionParser(object):
  379. """
  380. A do-nothing option parser, used for the initial OptionsParser variable.
  381. During normal SCons operation, the OptionsParser is created right
  382. away by the main() function. Certain tests scripts however, can
  383. introspect on different Tool modules, the initialization of which
  384. can try to add a new, local option to an otherwise uninitialized
  385. OptionsParser object. This allows that introspection to happen
  386. without blowing up.
  387. """
  388. class FakeOptionValues(object):
  389. def __getattr__(self, attr):
  390. return None
  391. values = FakeOptionValues()
  392. def add_local_option(self, *args, **kw):
  393. pass
  394. OptionsParser = FakeOptionParser()
  395. def AddOption(*args, **kw):
  396. if 'default' not in kw:
  397. kw['default'] = None
  398. result = OptionsParser.add_local_option(*args, **kw)
  399. return result
  400. def GetOption(name):
  401. return getattr(OptionsParser.values, name)
  402. def SetOption(name, value):
  403. return OptionsParser.values.set_option(name, value)
  404. def PrintHelp(file=None):
  405. OptionsParser.print_help(file=file)
  406. class Stats(object):
  407. def __init__(self):
  408. self.stats = []
  409. self.labels = []
  410. self.append = self.do_nothing
  411. self.print_stats = self.do_nothing
  412. def enable(self, outfp):
  413. self.outfp = outfp
  414. self.append = self.do_append
  415. self.print_stats = self.do_print
  416. def do_nothing(self, *args, **kw):
  417. pass
  418. class CountStats(Stats):
  419. def do_append(self, label):
  420. self.labels.append(label)
  421. self.stats.append(SCons.Debug.fetchLoggedInstances())
  422. def do_print(self):
  423. stats_table = {}
  424. for s in self.stats:
  425. for n in [t[0] for t in s]:
  426. stats_table[n] = [0, 0, 0, 0]
  427. i = 0
  428. for s in self.stats:
  429. for n, c in s:
  430. stats_table[n][i] = c
  431. i = i + 1
  432. self.outfp.write("Object counts:\n")
  433. pre = [" "]
  434. post = [" %s\n"]
  435. l = len(self.stats)
  436. fmt1 = ''.join(pre + [' %7s']*l + post)
  437. fmt2 = ''.join(pre + [' %7d']*l + post)
  438. labels = self.labels[:l]
  439. labels.append(("", "Class"))
  440. self.outfp.write(fmt1 % tuple([x[0] for x in labels]))
  441. self.outfp.write(fmt1 % tuple([x[1] for x in labels]))
  442. for k in sorted(stats_table.keys()):
  443. r = stats_table[k][:l] + [k]
  444. self.outfp.write(fmt2 % tuple(r))
  445. count_stats = CountStats()
  446. class MemStats(Stats):
  447. def do_append(self, label):
  448. self.labels.append(label)
  449. self.stats.append(SCons.Debug.memory())
  450. def do_print(self):
  451. fmt = 'Memory %-32s %12d\n'
  452. for label, stats in zip(self.labels, self.stats):
  453. self.outfp.write(fmt % (label, stats))
  454. memory_stats = MemStats()
  455. # utility functions
  456. def _scons_syntax_error(e):
  457. """Handle syntax errors. Print out a message and show where the error
  458. occurred.
  459. """
  460. etype, value, tb = sys.exc_info()
  461. lines = traceback.format_exception_only(etype, value)
  462. for line in lines:
  463. sys.stderr.write(line+'\n')
  464. sys.exit(2)
  465. def find_deepest_user_frame(tb):
  466. """
  467. Find the deepest stack frame that is not part of SCons.
  468. Input is a "pre-processed" stack trace in the form
  469. returned by traceback.extract_tb() or traceback.extract_stack()
  470. """
  471. tb.reverse()
  472. # find the deepest traceback frame that is not part
  473. # of SCons:
  474. for frame in tb:
  475. filename = frame[0]
  476. if filename.find(os.sep+'SCons'+os.sep) == -1:
  477. return frame
  478. return tb[0]
  479. def _scons_user_error(e):
  480. """Handle user errors. Print out a message and a description of the
  481. error, along with the line number and routine where it occured.
  482. The file and line number will be the deepest stack frame that is
  483. not part of SCons itself.
  484. """
  485. global print_stacktrace
  486. etype, value, tb = sys.exc_info()
  487. if print_stacktrace:
  488. traceback.print_exception(etype, value, tb)
  489. filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
  490. sys.stderr.write("\nscons: *** %s\n" % value)
  491. sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
  492. sys.exit(2)
  493. def _scons_user_warning(e):
  494. """Handle user warnings. Print out a message and a description of
  495. the warning, along with the line number and routine where it occured.
  496. The file and line number will be the deepest stack frame that is
  497. not part of SCons itself.
  498. """
  499. etype, value, tb = sys.exc_info()
  500. filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_tb(tb))
  501. sys.stderr.write("\nscons: warning: %s\n" % e)
  502. sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
  503. def _scons_internal_warning(e):
  504. """Slightly different from _scons_user_warning in that we use the
  505. *current call stack* rather than sys.exc_info() to get our stack trace.
  506. This is used by the warnings framework to print warnings."""
  507. filename, lineno, routine, dummy = find_deepest_user_frame(traceback.extract_stack())
  508. sys.stderr.write("\nscons: warning: %s\n" % e.args[0])
  509. sys.stderr.write('File "%s", line %d, in %s\n' % (filename, lineno, routine))
  510. def _scons_internal_error():
  511. """Handle all errors but user errors. Print out a message telling
  512. the user what to do in this case and print a normal trace.
  513. """
  514. print('internal error')
  515. traceback.print_exc()
  516. sys.exit(2)
  517. def _SConstruct_exists(dirname='', repositories=[], filelist=None):
  518. """This function checks that an SConstruct file exists in a directory.
  519. If so, it returns the path of the file. By default, it checks the
  520. current directory.
  521. """
  522. if not filelist:
  523. filelist = ['SConstruct', 'Sconstruct', 'sconstruct']
  524. for file in filelist:
  525. sfile = os.path.join(dirname, file)
  526. if os.path.isfile(sfile):
  527. return sfile
  528. if not os.path.isabs(sfile):
  529. for rep in repositories:
  530. if os.path.isfile(os.path.join(rep, sfile)):
  531. return sfile
  532. return None
  533. def _set_debug_values(options):
  534. global print_memoizer, print_objects, print_stacktrace, print_time
  535. debug_values = options.debug
  536. if "count" in debug_values:
  537. # All of the object counts are within "if track_instances:" blocks,
  538. # which get stripped when running optimized (with python -O or
  539. # from compiled *.pyo files). Provide a warning if __debug__ is
  540. # stripped, so it doesn't just look like --debug=count is broken.
  541. enable_count = False
  542. if __debug__: enable_count = True
  543. if enable_count:
  544. count_stats.enable(sys.stdout)
  545. SCons.Debug.track_instances = True
  546. else:
  547. msg = "--debug=count is not supported when running SCons\n" + \
  548. "\twith the python -O option or optimized (.pyo) modules."
  549. SCons.Warnings.warn(SCons.Warnings.NoObjectCountWarning, msg)
  550. if "dtree" in debug_values:
  551. options.tree_printers.append(TreePrinter(derived=True))
  552. options.debug_explain = ("explain" in debug_values)
  553. if "findlibs" in debug_values:
  554. SCons.Scanner.Prog.print_find_libs = "findlibs"
  555. options.debug_includes = ("includes" in debug_values)
  556. print_memoizer = ("memoizer" in debug_values)
  557. if "memory" in debug_values:
  558. memory_stats.enable(sys.stdout)
  559. print_objects = ("objects" in debug_values)
  560. if print_objects:
  561. SCons.Debug.track_instances = True
  562. if "presub" in debug_values:
  563. SCons.Action.print_actions_presub = 1
  564. if "stacktrace" in debug_values:
  565. print_stacktrace = 1
  566. if "stree" in debug_values:
  567. options.tree_printers.append(TreePrinter(status=True))
  568. if "time" in debug_values:
  569. print_time = 1
  570. if "tree" in debug_values:
  571. options.tree_printers.append(TreePrinter())
  572. if "prepare" in debug_values:
  573. SCons.Taskmaster.print_prepare = 1
  574. if "duplicate" in debug_values:
  575. SCons.Node.print_duplicate = 1
  576. def _create_path(plist):
  577. path = '.'
  578. for d in plist:
  579. if os.path.isabs(d):
  580. path = d
  581. else:
  582. path = path + '/' + d
  583. return path
  584. def _load_site_scons_dir(topdir, site_dir_name=None):
  585. """Load the site_scons dir under topdir.
  586. Prepends site_scons to sys.path, imports site_scons/site_init.py,
  587. and prepends site_scons/site_tools to default toolpath."""
  588. if site_dir_name:
  589. err_if_not_found = True # user specified: err if missing
  590. else:
  591. site_dir_name = "site_scons"
  592. err_if_not_found = False
  593. site_dir = os.path.join(topdir, site_dir_name)
  594. if not os.path.exists(site_dir):
  595. if err_if_not_found:
  596. raise SCons.Errors.UserError("site dir %s not found."%site_dir)
  597. return
  598. site_init_filename = "site_init.py"
  599. site_init_modname = "site_init"
  600. site_tools_dirname = "site_tools"
  601. # prepend to sys.path
  602. sys.path = [os.path.abspath(site_dir)] + sys.path
  603. site_init_file = os.path.join(site_dir, site_init_filename)
  604. site_tools_dir = os.path.join(site_dir, site_tools_dirname)
  605. if os.path.exists(site_init_file):
  606. import imp, re
  607. try:
  608. try:
  609. fp, pathname, description = imp.find_module(site_init_modname,
  610. [site_dir])
  611. # Load the file into SCons.Script namespace. This is
  612. # opaque and clever; m is the module object for the
  613. # SCons.Script module, and the exec ... in call executes a
  614. # file (or string containing code) in the context of the
  615. # module's dictionary, so anything that code defines ends
  616. # up adding to that module. This is really short, but all
  617. # the error checking makes it longer.
  618. try:
  619. m = sys.modules['SCons.Script']
  620. except Exception as e:
  621. fmt = 'cannot import site_init.py: missing SCons.Script module %s'
  622. raise SCons.Errors.InternalError(fmt % repr(e))
  623. try:
  624. sfx = description[0]
  625. modname = os.path.basename(pathname)[:-len(sfx)]
  626. site_m = {"__file__": pathname, "__name__": modname, "__doc__": None}
  627. re_special = re.compile("__[^_]+__")
  628. for k in list(m.__dict__.keys()):
  629. if not re_special.match(k):
  630. site_m[k] = m.__dict__[k]
  631. # This is the magic.
  632. exec(compile(fp.read(), fp.name, 'exec'), site_m)
  633. except KeyboardInterrupt:
  634. raise
  635. except Exception as e:
  636. fmt = '*** Error loading site_init file %s:\n'
  637. sys.stderr.write(fmt % repr(site_init_file))
  638. raise
  639. else:
  640. for k in site_m:
  641. if not re_special.match(k):
  642. m.__dict__[k] = site_m[k]
  643. except KeyboardInterrupt:
  644. raise
  645. except ImportError as e:
  646. fmt = '*** cannot import site init file %s:\n'
  647. sys.stderr.write(fmt % repr(site_init_file))
  648. raise
  649. finally:
  650. if fp:
  651. fp.close()
  652. if os.path.exists(site_tools_dir):
  653. # prepend to DefaultToolpath
  654. SCons.Tool.DefaultToolpath.insert(0, os.path.abspath(site_tools_dir))
  655. def _load_all_site_scons_dirs(topdir, verbose=None):
  656. """Load all of the predefined site_scons dir.
  657. Order is significant; we load them in order from most generic
  658. (machine-wide) to most specific (topdir).
  659. The verbose argument is only for testing.
  660. """
  661. platform = SCons.Platform.platform_default()
  662. def homedir(d):
  663. return os.path.expanduser('~/'+d)
  664. if platform == 'win32' or platform == 'cygwin':
  665. # Note we use $ here instead of %...% because older
  666. # pythons (prior to 2.6?) didn't expand %...% on Windows.
  667. # This set of dirs should work on XP, Vista, 7 and later.
  668. sysdirs=[
  669. os.path.expandvars('$ALLUSERSPROFILE\\Application Data\\scons'),
  670. os.path.expandvars('$USERPROFILE\\Local Settings\\Application Data\\scons')]
  671. appdatadir = os.path.expandvars('$APPDATA\\scons')
  672. if appdatadir not in sysdirs:
  673. sysdirs.append(appdatadir)
  674. sysdirs.append(homedir('.scons'))
  675. elif platform == 'darwin': # MacOS X
  676. sysdirs=['/Library/Application Support/SCons',
  677. '/opt/local/share/scons', # (for MacPorts)
  678. '/sw/share/scons', # (for Fink)
  679. homedir('Library/Application Support/SCons'),
  680. homedir('.scons')]
  681. elif platform == 'sunos': # Solaris
  682. sysdirs=['/opt/sfw/scons',
  683. '/usr/share/scons',
  684. homedir('.scons')]
  685. else: # Linux, HPUX, etc.
  686. # assume posix-like, i.e. platform == 'posix'
  687. sysdirs=['/usr/share/scons',
  688. homedir('.scons')]
  689. dirs=sysdirs + [topdir]
  690. for d in dirs:
  691. if verbose: # this is used by unit tests.
  692. print("Loading site dir ", d)
  693. _load_site_scons_dir(d)
  694. def test_load_all_site_scons_dirs(d):
  695. _load_all_site_scons_dirs(d, True)
  696. def version_string(label, module):
  697. version = module.__version__
  698. build = module.__build__
  699. if build:
  700. if build[0] != '.':
  701. build = '.' + build
  702. version = version + build
  703. fmt = "\t%s: v%s, %s, by %s on %s\n"
  704. return fmt % (label,
  705. version,
  706. module.__date__,
  707. module.__developer__,
  708. module.__buildsys__)
  709. def path_string(label, module):
  710. path = module.__path__
  711. return "\t%s path: %s\n"%(label,path)
  712. def _main(parser):
  713. global exit_status
  714. global this_build_status
  715. options = parser.values
  716. # Here's where everything really happens.
  717. # First order of business: set up default warnings and then
  718. # handle the user's warning options, so that we can issue (or
  719. # suppress) appropriate warnings about anything that might happen,
  720. # as configured by the user.
  721. default_warnings = [ SCons.Warnings.WarningOnByDefault,
  722. SCons.Warnings.DeprecatedWarning,
  723. ]
  724. for warning in default_warnings:
  725. SCons.Warnings.enableWarningClass(warning)
  726. SCons.Warnings._warningOut = _scons_internal_warning
  727. SCons.Warnings.process_warn_strings(options.warn)
  728. # Now that we have the warnings configuration set up, we can actually
  729. # issue (or suppress) any warnings about warning-worthy things that
  730. # occurred while the command-line options were getting parsed.
  731. try:
  732. dw = options.delayed_warnings
  733. except AttributeError:
  734. pass
  735. else:
  736. delayed_warnings.extend(dw)
  737. for warning_type, message in delayed_warnings:
  738. SCons.Warnings.warn(warning_type, message)
  739. if options.diskcheck:
  740. SCons.Node.FS.set_diskcheck(options.diskcheck)
  741. # Next, we want to create the FS object that represents the outside
  742. # world's file system, as that's central to a lot of initialization.
  743. # To do this, however, we need to be in the directory from which we
  744. # want to start everything, which means first handling any relevant
  745. # options that might cause us to chdir somewhere (-C, -D, -U, -u).
  746. if options.directory:
  747. script_dir = os.path.abspath(_create_path(options.directory))
  748. else:
  749. script_dir = os.getcwd()
  750. target_top = None
  751. if options.climb_up:
  752. target_top = '.' # directory to prepend to targets
  753. while script_dir and not _SConstruct_exists(script_dir,
  754. options.repository,
  755. options.file):
  756. script_dir, last_part = os.path.split(script_dir)
  757. if last_part:
  758. target_top = os.path.join(last_part, target_top)
  759. else:
  760. script_dir = ''
  761. if script_dir and script_dir != os.getcwd():
  762. if not options.silent:
  763. display("scons: Entering directory `%s'" % script_dir)
  764. try:
  765. os.chdir(script_dir)
  766. except OSError:
  767. sys.stderr.write("Could not change directory to %s\n" % script_dir)
  768. # Now that we're in the top-level SConstruct directory, go ahead
  769. # and initialize the FS object that represents the file system,
  770. # and make it the build engine default.
  771. fs = SCons.Node.FS.get_default_fs()
  772. for rep in options.repository:
  773. fs.Repository(rep)
  774. # Now that we have the FS object, the next order of business is to
  775. # check for an SConstruct file (or other specified config file).
  776. # If there isn't one, we can bail before doing any more work.
  777. scripts = []
  778. if options.file:
  779. scripts.extend(options.file)
  780. if not scripts:
  781. sfile = _SConstruct_exists(repositories=options.repository,
  782. filelist=options.file)
  783. if sfile:
  784. scripts.append(sfile)
  785. if not scripts:
  786. if options.help:
  787. # There's no SConstruct, but they specified -h.
  788. # Give them the options usage now, before we fail
  789. # trying to read a non-existent SConstruct file.
  790. raise SConsPrintHelpException
  791. raise SCons.Errors.UserError("No SConstruct file found.")
  792. if scripts[0] == "-":
  793. d = fs.getcwd()
  794. else:
  795. d = fs.File(scripts[0]).dir
  796. fs.set_SConstruct_dir(d)
  797. _set_debug_values(options)
  798. SCons.Node.implicit_cache = options.implicit_cache
  799. SCons.Node.implicit_deps_changed = options.implicit_deps_changed
  800. SCons.Node.implicit_deps_unchanged = options.implicit_deps_unchanged
  801. if options.no_exec:
  802. SCons.SConf.dryrun = 1
  803. SCons.Action.execute_actions = None
  804. if options.question:
  805. SCons.SConf.dryrun = 1
  806. if options.clean:
  807. SCons.SConf.SetBuildType('clean')
  808. if options.help:
  809. SCons.SConf.SetBuildType('help')
  810. SCons.SConf.SetCacheMode(options.config)
  811. SCons.SConf.SetProgressDisplay(progress_display)
  812. if options.no_progress or options.silent:
  813. progress_display.set_mode(0)
  814. if options.site_dir:
  815. _load_site_scons_dir(d.get_internal_path(), options.site_dir)
  816. elif not options.no_site_dir:
  817. _load_all_site_scons_dirs(d.get_internal_path())
  818. if options.include_dir:
  819. sys.path = options.include_dir + sys.path
  820. # If we're about to start SCons in the interactive mode,
  821. # inform the FS about this right here. Else, the release_target_info
  822. # method could get called on some nodes, like the used "gcc" compiler,
  823. # when using the Configure methods within the SConscripts.
  824. # This would then cause subtle bugs, as already happened in #2971.
  825. if options.interactive:
  826. SCons.Node.interactive = True
  827. # That should cover (most of) the options. Next, set up the variables
  828. # that hold command-line arguments, so the SConscript files that we
  829. # read and execute have access to them.
  830. targets = []
  831. xmit_args = []
  832. for a in parser.largs:
  833. if a[:1] == '-':
  834. continue
  835. if '=' in a:
  836. xmit_args.append(a)
  837. else:
  838. targets.append(a)
  839. SCons.Script._Add_Targets(targets + parser.rargs)
  840. SCons.Script._Add_Arguments(xmit_args)
  841. # If stdout is not a tty, replace it with a wrapper object to call flush
  842. # after every write.
  843. #
  844. # Tty devices automatically flush after every newline, so the replacement
  845. # isn't necessary. Furthermore, if we replace sys.stdout, the readline
  846. # module will no longer work. This affects the behavior during
  847. # --interactive mode. --interactive should only be used when stdin and
  848. # stdout refer to a tty.
  849. if not hasattr(sys.stdout, 'isatty') or not sys.stdout.isatty():
  850. sys.stdout = SCons.Util.Unbuffered(sys.stdout)
  851. if not hasattr(sys.stderr, 'isatty') or not sys.stderr.isatty():
  852. sys.stderr = SCons.Util.Unbuffered(sys.stderr)
  853. memory_stats.append('before reading SConscript files:')
  854. count_stats.append(('pre-', 'read'))
  855. # And here's where we (finally) read the SConscript files.
  856. progress_display("scons: Reading SConscript files ...")
  857. start_time = time.time()
  858. try:
  859. for script in scripts:
  860. SCons.Script._SConscript._SConscript(fs, script)
  861. except SCons.Errors.StopError as e:
  862. # We had problems reading an SConscript file, such as it
  863. # couldn't be copied in to the VariantDir. Since we're just
  864. # reading SConscript files and haven't started building
  865. # things yet, stop regardless of whether they used -i or -k
  866. # or anything else.
  867. revert_io()
  868. sys.stderr.write("scons: *** %s Stop.\n" % e)
  869. sys.exit(2)
  870. global sconscript_time
  871. sconscript_time = time.time() - start_time
  872. progress_display("scons: done reading SConscript files.")
  873. memory_stats.append('after reading SConscript files:')
  874. count_stats.append(('post-', 'read'))
  875. # Re-{enable,disable} warnings in case they disabled some in
  876. # the SConscript file.
  877. #
  878. # We delay enabling the PythonVersionWarning class until here so that,
  879. # if they explicitly disabled it in either in the command line or in
  880. # $SCONSFLAGS, or in the SConscript file, then the search through
  881. # the list of deprecated warning classes will find that disabling
  882. # first and not issue the warning.
  883. #SCons.Warnings.enableWarningClass(SCons.Warnings.PythonVersionWarning)
  884. SCons.Warnings.process_warn_strings(options.warn)
  885. # Now that we've read the SConscript files, we can check for the
  886. # warning about deprecated Python versions--delayed until here
  887. # in case they disabled the warning in the SConscript files.
  888. if python_version_deprecated():
  889. msg = "Support for pre-%s Python version (%s) is deprecated.\n" + \
  890. " If this will cause hardship, contact scons-dev@scons.org"
  891. deprecated_version_string = ".".join(map(str, deprecated_python_version))
  892. SCons.Warnings.warn(SCons.Warnings.PythonVersionWarning,
  893. msg % (deprecated_version_string, python_version_string()))
  894. if not options.help:
  895. # [ ] Clarify why we need to create Builder here at all, and
  896. # why it is created in DefaultEnvironment
  897. # https://bitbucket.org/scons/scons/commits/d27a548aeee8ad5e67ea75c2d19a7d305f784e30
  898. if SCons.SConf.NeedConfigHBuilder():
  899. SCons.SConf.CreateConfigHBuilder(SCons.Defaults.DefaultEnvironment())
  900. # Now re-parse the command-line options (any to the left of a '--'
  901. # argument, that is) with any user-defined command-line options that
  902. # the SConscript files may have added to the parser object. This will
  903. # emit the appropriate error message and exit if any unknown option
  904. # was specified on the command line.
  905. parser.preserve_unknown_options = False
  906. parser.parse_args(parser.largs, options)
  907. if options.help:
  908. help_text = SCons.Script.help_text
  909. if help_text is None:
  910. # They specified -h, but there was no Help() inside the
  911. # SConscript files. Give them the options usage.
  912. raise SConsPrintHelpException
  913. else:
  914. print(help_text)
  915. print("Use scons -H for help about command-line options.")
  916. exit_status = 0
  917. return
  918. # Change directory to the top-level SConstruct directory, then tell
  919. # the Node.FS subsystem that we're all done reading the SConscript
  920. # files and calling Repository() and VariantDir() and changing
  921. # directories and the like, so it can go ahead and start memoizing
  922. # the string values of file system nodes.
  923. fs.chdir(fs.Top)
  924. SCons.Node.FS.save_strings(1)
  925. # Now that we've read the SConscripts we can set the options
  926. # that are SConscript settable:
  927. SCons.Node.implicit_cache = options.implicit_cache
  928. SCons.Node.FS.set_duplicate(options.duplicate)
  929. fs.set_max_drift(options.max_drift)
  930. SCons.Job.explicit_stack_size = options.stack_size
  931. if options.md5_chunksize:
  932. SCons.Node.FS.File.md5_chunksize = options.md5_chunksize
  933. platform = SCons.Platform.platform_module()
  934. if options.interactive:
  935. SCons.Script.Interactive.interact(fs, OptionsParser, options,
  936. targets, target_top)
  937. else:
  938. # Build the targets
  939. nodes = _build_targets(fs, options, targets, target_top)
  940. if not nodes:
  941. revert_io()
  942. print('Found nothing to build')
  943. exit_status = 2
  944. def _build_targets(fs, options, targets, target_top):
  945. global this_build_status
  946. this_build_status = 0
  947. progress_display.set_mode(not (options.no_progress or options.silent))
  948. display.set_mode(not options.silent)
  949. SCons.Action.print_actions = not options.silent
  950. SCons.Action.execute_actions = not options.no_exec
  951. SCons.Node.do_store_info = not options.no_exec
  952. SCons.SConf.dryrun = options.no_exec
  953. if options.diskcheck:
  954. SCons.Node.FS.set_diskcheck(options.diskcheck)
  955. SCons.CacheDir.cache_enabled = not options.cache_disable
  956. SCons.CacheDir.cache_readonly = options.cache_readonly
  957. SCons.CacheDir.cache_debug = options.cache_debug
  958. SCons.CacheDir.cache_force = options.cache_force
  959. SCons.CacheDir.cache_show = options.cache_show
  960. if options.no_exec:
  961. CleanTask.execute = CleanTask.show
  962. else:
  963. CleanTask.execute = CleanTask.remove
  964. lookup_top = None
  965. if targets or SCons.Script.BUILD_TARGETS != SCons.Script._build_plus_default:
  966. # They specified targets on the command line or modified
  967. # BUILD_TARGETS in the SConscript file(s), so if they used -u,
  968. # -U or -D, we have to look up targets relative to the top,
  969. # but we build whatever they specified.
  970. if target_top:
  971. lookup_top = fs.Dir(target_top)
  972. target_top = None
  973. targets = SCons.Script.BUILD_TARGETS
  974. else:
  975. # There are no targets specified on the command line,
  976. # so if they used -u, -U or -D, we may have to restrict
  977. # what actually gets built.
  978. d = None
  979. if target_top:
  980. if options.climb_up == 1:
  981. # -u, local directory and below
  982. target_top = fs.Dir(target_top)
  983. lookup_top = target_top
  984. elif options.climb_up == 2:
  985. # -D, all Default() targets
  986. target_top = None
  987. lookup_top = None
  988. elif options.climb_up == 3:
  989. # -U, local SConscript Default() targets
  990. target_top = fs.Dir(target_top)
  991. def check_dir(x, target_top=target_top):
  992. if hasattr(x, 'cwd') and not x.cwd is None:
  993. cwd = x.cwd.srcnode()
  994. return cwd == target_top
  995. else:
  996. # x doesn't have a cwd, so it's either not a target,
  997. # or not a file, so go ahead and keep it as a default
  998. # target and let the engine sort it out:
  999. return 1
  1000. d = [tgt for tgt in SCons.Script.DEFAULT_TARGETS if check_dir(tgt)]
  1001. SCons.Script.DEFAULT_TARGETS[:] = d
  1002. target_top = None
  1003. lookup_top = None
  1004. targets = SCons.Script._Get_Default_Targets(d, fs)
  1005. if not targets:
  1006. sys.stderr.write("scons: *** No targets specified and no Default() targets found. Stop.\n")
  1007. return None
  1008. def Entry(x, ltop=lookup_top, ttop=target_top, fs=fs):
  1009. if isinstance(x, SCons.Node.Node):
  1010. node = x
  1011. else:
  1012. node = None
  1013. # Why would ltop be None? Unfortunately this happens.
  1014. if ltop is None: ltop = ''
  1015. # Curdir becomes important when SCons is called with -u, -C,
  1016. # or similar option that changes directory, and so the paths
  1017. # of targets given on the command line need to be adjusted.
  1018. curdir = os.path.join(os.getcwd(), str(ltop))
  1019. for lookup in SCons.Node.arg2nodes_lookups:
  1020. node = lookup(x, curdir=curdir)
  1021. if node is not None:
  1022. break
  1023. if node is None:
  1024. node = fs.Entry(x, directory=ltop, create=1)
  1025. if ttop and not node.is_under(ttop):
  1026. if isinstance(node, SCons.Node.FS.Dir) and ttop.is_under(node):
  1027. node = ttop
  1028. else:
  1029. node = None
  1030. return node
  1031. nodes = [_f for _f in map(Entry, targets) if _f]
  1032. task_class = BuildTask # default action is to build targets
  1033. opening_message = "Building targets ..."
  1034. closing_message = "done building targets."
  1035. if options.keep_going:
  1036. failure_message = "done building targets (errors occurred during build)."
  1037. else:
  1038. failure_message = "building terminated because of errors."
  1039. if options.question:
  1040. task_class = QuestionTask
  1041. try:
  1042. if options.clean:
  1043. task_class = CleanTask
  1044. opening_message = "Cleaning targets ..."
  1045. closing_message = "done cleaning targets."
  1046. if options.keep_going:
  1047. failure_message = "done cleaning targets (errors occurred during clean)."
  1048. else:
  1049. failure_message = "cleaning terminated because of errors."
  1050. except AttributeError:
  1051. pass
  1052. task_class.progress = ProgressObject
  1053. if options.random:
  1054. def order(dependencies):
  1055. """Randomize the dependencies."""
  1056. import random
  1057. random.shuffle(dependencies)
  1058. return dependencies
  1059. else:
  1060. def order(dependencies):
  1061. """Leave the order of dependencies alone."""
  1062. return dependencies
  1063. if options.taskmastertrace_file == '-':
  1064. tmtrace = sys.stdout
  1065. elif options.taskmastertrace_file:
  1066. tmtrace = open(options.taskmastertrace_file, 'w')
  1067. else:
  1068. tmtrace = None
  1069. taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace)
  1070. # Let the BuildTask objects get at the options to respond to the
  1071. # various print_* settings, tree_printer list, etc.
  1072. BuildTask.options = options
  1073. python_has_threads = sysconfig.get_config_var('WITH_THREAD')
  1074. # to check if python configured with threads.
  1075. global num_jobs
  1076. num_jobs = options.num_jobs
  1077. jobs = SCons.Job.Jobs(num_jobs, taskmaster)
  1078. if num_jobs > 1:
  1079. msg = None
  1080. if sys.platform == 'win32':
  1081. msg = fetch_win32_parallel_msg()
  1082. elif jobs.num_jobs == 1 or not python_has_threads:
  1083. msg = "parallel builds are unsupported by this version of Python;\n" + \
  1084. "\tignoring -j or num_jobs option.\n"
  1085. if msg:
  1086. SCons.Warnings.warn(SCons.Warnings.NoParallelSupportWarning, msg)
  1087. memory_stats.append('before building targets:')
  1088. count_stats.append(('pre-', 'build'))
  1089. def jobs_postfunc(
  1090. jobs=jobs,
  1091. options=options,
  1092. closing_message=closing_message,
  1093. failure_message=failure_message
  1094. ):
  1095. if jobs.were_interrupted():
  1096. if not options.no_progress and not options.silent:
  1097. sys.stderr.write("scons: Build interrupted.\n")
  1098. global exit_status
  1099. global this_build_status
  1100. exit_status = 2
  1101. this_build_status = 2
  1102. if this_build_status:
  1103. progress_display("scons: " + failure_message)
  1104. else:
  1105. progress_display("scons: " + closing_message)
  1106. if not options.no_exec:
  1107. if jobs.were_interrupted():
  1108. progress_display("scons: writing .sconsign file.")
  1109. SCons.SConsign.write()
  1110. progress_display("scons: " + opening_message)
  1111. jobs.run(postfunc = jobs_postfunc)
  1112. memory_stats.append('after building targets:')
  1113. count_stats.append(('post-', 'build'))
  1114. return nodes
  1115. def _exec_main(parser, values):
  1116. sconsflags = os.environ.get('SCONSFLAGS', '')
  1117. all_args = sconsflags.split() + sys.argv[1:]
  1118. options, args = parser.parse_args(all_args, values)
  1119. if isinstance(options.debug, list) and "pdb" in options.debug:
  1120. import pdb
  1121. pdb.Pdb().runcall(_main, parser)
  1122. elif options.profile_file:
  1123. # compat layer imports "cProfile" for us if it's available.
  1124. from profile import Profile
  1125. prof = Profile()
  1126. try:
  1127. prof.runcall(_main, parser)
  1128. finally:
  1129. prof.dump_stats(options.profile_file)
  1130. else:
  1131. _main(parser)
  1132. def main():
  1133. global OptionsParser
  1134. global exit_status
  1135. global first_command_start
  1136. # Check up front for a Python version we do not support. We
  1137. # delay the check for deprecated Python versions until later,
  1138. # after the SConscript files have been read, in case they
  1139. # disable that warning.
  1140. if python_version_unsupported():
  1141. msg = "scons: *** SCons version %s does not run under Python version %s.\n"
  1142. sys.stderr.write(msg % (SCons.__version__, python_version_string()))
  1143. sys.exit(1)
  1144. parts = ["SCons by Steven Knight et al.:\n"]
  1145. try:
  1146. import __main__
  1147. parts.append(version_string("script", __main__))
  1148. except (ImportError, AttributeError):
  1149. # On Windows there is no scons.py, so there is no
  1150. # __main__.__version__, hence there is no script version.
  1151. pass
  1152. parts.append(version_string("engine", SCons))
  1153. parts.append(path_string("engine", SCons))
  1154. parts.append("Copyright (c) 2001 - 2017 The SCons Foundation")
  1155. version = ''.join(parts)
  1156. from . import SConsOptions
  1157. parser = SConsOptions.Parser(version)
  1158. values = SConsOptions.SConsValues(parser.get_default_values())
  1159. OptionsParser = parser
  1160. try:
  1161. try:
  1162. _exec_main(parser, values)
  1163. finally:
  1164. revert_io()
  1165. except SystemExit as s:
  1166. if s:
  1167. exit_status = s
  1168. except KeyboardInterrupt:
  1169. print("scons: Build interrupted.")
  1170. sys.exit(2)
  1171. except SyntaxError as e:
  1172. _scons_syntax_error(e)
  1173. except SCons.Errors.InternalError:
  1174. _scons_internal_error()
  1175. except SCons.Errors.UserError as e:
  1176. _scons_user_error(e)
  1177. except SConsPrintHelpException:
  1178. parser.print_help()
  1179. exit_status = 0
  1180. except SCons.Errors.BuildError as e:
  1181. print(e)
  1182. exit_status = e.exitstatus
  1183. except:
  1184. # An exception here is likely a builtin Python exception Python
  1185. # code in an SConscript file. Show them precisely what the
  1186. # problem was and where it happened.
  1187. SCons.Script._SConscript.SConscript_exception()
  1188. sys.exit(2)
  1189. memory_stats.print_stats()
  1190. count_stats.print_stats()
  1191. if print_objects:
  1192. SCons.Debug.listLoggedInstances('*')
  1193. #SCons.Debug.dumpLoggedInstances('*')
  1194. if print_memoizer:
  1195. SCons.Memoize.Dump("Memoizer (memory cache) hits and misses:")
  1196. # Dump any development debug info that may have been enabled.
  1197. # These are purely for internal debugging during development, so
  1198. # there's no need to control them with --debug= options; they're
  1199. # controlled by changing the source code.
  1200. SCons.Debug.dump_caller_counts()
  1201. SCons.Taskmaster.dump_stats()
  1202. if print_time:
  1203. total_time = time.time() - SCons.Script.start_time
  1204. if num_jobs == 1:
  1205. ct = cumulative_command_time
  1206. else:
  1207. if last_command_end is None or first_command_start is None:
  1208. ct = 0.0
  1209. else:
  1210. ct = last_command_end - first_command_start
  1211. scons_time = total_time - sconscript_time - ct
  1212. print("Total build time: %f seconds"%total_time)
  1213. print("Total SConscript file execution time: %f seconds"%sconscript_time)
  1214. print("Total SCons execution time: %f seconds"%scons_time)
  1215. print("Total command execution time: %f seconds"%ct)
  1216. sys.exit(exit_status)
  1217. # Local Variables:
  1218. # tab-width:4
  1219. # indent-tabs-mode:nil
  1220. # End:
  1221. # vim: set expandtab tabstop=4 shiftwidth=4: