diff --git a/v2/engine/execnt.c b/v2/engine/execnt.c index 1616abcb8..5f5a9bea5 100644 --- a/v2/engine/execnt.c +++ b/v2/engine/execnt.c @@ -233,13 +233,9 @@ int exec_check if ( is_raw_command_request( *pShell ) ) { int const raw_cmd_length = raw_command_length( command->value ); - if ( !raw_cmd_length ) + if ( raw_cmd_length < 0 ) { - return EXEC_CHECK_SKIP; - } - else if ( raw_cmd_length < maxline() ) - { - /* Fallback to default shell. */ + /* Invalid characters detected - fallback to default shell. */ list_free( *pShell ); *pShell = L0; } @@ -250,7 +246,7 @@ int exec_check return EXEC_CHECK_TOO_LONG; } else - return EXEC_CHECK_OK; + return raw_cmd_length ? EXEC_CHECK_OK : EXEC_CHECK_SKIP; } /* Now we know we are using an external shell. Note that there is no need to diff --git a/v2/test/core_nt_cmd_line.py b/v2/test/core_nt_cmd_line.py new file mode 100755 index 000000000..77b05f180 --- /dev/null +++ b/v2/test/core_nt_cmd_line.py @@ -0,0 +1,243 @@ +#!/usr/bin/python + +# Copyright 2001 Dave Abrahams +# Copyright 2011 Steven Watanabe +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) + +# Tests Windows command line construction. +# +# Note that the regular 'echo' is an internal shell command on Windows and +# therefore can not be called directly as a standalone Windows process. + +import BoostBuild +import os +import re +import sys + + +executable = sys.executable.replace("\\", "/") +if " " in executable: + executable = '"%s"' % executable + + +def string_of_length(n): + if n <= 0: + return "" + n -= 1 + y = ['', '$(1x10-1)', '$(10x10-1)', '$(100x10-1)', '$(1000x10-1)'] + result = [] + for i in reversed(xrange(5)): + x, n = divmod(n, 10 ** i) + result += [y[i]] * x + result.append('x') + return " ".join(result) + + +# Boost Jam currently does not allow preparing actions with completly empty +# content as it always requires at least a single whitespace after the opening +# brace in order to satisfy. +def test_raw_empty(): + t = BoostBuild.Tester("-d2 -d+4", pass_d0=False, pass_toolset=0, + use_test_config=False) + t.write("file.jam", """\ +actions do_empty { %s} +JAMSHELL = %% ; +do_empty all ; +""" % (" \n\n\r\r\v\v\t\t \t \r\r \n\n")) + t.run_build_system("-ffile.jam") + t.expect_output_line("do_empty*", False) + t.expect_output_line("Executing raw command directly", False) + t.cleanup() + + +def test_raw_nt(n=None, error=False): + t = BoostBuild.Tester("-d1 -d+4", pass_d0=False, pass_toolset=0, + use_test_config=False) + + cmd_prefix = "%s -c \"print('XXX: " % executable + cmd_suffix = "')\"" + cmd_extra_length = len(cmd_prefix) + len(cmd_suffix) + + if n == None: + n = cmd_extra_length + + data_length = n - cmd_extra_length + if data_length < 0: + BoostBuild.annotation("failure", """\ +Can not construct Windows command of desired length. Requested command length +too short for the current test configuration. + Requested command length: %d + Minimal supported command length: %d +""" % (n, cmd_extra_length)) + t.fail_test(1, dump_difference=False) + + # Each $(Xx10-1) variable contains X words of 9 characters each, which, + # including spaces between words, brings the total number of characters in + # its string representation to X * 10 - 1 (X * 9 characters + (X - 1) + # spaces). + t.write("file.jam", """\ +ten = 0 1 2 3 4 5 6 7 8 9 ; + +1x10-1 = 123456789 ; +10x10-1 = $(ten)12345678 ; +100x10-1 = $(ten)$(ten)1234567 ; +1000x10-1 = $(ten)$(ten)$(ten)123456 ; + +actions do_echo +{ + %s%s%s +} +JAMSHELL = %% ; +do_echo all ; +""" % (cmd_prefix, string_of_length(data_length), cmd_suffix)) + t.run_build_system("-ffile.jam", status=1 if error else 0) + if error: + t.expect_output_line("Executing raw command directly", False) + t.expect_output_line("do_echo action is too long (%d, max 32766):" % n) + t.expect_output_line("XXX: *", False) + else: + t.expect_output_line("Executing raw command directly") + t.expect_output_line("do_echo action is too long*", False) + + m = re.search("^XXX: (.*)$", t.stdout(), re.MULTILINE) + if not m: + BoostBuild.annotation("failure", "Expected output line starting " + "with 'XXX: ' not found.") + t.fail_test(1, dump_difference=False) + if len(m.group(1)) != data_length: + BoostBuild.annotation("failure", """Unexpected output data length. + Expected: %d + Received: %d""" % (n, len(m.group(1)))) + t.fail_test(1, dump_difference=False) + + t.cleanup() + + +def test_raw_to_shell_fallback_nt(): + t = BoostBuild.Tester("-d1 -d+4", pass_d0=False, pass_toolset=0, + use_test_config=False) + + cmd_prefix = '%s -c print(' % executable + cmd_suffix = ')' + + t.write("file_multiline.jam", """\ +actions do_multiline +{ + echo one + + + echo two +} +JAMSHELL = % ; +do_multiline all ; +""") + t.run_build_system("-ffile_multiline.jam") + t.expect_output_line("do_multiline all") + t.expect_output_line("one") + t.expect_output_line("two") + t.expect_output_line("Executing raw command directly", False) + t.expect_output_line("Executing using a command file and the shell: " + "cmd.exe /Q/C") + + t.write("file_redirect.jam", """\ +actions do_redirect { echo one > two.txt } +JAMSHELL = % ; +do_redirect all ; +""") + t.run_build_system("-ffile_redirect.jam") + t.expect_output_line("do_redirect all") + t.expect_output_line("one", False) + t.expect_output_line("Executing raw command directly", False) + t.expect_output_line("Executing using a command file and the shell: " + "cmd.exe /Q/C") + t.expect_addition("two.txt") + + t.write("file_pipe.jam", """\ +actions do_pipe { echo one | echo two } +JAMSHELL = % ; +do_pipe all ; +""") + t.run_build_system("-ffile_pipe.jam") + t.expect_output_line("do_pipe all") + t.expect_output_line("one", False) + t.expect_output_line("two") + t.expect_output_line("Executing raw command directly", False) + t.expect_output_line("Executing using a command file and the shell: " + "cmd.exe /Q/C") + + t.write("file_single_quoted.jam", """\ +actions do_single_quoted { %s'5>10'%s } +JAMSHELL = %% ; +do_single_quoted all ; +""" % (cmd_prefix, cmd_suffix)) + t.run_build_system("-ffile_single_quoted.jam") + t.expect_output_line("do_single_quoted all") + t.expect_output_line("5>10") + t.expect_output_line("Executing raw command directly") + t.expect_output_line("Executing using a command file and the shell: " + "cmd.exe /Q/C", False) + t.expect_nothing_more() + + t.write("file_double_quoted.jam", """\ +actions do_double_quoted { %s"5>10"%s } +JAMSHELL = %% ; +do_double_quoted all ; +""" % (cmd_prefix, cmd_suffix)) + t.run_build_system("-ffile_double_quoted.jam") + t.expect_output_line("do_double_quoted all") + # The difference between this example and the similar previous one using + # single instead of double quotes stems from how the used Python executable + # parses the command-line string received from Windows. + t.expect_output_line("False") + t.expect_output_line("Executing raw command directly") + t.expect_output_line("Executing using a command file and the shell: " + "cmd.exe /Q/C", False) + t.expect_nothing_more() + + t.write("file_escaped_quote.jam", """\ +actions do_escaped_quote { %s\\"5>10\\"%s } +JAMSHELL = %% ; +do_escaped_quote all ; +""" % (cmd_prefix, cmd_suffix)) + t.run_build_system("-ffile_escaped_quote.jam") + t.expect_output_line("do_escaped_quote all") + t.expect_output_line("5>10") + t.expect_output_line("Executing raw command directly", False) + t.expect_output_line("Executing using a command file and the shell: " + "cmd.exe /Q/C") + t.expect_nothing_more() + + t.cleanup() + + +############################################################################### +# +# main() +# ------ +# +############################################################################### + +if os.name == 'nt': + test_raw_empty() + + # Can not test much shorter lengths as the shortest possible command line + # line length constructed in this depends on the runtime environment, e.g. + # path to the Panther executable running this test. + test_raw_nt() + test_raw_nt(255) + test_raw_nt(1000) + test_raw_nt(8000) + test_raw_nt(8191) + test_raw_nt(8192) + test_raw_nt(10000) + test_raw_nt(30000) + test_raw_nt(32766) + # CreateProcessA() Windows API places a limit of 32768 on the allowed + # command-line length, including a trailing Unicode (2-byte) nul-terminator + # character. + test_raw_nt(32767, error=True) + test_raw_nt(40000, error=True) + test_raw_nt(100001, error=True) + + test_raw_to_shell_fallback_nt() \ No newline at end of file diff --git a/v2/test/core_nt_line_length.py b/v2/test/core_nt_line_length.py deleted file mode 100755 index 809c5e1ec..000000000 --- a/v2/test/core_nt_line_length.py +++ /dev/null @@ -1,52 +0,0 @@ -#!/usr/bin/python - -# Copyright 2001 Dave Abrahams -# Copyright 2011 Steven Watanabe -# Distributed under the Boost Software License, Version 1.0. -# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) - -import BoostBuild -import os - -t = BoostBuild.Tester(pass_toolset=0, pass_d0=False) - -t.write("file.jam", """ -if $(NT) -{ - # - # Build a really long commandline. (> 10K characters). - # - ten = 0 1 2 3 4 5 6 7 8 9 ; - 1x7chars = 0_____ ; - # add a digit and multiply by 10 - 10x8chars = $(ten)$(1x7chars) ; - # add a digit to each of 10 strings and multiply by 10 - 100x9chars = $(ten)$(10x8chars) ; - # add a digit to each of 100 strings and multiply by 10 - 1000x10chars = $(ten)$(100x9chars) ; - - # - # Cause line_length_test to be built - # - actions do_echo - { - echo $(text) - } - - 400x10chars = $(ten[1-4])$(100x9chars) ; - - text on line_length_test = $(400x10chars) 40$(10x8chars[1-9]) 01234 ; - text on line_length_test = $(1000x10chars) $(1000x10chars) ; - JAMSHELL on line_length_test = % ; - DEPENDS all : line_length_test ; - - do_echo line_length_test ; -} -else -{ - NOCARE all ; -} -""") -t.run_build_system("-ffile.jam") - -t.cleanup() diff --git a/v2/test/test_all.py b/v2/test/test_all.py index e746e2b24..bfb5bf604 100644 --- a/v2/test/test_all.py +++ b/v2/test/test_all.py @@ -175,7 +175,7 @@ tests = ["absolute_sources", "core_actions_quietly", "core_at_file", "core_bindrule", - "core_nt_line_length", + "core_nt_cmd_line", "core_option_d2", "core_option_l", "core_option_n",