From 5c6d4d988b7e318428c158ded6be7f29a5e99d5e Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Tue, 18 Jan 2022 08:09:32 -0700 Subject: Tweak the scons help message Processing changed a bit - some lines can now be joined instead of split (if the usage part is short so the combination will still fit). Dropped the "Ingored for compatibility" chunk from the printout. The usage: message was changed as it didn't mention variables. A number of tests expected the exact value of that line, and so were updated. Updated docstrings (for the API docs). Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + SCons/Script/SConsOptions.py | 167 +++++++++++++++++++----------------- test/AddOption/longopts.py | 9 +- test/AddOption/multi-arg.py | 2 +- test/Removed/debug-dtree.py | 9 +- test/Removed/debug-nomemoizer.py | 9 +- test/Removed/debug-stree.py | 9 +- test/Removed/debug-tree.py | 9 +- test/SCONSFLAGS.py | 9 +- test/option/option--duplicate.py | 7 +- test/option/option--experimental.py | 2 +- test/option/option--tree.py | 9 +- test/option/option-unknown.py | 11 ++- test/option/option_profile.py | 11 ++- 14 files changed, 133 insertions(+), 131 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 005b0a4..397df10 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -46,6 +46,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Calls now look like 'debug("template %s", text)' rather than 'debug("template %s" % text)' so the logging system does the interpolation only when/if needed (was a pylint warning). + - Update Help display a bit - join some lines, drop "ignored for compat". RELEASE 4.3.0 - Tue, 16 Nov 2021 18:12:46 -0700 diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index d38380b..3a13934 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -239,8 +239,7 @@ class SConsOption(optparse.Option): if value is not None: if self.nargs in (1, '?'): return self.check_value(opt, value) - else: - return tuple([self.check_value(opt, v) for v in value]) + return tuple([self.check_value(opt, v) for v in value]) def process(self, opt, value, values, parser): @@ -276,9 +275,10 @@ class SConsOptionGroup(optparse.OptionGroup): """ def format_help(self, formatter): - """ - Format an option group's help text, outdenting the title so it's - flush with the "SCons Options" title we print at the top. + """ Format an option group's help text. + + The title is dedented so it's flush with the "SCons Options" + title we print at the top. """ formatter.dedent() result = formatter.format_heading(self.title) @@ -297,14 +297,14 @@ class SConsOptionParser(optparse.OptionParser): sys.exit(2) def _process_long_opt(self, rargs, values): - """ - SCons-specific processing of long options. + """ SCons-specific processing of long options. This is copied directly from the normal - optparse._process_long_opt() method, except that, if configured + ``optparse._process_long_opt()`` method, except that, if configured to do so, we catch the exception thrown when an unknown option is encountered and just stick it back on the "leftover" arguments - for later (re-)processing. + for later (re-)processing. This is because we may see the option + definition later, while processing SConscript files. """ arg = rargs.pop(0) @@ -433,10 +433,9 @@ class SConsOptionParser(optparse.OptionParser): self.largs = self.largs + largs_restore def add_local_option(self, *args, **kw): - """ - Adds a local option to the parser. + """ Adds a local option to the parser. - This is initiated by an AddOption() call to add a user-defined + This is initiated by an :func:`AddOption` 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. """ @@ -466,54 +465,67 @@ class SConsOptionParser(optparse.OptionParser): class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): def format_usage(self, usage): + """ Formats the usage message. """ return "usage: %s\n" % usage def format_heading(self, heading): - """ - This translates any heading of "options" or "Options" into - "SCons Options." Unfortunately, we have to do this here, - because those titles are hard-coded in the optparse calls. + """ Translates heading to "SCons Options" + + Heading of "Options" changed to "SCons Options." + Unfortunately, we have to do this here, because those titles + are hard-coded in the optparse calls. """ if heading == 'Options': heading = "SCons Options" return optparse.IndentedHelpFormatter.format_heading(self, heading) def format_option(self, option): - """ - A copy of the normal optparse.IndentedHelpFormatter.format_option() + """ Customized option formatter. + + A copy of the normal ``optparse.IndentedHelpFormatter.format_option()`` method. This has been snarfed so we can modify text wrapping to - out liking: + our liking: + + * add our own regular expression that doesn't break on hyphens + (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). + * if it would all fit on one line even if opts are long, don't break. + * set the :attr:`subsequent_indent` when wrapping the :attr:`help_text`. + + The help for each option consists of two parts: + + * the opt strings and metavars e.g. ("-x", or + "-fFILENAME, --file=FILENAME") + * the user-supplied help string e.g. + ("turn on expert mode", "read data from FILENAME") - -- add our own regular expression that doesn't break on hyphens - (so things like --no-print-directory don't get broken); + If possible, we write both of these on the same line:: - -- wrap the list of options themselves when it's too long - (the wrapper.fill(opts) call below); + -x turn on expert mode - -- set the subsequent_indent when wrapping the help_text. + But if the opt string list is too long, we put the help + string on a second line, indented to the same column it would + start in if it fit on the first line:: + + -fFILENAME, --file=FILENAME + read data from FILENAME """ - # The help for each option consists of two parts: - # * the opt strings and metavars - # eg. ("-x", or "-fFILENAME, --file=FILENAME") - # * the user-supplied help string - # eg. ("turn on expert mode", "read data from FILENAME") - # - # If possible, we write both of these on the same line: - # -x turn on expert mode - # - # But if the opt string list is too long, we put the help - # string on a second line, indented to the same column it would - # start in if it fit on the first line. - # -fFILENAME, --file=FILENAME - # read data from FILENAME result = [] - opts = self.option_strings[option] opt_width = self.help_position - self.current_indent - 2 - if len(opts) > opt_width: - wrapper = textwrap.TextWrapper(width=self.width, - initial_indent=' ', - subsequent_indent=' ') + # SCons: pre-compute if we could combine opts and text on one line, + # even if opts spills over opt_width. Saves some lines. + combine_anyway = False + if option.help: + help_text = self.expand_default(option) + if len(opts) > opt_width and len(opts) + len(help_text) + 2 <= self.width: + combine_anyway = True + if len(opts) > opt_width and not combine_anyway: + # SCons: wrap options if needed + wrapper = textwrap.TextWrapper( + width=self.width, initial_indent=' ', subsequent_indent=' ' + ) wrapper.wordsep_re = no_hyphen_re opts = wrapper.fill(opts) + '\n' indent_first = self.help_position @@ -522,12 +534,12 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): indent_first = 0 result.append(opts) if option.help: - - help_text = self.expand_default(option) - - # SCons: indent every line of the help text but the first. - wrapper = textwrap.TextWrapper(width=self.help_width, - subsequent_indent=' ') + # this is now done above in the pre-check. + # help_text = self.expand_default(option) + # SCons: indent every line of the help text but the first. + wrapper = textwrap.TextWrapper( + width=self.help_width, subsequent_indent=' ' + ) wrapper.wordsep_re = no_hyphen_re help_lines = wrapper.wrap(help_text) result.append("%*s%s\n" % (indent_first, "", help_lines[0])) @@ -539,43 +551,39 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): def Parser(version): + """Returns a parser object initialized with the standard SCons options. + + Add options in the order we want them to show up in the ``-H`` help + text, basically alphabetical. Each ``op.add_option()`` call + should have a consistent format:: + + op.add_option("-L", "--long-option-name", + nargs=1, type="string", + dest="long_option_name", default='foo', + action="callback", callback=opt_long_option, + help="help text goes here", + metavar="VAR") + + Even though the :mod:`optparse` module constructs reasonable default + destination names from the long option names, we're going to be + explicit about each one for easier readability and so this code + will at least show up when grepping the source for option attribute + names, or otherwise browsing the source code. """ - Returns an options parser object initialized with the standard - SCons options. - """ - formatter = SConsIndentedHelpFormatter(max_help_position=30) - - op = SConsOptionParser(option_class=SConsOption, - add_help_option=False, - formatter=formatter, - usage="usage: scons [OPTION] [TARGET] ...",) - + op = SConsOptionParser( + option_class=SConsOption, + add_help_option=False, + formatter=formatter, + usage="usage: scons [OPTIONS] [VARIABLES] [TARGETS]", + ) op.preserve_unknown_options = True op.version = version - # Add the options to the parser we just created. - # - # These are in the order we want them to show up in the -H help - # text, basically alphabetical. Each op.add_option() call below - # should have a consistent format: - # - # op.add_option("-L", "--long-option-name", - # nargs=1, type="string", - # dest="long_option_name", default='foo', - # action="callback", callback=opt_long_option, - # help="help text goes here", - # metavar="VAR") - # - # Even though the optparse module constructs reasonable default - # destination names from the long option names, we're going to be - # explicit about each one for easier readability and so this code - # will at least show up when grepping the source for option attribute - # names, or otherwise browsing the source code. - # options ignored for compatibility def opt_ignore(option, opt, value, parser): sys.stderr.write("Warning: ignoring %s option\n" % opt) + op.add_option("-b", "-d", "-e", "-m", "-S", "-t", "-w", "--environment-overrides", "--no-keep-going", @@ -584,7 +592,7 @@ def Parser(version): "--stop", "--touch", action="callback", callback=opt_ignore, - help="Ignored for compatibility.") + help=SUPPRESS_HELP) op.add_option('-c', '--clean', '--remove', dest="clean", default=False, @@ -968,6 +976,7 @@ def Parser(version): def opt_version(option, opt, value, parser): sys.stdout.write(parser.version + '\n') sys.exit(0) + op.add_option("-v", "--version", action="callback", callback=opt_version, help="Print the SCons version number and exit.") diff --git a/test/AddOption/longopts.py b/test/AddOption/longopts.py index 48c3502..e0c0df4 100644 --- a/test/AddOption/longopts.py +++ b/test/AddOption/longopts.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" """ Verifies that the default name matching of optparse for long options @@ -55,7 +54,7 @@ test.run('-Q -q . --myarg=helloworld', test.run('-Q -q . --myargumen=helloworld', status=2, stdout="myargument: gully\nmyarg: balla\n", stderr="""\ -usage: scons [OPTION] [TARGET] ... +usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: no such option: '--myargumen'. Did you mean '--myargument'? """) diff --git a/test/AddOption/multi-arg.py b/test/AddOption/multi-arg.py index 44928c4..1a71cfe 100644 --- a/test/AddOption/multi-arg.py +++ b/test/AddOption/multi-arg.py @@ -56,7 +56,7 @@ test.run( '-Q -q . --extras A', status=2, stderr="""\ -usage: scons [OPTION] [TARGET] ... +usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: --extras option requires 2 arguments """, diff --git a/test/Removed/debug-dtree.py b/test/Removed/debug-dtree.py index a016f96..0cd41b3 100644 --- a/test/Removed/debug-dtree.py +++ b/test/Removed/debug-dtree.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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 the --debug=dtree option fails with expected exception @@ -37,7 +36,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', "") -expect = r"""usage: scons [OPTION] [TARGET] ... +expect = r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: `dtree' is not a valid debug option type ; please use --tree=derived instead """ diff --git a/test/Removed/debug-nomemoizer.py b/test/Removed/debug-nomemoizer.py index a830a88..827ace6 100644 --- a/test/Removed/debug-nomemoizer.py +++ b/test/Removed/debug-nomemoizer.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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 the --debug=nomemoizer option fails with expected exception @@ -37,7 +36,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', "") -expect=r"""usage: scons [OPTION] [TARGET] ... +expect=r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: `nomemoizer' is not a valid debug option type ; there is no replacement """ diff --git a/test/Removed/debug-stree.py b/test/Removed/debug-stree.py index 60ce4f2..81d21f7 100644 --- a/test/Removed/debug-stree.py +++ b/test/Removed/debug-stree.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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 the --debug=stree option fails with expected exception @@ -37,7 +36,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', "") -expect = r"""usage: scons [OPTION] [TARGET] ... +expect = r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: `stree' is not a valid debug option type ; please use --tree=all,status instead """ diff --git a/test/Removed/debug-tree.py b/test/Removed/debug-tree.py index 06287de..81063da 100644 --- a/test/Removed/debug-tree.py +++ b/test/Removed/debug-tree.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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 the --debug=tree option fails with expected exception @@ -38,7 +37,7 @@ test = TestSCons.TestSCons() test.write('SConstruct', "") -expect = r"""usage: scons [OPTION] [TARGET] ... +expect = r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: `tree' is not a valid debug option type ; please use --tree=all instead """ diff --git a/test/SCONSFLAGS.py b/test/SCONSFLAGS.py index db866e8..6b2ea8b 100644 --- a/test/SCONSFLAGS.py +++ b/test/SCONSFLAGS.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" import os @@ -65,7 +64,7 @@ test.must_contain_all_lines(test.stdout(), ['-H, --help-options']) os.environ['SCONSFLAGS'] = '-Z' -expect = r"""usage: scons [OPTION] [TARGET] ... +expect = r"""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: no such option: -Z """ diff --git a/test/option/option--duplicate.py b/test/option/option--duplicate.py index 91dc61c..74de64b 100644 --- a/test/option/option--duplicate.py +++ b/test/option/option--duplicate.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,7 +22,6 @@ # 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. -# """ This tests the --duplicate command line option, and the duplicate @@ -125,7 +126,7 @@ type='copy' RunTest('copy', type, bss) test.run(arguments='--duplicate=nonsense', status=2, stderr="""\ -usage: scons [OPTION] [TARGET] ... +usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: `nonsense' is not a valid duplication option type, try: hard-soft-copy, soft-hard-copy, hard-copy, soft-copy, copy diff --git a/test/option/option--experimental.py b/test/option/option--experimental.py index 2e06dc3..51f3546 100644 --- a/test/option/option--experimental.py +++ b/test/option/option--experimental.py @@ -48,7 +48,7 @@ Experimental=%s stdout=test.wrap_stdout(read_str=read_string, build_str="scons: `.' is up to date.\n")) test.run(arguments='--experimental=warp_drive', - stderr="""usage: scons [OPTION] [TARGET] ... + stderr="""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: option --experimental: invalid choice: 'warp_drive' (choose from 'all','none','ninja','transporter','warp_speed') """, diff --git a/test/option/option--tree.py b/test/option/option--tree.py index 5192ac0..b575fe5 100644 --- a/test/option/option--tree.py +++ b/test/option/option--tree.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" import TestSCons @@ -37,7 +36,7 @@ test.run(arguments='-Q --tree=prune', """) test.run(arguments='-Q --tree=foofoo', - stderr="""usage: scons [OPTION] [TARGET] ... + stderr="""usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: `foofoo' is not a valid --tree option type, try: all, derived, prune, status, linedraw diff --git a/test/option/option-unknown.py b/test/option/option-unknown.py index 8b42762..62928be 100644 --- a/test/option/option-unknown.py +++ b/test/option/option-unknown.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # 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__" import TestSCons @@ -31,14 +30,14 @@ test = TestSCons.TestSCons() test.write('SConstruct', "") test.run(arguments = '-Z', - stderr = """usage: scons [OPTION] [TARGET] ... + stderr = """usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: no such option: -Z """, status = 2) test.run(arguments = '--ZizzerZazzerZuzz', - stderr = """usage: scons [OPTION] [TARGET] ... + stderr = """usage: scons [OPTIONS] [VARIABLES] [TARGETS] SCons Error: no such option: --ZizzerZazzerZuzz """, diff --git a/test/option/option_profile.py b/test/option/option_profile.py index 8c7d80a..19e68f6 100644 --- a/test/option/option_profile.py +++ b/test/option/option_profile.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -21,9 +23,6 @@ # 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__" - import contextlib import sys from io import StringIO @@ -47,7 +46,7 @@ test.write('file.in', "file.in\n") scons_prof = test.workpath('scons.prof') test.run(arguments = "--profile=%s -h" % scons_prof) -test.must_contain_all_lines(test.stdout(), ['usage: scons [OPTION]']) +test.must_contain_all_lines(test.stdout(), ['usage: scons [OPTIONS] [VARIABLES] [TARGETS]']) try: save_stdout = sys.stdout @@ -89,7 +88,7 @@ scons_prof = test.workpath('scons3.prof') test.run(arguments = "--profile %s --debug=memory -h" % scons_prof) expect = [ - 'usage: scons [OPTION]', + 'usage: scons [OPTIONS] [VARIABLES] [TARGETS]', 'Options:' ] test.must_contain_all_lines(test.stdout(), expect) -- cgit v0.12 From 93eba59cf0b8d005d0107c69317b3fed59975219 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 4 Feb 2022 08:16:40 -0700 Subject: Additional Help updates Drop the previous change to join lines even if the option part overflows its gutter. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 +- SCons/Script/SConsOptions.py | 123 ++++++++++++++++++++++--------------------- 2 files changed, 65 insertions(+), 61 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 397df10..3b884b8 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -46,7 +46,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER Calls now look like 'debug("template %s", text)' rather than 'debug("template %s" % text)' so the logging system does the interpolation only when/if needed (was a pylint warning). - - Update Help display a bit - join some lines, drop "ignored for compat". + - Update Help (-H) output a bit. Drop "ignored for compat" entry. + Pass window size to formatter so it formats for wider displays too. RELEASE 4.3.0 - Tue, 16 Nov 2021 18:12:46 -0700 diff --git a/SCons/Script/SConsOptions.py b/SCons/Script/SConsOptions.py index 3a13934..e2631fb 100644 --- a/SCons/Script/SConsOptions.py +++ b/SCons/Script/SConsOptions.py @@ -24,6 +24,7 @@ import gettext import optparse import re +import shutil import sys import textwrap @@ -490,7 +491,6 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): (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). - * if it would all fit on one line even if opts are long, don't break. * set the :attr:`subsequent_indent` when wrapping the :attr:`help_text`. The help for each option consists of two parts: @@ -516,11 +516,13 @@ class SConsIndentedHelpFormatter(optparse.IndentedHelpFormatter): opt_width = self.help_position - self.current_indent - 2 # SCons: pre-compute if we could combine opts and text on one line, # even if opts spills over opt_width. Saves some lines. + # Note: check is currently disabled and this removed from docstring: + # * if it would all fit on one line even if opts are long, don't break. combine_anyway = False if option.help: help_text = self.expand_default(option) - if len(opts) > opt_width and len(opts) + len(help_text) + 2 <= self.width: - combine_anyway = True + # if len(opts) > opt_width and len(opts) + len(help_text) + 2 <= self.width: + # combine_anyway = True if len(opts) > opt_width and not combine_anyway: # SCons: wrap options if needed wrapper = textwrap.TextWrapper( @@ -570,7 +572,8 @@ def Parser(version): will at least show up when grepping the source for option attribute names, or otherwise browsing the source code. """ - formatter = SConsIndentedHelpFormatter(max_help_position=30) + columns, lines = shutil.get_terminal_size() + formatter = SConsIndentedHelpFormatter(max_help_position=30, width=columns) op = SConsOptionParser( option_class=SConsOption, add_help_option=False, @@ -597,41 +600,41 @@ def Parser(version): op.add_option('-c', '--clean', '--remove', dest="clean", default=False, action="store_true", - help="Remove specified targets and dependencies.") + help="Remove specified targets and dependencies") op.add_option('-C', '--directory', nargs=1, type="string", dest="directory", default=[], action="append", - help="Change to DIR before doing anything.", + help="Change to DIR before doing anything", metavar="DIR") op.add_option('--cache-debug', nargs=1, dest="cache_debug", default=None, action="store", - help="Print CacheDir debug info to FILE.", + help="Print CacheDir debug info to FILE", metavar="FILE") op.add_option('--cache-disable', '--no-cache', dest='cache_disable', default=False, action="store_true", - help="Do not retrieve built targets from CacheDir.") + help="Do not retrieve built targets from CacheDir") op.add_option('--cache-force', '--cache-populate', dest='cache_force', default=False, action="store_true", - help="Copy already-built targets into the CacheDir.") + 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.") + help="Do not update CacheDir with built targets") op.add_option('--cache-show', dest='cache_show', default=False, action="store_true", - help="Print build actions for files from CacheDir.") + help="Print build actions for files from CacheDir") def opt_invalid(group, value, options): """report an invalid option from a group""" @@ -645,7 +648,7 @@ def Parser(version): config_options = ["auto", "force", "cache"] - opt_config_help = "Controls Configure subsystem: %s." \ + opt_config_help = "Controls Configure subsystem [%s]" \ % ", ".join(config_options) op.add_option('--config', @@ -657,8 +660,8 @@ def Parser(version): op.add_option('-D', dest="climb_up", default=None, action="store_const", const=2, - help="Search up directory tree for SConstruct, " - "build all Default() targets.") + help="Search up directory tree for SConstruct, " + "build all Default() targets") deprecated_debug_options = {} @@ -698,7 +701,7 @@ def Parser(version): raise OptionValueError(opt_invalid( 'debug', value, debug_options)) - opt_debug_help = "Print various types of debugging information: %s." \ + opt_debug_help = "Print various types of debugging information [%s]" \ % ", ".join(debug_options) op.add_option('--debug', nargs=1, type="string", @@ -718,7 +721,7 @@ def Parser(version): nargs=1, type="string", dest='diskcheck', default=None, action="callback", callback=opt_diskcheck, - help="Enable specific on-disk checks.", + help="Enable specific on-disk checks", metavar="TYPE") def opt_duplicate(option, opt, value, parser): @@ -730,8 +733,8 @@ def Parser(version): # of SConscript files. SCons.Node.FS.set_duplicate(value) - opt_duplicate_help = "Set the preferred duplication methods. Must be one of " \ - + ", ".join(SCons.Node.FS.Valid_Duplicates) + opt_duplicate_help = "Set the preferred duplication methods [%s]" \ + % ", ".join(SCons.Node.FS.Valid_Duplicates) op.add_option('--duplicate', nargs=1, type="string", @@ -782,16 +785,16 @@ def Parser(version): nargs=1, type="string", dest="file", default=[], action="append", - help="Read FILE as the top-level SConstruct file.") + help="Read FILE as the top-level SConstruct file") op.add_option('-h', '--help', dest="help", default=False, action="store_true", - help="Print defined help message, or this one.") + help="Print defined help message, or this one") op.add_option("-H", "--help-options", action="help", - help="Print this message and exit.") + help="Print this message and exit") def warn_md5_chunksize_deprecated(option, opt, value, parser): if opt == '--md5-chunksize': @@ -805,25 +808,25 @@ def Parser(version): nargs=1, type="int", dest='md5_chunksize', default=SCons.Node.FS.File.hash_chunksize, action="callback", - help="Set chunk-size for hash signature computation to N kilobytes.", + help="Set chunk-size for hash signature computation to N kilobytes", callback=warn_md5_chunksize_deprecated, metavar="N") op.add_option('--hash-format', dest='hash_format', action='store', - help='Hash format (e.g. md5, sha1, or sha256).') + help='Hash format [md5, sha1, sha256, etc].') op.add_option('-i', '--ignore-errors', dest='ignore_errors', default=False, action="store_true", - help="Ignore errors from build actions.") + help="Ignore errors from build actions") op.add_option('-I', '--include-dir', nargs=1, dest='include_dir', default=[], action="append", - help="Search DIR for imported Python modules.", + help="Search DIR for imported Python modules", metavar="DIR") op.add_option('--ignore-virtualenv', @@ -843,93 +846,93 @@ def Parser(version): op.add_option('--implicit-deps-changed', dest="implicit_deps_changed", default=False, action="callback", callback=opt_implicit_deps, - help="Ignore cached implicit dependencies.") + help="Ignore cached implicit dependencies") op.add_option('--implicit-deps-unchanged', dest="implicit_deps_unchanged", default=False, action="callback", callback=opt_implicit_deps, - help="Ignore changes in implicit dependencies.") + help="Ignore changes in implicit dependencies") op.add_option('--interact', '--interactive', dest='interactive', default=False, action="store_true", - help="Run in interactive mode.") + help="Run in interactive mode") op.add_option('-j', '--jobs', nargs=1, type="int", dest="num_jobs", default=1, action="store", - help="Allow N jobs at once.", + help="Allow N jobs at once", metavar="N") op.add_option('-k', '--keep-going', dest='keep_going', default=False, action="store_true", - help="Keep going when a target can't be made.") + help="Keep going when a target can't be made") op.add_option('--max-drift', nargs=1, type="int", dest='max_drift', default=SCons.Node.FS.default_max_drift, action="store", - help="Set maximum system clock drift to N seconds.", + help="Set maximum system clock drift to N seconds", metavar="N") op.add_option('-n', '--no-exec', '--just-print', '--dry-run', '--recon', dest='no_exec', default=False, action="store_true", - help="Don't build; just print commands.") + help="Don't build; just print commands") op.add_option('--no-site-dir', dest='site_dir', action="store_false", - help="Don't search or use the usual site_scons dir.") + help="Don't search or use the usual site_scons dir") op.add_option('--profile', nargs=1, dest="profile_file", default=None, action="store", - help="Profile SCons and put results in FILE.", + help="Profile SCons and put results in FILE", metavar="FILE") op.add_option('-q', '--question', dest="question", default=False, action="store_true", - help="Don't build; exit status says if up to date.") + help="Don't build; exit status says if up to date") op.add_option('-Q', dest='no_progress', default=False, action="store_true", - help="Suppress \"Reading/Building\" progress messages.") + help="Suppress \"Reading/Building\" progress messages") op.add_option('--random', dest="random", default=False, action="store_true", - help="Build dependencies in random order.") + help="Build dependencies in random order") op.add_option('-s', '--silent', '--quiet', dest="silent", default=False, action="store_true", - help="Don't print commands.") + help="Don't print commands") op.add_option('--site-dir', nargs=1, dest='site_dir', default=None, action="store", - help="Use DIR instead of the usual site_scons dir.", + help="Use DIR instead of the usual site_scons dir", metavar="DIR") op.add_option('--stack-size', nargs=1, type="int", dest='stack_size', action="store", - help="Set the stack size of the threads used to run jobs to N kilobytes.", + help="Set the stack size of the threads used to run jobs to N kilobytes", metavar="N") op.add_option('--taskmastertrace', nargs=1, dest="taskmastertrace_file", default=None, action="store", - help="Trace Node evaluation to FILE.", + help="Trace Node evaluation to FILE", metavar="FILE") tree_options = ["all", "derived", "prune", "status", "linedraw"] @@ -951,7 +954,7 @@ def Parser(version): raise OptionValueError(opt_invalid('--tree', o, tree_options)) parser.values.tree_printers.append(tp) - opt_tree_help = "Print a dependency tree in various formats: %s." \ + opt_tree_help = "Print a dependency tree in various formats [%s]" \ % ", ".join(tree_options) op.add_option('--tree', @@ -964,14 +967,14 @@ def Parser(version): op.add_option('-u', '--up', '--search-up', dest="climb_up", default=0, action="store_const", const=1, - help="Search up directory tree for SConstruct, " - "build targets at or below current directory.") + help="Search up directory tree for SConstruct, " + "build targets at or below current directory") op.add_option('-U', dest="climb_up", default=0, action="store_const", const=3, - help="Search up directory tree for SConstruct, " - "build Default() targets from local SConscript.") + help="Search up directory tree for SConstruct, " + "build Default() targets from local SConscript") def opt_version(option, opt, value, parser): sys.stdout.write(parser.version + '\n') @@ -979,7 +982,7 @@ def Parser(version): op.add_option("-v", "--version", action="callback", callback=opt_version, - help="Print the SCons version number and exit.") + help="Print the SCons version number and exit") def opt_warn(option, opt, value, parser, tree_options=tree_options): if SCons.Util.is_String(value): @@ -990,14 +993,14 @@ def Parser(version): nargs=1, type="string", dest="warn", default=[], action="callback", callback=opt_warn, - help="Enable or disable warnings.", + help="Enable or disable warnings", metavar="WARNING-SPEC") op.add_option('-Y', '--repository', '--srcdir', nargs=1, dest="repository", default=[], action="append", - help="Search REPOSITORY for source and target files.") + help="Search REPOSITORY for source and target files") # Options from Make and Cons classic that we do not yet support, # but which we may support someday and whose (potential) meanings @@ -1014,62 +1017,62 @@ def Parser(version): action="callback", callback=opt_not_yet, # action="store", # help="Don't start multiple jobs unless load is below " - # "LOAD-AVERAGE." + # "LOAD-AVERAGE" help=SUPPRESS_HELP) op.add_option('--list-actions', dest="list_actions", action="callback", callback=opt_not_yet, - # help="Don't build; list files and build actions." + # help="Don't build; list files and build actions" help=SUPPRESS_HELP) op.add_option('--list-derived', dest="list_derived", action="callback", callback=opt_not_yet, - # help="Don't build; list files that would be built." + # help="Don't build; list files that would be built" help=SUPPRESS_HELP) op.add_option('--list-where', dest="list_where", action="callback", callback=opt_not_yet, - # help="Don't build; list files and where defined." + # help="Don't build; list files and where defined" help=SUPPRESS_HELP) op.add_option('-o', '--old-file', '--assume-old', nargs=1, type="string", dest="old_file", default=[], action="callback", callback=opt_not_yet, # action="append", - # help = "Consider FILE to be old; don't rebuild it." + # help = "Consider FILE to be old; don't rebuild it" help=SUPPRESS_HELP) op.add_option('--override', nargs=1, type="string", action="callback", callback=opt_not_yet, dest="override", - # help="Override variables as specified in FILE." + # help="Override variables as specified in FILE" help=SUPPRESS_HELP) op.add_option('-p', action="callback", callback=opt_not_yet, dest="p", - # help="Print internal environments/objects." + # help="Print internal environments/objects" help=SUPPRESS_HELP) op.add_option('-r', '-R', '--no-builtin-rules', '--no-builtin-variables', action="callback", callback=opt_not_yet, dest="no_builtin_rules", - # help="Clear default environments and variables." + # help="Clear default environments and variables" help=SUPPRESS_HELP) op.add_option('--write-filenames', nargs=1, type="string", dest="write_filenames", action="callback", callback=opt_not_yet, - # help="Write all filenames examined into FILE." + # help="Write all filenames examined into FILE" help=SUPPRESS_HELP) op.add_option('-W', '--new-file', '--assume-new', '--what-if', nargs=1, type="string", dest="new_file", action="callback", callback=opt_not_yet, - # help="Consider FILE to be changed." + # help="Consider FILE to be changed" help=SUPPRESS_HELP) op.add_option('--warn-undefined-variables', dest="warn_undefined_variables", action="callback", callback=opt_not_yet, - # help="Warn when an undefined variable is referenced." + # help="Warn when an undefined variable is referenced" help=SUPPRESS_HELP) return op -- cgit v0.12 From a08e9dfe4c91e46072460cb3f58efd224619cbd9 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 6 Feb 2022 18:06:21 -0700 Subject: Add info on Help changes to RELEASE.txt [ci skip] Signed-off-by: Mats Wichmann --- RELEASE.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASE.txt b/RELEASE.txt index 6c26a34..aeffddf 100755 --- a/RELEASE.txt +++ b/RELEASE.txt @@ -30,6 +30,10 @@ CHANGED/ENHANCED EXISTING FUNCTIONALITY an "Application Data" subdirectory here. - Action._subproc() can now be used as a python context manager to ensure that the POpen object is properly closed. +- SCons help (-H) no longer prints the "ignored for compatibility" options, + which are still listed in the manpage. +- Help is now sensitive to the size of the terminal window: the width of the + help text will scale to wider (or narrower) terminals than 80 characters. FIXES -- cgit v0.12