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