From 01f45ca84f2b1c9f2b8113e09a270503ab8581e0 Mon Sep 17 00:00:00 2001 From: Tom Tanner Date: Mon, 30 Sep 2013 15:18:50 +0100 Subject: Allow multiple options with --debug --- src/engine/SCons/Script/SConsOptions.py | 40 +++++++++++++++++---------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py index 645ab11..c1f389a 100644 --- a/src/engine/SCons/Script/SConsOptions.py +++ b/src/engine/SCons/Script/SConsOptions.py @@ -248,7 +248,7 @@ class SConsOption(optparse.Option): class SConsOptionGroup(optparse.OptionGroup): """ A subclass for SCons-specific option groups. - + The only difference between this and the base class is that we print the group's help text flush left, underneath their own title but lined up with the normal "SCons Options". @@ -340,7 +340,7 @@ class SConsOptionParser(optparse.OptionParser): def add_local_option(self, *args, **kw): """ Adds a local option to the parser. - + This is initiated by a SetOption() call to add a user-defined command-line option. We add the option to a separate option group for the local options, creating the group if necessary. @@ -394,11 +394,11 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): out liking: -- add our own regular expression that doesn't break on hyphens - (so things like --no-print-directory don't get broken); + (so things like --no-print-directory don't get broken); -- wrap the list of options themselves when it's too long (the wrapper.fill(opts) call below); - + -- set the subsequent_indent when wrapping the help_text. """ # The help for each option consists of two parts: @@ -606,23 +606,25 @@ def Parser(version): "pdb", "prepare", "presub", "stacktrace", "time"] - def opt_debug(option, opt, value, parser, + def opt_debug(option, opt, value__, parser, debug_options=debug_options, deprecated_debug_options=deprecated_debug_options): - if value in debug_options: - parser.values.debug.append(value) - elif value in deprecated_debug_options.keys(): - parser.values.debug.append(value) - try: - parser.values.delayed_warnings - except AttributeError: - parser.values.delayed_warnings = [] - msg = deprecated_debug_options[value] - w = "The --debug=%s option is deprecated%s." % (value, msg) - t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) - parser.values.delayed_warnings.append(t) - else: - raise OptionValueError(opt_invalid('debug', value, debug_options)) + for value in value__.split(','): + if value in debug_options: + parser.values.debug.append(value) + elif value in deprecated_debug_options.keys(): + parser.values.debug.append(value) + try: + parser.values.delayed_warnings + except AttributeError: + parser.values.delayed_warnings = [] + msg = deprecated_debug_options[value] + w = "The --debug=%s option is deprecated%s." % (value, msg) + t = (SCons.Warnings.DeprecatedDebugOptionsWarning, w) + parser.values.delayed_warnings.append(t) + else: + raise OptionValueError(opt_invalid('debug', value, debug_options)) + opt_debug_help = "Print various types of debugging information: %s." \ % ", ".join(debug_options) op.add_option('--debug', -- cgit v0.12 From 56875f9b7463ebb9cd1478187d9cd3ca25fd40d2 Mon Sep 17 00:00:00 2001 From: Tom Tanner Date: Tue, 1 Oct 2013 11:38:32 +0100 Subject: support for --cache-readonly Setting this will fetch data from the cache but won't update it. --- doc/man/scons.xml | 23 +++++++++++++++++------ src/engine/SCons/CacheDir.py | 8 +++++++- src/engine/SCons/Script/Main.py | 1 + src/engine/SCons/Script/SConsOptions.py | 7 +++++++ 4 files changed, 32 insertions(+), 7 deletions(-) diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 602bdbe..947d86d 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -61,12 +61,12 @@ version &buildversion; - + SCons &buildversion; MAN page - - - + + + SCONS 1 @@ -79,7 +79,7 @@ - scons + scons options name=val targets @@ -381,6 +381,7 @@ will be retrieved from the cache instead of being rebuilt locally. Caching behavior may be disabled and controlled in other ways by the , , +, and command-line options. The @@ -542,6 +543,15 @@ option. + + --cache-readonly + +Use the cache (if enabled) for reading, but do not not update the +cache with changed files. + + + + --cache-show @@ -1094,6 +1104,7 @@ command: --cache-debug=FILE --cache-disable, --no-cache --cache-force, --cache-populate +--cache-readonly --cache-show --debug=TYPE -i, --ignore-errors @@ -6775,7 +6786,7 @@ specified in the $MYPATH construction variable. It lets SCons detect the file incs/foo.inc -, even if +, even if foo.x contains the line include foo.inc diff --git a/src/engine/SCons/CacheDir.py b/src/engine/SCons/CacheDir.py index 3516018..9dd18e5 100644 --- a/src/engine/SCons/CacheDir.py +++ b/src/engine/SCons/CacheDir.py @@ -37,6 +37,7 @@ cache_enabled = True cache_debug = False cache_force = False cache_show = False +cache_readonly = False def CacheRetrieveFunc(target, source, env): t = target[0] @@ -70,6 +71,8 @@ CacheRetrieve = SCons.Action.Action(CacheRetrieveFunc, CacheRetrieveString) CacheRetrieveSilent = SCons.Action.Action(CacheRetrieveFunc, None) def CachePushFunc(target, source, env): + if cache_readonly: return + t = target[0] if t.nocache: return @@ -150,6 +153,9 @@ class CacheDir(object): def is_enabled(self): return (cache_enabled and not self.path is None) + def is_readonly(self): + return cache_readonly + def cachepath(self, node): """ """ @@ -201,7 +207,7 @@ class CacheDir(object): return False def push(self, node): - if not self.is_enabled(): + if self.is_readonly() or not self.is_enabled(): return return CachePush(node, [], node.get_build_env()) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 837c103..fea0916 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -1089,6 +1089,7 @@ def _build_targets(fs, options, targets, target_top): SCons.Node.FS.set_diskcheck(options.diskcheck) SCons.CacheDir.cache_enabled = not options.cache_disable + SCons.CacheDir.cache_readonly = options.cache_readonly SCons.CacheDir.cache_debug = options.cache_debug SCons.CacheDir.cache_force = options.cache_force SCons.CacheDir.cache_show = options.cache_show diff --git a/src/engine/SCons/Script/SConsOptions.py b/src/engine/SCons/Script/SConsOptions.py index c1f389a..62033ba 100644 --- a/src/engine/SCons/Script/SConsOptions.py +++ b/src/engine/SCons/Script/SConsOptions.py @@ -564,6 +564,11 @@ def Parser(version): action="store_true", help="Copy already-built targets into the CacheDir.") + op.add_option('--cache-readonly', + dest='cache_readonly', default=False, + action="store_true", + help="Do not update CacheDir with built targets.") + op.add_option('--cache-show', dest='cache_show', default=False, action="store_true", @@ -579,8 +584,10 @@ def Parser(version): if not value in c_options: raise OptionValueError(opt_invalid('config', value, c_options)) setattr(parser.values, option.dest, value) + opt_config_help = "Controls Configure subsystem: %s." \ % ", ".join(config_options) + op.add_option('--config', nargs=1, type="string", dest="config", default="auto", -- cgit v0.12 From c9e2d5a843115f68e37c3e0f00c2594f83150f3b Mon Sep 17 00:00:00 2001 From: Tom Tanner Date: Tue, 1 Oct 2013 12:18:49 +0100 Subject: Added stack dump on build error Added message if nothing found to build Always produce profile stats, no matter what sort of exit Print message if you drop out with a build error --- src/engine/SCons/Script/Main.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index fea0916..9c2e0b9 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -272,6 +272,9 @@ class BuildTask(SCons.Taskmaster.OutOfDateTask): (EnvironmentError, SCons.Errors.StopError, SCons.Errors.UserError))): type, value, trace = buildError.exc_info + if tb and print_stacktrace: + sys.stderr.write("scons: internal stack trace:\n") + traceback.print_tb(tb, file=sys.stderr) traceback.print_exception(type, value, trace) elif tb and print_stacktrace: sys.stderr.write("scons: internal stack trace:\n") @@ -1071,6 +1074,7 @@ def _main(parser): # Build the targets nodes = _build_targets(fs, options, targets, target_top) if not nodes: + print 'Found nothing to build' exit_status = 2 def _build_targets(fs, options, targets, target_top): @@ -1299,12 +1303,8 @@ def _exec_main(parser, values): prof = Profile() try: prof.runcall(_main, parser) - except SConsPrintHelpException, e: + finally: prof.dump_stats(options.profile_file) - raise e - except SystemExit: - pass - prof.dump_stats(options.profile_file) else: _main(parser) @@ -1359,6 +1359,7 @@ def main(): parser.print_help() exit_status = 0 except SCons.Errors.BuildError, e: + print e exit_status = e.exitstatus except: # An exception here is likely a builtin Python exception Python -- cgit v0.12 From 0359ca7ad334f499031aacb46c0e1a6b33789024 Mon Sep 17 00:00:00 2001 From: Tom Tanner Date: Tue, 1 Oct 2013 13:21:08 +0100 Subject: Revert stdout/stderr to the initial ones in a few places. There are some situations where scons error messages can just disappear, and sometimes it's because a build rule or an SConscript or something has temporarily redirected stdout/stderr. --- src/engine/SCons/Script/Main.py | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/engine/SCons/Script/Main.py b/src/engine/SCons/Script/Main.py index 9c2e0b9..1651d1e 100644 --- a/src/engine/SCons/Script/Main.py +++ b/src/engine/SCons/Script/Main.py @@ -79,7 +79,12 @@ def fetch_win32_parallel_msg(): import SCons.Platform.win32 return SCons.Platform.win32.parallel_msg -# +def revert_io(): + # This call is added to revert stderr and stdout to the original + # ones just in case some build rule or something else in the system + # has redirected them elsewhere. + sys.stderr = sys.__stderr__ + sys.stdout = sys.__stdout__ class SConsPrintHelpException(Exception): pass @@ -986,9 +991,9 @@ def _main(parser): # reading SConscript files and haven't started building # things yet, stop regardless of whether they used -i or -k # or anything else. + revert_io() sys.stderr.write("scons: *** %s Stop.\n" % e) - exit_status = 2 - sys.exit(exit_status) + sys.exit(2) global sconscript_time sconscript_time = time.time() - start_time @@ -1074,6 +1079,7 @@ def _main(parser): # Build the targets nodes = _build_targets(fs, options, targets, target_top) if not nodes: + revert_io() print 'Found nothing to build' exit_status = 2 @@ -1342,7 +1348,10 @@ def main(): OptionsParser = parser try: - _exec_main(parser, values) + try: + _exec_main(parser, values) + finally: + revert_io() except SystemExit, s: if s: exit_status = s -- cgit v0.12 From 91020237d64f53b2fa6e2b5bf155a1d8bfbdb547 Mon Sep 17 00:00:00 2001 From: Tom Tanner Date: Tue, 8 Oct 2013 12:54:25 +0100 Subject: Adding documentation and a couple of tests --- QMTest/TestSCons.py | 6 +- doc/man/scons.xml | 5 +- test/CacheDir/option--cr.py | 138 ++++++++++++++++++++++++++++++++++++++++++ test/option/debug-multiple.py | 86 ++++++++++++++++++++++++++ 4 files changed, 230 insertions(+), 5 deletions(-) create mode 100644 test/CacheDir/option--cr.py create mode 100644 test/option/debug-multiple.py diff --git a/QMTest/TestSCons.py b/QMTest/TestSCons.py index 84cc60f..c45ef5e 100644 --- a/QMTest/TestSCons.py +++ b/QMTest/TestSCons.py @@ -1131,9 +1131,9 @@ SConscript( sconscript ) self.run(program = python, stdin = """\ import os, sys try: - py_ver = 'python%d.%d' % sys.version_info[:2] + py_ver = 'python%d.%d' % sys.version_info[:2] except AttributeError: - py_ver = 'python' + sys.version[:3] + py_ver = 'python' + sys.version[:3] print os.path.join(sys.prefix, 'include', py_ver) print os.path.join(sys.prefix, 'lib', py_ver, 'config') print py_ver @@ -1357,7 +1357,7 @@ class TimeSCons(TestSCons): options = kw.get('options', '') if additional is not None: options += additional - kw['options'] = options + ' --debug=memory --debug=time' + kw['options'] = options + ' --debug=memory,time' def startup(self, *args, **kw): """ diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 947d86d..80bbd44 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -671,8 +671,9 @@ directory. --debug=type Debug the build process. -type -specifies what type of debugging: +type[,type...] +specifies what type of debugging. Multiple types may be specified, +separated by commas. The following types are valid: diff --git a/test/CacheDir/option--cr.py b/test/CacheDir/option--cr.py new file mode 100644 index 0000000..de6bbc8 --- /dev/null +++ b/test/CacheDir/option--cr.py @@ -0,0 +1,138 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test the --cache-readonly option when retrieving derived files from a +CacheDir. It should retrieve as normal but not update files. +""" + +import os.path +import shutil + +import TestSCons + +test = TestSCons.TestSCons() + +test.subdir('cache', 'src') + +test.write(['src', 'SConstruct'], """ +def cat(env, source, target): + target = str(target[0]) + open('cat.out', 'ab').write(target + "\\n") + f = open(target, "wb") + for src in source: + f.write(open(str(src), "rb").read()) + f.close() +env = Environment(BUILDERS={'Cat':Builder(action=cat)}) +env.Cat('aaa.out', 'aaa.in') +env.Cat('bbb.out', 'bbb.in') +env.Cat('ccc.out', 'ccc.in') +env.Cat('all', ['aaa.out', 'bbb.out', 'ccc.out']) +CacheDir(r'%s') +""" % test.workpath('cache')) + +test.write(['src', 'aaa.in'], "aaa.in\n") +test.write(['src', 'bbb.in'], "bbb.in\n") +test.write(['src', 'ccc.in'], "ccc.in\n") + +# Verify that a normal build works correctly, and clean up. +# This should populate the cache with our derived files. +test.run(chdir = 'src', arguments = '.') + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_match(['src', 'cat.out'], "aaa.out\nbbb.out\nccc.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that we now retrieve the derived files from cache, +# not rebuild them. Then clean up. +test.run(chdir = 'src', arguments = '--cache-readonly .', + stdout = test.wrap_stdout("""\ +Retrieved `aaa.out' from cache +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +Retrieved `all' from cache +""")) + +test.must_match(['src', 'all'], "aaa.in\nbbb.in\nccc.in\n") +test.must_not_exist(test.workpath('src', 'cat.out')) + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') + +# What we do now is to change one of the files and rebuild +test.write(['src', 'aaa.in'], "aaa.rebuild\n") + +# This should just rebuild aaa.out (and all) +test.run(chdir = 'src', + arguments = '--cache-readonly .', + stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.rebuild\nbbb.in\nccc.in\n") +# cat.out contains only the things we built (not got from cache) +test.must_match(['src', 'cat.out'], "aaa.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# Verify that aaa.out contents weren't updated with the last build +# Then clean up. +test.run(chdir = 'src', + arguments = '--cache-readonly .', + stdout = test.wrap_stdout("""\ +cat(["aaa.out"], ["aaa.in"]) +Retrieved `bbb.out' from cache +Retrieved `ccc.out' from cache +cat(["all"], ["aaa.out", "bbb.out", "ccc.out"]) +""")) + +test.must_match(['src', 'all'], "aaa.rebuild\nbbb.in\nccc.in\n") +test.must_match(['src', 'cat.out'], "aaa.out\nall\n") + +test.up_to_date(chdir = 'src', arguments = '.') + +test.run(chdir = 'src', arguments = '-c .') +test.unlink(['src', 'cat.out']) + +# All done. +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/option/debug-multiple.py b/test/option/debug-multiple.py new file mode 100644 index 0000000..f5bbdf0 --- /dev/null +++ b/test/option/debug-multiple.py @@ -0,0 +1,86 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test that --debug can take multiple options +""" + +import re + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """ +def cat(target, source, env): + open(str(target[0]), 'wb').write(open(str(source[0]), 'rb').read()) +env = Environment(BUILDERS={'Cat':Builder(action=Action(cat))}) +env.Cat('file.out', 'file.in') +""") + +test.write('file.in', "file.in\n") + +# Just check that object counts for some representative classes +# show up in the output. + +def find_object_count(s, stdout): + re_string = '\d+ +\d+ %s' % re.escape(s) + return re.search(re_string, stdout) + +objects = [ + 'Action.CommandAction', + 'Builder.BuilderBase', + 'Environment.Base', + 'Executor.Executor', + 'Node.FS', + 'Node.FS.Base', + 'Node.Node', +] + +for args in ['--debug=prepare,count', '--debug=count,prepare']: + test.run(arguments = args) + stdout = test.stdout() + missing = [o for o in objects if find_object_count(o, stdout) is None] + + if missing: + print "Missing the following object lines from '%s' output:" % args + print "\t", ' '.join(missing) + print "STDOUT ==========" + print stdout + test.fail_test(1) + + if 'Preparing target file.out...' not in stdout: + print "Missing 'Preparing' lines from '%s' output:" % args + print "STDOUT ==========" + print stdout + test.fail_test(1) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: -- cgit v0.12