SCons
[blender.git] / tools / mstoolkit.py
1 """tools.mstoolkit
2
3 Tool-specific initialization for Microsoft Visual C/C++ Toolkit Commandline
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 (c) 2004 John Connors
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
35 import os.path
36 import re
37 import string
38 import types
39
40 import SCons.Action
41 import SCons.Builder
42 import SCons.Errors
43 import SCons.Platform.win32
44 import SCons.Tool
45 import SCons.Util
46 import SCons.Warnings
47
48 CSuffixes = ['.c', '.C']
49 CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++']
50
51 def get_msvctoolkit_paths():
52         """Return a 4-tuple of (INCLUDE, LIB, PATH, TOOLKIT) as the values of those
53         three environment variables that should be set in order to execute
54         the MSVC .NET tools properly, if the information wasn't available
55         from the registry."""
56
57         MSToolkitDir = None
58         paths = {}
59         exe_path = ''
60         lib_path = ''
61         include_path = ''
62
63         # First, we get the shell folder for this user:
64         if not SCons.Util.can_read_reg:
65                 raise SCons.Errors.InternalError, "No Windows registry module was found"
66
67         # look for toolkit
68         if os.environ.has_key('VCToolkitInstallDir'):
69                 MSToolkitDir = os.path.normpath(os.environ['VCToolkitInstallDir'])
70         else:
71                 # last resort -- default install location
72                 MSToolkitDir = r'C:\Program Files\Microsoft Visual C++ Toolkit 2003'
73
74         # look for platform sdk
75         if os.environ.has_key('MSSdk'):
76                 PlatformSDKDir = os.path.normpath(os.environ['MSSdk'])
77         else:
78                 try:
79                         PlatformSDKDir = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\MicrosoftSDK\Directories\Install Dir')[0]
80                         PlatformSDKDir = str(PlatformSDKDir)
81                 except SCons.Util.RegError:
82                         raise SCons.Errors.InternalError, "The Platform SDK directory was not found in the registry or in the `MSSdk` environment variable."
83
84         # look for DX Sdk (expecting DX9)
85         # dxsdk docs have a directory key, look for it, extract path
86         #dxsdkdocs = ""
87         DXsdkDir = ""
88         #try:
89         #        dxsdkdocs = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, r'SOFTWARE\Microsoft\DirectX SDK\DX9SDK Doc Path')
90         #except SCons.Util.RegError:
91         #        raise SCons.Errors.InternalError, "The DXSDK directory was not found in the registry."
92         if os.environ.has_key('DXSDK_DIR'):
93                 DXsdkDir = os.path.normpath(os.environ['DXSDK_DIR'])
94
95         #DXsdkDir = os.path.split(dxsdkdocs[0])[0]
96         DXsdkDir = os.path.split(DXsdkDir)[0]
97
98         include_path = r'%s\include;%s\include;%s\include' % (MSToolkitDir, PlatformSDKDir, DXsdkDir)
99         lib_path = r'%s\lib;%s\lib;%s\lib' % (MSToolkitDir, PlatformSDKDir, DXsdkDir)
100         exe_path = r'%s\bin;%s\bin\win95;%s\bin' % (MSToolkitDir, PlatformSDKDir, PlatformSDKDir)
101         return (include_path, lib_path, exe_path, PlatformSDKDir)
102
103 def validate_vars(env):
104         """Validate the PDB, PCH, and PCHSTOP construction variables."""
105         if env.has_key('PCH') and env['PCH']:
106                 if not env.has_key('PCHSTOP'):
107                         raise SCons.Errors.UserError, "The PCHSTOP construction must be defined if PCH is defined."
108                 if not SCons.Util.is_String(env['PCHSTOP']):
109                         raise SCons.Errors.UserError, "The PCHSTOP construction variable must be a string: %r"%env['PCHSTOP']
110
111 def pch_emitter(target, source, env):
112         """Sets up the PDB dependencies for a pch file, and adds the object
113         file target."""
114
115         validate_vars(env)
116
117         pch = None
118         obj = None
119
120         for t in target:
121                 if SCons.Util.splitext(str(t))[1] == '.pch':
122                         pch = t
123                 if SCons.Util.splitext(str(t))[1] == '.obj':
124                         obj = t
125
126         if not obj:
127                 obj = SCons.Util.splitext(str(pch))[0]+'.obj'
128
129         target = [pch, obj] # pch must be first, and obj second for the PCHCOM to work
130
131         if env.has_key('PDB') and env['PDB']:
132                 env.SideEffect(env['PDB'], target)
133                 env.Precious(env['PDB'])
134
135         return (target, source)
136
137 def object_emitter(target, source, env, parent_emitter):
138         """Sets up the PDB and PCH dependencies for an object file."""
139
140         validate_vars(env)
141
142         parent_emitter(target, source, env)
143
144         if env.has_key('PDB') and env['PDB']:
145                 env.SideEffect(env['PDB'], target)
146                 env.Precious(env['PDB'])
147
148         if env.has_key('PCH') and env['PCH']:
149                 env.Depends(target, env['PCH'])
150
151         return (target, source)
152
153 def static_object_emitter(target, source, env):
154         return object_emitter(target, source, env,
155                                                   SCons.Defaults.StaticObjectEmitter)
156
157 def shared_object_emitter(target, source, env):
158         return object_emitter(target, source, env,
159                                                   SCons.Defaults.SharedObjectEmitter)
160
161 pch_builder = SCons.Builder.Builder(action='$PCHCOM', suffix='.pch', emitter=pch_emitter)
162 res_builder = SCons.Builder.Builder(action='$RCCOM', suffix='.res')
163
164 def pdbGenerator(env, target, source, for_signature):
165         if target and env.has_key('PDB') and env['PDB']:
166                 return ['/PDB:%s'%target[0].File(env['PDB']).get_string(for_signature),
167                                 '/DEBUG']
168
169 def win32ShlinkTargets(target, source, env, for_signature):
170         listCmd = []
171         dll = env.FindIxes(target, 'SHLIBPREFIX', 'SHLIBSUFFIX')
172         if dll: listCmd.append("/out:%s"%dll.get_string(for_signature))
173
174         implib = env.FindIxes(target, 'LIBPREFIX', 'LIBSUFFIX')
175         if implib: listCmd.append("/implib:%s"%implib.get_string(for_signature))
176
177         return listCmd
178
179 def win32ShlinkSources(target, source, env, for_signature):
180         listCmd = []
181
182         deffile = env.FindIxes(source, "WIN32DEFPREFIX", "WIN32DEFSUFFIX")
183         for src in source:
184                 if src == deffile:
185                         # Treat this source as a .def file.
186                         listCmd.append("/def:%s" % src.get_string(for_signature))
187                 else:
188                         # Just treat it as a generic source file.
189                         listCmd.append(src)
190         return listCmd
191
192 def win32LibEmitter(target, source, env):
193         # SCons.Tool.msvc.validate_vars(env)
194
195         dll = env.FindIxes(target, "SHLIBPREFIX", "SHLIBSUFFIX")
196         no_import_lib = env.get('no_import_lib', 0)
197
198         if not dll:
199                 raise SCons.Errors.UserError, "A shared library should have exactly one target with the suffix: %s" % env.subst("$SHLIBSUFFIX")
200
201         if env.get("WIN32_INSERT_DEF", 0) and \
202            not env.FindIxes(source, "WIN32DEFPREFIX", "WIN32DEFSUFFIX"):
203
204                 # append a def file to the list of sources
205                 source.append(env.ReplaceIxes(dll,
206                                                                           "SHLIBPREFIX", "SHLIBSUFFIX",
207                                                                           "WIN32DEFPREFIX", "WIN32DEFSUFFIX"))
208
209         if env.has_key('PDB') and env['PDB']:
210                 env.SideEffect(env['PDB'], target)
211                 env.Precious(env['PDB'])
212
213         if not no_import_lib and \
214            not env.FindIxes(target, "LIBPREFIX", "LIBSUFFIX"):
215                 # Append an import library to the list of targets.
216                 target.append(env.ReplaceIxes(dll,
217                                                                           "SHLIBPREFIX", "SHLIBSUFFIX",
218                                                                           "LIBPREFIX", "LIBSUFFIX"))
219                 # and .exp file is created if there are exports from a DLL
220                 target.append(env.ReplaceIxes(dll,
221                                                                           "SHLIBPREFIX", "SHLIBSUFFIX",
222                                                                           "WIN32EXPPREFIX", "WIN32EXPSUFFIX"))
223
224         return (target, source)
225
226 def prog_emitter(target, source, env):
227         #SCons.Tool.msvc.validate_vars(env)
228
229         if env.has_key('PDB') and env['PDB']:
230                 env.SideEffect(env['PDB'], target)
231                 env.Precious(env['PDB'])
232
233         return (target,source)
234
235 def RegServerFunc(target, source, env):
236         if env.has_key('register') and env['register']:
237                 ret = regServerAction([target[0]], [source[0]], env)
238                 if ret:
239                         raise SCons.Errors.UserError, "Unable to register %s" % target[0]
240                 else:
241                         print "Registered %s sucessfully" % target[0]
242                 return ret
243         return 0
244
245 regServerAction = SCons.Action.Action("$REGSVRCOM")
246 regServerCheck = SCons.Action.Action(RegServerFunc, None)
247 shlibLinkAction = SCons.Action.Action('${TEMPFILE("$SHLINK $SHLINKFLAGS $_SHLINK_TARGETS $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $_SHLINK_SOURCES")}')
248 compositeLinkAction = shlibLinkAction + regServerCheck
249
250 def generate(env):
251         """Add Builders and construction variables for MSVC++ to an Environment."""
252         static_obj, shared_obj = SCons.Tool.createObjBuilders(env)
253
254         for suffix in CSuffixes:
255                 static_obj.add_action(suffix, SCons.Defaults.CAction)
256                 shared_obj.add_action(suffix, SCons.Defaults.ShCAction)
257
258         for suffix in CXXSuffixes:
259                 static_obj.add_action(suffix, SCons.Defaults.CXXAction)
260                 shared_obj.add_action(suffix, SCons.Defaults.ShCXXAction)
261
262         SCons.Tool.createStaticLibBuilder(env)
263         SCons.Tool.createSharedLibBuilder(env)
264         SCons.Tool.createProgBuilder(env)
265
266         env['CCPDBFLAGS'] = SCons.Util.CLVar(['${(PDB and "/Zi /Fd%s"%File(PDB)) or ""}'])
267         env['CCPCHFLAGS'] = SCons.Util.CLVar(['${(PCH and "/Yu%s /Fp%s"%(PCHSTOP or "",File(PCH))) or ""}'])
268         env['CCCOMFLAGS'] = '$CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo$TARGET $CCPCHFLAGS $CCPDBFLAGS'
269         env['CC']                 = 'cl'
270         env['CCFLAGS']    = SCons.Util.CLVar('/nologo')
271         env['CCCOM']      = '$CC $CCFLAGS $CCCOMFLAGS'
272         env['SHCC']               = '$CC'
273         env['SHCCFLAGS']  = SCons.Util.CLVar('$CCFLAGS')
274         env['SHCCCOM']    = '$SHCC $SHCCFLAGS $CCCOMFLAGS'
275         env['CXX']                = '$CC'
276         env['CXXFLAGS']   = SCons.Util.CLVar('$CCFLAGS $( /TP $)')
277         env['CXXCOM']     = '$CXX $CXXFLAGS $CCCOMFLAGS'
278         env['SHCXX']      = '$CXX'
279         env['SHCXXFLAGS'] = SCons.Util.CLVar('$CXXFLAGS')
280         env['SHCXXCOM']   = '$SHCXX $SHCXXFLAGS $CCCOMFLAGS'
281         env['CPPDEFPREFIX']  = '/D'
282         env['CPPDEFSUFFIX']  = ''
283         env['INCPREFIX']  = '/I'
284         env['INCSUFFIX']  = ''
285         env['OBJEMITTER'] = static_object_emitter
286         env['SHOBJEMITTER'] = shared_object_emitter
287         env['STATIC_AND_SHARED_OBJECTS_ARE_THE_SAME'] = 1
288
289         env['RC'] = 'rc'
290         env['RCFLAGS'] = SCons.Util.CLVar('')
291         env['RCCOM'] = '$RC $_CPPDEFFLAGS $_CPPINCFLAGS $RCFLAGS /fo$TARGET $SOURCES'
292         CScan = env.get_scanner('.c')
293         if CScan:
294                 CScan.add_skey('.rc')
295         env['BUILDERS']['RES'] = res_builder
296
297         include_path, lib_path, exe_path, sdk_path = get_msvctoolkit_paths()
298         env.PrependENVPath('INCLUDE', include_path)
299         env.PrependENVPath('LIB', lib_path)
300         env.PrependENVPath('PATH', exe_path)
301
302         env['ENV']['CPU'] = 'i386'
303         env['ENV']['MSSDK'] = sdk_path
304         env['ENV']['BkOffice'] = sdk_path
305         env['ENV']['Basemake'] = sdk_path + "\\Include\\BKOffice.Mak"
306         env['ENV']['INETSDK'] = sdk_path
307         env['ENV']['MSSDK'] = sdk_path
308         env['ENV']['MSTOOLS'] = sdk_path
309         env['ENV']['TARGETOS'] = 'WINNT'
310         env['ENV']['APPVER'] = '5.0'
311
312         env['CFILESUFFIX'] = '.c'
313         env['CXXFILESUFFIX'] = '.cc'
314
315         env['PCHCOM'] = '$CXX $CXXFLAGS $CPPFLAGS $_CPPDEFFLAGS $_CPPINCFLAGS /c $SOURCES /Fo${TARGETS[1]} /Yc$PCHSTOP /Fp${TARGETS[0]} $CCPDBFLAGS'
316         env['BUILDERS']['PCH'] = pch_builder
317
318         env['AR']          = 'lib.exe' #'"' +sdk_path + '\\bin\\Win64\\lib.exe"'
319         env['ARFLAGS']     = SCons.Util.CLVar('/nologo')
320         env['ARCOM']       = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}"
321
322         env['SHLINK']      = '$LINK'
323         env['SHLINKFLAGS'] = SCons.Util.CLVar('$LINKFLAGS /dll')
324         env['_SHLINK_TARGETS'] = win32ShlinkTargets
325         env['_SHLINK_SOURCES'] = win32ShlinkSources
326         env['SHLINKCOM']   =  compositeLinkAction
327         env['SHLIBEMITTER']= win32LibEmitter
328         env['LINK'] =  'link.exe' #'"' +sdk_path + '\\bin\\Win64\\' + 'link.exe"'
329         env['LINKFLAGS']   = SCons.Util.CLVar('/nologo')
330         env['_PDB'] = pdbGenerator
331         env["TEMPFILE"] = SCons.Platform.win32.TempFileMunge
332         env['LINKCOM'] = '${TEMPFILE("$LINK $LINKFLAGS /OUT:$TARGET $( $_LIBDIRFLAGS $) $_LIBFLAGS $_PDB $SOURCES")}'
333         env['PROGEMITTER'] = prog_emitter
334         env['LIBDIRPREFIX']='/LIBPATH:'
335         env['LIBDIRSUFFIX']=''
336         env['LIBLINKPREFIX']=''
337         env['LIBLINKSUFFIX']='$LIBSUFFIX'
338
339         env['WIN32DEFPREFIX']        = ''
340         env['WIN32DEFSUFFIX']        = '.def'
341         env['WIN32_INSERT_DEF']      = 0
342
343         env['WIN32EXPPREFIX']        = ''
344         env['WIN32EXPSUFFIX']        = '.exp'
345
346         env['REGSVRACTION'] = regServerCheck
347         env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32')
348         env['REGSVRFLAGS'] = '/s '
349         env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS $TARGET'
350
351
352 def exists(env):
353         return env.Detect('cl')