OLD | NEW |
1 # Copyright (c) 2012 Google Inc. All rights reserved. | 1 # Copyright (c) 2012 Google Inc. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
4 | 4 |
5 """ | 5 """ |
6 This module helps emulate Visual Studio 2008 behavior on top of other | 6 This module helps emulate Visual Studio 2008 behavior on top of other |
7 build systems, primarily ninja. | 7 build systems, primarily ninja. |
8 """ | 8 """ |
9 | 9 |
10 import os | 10 import os |
11 import re | 11 import re |
12 import subprocess | 12 import subprocess |
13 import sys | 13 import sys |
14 | 14 |
15 import gyp.MSVSVersion | 15 import gyp.MSVSVersion |
16 | 16 |
17 windows_quoter_regex = re.compile(r'(\\*)"') | 17 windows_quoter_regex = re.compile(r'(\\*)"') |
18 | 18 |
19 def QuoteForRspFile(arg): | 19 def QuoteForRspFile(arg, quote_cmd=True): |
20 """Quote a command line argument so that it appears as one argument when | 20 """Quote a command line argument so that it appears as one argument when |
21 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for | 21 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for |
22 Windows programs).""" | 22 Windows programs).""" |
23 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment | 23 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment |
24 # threads. This is actually the quoting rules for CommandLineToArgvW, not | 24 # threads. This is actually the quoting rules for CommandLineToArgvW, not |
25 # for the shell, because the shell doesn't do anything in Windows. This | 25 # for the shell, because the shell doesn't do anything in Windows. This |
26 # works more or less because most programs (including the compiler, etc.) | 26 # works more or less because most programs (including the compiler, etc.) |
27 # use that function to handle command line arguments. | 27 # use that function to handle command line arguments. |
28 | 28 |
29 # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes | 29 # For a literal quote, CommandLineToArgvW requires 2n+1 backslashes |
30 # preceding it, and results in n backslashes + the quote. So we substitute | 30 # preceding it, and results in n backslashes + the quote. So we substitute |
31 # in 2* what we match, +1 more, plus the quote. | 31 # in 2* what we match, +1 more, plus the quote. |
32 arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg) | 32 if quote_cmd: |
| 33 arg = windows_quoter_regex.sub(lambda mo: 2 * mo.group(1) + '\\"', arg) |
33 | 34 |
34 # %'s also need to be doubled otherwise they're interpreted as batch | 35 # %'s also need to be doubled otherwise they're interpreted as batch |
35 # positional arguments. Also make sure to escape the % so that they're | 36 # positional arguments. Also make sure to escape the % so that they're |
36 # passed literally through escaping so they can be singled to just the | 37 # passed literally through escaping so they can be singled to just the |
37 # original %. Otherwise, trying to pass the literal representation that | 38 # original %. Otherwise, trying to pass the literal representation that |
38 # looks like an environment variable to the shell (e.g. %PATH%) would fail. | 39 # looks like an environment variable to the shell (e.g. %PATH%) would fail. |
39 arg = arg.replace('%', '%%') | 40 arg = arg.replace('%', '%%') |
40 | 41 |
41 # These commands are used in rsp files, so no escaping for the shell (via ^) | 42 # These commands are used in rsp files, so no escaping for the shell (via ^) |
42 # is necessary. | 43 # is necessary. |
43 | 44 |
44 # Finally, wrap the whole thing in quotes so that the above quote rule | 45 # As a workaround for programs that don't use CommandLineToArgvW, gyp |
45 # applies and whitespace isn't a word break. | 46 # supports msvs_quote_cmd=0, which simply disables all quoting. |
46 return '"' + arg + '"' | 47 if quote_cmd: |
| 48 # Finally, wrap the whole thing in quotes so that the above quote rule |
| 49 # applies and whitespace isn't a word break. |
| 50 arg = '"' + arg + '"' |
47 | 51 |
| 52 return arg |
48 | 53 |
49 def EncodeRspFileList(args): | 54 def EncodeRspFileList(args, quote_cmd): |
50 """Process a list of arguments using QuoteCmdExeArgument.""" | 55 """Process a list of arguments using QuoteCmdExeArgument.""" |
51 # Note that the first argument is assumed to be the command. Don't add | 56 # Note that the first argument is assumed to be the command. Don't add |
52 # quotes around it because then built-ins like 'echo', etc. won't work. | 57 # quotes around it because then built-ins like 'echo', etc. won't work. |
53 # Take care to normpath only the path in the case of 'call ../x.bat' because | 58 # Take care to normpath only the path in the case of 'call ../x.bat' because |
54 # otherwise the whole thing is incorrectly interpreted as a path and not | 59 # otherwise the whole thing is incorrectly interpreted as a path and not |
55 # normalized correctly. | 60 # normalized correctly. |
56 if not args: return '' | 61 if not args: return '' |
57 if args[0].startswith('call '): | 62 if args[0].startswith('call '): |
58 call, program = args[0].split(' ', 1) | 63 call, program = args[0].split(' ', 1) |
59 program = call + ' ' + os.path.normpath(program) | 64 program = call + ' ' + os.path.normpath(program) |
60 else: | 65 else: |
61 program = os.path.normpath(args[0]) | 66 program = os.path.normpath(args[0]) |
62 return program + ' ' + ' '.join(QuoteForRspFile(arg) for arg in args[1:]) | 67 return (program + ' ' + |
| 68 ' '.join(QuoteForRspFile(arg, quote_cmd) for arg in args[1:])) |
63 | 69 |
64 | 70 |
65 def _GenericRetrieve(root, default, path): | 71 def _GenericRetrieve(root, default, path): |
66 """Given a list of dictionary keys |path| and a tree of dicts |root|, find | 72 """Given a list of dictionary keys |path| and a tree of dicts |root|, find |
67 value at path, or return |default| if any of the path doesn't exist.""" | 73 value at path, or return |default| if any of the path doesn't exist.""" |
68 if not root: | 74 if not root: |
69 return default | 75 return default |
70 if not path: | 76 if not path: |
71 return root | 77 return root |
72 return _GenericRetrieve(root.get(path[0]), default, path[1:]) | 78 return _GenericRetrieve(root.get(path[0]), default, path[1:]) |
(...skipping 318 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
391 os.path.join(path_to_base, self.msvs_cygwin_dirs[0])) | 397 os.path.join(path_to_base, self.msvs_cygwin_dirs[0])) |
392 cd = ('cd %s' % path_to_base).replace('\\', '/') | 398 cd = ('cd %s' % path_to_base).replace('\\', '/') |
393 args = [a.replace('\\', '/') for a in args] | 399 args = [a.replace('\\', '/') for a in args] |
394 args = ["'%s'" % a.replace("'", "\\'") for a in args] | 400 args = ["'%s'" % a.replace("'", "\\'") for a in args] |
395 bash_cmd = ' '.join(args) | 401 bash_cmd = ' '.join(args) |
396 cmd = ( | 402 cmd = ( |
397 'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir + | 403 'call "%s\\setup_env.bat" && set CYGWIN=nontsec && ' % cygwin_dir + |
398 'bash -c "%s ; %s"' % (cd, bash_cmd)) | 404 'bash -c "%s ; %s"' % (cd, bash_cmd)) |
399 return cmd | 405 return cmd |
400 | 406 |
401 def IsRuleRunUnderCygwin(self, rule): | 407 class RuleShellFlags: |
402 """Determine if an action should be run under cygwin. If the variable is | 408 def __init__(self, cygwin, quote): |
403 unset, or set to 1 we use cygwin.""" | 409 self.cygwin = cygwin |
404 return int(rule.get('msvs_cygwin_shell', | 410 self.quote = quote |
405 self.spec.get('msvs_cygwin_shell', 1))) != 0 | 411 |
| 412 def GetRuleShellFlags(self, rule): |
| 413 """Return RuleShellFlags about how the given rule should be run. This |
| 414 includes whether it should run under cygwin (msvs_cygwin_shell), and |
| 415 whether the commands should be quoted (msvs_quote_cmd).""" |
| 416 # If the variable is unset, or set to 1 we use cygwin |
| 417 cygwin = int(rule.get('msvs_cygwin_shell', |
| 418 self.spec.get('msvs_cygwin_shell', 1))) != 0 |
| 419 # Default to quoting. There's only a few special instances where the |
| 420 # target command uses non-standard command line parsing and handle quotes |
| 421 # and quote escaping differently. |
| 422 quote_cmd = int(rule.get('msvs_quote_cmd', 1)) |
| 423 return MsvsSettings.RuleShellFlags(cygwin, quote_cmd) |
406 | 424 |
407 def HasExplicitIdlRules(self, spec): | 425 def HasExplicitIdlRules(self, spec): |
408 """Determine if there's an explicit rule for idl files. When there isn't we | 426 """Determine if there's an explicit rule for idl files. When there isn't we |
409 need to generate implicit rules to build MIDL .idl files.""" | 427 need to generate implicit rules to build MIDL .idl files.""" |
410 for rule in spec.get('rules', []): | 428 for rule in spec.get('rules', []): |
411 if rule['extension'] == 'idl' and int(rule.get('msvs_external_rule', 0)): | 429 if rule['extension'] == 'idl' and int(rule.get('msvs_external_rule', 0)): |
412 return True | 430 return True |
413 return False | 431 return False |
414 | 432 |
415 def GetIdlBuildData(self, source, config): | 433 def GetIdlBuildData(self, source, config): |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
468 return vs.SetupScript() | 486 return vs.SetupScript() |
469 | 487 |
470 def ExpandMacros(string, expansions): | 488 def ExpandMacros(string, expansions): |
471 """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv | 489 """Expand $(Variable) per expansions dict. See MsvsSettings.GetVSMacroEnv |
472 for the canonical way to retrieve a suitable dict.""" | 490 for the canonical way to retrieve a suitable dict.""" |
473 if '$' in string: | 491 if '$' in string: |
474 for old, new in expansions.iteritems(): | 492 for old, new in expansions.iteritems(): |
475 assert '$(' not in new, new | 493 assert '$(' not in new, new |
476 string = string.replace(old, new) | 494 string = string.replace(old, new) |
477 return string | 495 return string |
OLD | NEW |