Chromium Code Reviews| 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(field, default, path): | |
|
Nico
2012/02/27 16:23:30
Functions that are private to a class / module sho
scottmg
2012/02/28 00:01:13
Done.
| |
| 52 """Walk down a tree of dicts starting from |field|, returning |default| if | |
|
Nico
2012/02/27 16:23:30
s/field/root/?
I found the comment confusing unti
scottmg
2012/02/28 00:01:13
Done.
| |
| 53 any part of the path is not found along the way.""" | |
| 54 cur = field | |
|
Nico
2012/02/27 16:23:30
I'd just assign to the parameter variable directly
scottmg
2012/02/28 00:01:13
Done.
| |
| 55 for p in path: | |
| 56 if not cur: | |
| 57 return default | |
| 58 cur = cur.get(p) | |
| 59 if cur == None: | |
|
Nico
2012/02/27 16:23:30
http://jaredgrubb.blogspot.com/2009/04/python-is-n
scottmg
2012/02/28 00:01:13
Done.
| |
| 60 return default | |
| 61 return cur | |
|
Nico
2012/02/27 16:23:30
…could this maybe be more compact recursively?
i
scottmg
2012/02/28 00:01:13
Hmm, shrug? Sure. Done.
| |
| 62 | |
| 63 | |
| 64 def AddPrefix(element, prefix): | |
| 65 """Add prefix to element or each subelement if element is iterable.""" | |
|
Nico
2012/02/27 16:23:30
s/prefix/|prefix|/
s/element/|element|/
scottmg
2012/02/28 00:01:13
Done.
| |
| 66 if element == None: | |
|
Nico
2012/02/27 16:23:30
is None
scottmg
2012/02/28 00:01:13
Done.
| |
| 67 return element | |
| 68 # Note, not Iterable because we don't want to handle strings like that. | |
| 69 if isinstance(element, list) or isinstance(element, tuple): | |
| 70 return [prefix + e for e in element] | |
| 71 else: | |
| 72 return prefix + element | |
| 73 | |
| 74 | |
| 75 def DoRemapping(element, map): | |
| 76 """If |element| then remap it through |map|. If |element| is iterable then | |
| 77 each item will be remapped. Any elements not found will be removed.""" | |
| 78 if map != None and element != None: | |
|
Nico
2012/02/27 16:23:30
is not None
scottmg
2012/02/28 00:01:13
Done.
| |
| 79 if isinstance(element, list) or isinstance(element, tuple): | |
| 80 element = [map.get(elem) for elem in element] | |
| 81 element = [elem for elem in element if elem != None] | |
| 82 else: | |
| 83 element = map.get(element) | |
| 84 return element | |
| 85 | |
| 86 | |
| 87 def AppendOrReturn(append, element): | |
| 88 if append != None and element != None: | |
| 89 if isinstance(element, list) or isinstance(element, tuple): | |
| 90 append.extend(element) | |
| 91 else: | |
| 92 append.append(element) | |
| 93 else: | |
| 94 return element | |
| 95 | |
| 96 | |
| 97 class MsvsSettings(object): | |
| 98 """A class that understands the gyp 'msvs_...' values (especially the | |
| 99 msvs_settings field). They largely correpond to the VS2008 IDE DOM. This | |
| 100 class helps map those settings to command line options.""" | |
| 101 | |
| 102 def __init__(self, spec): | |
| 103 self.spec = spec | |
| 104 self.configname = None | |
| 105 | |
| 106 supported_fields = [ | |
| 107 'msvs_configuration_attributes', | |
| 108 'msvs_settings', | |
| 109 'msvs_system_include_dirs', | |
| 110 'msvs_disabled_warnings', | |
| 111 ] | |
| 112 configs = spec['configurations'] | |
| 113 for field in supported_fields: | |
| 114 setattr(self, field, {}) | |
| 115 for configname, config in configs.iteritems(): | |
| 116 getattr(self, field)[configname] = config.get(field, {}) | |
| 117 | |
| 118 def ConvertVSMacros(self, s): | |
| 119 """Convert from VS macro names to something equivalent.""" | |
| 120 if '$' in s: | |
| 121 replacements = { | |
| 122 # TODO(scottmg): obviously | |
| 123 '$(VSInstallDir)': | |
| 124 r'C:\Program Files (x86)\Microsoft Visual Studio 9.0\\', | |
|
Nico
2012/02/27 16:23:30
Does this intentionally have two trailing \?
scottmg
2012/02/28 00:01:13
Vim syntax-highlighting made me do it (I think \'
| |
| 125 } | |
| 126 for old, new in replacements.iteritems(): | |
| 127 s = s.replace(old, new) | |
| 128 return s | |
| 129 | |
| 130 def GetAndMunge(self, field, path, default, prefix, append, map): | |
| 131 """Retrieve a value from |field| at |path| or return |default|. | |
| 132 Uses self.configname. If |append| is specified, and the item is found, | |
|
Nico
2012/02/27 16:23:30
Looks like this doesn't use self.configname
scottmg
2012/02/28 00:01:13
Done.
| |
| 133 it will be appended to that object instead of returned. If |map| is | |
| 134 specified, results will be remapped through |map| before being returned | |
| 135 or appended.""" | |
| 136 result = GenericRetrieve(field, default, path) | |
| 137 result = DoRemapping(result, map) | |
| 138 result = AddPrefix(result, prefix) | |
| 139 return AppendOrReturn(append, result) | |
| 140 | |
| 141 class GetWrapper(object): | |
| 142 def __init__(self, parent, field, base_path, append=None): | |
|
Nico
2012/02/27 16:23:30
It looks like base_path is always 1 element long.
scottmg
2012/02/28 00:01:13
The __call__ retrieve uses root + passed-in as a 2
| |
| 143 self.parent = parent | |
| 144 self.field = field | |
| 145 self.base_path = list(base_path) | |
| 146 self.append = append | |
| 147 def __call__(self, name, map=None, prefix=''): | |
| 148 return self.parent.GetAndMunge(self.field, self.base_path + [name], | |
| 149 default=None, prefix=prefix, append=self.append, map=map) | |
| 150 | |
| 151 def Setting(self, path, default=None, prefix='', append=None, map=None): | |
| 152 return self.GetAndMunge( | |
| 153 self.msvs_settings[self.configname], path, default, prefix, append, map) | |
| 154 | |
| 155 def ConfigAttrib(self, path, default=None, prefix='', append=None, map=None): | |
| 156 """GetAndMunge for msvs_configuration_attributes.""" | |
| 157 return self.GetAndMunge( | |
| 158 self.msvs_configuration_attributes[self.configname], | |
| 159 path, default, prefix, append, map) | |
| 160 | |
| 161 def GetSystemIncludes(self, config): | |
| 162 """Returns the extra set of include paths that are used for the Windows | |
| 163 SDK and similar.""" | |
| 164 return [self.ConvertVSMacros(p) | |
| 165 for p in self.msvs_system_include_dirs[config]] | |
| 166 | |
| 167 def GetComputedDefines(self, config): | |
| 168 """Returns the set of defines that are injected to the defines list based | |
| 169 on other VS settings.""" | |
| 170 self.configname = config | |
| 171 defines = [] | |
| 172 if self.ConfigAttrib(['CharacterSet']) == '1': | |
| 173 defines.extend(('_UNICODE', 'UNICODE')) | |
| 174 if self.ConfigAttrib(['CharacterSet']) == '2': | |
| 175 defines.append('_MBCS') | |
| 176 defines.extend(self.Setting(('VCCLCompilerTool', 'PreprocessorDefinitions'), | |
| 177 default=[])) | |
| 178 self.configname = None | |
| 179 return defines | |
| 180 | |
| 181 def GetCflags(self, config): | |
| 182 """Returns the flags that need to be added to .c and .cc compilations.""" | |
| 183 cflags = [] | |
| 184 cflags.extend(['$!/wd' + w for w in self.msvs_disabled_warnings[config]]) | |
| 185 cl = self.GetWrapper(self, self.msvs_settings[config], | |
| 186 ('VCCLCompilerTool',), cflags) | |
|
Nico
2012/02/27 16:23:30
Naming the last parameter ("append=cflags") might
scottmg
2012/02/28 00:01:13
Done.
| |
| 187 cl('Optimization', map={'0':'d', '2':'s'}, prefix='$!/O') | |
| 188 cl('InlineFunctionExpansion', prefix='$!/Ob') | |
| 189 cl('OmitFramePointers', map={'false':'-', 'true':''}, prefix='$!/Oy') | |
| 190 cl('FavorSizeOrSpeed', map={'1':'s', '2':'t'}, prefix='$!/O') | |
| 191 cl('WholeProgramOptimization', map={'true':'$!/GL'}) | |
| 192 cl('WarningLevel', prefix='$!/W') | |
| 193 cl('WarnAsError', map={'true':'$!/WX'}) | |
| 194 cl('DebugInformationFormat', 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',), 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 |