Make the static/shared object check work even if the object was already created.
[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, paths=paths):
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:
265         versions = SCons.Tool.msvs.get_visualstudio_versions()
266         if versions:
267             version = versions[0] #use highest version by default
268         else:
269             version = '6.0'
270
271     # Some of the configured directories only
272     # appear if the user changes them from the default.
273     # Therefore, we'll see if we can get the path to the MSDev
274     # base installation from the registry and deduce the default
275     # directories.
276     if float(version) >= 7.0:
277         defpaths = _get_msvc7_default_paths(version)
278     else:
279         defpaths = _get_msvc6_default_paths(version)
280     
281     try:
282         include_path = get_msvc_path("include", version)
283     except (SCons.Util.RegError, SCons.Errors.InternalError):
284         include_path = defpaths[0]
285
286     try:
287         lib_path = get_msvc_path("lib", version)
288     except (SCons.Util.RegError, SCons.Errors.InternalError):
289         lib_path = defpaths[1]
290
291     try:
292         exe_path = get_msvc_path("path", version)
293     except (SCons.Util.RegError, SCons.Errors.InternalError):
294         exe_path = defpaths[2]
295     
296     return (include_path, lib_path, exe_path)
297
298 def get_msvc_default_paths(version = None):
299     """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those
300     three environment variables that should be set in order to execute
301     the MSVC tools properly.  This will only return the default
302     locations for the tools, not the values used by MSVS in their
303     directory setup area.  This can help avoid problems with different
304     developers having different settings, and should allow the tools
305     to run in most cases."""
306
307     if not version and not SCons.Util.can_read_reg:
308         version = '6.0'
309
310     try:
311         if not version:
312             version = SCons.Tool.msvs.get_visualstudio_versions()[0] #use highest version
313     except KeyboardInterrupt:
314         raise
315     except:
316         pass
317
318     if float(version) >= 7.0:
319         return _get_msvc7_default_paths(version)
320     else:
321         return _get_msvc6_default_paths(version)
322
323 def validate_vars(env):
324     """Validate the PDB, PCH, and PCHSTOP construction variables."""
325     if env.has_key('PCH') and env['PCH']:
326         if not env.has_key('PCHSTOP'):
327             raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
328         if not SCons.Util.is_String(env['PCHSTOP']):
329             raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
330
331 def pch_emitter(target, source, env):
332     """Sets up the PDB dependencies for a pch file, and adds the object
333     file target."""
334
335     validate_vars(env)
336
337     pch = None
338     obj = None
339
340     for t in target:
341         if SCons.Util.splitext(str(t))[1] == '.pch':
342             pch = t
343         if SCons.Util.splitext(str(t))[1] == '.obj':
344             obj = t
345
346     if not obj:
347         obj = SCons.Util.splitext(str(pch))[0]+'.obj'
348
349     target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
350
351     if env.has_key('PDB') and env['PDB']:
352         env.SideEffect(env['PDB'], target)
353         env.Precious(env['PDB'])
354
355     return (target, source)
356
357 def object_emitter(target, source, env, parent_emitter):
358     """Sets up the PDB and PCH dependencies for an object file."""
359
360     validate_vars(env)
361
362     parent_emitter(target, source, env)
363
364     if env.has_key('PDB') and env['PDB']:
365         env.SideEffect(env['PDB'], target)
366         env.Precious(env['PDB'])
367
368     if env.has_key('PCH') and env['PCH']:
369         env.Depends(target, env['PCH'])
370
371     return (target, source)
372
373 def static_object_emitter(target, source, env):
374     return object_emitter(target, source, env,
375                           SCons.Defaults.StaticObjectEmitter)
376
377 def shared_object_emitter(target, source, env):
378     return object_emitter(target, source, env,
379                           SCons.Defaults.SharedObjectEmitter)
380
381 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
382 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res')
383
384 def generate(env):
385     """Add Builders and construction variables for MSVC++ to an Environment."""
386     static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
387
388     for suffix in CSuffixes:
389         static_obj.add_action(suffix, SCons.Defaults.CAction)
390         shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
391
392     for suffix in CXXSuffixes:
393         static_obj.add_action(suffix, SCons.Defaults.CXXAction)
394         shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
395
396     env['CCPDBFLAGS'] = '${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}'
397     env['CCPCHFLAGS'] = '${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'
398     env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
399     env['CC']         = 'cl'
400     env['CCFLAGS']    = '/nologo'
401     env['CCCOM']      = '$CC $CCFLAGS $CCCOMFLAGS' 
402     env['SHCC']       = '$CC'
403     env['SHCCFLAGS']  = '$CCFLAGS'
404     env['SHCCCOM']    = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
405     env['CXX']        = '$CC'
406     env['CXXFLAGS']   = ['$CCFLAGS', '$(', '/TP', '$)']
407     env['CXXCOM']     = '$CXX $CXXFLAGS $CCCOMFLAGS'
408     env['SHCXX']      = '$CXX'
409     env['SHCXXFLAGS'] = '$CXXFLAGS'
410     env['SHCXXCOM']   = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
411     env['CPPDEFPREFIX']  = '/D'
412     env['CPPDEFSUFFIX']  = ''
413     env['INCPREFIX']  = '/I'
414     env['INCSUFFIX']  = ''
415     env['OBJEMITTER'] = static_object_emitter
416     env['SHOBJEMITTER'] = shared_object_emitter
417     env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
418
419     env['RC'] = 'rc'
420     env['RCFLAGS'] = ''
421     env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
422     CScan = env.get_scanner('.c')
423     if CScan:
424         CScan.add_skey('.rc')
425     env['BUILDERS']['RES'] = res_builder
426
427     try:
428         version = SCons.Tool.msvs.get_default_visualstudio_version(env)
429
430         if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']:
431             include_path, lib_path, exe_path = get_msvc_default_paths(version)
432         else:
433             include_path, lib_path, exe_path = get_msvc_paths(version)
434
435         # since other tools can set these, we just make sure that the
436         # relevant stuff from MSVS is in there somewhere.
437         env.PrependENVPath('INCLUDE', include_path)
438         env.PrependENVPath('LIB', lib_path)
439         env.PrependENVPath('PATH', exe_path)
440     except (SCons.Util.RegError, SCons.Errors.InternalError):
441         pass
442
443     env['CFILESUFFIX'] = '.c'
444     env['CXXFILESUFFIX'] = '.cc'
445
446     env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
447     env['BUILDERS']['PCH'] = pch_builder
448
449 def exists(env):
450     try:
451         v = SCons.Tool.msvs.get_visualstudio_versions()
452     except (SCons.Util.RegError, SCons.Errors.InternalError):
453         pass
454     
455     if not v:
456         return env.Detect('cl')
457     else:
458         # there's at least one version of MSVS installed.
459         return 1