77build systems, primarily ninja.
88"""
99
10+ import collections
1011import os
1112import re
1213import subprocess
1920windows_quoter_regex = re .compile (r'(\\*)"' )
2021
2122
22- def QuoteForRspFile (arg ):
23+ def QuoteForRspFile (arg , quote_cmd = True ):
2324 """Quote a command line argument so that it appears as one argument when
2425 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for
2526 Windows programs)."""
@@ -36,7 +37,8 @@ def QuoteForRspFile(arg):
3637 # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes
3738 # preceding it, and results in n backslashes + the quote. So we substitute
3839 # in 2* what we match, +1 more, plus the quote.
39- arg = windows_quoter_regex .sub (lambda mo : 2 * mo .group (1 ) + '\\ "' , arg )
40+ if quote_cmd :
41+ arg = windows_quoter_regex .sub (lambda mo : 2 * mo .group (1 ) + '\\ "' , arg )
4042
4143 # %'s also need to be doubled otherwise they're interpreted as batch
4244 # positional arguments. Also make sure to escape the % so that they're
@@ -48,12 +50,17 @@ def QuoteForRspFile(arg):
4850 # These commands are used in rsp files, so no escaping for the shell (via ^)
4951 # is necessary.
5052
51- # Finally, wrap the whole thing in quotes so that the above quote rule
52- # applies and whitespace isn't a word break.
53- return '"' + arg + '"'
53+ # As a workaround for programs that don't use CommandLineToArgvW, gyp
54+ # supports msvs_quote_cmd=0, which simply disables all quoting.
55+ if quote_cmd :
56+ # Finally, wrap the whole thing in quotes so that the above quote rule
57+ # applies and whitespace isn't a word break.
58+ return f'"{ arg } "'
5459
60+ return arg
5561
56- def EncodeRspFileList (args ):
62+
63+ def EncodeRspFileList (args , quote_cmd ):
5764 """Process a list of arguments using QuoteCmdExeArgument."""
5865 # Note that the first argument is assumed to be the command. Don't add
5966 # quotes around it because then built-ins like 'echo', etc. won't work.
@@ -67,7 +74,8 @@ def EncodeRspFileList(args):
6774 program = call + " " + os .path .normpath (program )
6875 else :
6976 program = os .path .normpath (args [0 ])
70- return program + " " + " " .join (QuoteForRspFile (arg ) for arg in args [1 :])
77+ return (program + " " +
78+ " " .join (QuoteForRspFile (arg , quote_cmd ) for arg in args [1 :]))
7179
7280
7381def _GenericRetrieve (root , default , path ):
@@ -933,13 +941,22 @@ def BuildCygwinBashCommandLine(self, args, path_to_base):
933941 )
934942 return cmd
935943
936- def IsRuleRunUnderCygwin (self , rule ):
937- """Determine if an action should be run under cygwin. If the variable is
938- unset, or set to 1 we use cygwin."""
939- return (
940- int (rule .get ("msvs_cygwin_shell" , self .spec .get ("msvs_cygwin_shell" , 1 )))
941- != 0
942- )
944+ RuleShellFlags = collections .namedtuple ("RuleShellFlags" , ["cygwin" , "quote" ])
945+
946+ def GetRuleShellFlags (self , rule ):
947+ """Return RuleShellFlags about how the given rule should be run. This
948+ includes whether it should run under cygwin (msvs_cygwin_shell), and
949+ whether the commands should be quoted (msvs_quote_cmd)."""
950+ # If the variable is unset, or set to 1 we use cygwin
951+ cygwin = int (rule .get ("msvs_cygwin_shell" ,
952+ self .spec .get ("msvs_cygwin_shell" , 1 ))) != 0
953+ # Default to quoting. There's only a few special instances where the
954+ # target command uses non-standard command line parsing and handle quotes
955+ # and quote escaping differently.
956+ quote_cmd = int (rule .get ("msvs_quote_cmd" , 1 ))
957+ assert quote_cmd != 0 or cygwin != 1 , \
958+ "msvs_quote_cmd=0 only applicable for msvs_cygwin_shell=0"
959+ return MsvsSettings .RuleShellFlags (cygwin , quote_cmd )
943960
944961 def _HasExplicitRuleForExtension (self , spec , extension ):
945962 """Determine if there's an explicit rule for a particular extension."""
0 commit comments