Support Jar manifest files and the -C option. Tool/ms*.py fixes for pre-Python 2...
[scons.git] / src / engine / SCons / Tool / msvc.py
1 """engine.SCons.Tool.msvc
2
3 Tool-specific initialization for Microsoft Visual C/C++.
4
5 There normally shouldn't be any need to import this module directly.
6 It will usually be imported through the generic SCons.Tool.Tool()
7 selection method.
8
9 """
10
11 #
12 # __COPYRIGHT__
13 #
14 # Permission is hereby granted, free of charge, to any person obtaining
15 # a copy of this software and associated documentation files (the
16 # "Software"), to deal in the Software without restriction, including
17 # without limitation the rights to use, copy, modify, merge, publish,
18 # distribute, sublicense, and/or sell copies of the Software, and to
19 # permit persons to whom the Software is furnished to do so, subject to
20 # the following conditions:
21 #
22 # The above copyright notice and this permission notice shall be included
23 # in all copies or substantial portions of the Software.
24 #
25 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
26 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
27 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 #
33
34 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
35
36 import os.path
37 import string
38 import types
39 import re
40
41 import SCons.Action
42 import SCons.Tool
43 import SCons.Errors
44 import SCons.Warnings
45 import SCons.Builder
46 import SCons.Util
47 import SCons.Platform.win32
48 import SCons.Tool.msvs
49
50 CSuffixes = ['.c', '.C']
51 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
52
53 def _parse_msvc7_overrides(version):
54     """ Parse any overridden defaults for MSVS directory locations in MSVS .NET. """
55     
56     # First, we get the shell folder for this user:
57     if not SCons.Util.can_read_reg:
58         raise SCons.Errors.InternalError, "No Windows registry module was found"
59
60     comps = ""
61     try:
62         (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER,
63                                             r'Software\Microsoft\Windows\CurrentVersion' +\
64                                             r'\Explorer\Shell Folders\Local AppData')
65     except SCons.Util.RegError:
66         raise SCons.Errors.InternalError, "The Local AppData directory was not found in the registry."
67
68     comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VSComponents.dat'
69     dirs = {}
70
71     if os.path.exists(comps):
72         # now we parse the directories from this file, if it exists.
73         # We only look for entries after: [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories],
74         # since this file could contain a number of things...
75         f = open(comps,'r')
76         line = f.readline()
77         found = 0
78         while line:
79             line.strip()
80             if found == 1:
81                 (key, val) = line.split('=',1)
82                 key = key.replace(' Dirs','')
83                 dirs[key.upper()] = val
84             if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0:
85                 found = 1
86             if line == '':
87                 found = 0
88             line = f.readline()
89         f.close()
90     else:
91         # since the file didn't exist, we have only the defaults in
92         # the registry to work with.
93         try:
94             K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version
95             K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories'
96             k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K)
97             i = 0
98             while 1:
99                 try:
100                     (key,val,t) = SCons.Util.RegEnumValue(k,i)
101                     key = key.replace(' Dirs','')
102                     dirs[key.upper()] = val
103                     i = i + 1
104                 except SCons.Util.RegError:
105                     break
106         except SCons.Util.RegError:
107             # if we got here, then we didn't find the registry entries:
108             raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry."
109     return dirs
110
111 def _get_msvc7_path(path, version, platform):
112     """
113     Get Visual Studio directories from version 7 (MSVS .NET)
114     (it has a different registry structure than versions before it)
115     """
116     # first, look for a customization of the default values in the
117     # registry: These are sometimes stored in the Local Settings area
118     # for Visual Studio, in a file, so we have to parse it.
119     dirs = _parse_msvc7_overrides(version)
120
121     if dirs.has_key(path):
122         p = dirs[path]
123     else:
124         raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path
125
126     # collect some useful information for later expansions...
127     paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
128
129     # expand the directory path variables that we support.  If there
130     # is a variable we don't support, then replace that entry with
131     # "---Unknown Location VSInstallDir---" or something similar, to clue
132     # people in that we didn't find something, and so env expansion doesn't
133     # do weird things with the $(xxx)'s
134     s = re.compile('\$\(([a-zA-Z0-9_]+?)\)')
135
136     def repl(match):
137         key = string.upper(match.group(1))
138         if paths.has_key(key):
139             return paths[key]
140         else:
141             return '---Unknown Location %s---' % match.group()
142
143     rv = []
144     for entry in p.split(os.pathsep):
145         entry = s.sub(repl,entry)
146         rv.append(entry)
147
148     return string.join(rv,os.pathsep)
149
150 def get_msvc_path (path, version, platform='x86'):
151     """
152     Get a list of visualstudio directories (include, lib or path).  Return
153     a string delimited by ';'. An exception will be raised if unable to
154     access the registry or appropriate registry keys not found.
155     """
156
157     if not SCons.Util.can_read_reg:
158         raise SCons.Errors.InternalError, "No Windows registry module was found"
159
160     # normalize the case for comparisons (since the registry is case
161     # insensitive)
162     path = string.upper(path)
163
164     if path=='LIB':
165         path= 'LIBRARY'
166
167     if float(version) >= 7.0:
168         return _get_msvc7_path(path, version, platform)
169
170     path = string.upper(path + ' Dirs')
171     K = ('Software\\Microsoft\\Devstudio\\%s\\' +
172          'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \
173         (version,platform)
174     for base in (SCons.Util.HKEY_CURRENT_USER,
175                  SCons.Util.HKEY_LOCAL_MACHINE):
176         try:
177             k = SCons.Util.RegOpenKeyEx(base,K)
178             i = 0
179             while 1:
180                 try:
181                     (p,v,t) = SCons.Util.RegEnumValue(k,i)
182                     if string.upper(p) == path:
183                         return v
184                     i = i + 1
185                 except SCons.Util.RegError:
186                     break
187         except SCons.Util.RegError:
188             pass
189
190     # if we got here, then we didn't find the registry entries:
191     raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path
192
193 def _get_msvc6_default_paths(version):
194     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
195     three environment variables that should be set in order to execute
196     the MSVC 6.0 tools properly, if the information wasn't available
197     from the registry."""
198     MVSdir = None
199     paths = {}
200     exe_path = ''
201     lib_path = ''
202     include_path = ''
203     try:
204         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
205         MVSdir = paths['VSINSTALLDIR']
206     except (SCons.Util.RegError, SCons.Errors.InternalError, KeyError):
207         if os.environ.has_key('MSDEVDIR'):
208             MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..'))
209         else:
210             MVSdir = r'C:\Program Files\Microsoft Visual Studio'
211     if MVSdir:
212         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
213             MVSVCdir = paths['VCINSTALLDIR']
214         else:
215             MVSVCdir = os.path.join(MVSdir,'VC98')
216
217         MVSCommondir = r'%s\Common' % MVSdir
218         include_path = r'%s\ATL\include;%s\MFC\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
219         lib_path = r'%s\MFC\lib;%s\lib' % (MVSVCdir, MVSVCdir)
220         exe_path = r'%s\MSDev98\bin;%s\bin' % (MVSCommondir, MVSVCdir)
221     return (include_path, lib_path, exe_path)
222
223 def _get_msvc7_default_paths(version):
224     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
225     three environment variables that should be set in order to execute
226     the MSVC .NET tools properly, if the information wasn't available
227     from the registry."""
228             
229     MVSdir = None
230     paths = {}
231     exe_path = ''
232     lib_path = ''
233     include_path = ''
234     try:
235         paths = SCons.Tool.msvs.get_msvs_install_dirs(version)
236         MVSdir = paths['VSINSTALLDIR']
237     except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError):
238         if os.environ.has_key('VSCOMNTOOLS'):
239             MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..'))
240         else:
241             # last resort -- default install location
242             MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET'
243
244     if not MVSdir:
245         if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'):
246             MVSVCdir = paths['VCINSTALLDIR']
247         else:
248             MVSVCdir = os.path.join(MVSdir,'Vc7')
249
250         MVSCommondir = r'%s\Common7' % MVSdir
251         include_path = r'%s\atlmfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir)
252         lib_path = r'%s\atlmfc\lib;%s\lib' % (MVSVCdir, MVSVCdir)
253         exe_path = r'%s\Tools\bin;%s\Tools;%s\bin' % (MVSCommondir, MVSCommondir, MVSVCdir)
254     return (include_path, lib_path, exe_path)
255
256 def get_msvc_paths(version=None):
257     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values
258     of those three environment variables that should be set
259     in order to execute the MSVC tools properly."""
260     exe_path = ''
261     lib_path = ''
262     include_path = ''
263
264     if not version and not SCons.Util.can_read_reg:
265         version = '6.0'
266             
267     try:
268         if not version:
269             version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
270
271         include_path = get_msvc_path("include", version)
272         lib_path = get_msvc_path("lib", version)
273         exe_path = get_msvc_path("path", version)
274
275     except (SCons.Util.RegError, SCons.Errors.InternalError):
276         # Could not get all the configured directories from the
277         # registry.  However, some of the configured directories only
278         # appear if the user changes them from the default.
279         # Therefore, we'll see if we can get the path to the MSDev
280         # base installation from the registry and deduce the default
281         # directories.
282         if float(version) >= 7.0:
283             return _get_msvc7_default_paths(version)
284         else:
285             return _get_msvc6_default_paths(version)
286             
287     return (include_path, lib_path, exe_path)
288
289 def get_msvc_default_paths(version = None):
290     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
291     three environment variables that should be set in order to execute
292     the MSVC tools properly.  This will only return the default
293     locations for the tools, not the values used by MSVS in their
294     directory setup area.  This can help avoid problems with different
295     developers having different settings, and should allow the tools
296     to run in most cases."""
297
298     if not version and not SCons.Util.can_read_reg:
299         version = '6.0'
300
301     try:
302         if not version:
303             version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
304     except:
305         pass
306
307     if float(version) >= 7.0:
308         return _get_msvc7_default_paths(version)
309     else:
310         return _get_msvc6_default_paths(version)
311
312 def validate_vars(env):
313     """Validate the PDB, PCH, and PCHSTOP construction variables."""
314     if env.has_key('PCH') and env['PCH']:
315         if not env.has_key('PCHSTOP'):
316             raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
317         if not SCons.Util.is_String(env['PCHSTOP']):
318             raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
319
320 def pch_emitter(target, source, env):
321     """Sets up the PDB dependencies for a pch file, and adds the object
322     file target."""
323
324     validate_vars(env)
325
326     pch = None
327     obj = None
328
329     for t in target:
330         if SCons.Util.splitext(str(t))[1] == '.pch':
331             pch = t
332         if SCons.Util.splitext(str(t))[1] == '.obj':
333             obj = t
334
335     if not obj:
336         obj = SCons.Util.splitext(str(pch))[0]+'.obj'
337
338     target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
339
340     if env.has_key('PDB') and env['PDB']:
341         env.SideEffect(env['PDB'], target)
342         env.Precious(env['PDB'])
343
344     return (target, source)
345
346 def object_emitter(target, source, env):
347     """Sets up the PDB and PCH dependencies for an object file."""
348
349     validate_vars(env)
350
351     if env.has_key('PDB') and env['PDB']:
352         env.SideEffect(env['PDB'], target)
353         env.Precious(env['PDB'])
354
355     if env.has_key('PCH') and env['PCH']:
356         env.Depends(target, env['PCH'])
357
358     return (target, source)
359
360 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
361 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res')
362
363 def generate(env):
364     """Add Builders and construction variables for MSVC++ to an Environment."""
365     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
366
367     for suffix in CSuffixes:
368         static_obj.add_action(suffix, SCons.Defaults.CAction)
369         shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
370
371     for suffix in CXXSuffixes:
372         static_obj.add_action(suffix, SCons.Defaults.CXXAction)
373         shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
374
375     env['CCPDBFLAGS'] = '${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}'
376     env['CCPCHFLAGS'] = '${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'
377     env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
378     env['CC']         = 'cl'
379     env['CCFLAGS']    = '/nologo'
380     env['CCCOM']      = '$CC $CCFLAGS $CCCOMFLAGS' 
381     env['SHCC']       = '$CC'
382     env['SHCCFLAGS']  = '$CCFLAGS'
383     env['SHCCCOM']    = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
384     env['CXX']        = '$CC'
385     env['CXXFLAGS']   = '$CCFLAGS $( /TP $)'
386     env['CXXCOM']     = '$CXX $CXXFLAGS $CCCOMFLAGS'
387     env['SHCXX']      = '$CXX'
388     env['SHCXXFLAGS'] = '$CXXFLAGS'
389     env['SHCXXCOM']   = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
390     env['CPPDEFPREFIX']  = '/D'
391     env['CPPDEFSUFFIX']  = ''
392     env['INCPREFIX']  = '/I'
393     env['INCSUFFIX']  = ''
394     env['OBJEMITTER'] = object_emitter
395     env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
396
397     env['RC'] = 'rc'
398     env['RCFLAGS'] = ''
399     env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
400     CScan = env.get_scanner('.c')
401     if CScan:
402         CScan.add_skey('.rc')
403     env['BUILDERS']['RES'] = res_builder
404
405     try:
406         version = SCons.Tool.msvs.get_default_visualstudio_version(env)
407
408         if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
409             include_path, lib_path, exe_path = get_msvc_default_paths(version)
410         else:
411             include_path, lib_path, exe_path = get_msvc_paths(version)
412
413         # since other tools can set these, we just make sure that the
414         # relevant stuff from MSVS is in there somewhere.
415         env.PrependENVPath('INCLUDE', include_path)
416         env.PrependENVPath('LIB', lib_path)
417         env.PrependENVPath('PATH', exe_path)
418     except (SCons.Util.RegError, SCons.Errors.InternalError):
419         pass
420
421     env['CFILESUFFIX'] = '.c'
422     env['CXXFILESUFFIX'] = '.cc'
423
424     env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
425     env['BUILDERS']['PCH'] = pch_builder
426
427 def exists(env):
428     try:
429         v = SCons.Tool.msvs.get_visualstudio_versions()
430     except (SCons.Util.RegError, SCons.Errors.InternalError):
431         pass
432     
433     if not v:
434         return env.Detect('cl')
435     else:
436         # there's at least one version of MSVS installed.
437         return 1