Please note:The SCons wiki is in read-only mode due to ongoing spam/DoS issues. Also, new account creation is currently disabled. We are looking into alternative wiki hosts.

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.

MicrosoftPlatformTool (last edited 2010-01-17 06:39:20 by s235-200)