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 re | 10 import re |
| 11 import sys |
11 | 12 |
12 windows_quoter_regex = re.compile(r'(\\*)"') | 13 windows_quoter_regex = re.compile(r'(\\*)"') |
13 | 14 |
14 def QuoteCmdExeArgument(arg): | 15 def QuoteCmdExeArgument(arg): |
15 """Quote a command line argument so that it appears as one argument when | 16 """Quote a command line argument so that it appears as one argument when |
16 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for | 17 processed via cmd.exe and parsed by CommandLineToArgvW (as is typical for |
17 Windows programs).""" | 18 Windows programs).""" |
18 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment | 19 # See http://goo.gl/cuFbX and http://goo.gl/dhPnp including the comment |
19 # threads. This is actually the quoting rules for CommandLineToArgvW, not | 20 # threads. This is actually the quoting rules for CommandLineToArgvW, not |
20 # for the shell, because the shell doesn't do anything in Windows. This | 21 # for the shell, because the shell doesn't do anything in Windows. This |
(...skipping 13 matching lines...) Expand all Loading... |
34 # positional arguments. Also make sure to escape the % so that they're | 35 # positional arguments. Also make sure to escape the % so that they're |
35 # passed literally through escaping so they can be singled to just the | 36 # passed literally through escaping so they can be singled to just the |
36 # original %. Otherwise, trying to pass the literal representation that | 37 # original %. Otherwise, trying to pass the literal representation that |
37 # looks like an environment variable to the shell (e.g. %PATH%) would fail. | 38 # looks like an environment variable to the shell (e.g. %PATH%) would fail. |
38 tmp = tmp.replace('%', '^%^%') | 39 tmp = tmp.replace('%', '^%^%') |
39 | 40 |
40 # Finally, wrap the whole thing in quotes so that the above quote rule | 41 # Finally, wrap the whole thing in quotes so that the above quote rule |
41 # applies and whitespace isn't a word break. | 42 # applies and whitespace isn't a word break. |
42 return '"' + tmp + '"' | 43 return '"' + tmp + '"' |
43 | 44 |
| 45 |
44 def EncodeCmdExeList(args): | 46 def EncodeCmdExeList(args): |
45 """Process a list of arguments using QuoteCmdExeArgument.""" | 47 """Process a list of arguments using QuoteCmdExeArgument.""" |
46 return ' '.join(QuoteCmdExeArgument(arg) for arg in args) | 48 return ' '.join(QuoteCmdExeArgument(arg) for arg in args) |
| 49 |
| 50 |
| 51 def _GenericRetrieve(root, default, path): |
| 52 """Given a list of dictionary keys |path| and a tree of dicts |root|, find |
| 53 value at path, or return |default| if any of the path doesn't exist.""" |
| 54 if not root: |
| 55 return default |
| 56 if not path: |
| 57 return root |
| 58 return _GenericRetrieve(root.get(path[0]), default, path[1:]) |
| 59 |
| 60 |
| 61 def _AddPrefix(element, prefix): |
| 62 """Add |prefix| to |element| or each subelement if element is iterable.""" |
| 63 if element is None: |
| 64 return element |
| 65 # Note, not Iterable because we don't want to handle strings like that. |
| 66 if isinstance(element, list) or isinstance(element, tuple): |
| 67 return [prefix + e for e in element] |
| 68 else: |
| 69 return prefix + element |
| 70 |
| 71 |
| 72 def _DoRemapping(element, map): |
| 73 """If |element| then remap it through |map|. If |element| is iterable then |
| 74 each item will be remapped. Any elements not found will be removed.""" |
| 75 if map is not None and element is not None: |
| 76 if isinstance(element, list) or isinstance(element, tuple): |
| 77 element = filter(None, [map.get(elem) for elem in element]) |
| 78 else: |
| 79 element = map.get(element) |
| 80 return element |
| 81 |
| 82 |
| 83 def _AppendOrReturn(append, element): |
| 84 """If |append| is None, simply return |element|. If |append| is not None, |
| 85 then add |element| to it, adding each item in |element| if it's a list or |
| 86 tuple.""" |
| 87 if append is not None and element is not None: |
| 88 if isinstance(element, list) or isinstance(element, tuple): |
| 89 append.extend(element) |
| 90 else: |
| 91 append.append(element) |
| 92 else: |
| 93 return element |
| 94 |
| 95 |
| 96 class MsvsSettings(object): |
| 97 """A class that understands the gyp 'msvs_...' values (especially the |
| 98 msvs_settings field). They largely correpond to the VS2008 IDE DOM. This |
| 99 class helps map those settings to command line options.""" |
| 100 |
| 101 def __init__(self, spec): |
| 102 self.spec = spec |
| 103 self.configname = None |
| 104 |
| 105 supported_fields = [ |
| 106 'msvs_configuration_attributes', |
| 107 'msvs_settings', |
| 108 'msvs_system_include_dirs', |
| 109 'msvs_disabled_warnings', |
| 110 ] |
| 111 configs = spec['configurations'] |
| 112 for field in supported_fields: |
| 113 setattr(self, field, {}) |
| 114 for configname, config in configs.iteritems(): |
| 115 getattr(self, field)[configname] = config.get(field, {}) |
| 116 |
| 117 def _ConvertVSMacros(self, s): |
| 118 """Convert from VS macro names to something equivalent.""" |
| 119 if '$' in s: |
| 120 replacements = { |
| 121 # TODO(scottmg): obviously |
| 122 '$(VSInstallDir)': |
| 123 r'C:\Program Files (x86)\Microsoft Visual Studio 9.0\\', |
| 124 } |
| 125 for old, new in replacements.iteritems(): |
| 126 s = s.replace(old, new) |
| 127 return s |
| 128 |
| 129 def _GetAndMunge(self, field, path, default, prefix, append, map): |
| 130 """Retrieve a value from |field| at |path| or return |default|. If |
| 131 |append| is specified, and the item is found, it will be appended to that |
| 132 object instead of returned. If |map| is specified, results will be |
| 133 remapped through |map| before being returned or appended.""" |
| 134 result = _GenericRetrieve(field, default, path) |
| 135 result = _DoRemapping(result, map) |
| 136 result = _AddPrefix(result, prefix) |
| 137 return _AppendOrReturn(append, result) |
| 138 |
| 139 class _GetWrapper(object): |
| 140 def __init__(self, parent, field, base_path, append=None): |
| 141 self.parent = parent |
| 142 self.field = field |
| 143 self.base_path = [base_path] |
| 144 self.append = append |
| 145 def __call__(self, name, map=None, prefix=''): |
| 146 return self.parent._GetAndMunge(self.field, self.base_path + [name], |
| 147 default=None, prefix=prefix, append=self.append, map=map) |
| 148 |
| 149 def Setting(self, path, default=None, prefix='', append=None, map=None): |
| 150 """_GetAndMunge for msvs_settings.""" |
| 151 return self._GetAndMunge( |
| 152 self.msvs_settings[self.configname], path, default, prefix, append, map) |
| 153 |
| 154 def ConfigAttrib(self, path, default=None, prefix='', append=None, map=None): |
| 155 """_GetAndMunge for msvs_configuration_attributes.""" |
| 156 return self._GetAndMunge( |
| 157 self.msvs_configuration_attributes[self.configname], |
| 158 path, default, prefix, append, map) |
| 159 |
| 160 def GetSystemIncludes(self, config): |
| 161 """Returns the extra set of include paths that are used for the Windows |
| 162 SDK and similar.""" |
| 163 return [self._ConvertVSMacros(p) |
| 164 for p in self.msvs_system_include_dirs[config]] |
| 165 |
| 166 def GetComputedDefines(self, config): |
| 167 """Returns the set of defines that are injected to the defines list based |
| 168 on other VS settings.""" |
| 169 self.configname = config |
| 170 defines = [] |
| 171 if self.ConfigAttrib(['CharacterSet']) == '1': |
| 172 defines.extend(('_UNICODE', 'UNICODE')) |
| 173 if self.ConfigAttrib(['CharacterSet']) == '2': |
| 174 defines.append('_MBCS') |
| 175 defines.extend(self.Setting(('VCCLCompilerTool', 'PreprocessorDefinitions'), |
| 176 default=[])) |
| 177 self.configname = None |
| 178 return defines |
| 179 |
| 180 def GetCflags(self, config): |
| 181 """Returns the flags that need to be added to .c and .cc compilations.""" |
| 182 cflags = [] |
| 183 cflags.extend(['/wd' + w for w in self.msvs_disabled_warnings[config]]) |
| 184 cl = self._GetWrapper(self, self.msvs_settings[config], |
| 185 'VCCLCompilerTool', append=cflags) |
| 186 cl('Optimization', map={'0': 'd', '2': 's'}, prefix='/O') |
| 187 cl('InlineFunctionExpansion', prefix='/Ob') |
| 188 cl('OmitFramePointers', map={'false': '-', 'true': ''}, prefix='/Oy') |
| 189 cl('FavorSizeOrSpeed', map={'1': 's', '2': 't'}, prefix='/O') |
| 190 cl('WholeProgramOptimization', map={'true': '/GL'}) |
| 191 cl('WarningLevel', prefix='/W') |
| 192 cl('WarnAsError', map={'true': '/WX'}) |
| 193 cl('DebugInformationFormat', |
| 194 map={'1': '7', '3': 'i', '4': 'I'}, prefix='/Z') |
| 195 cl('RuntimeTypeInfo', map={'true': '/GR', 'false': '/GR-'}) |
| 196 cl('EnableFunctionLevelLinking', map={'true': '/Gy', 'false': '/Gy-'}) |
| 197 cl('MinimalRebuild', map={'true': '/Gm'}) |
| 198 cl('BufferSecurityCheck', map={'true': '/GS', 'false': '/GS-'}) |
| 199 cl('BasicRuntimeChecks', map={'1': 's', '2': 'u', '3': '1'}, prefix='/RTC') |
| 200 cl('RuntimeLibrary', |
| 201 map={'0': 'T', '1': 'Td', '2': 'D', '3': 'Dd'}, prefix='/M') |
| 202 cl('ExceptionHandling', map={'1': 'sc','2': 'a'}, prefix='/EH') |
| 203 cl('AdditionalOptions', prefix='') |
| 204 return cflags |
| 205 |
| 206 def GetCflagsC(self, config): |
| 207 """Returns the flags that need to be added to .c compilations.""" |
| 208 return [] |
| 209 |
| 210 def GetCflagsCC(self, config): |
| 211 """Returns the flags that need to be added to .cc compilations.""" |
| 212 return ['/TP'] |
| 213 |
| 214 def GetLdflags(self, config, product_dir, gyp_to_build_path): |
| 215 """Returns the flags that need to be added to link and lib commands.""" |
| 216 ldflags = [] |
| 217 ld = self._GetWrapper(self, self.msvs_settings[config], |
| 218 'VCLinkerTool', append=ldflags) |
| 219 ld('GenerateDebugInformation', map={'true': '/DEBUG'}) |
| 220 ld('TargetMachine', map={'1': 'X86', '17': 'X64'}, prefix='/MACHINE:') |
| 221 ld('AdditionalLibraryDirectories', prefix='/LIBPATH:') |
| 222 ld('DelayLoadDLLs', prefix='/DELAYLOAD:') |
| 223 ld('AdditionalOptions', prefix='') |
| 224 ld('SubSystem', map={'1': 'CONSOLE', '2': 'WINDOWS'}, prefix='/SUBSYSTEM:') |
| 225 ld('LinkIncremental', map={'1': ':NO', '2': ''}, prefix='/INCREMENTAL') |
| 226 ld('FixedBaseAddress', map={'1': ':NO', '2': ''}, prefix='/FIXED') |
| 227 ld('RandomizedBaseAddress', |
| 228 map={'1': ':NO', '2': ''}, prefix='/DYNAMICBASE') |
| 229 ld('DataExecutionPrevention', |
| 230 map={'1': ':NO', '2': ''}, prefix='/NXCOMPAT') |
| 231 ld('OptimizeReferences', map={'1': 'NOREF', '2': 'REF'}, prefix='/OPT:') |
| 232 ld('EnableCOMDATFolding', map={'1': 'NOICF', '2': 'ICF'}, prefix='/OPT:') |
| 233 ld('LinkTimeCodeGeneration', map={'1': '/LTCG'}) |
| 234 # TODO(scottmg): This should sort of be somewhere else (not really a flag). |
| 235 ld('AdditionalDependencies', prefix='') |
| 236 # TODO(scottmg): These too. |
| 237 ldflags.extend(('kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib', |
| 238 'comdlg32.lib', 'advapi32.lib', 'shell32.lib', 'ole32.lib', |
| 239 'oleaut32.lib', 'uuid.lib', 'odbc32.lib', 'odbccp32.lib', |
| 240 'DelayImp.lib')) |
| 241 return ldflags |
OLD | NEW |