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): | |
Nico
2012/02/29 19:44:18
I think this would become simpler if you cribbed t
scottmg
2012/03/01 18:09:04
I tried that.. but it loses the listness-or-not of
| |
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 = [map.get(elem) for elem in element] | |
78 element = [elem for elem in element if elem is not None] | |
Nico
2012/02/29 19:44:18
element = filter(none, [map.get(elem) for elem in
scottmg
2012/03/01 18:09:04
Done.
| |
79 else: | |
80 element = map.get(element) | |
81 return element | |
82 | |
83 | |
84 def _AppendOrReturn(append, element): | |
Nico
2012/02/29 19:44:18
Docstring
scottmg
2012/03/01 18:09:04
Done.
| |
85 if append is not None and element is not None: | |
86 if isinstance(element, list) or isinstance(element, tuple): | |
87 append.extend(element) | |
88 else: | |
89 append.append(element) | |
90 else: | |
91 return element | |
92 | |
93 | |
94 class MsvsSettings(object): | |
95 """A class that understands the gyp 'msvs_...' values (especially the | |
96 msvs_settings field). They largely correpond to the VS2008 IDE DOM. This | |
97 class helps map those settings to command line options.""" | |
98 | |
99 def __init__(self, spec): | |
100 self.spec = spec | |
101 self.configname = None | |
102 | |
103 supported_fields = [ | |
104 'msvs_configuration_attributes', | |
105 'msvs_settings', | |
106 'msvs_system_include_dirs', | |
107 'msvs_disabled_warnings', | |
108 ] | |
109 configs = spec['configurations'] | |
110 for field in supported_fields: | |
111 setattr(self, field, {}) | |
112 for configname, config in configs.iteritems(): | |
113 getattr(self, field)[configname] = config.get(field, {}) | |
114 | |
115 def ConvertVSMacros(self, s): | |
Nico
2012/02/29 19:44:18
Private function?
scottmg
2012/03/01 18:09:04
Done.
| |
116 """Convert from VS macro names to something equivalent.""" | |
117 if '$' in s: | |
118 replacements = { | |
119 # TODO(scottmg): obviously | |
120 '$(VSInstallDir)': | |
121 r'C:\Program Files (x86)\Microsoft Visual Studio 9.0\\', | |
122 } | |
123 for old, new in replacements.iteritems(): | |
124 s = s.replace(old, new) | |
125 return s | |
126 | |
127 def GetAndMunge(self, field, path, default, prefix, append, map): | |
Nico
2012/02/29 19:44:18
Private function?
scottmg
2012/03/01 18:09:04
Done.
| |
128 """Retrieve a value from |field| at |path| or return |default|. If | |
129 |append| is specified, and the item is found, it will be appended to that | |
130 object instead of returned. If |map| is specified, results will be | |
131 remapped through |map| before being returned or appended.""" | |
132 result = _GenericRetrieve(field, default, path) | |
133 result = _DoRemapping(result, map) | |
134 result = _AddPrefix(result, prefix) | |
135 return _AppendOrReturn(append, result) | |
136 | |
137 class GetWrapper(object): | |
Nico
2012/02/29 19:44:18
Private nested class?
scottmg
2012/03/01 18:09:04
Done.
| |
138 def __init__(self, parent, field, base_path, append=None): | |
139 self.parent = parent | |
140 self.field = field | |
141 self.base_path = [base_path] | |
142 self.append = append | |
143 def __call__(self, name, map=None, prefix=''): | |
144 return self.parent.GetAndMunge(self.field, self.base_path + [name], | |
145 default=None, prefix=prefix, append=self.append, map=map) | |
146 | |
147 def Setting(self, path, default=None, prefix='', append=None, map=None): | |
148 return self.GetAndMunge( | |
149 self.msvs_settings[self.configname], path, default, prefix, append, map) | |
150 | |
151 def ConfigAttrib(self, path, default=None, prefix='', append=None, map=None): | |
152 """GetAndMunge for msvs_configuration_attributes.""" | |
153 return self.GetAndMunge( | |
154 self.msvs_configuration_attributes[self.configname], | |
155 path, default, prefix, append, map) | |
156 | |
157 def GetSystemIncludes(self, config): | |
158 """Returns the extra set of include paths that are used for the Windows | |
159 SDK and similar.""" | |
160 return [self.ConvertVSMacros(p) | |
161 for p in self.msvs_system_include_dirs[config]] | |
162 | |
163 def GetComputedDefines(self, config): | |
164 """Returns the set of defines that are injected to the defines list based | |
165 on other VS settings.""" | |
166 self.configname = config | |
167 defines = [] | |
168 if self.ConfigAttrib(['CharacterSet']) == '1': | |
169 defines.extend(('_UNICODE', 'UNICODE')) | |
170 if self.ConfigAttrib(['CharacterSet']) == '2': | |
171 defines.append('_MBCS') | |
172 defines.extend(self.Setting(('VCCLCompilerTool', 'PreprocessorDefinitions'), | |
173 default=[])) | |
174 self.configname = None | |
175 return defines | |
176 | |
177 def GetCflags(self, config): | |
178 """Returns the flags that need to be added to .c and .cc compilations.""" | |
179 cflags = [] | |
180 cflags.extend(['$!/wd' + w for w in self.msvs_disabled_warnings[config]]) | |
181 cl = self.GetWrapper(self, self.msvs_settings[config], | |
182 'VCCLCompilerTool', append=cflags) | |
183 cl('Optimization', map={'0':'d', '2':'s'}, prefix='$!/O') | |
184 cl('InlineFunctionExpansion', prefix='$!/Ob') | |
185 cl('OmitFramePointers', map={'false':'-', 'true':''}, prefix='$!/Oy') | |
186 cl('FavorSizeOrSpeed', map={'1':'s', '2':'t'}, prefix='$!/O') | |
187 cl('WholeProgramOptimization', map={'true':'$!/GL'}) | |
188 cl('WarningLevel', prefix='$!/W') | |
189 cl('WarnAsError', map={'true':'$!/WX'}) | |
190 cl('DebugInformationFormat', map={'1':'7', '3':'i', '4':'I'}, prefix='$!/Z') | |
191 cl('RuntimeTypeInfo', map={'true':'$!/GR', 'false':'$!/GR-'}) | |
192 cl('EnableFunctionLevelLinking', map={'true':'$!/Gy', 'false':'$!/Gy-'}) | |
193 cl('MinimalRebuild', map={'true':'$!/Gm'}) | |
194 cl('BufferSecurityCheck', map={'true':'$!/GS', 'false':'$!/GS-'}) | |
195 cl('BasicRuntimeChecks', map={'1':'s', '2':'u', '3':'1'}, prefix='$!/RTC') | |
196 cl('RuntimeLibrary', | |
197 map={'0':'T', '1':'Td', '2':'D', '3':'Dd'}, prefix='$!/M') | |
198 cl('ExceptionHandling', map={'1':'sc','2':'a'}, prefix='$!/EH') | |
199 cl('AdditionalOptions', prefix='$!') | |
200 return cflags | |
201 | |
202 def GetCflagsC(self, config): | |
203 """Returns the flags that need to be added to .c compilations.""" | |
204 return [] | |
205 | |
206 def GetCflagsCC(self, config): | |
207 """Returns the flags that need to be added to .cc compilations.""" | |
208 return ['$!/TP'] | |
209 | |
210 def GetLdflags(self, config, product_dir, gyp_to_build_path): | |
211 """Returns the flags that need to be added to link and lib commands.""" | |
212 ldflags = [] | |
213 ld = self.GetWrapper(self, self.msvs_settings[config], | |
214 'VCLinkerTool', append=ldflags) | |
215 ld('GenerateDebugInformation', map={'true':'$!/DEBUG'}) | |
216 ld('TargetMachine', map={'1':'X86', '17':'X64'}, prefix='$!/MACHINE:') | |
217 ld('AdditionalLibraryDirectories', prefix='$!/LIBPATH:') | |
218 ld('DelayLoadDLLs', prefix='$!/DELAYLOAD:') | |
219 ld('AdditionalOptions', prefix='$!') | |
220 ld('SubSystem', map={'1':'CONSOLE', '2':'WINDOWS'}, prefix='$!/SUBSYSTEM:') | |
221 ld('LinkIncremental', map={'1':':NO', '2':''}, prefix='$!/INCREMENTAL') | |
222 ld('FixedBaseAddress', map={'1':':NO', '2':''}, prefix='$!/FIXED') | |
223 ld('RandomizedBaseAddress', | |
224 map={'1':':NO', '2':''}, prefix='$!/DYNAMICBASE') | |
225 ld('DataExecutionPrevention', | |
226 map={'1':':NO', '2':''}, prefix='$!/NXCOMPAT') | |
227 ld('OptimizeReferences', map={'1':'NOREF', '2':'REF'}, prefix='$!/OPT:') | |
228 ld('EnableCOMDATFolding', map={'1':'NOICF', '2':'ICF'}, prefix='$!/OPT:') | |
229 ld('LinkTimeCodeGeneration', map={'1':'$!/LTCG'}) | |
230 # TODO(scottmg): This should sort of be somewhere else (not really a flag). | |
231 ld('AdditionalDependencies', prefix='$!') | |
232 # TODO(scottmg): These too. | |
233 ldflags.extend(('kernel32.lib', 'user32.lib', 'gdi32.lib', 'winspool.lib', | |
234 'comdlg32.lib', 'advapi32.lib', 'shell32.lib', 'ole32.lib', | |
235 'oleaut32.lib', 'uuid.lib', 'odbc32.lib', 'odbccp32.lib', | |
236 'DelayImp.lib')) | |
237 return ldflags | |
OLD | NEW |