Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(75)

Side by Side Diff: pylib/gyp/msvs_emulation.py

Issue 10384100: ninja windows: support msvs_quote_cmd support (Closed) Base URL: http://git.chromium.org/external/gyp.git@master
Patch Set: add test for msvs_quote_cmd Created 8 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
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
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
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
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698