From 4d7ed00302f2f872f9719125b4fcb048e4ecb7ef Mon Sep 17 00:00:00 2001 From: Steven Knight Date: Sun, 8 Jun 2003 13:22:57 +0000 Subject: Add MSVS Project file support. (Greg Spencer) --- bin/files | 1 + doc/man/scons.1 | 68 ++- runtest.py | 1 + src/CHANGES.txt | 10 + src/RELEASE.txt | 7 +- src/engine/MANIFEST.in | 1 + src/engine/SCons/Environment.py | 47 +- src/engine/SCons/EnvironmentTests.py | 48 ++ src/engine/SCons/Platform/win32.py | 42 +- src/engine/SCons/Script/SConscript.py | 9 +- src/engine/SCons/Tool/__init__.py | 2 +- src/engine/SCons/Tool/mslib.py | 21 +- src/engine/SCons/Tool/mslink.py | 34 +- src/engine/SCons/Tool/msvc.py | 320 +++++++--- src/engine/SCons/Tool/msvs.py | 1030 +++++++++++++++++++++++++++++++++ src/engine/SCons/Tool/msvsTests.py | 476 +++++++++++++++ src/engine/SCons/Util.py | 88 +++ src/engine/SCons/UtilTests.py | 24 + test/import.py | 25 +- test/midl.py | 2 +- test/msvs.py | 415 +++++++++++++ 21 files changed, 2546 insertions(+), 125 deletions(-) create mode 100644 src/engine/SCons/Tool/msvs.py create mode 100644 src/engine/SCons/Tool/msvsTests.py create mode 100644 test/msvs.py diff --git a/bin/files b/bin/files index 86f297c..4e3a863 100644 --- a/bin/files +++ b/bin/files @@ -64,6 +64,7 @@ ./SCons/Tool/mslib.py ./SCons/Tool/mslink.py ./SCons/Tool/msvc.py +./SCons/Tool/msvs.py ./SCons/Tool/nasm.py ./SCons/Tool/pdflatex.py ./SCons/Tool/pdftex.py diff --git a/doc/man/scons.1 b/doc/man/scons.1 index 1a1b81f..32546ed 100644 --- a/doc/man/scons.1 +++ b/doc/man/scons.1 @@ -31,7 +31,7 @@ .RE .fi .. -.TH SCONS 1 "May 2003" +.TH SCONS 1 "June 2003" .SH NAME scons \- a software construction tool .SH SYNOPSIS @@ -887,6 +887,7 @@ mingw mslib mslink msvc +msvs nasm pdflatex pdftex @@ -1150,6 +1151,49 @@ Example: env.Program(target = 'foo', source = ['foo.o', 'bar.c', 'baz.f']) .EE +.IP MSVSProject +Builds Microsoft Visual Studio project files. +.B scons +will detect installed versions of Visual Studio +up to and including versions 7.x (.NET). +When one is detected, +this builder will generate the correct +project file +.RI ( .vcproj +or +.IR .dsp ) +and solution file +.RI ( .sln +or +.IR .dsw ). +This builder takes a number of additional +keyword arguments that supply information +necessary to build the proper project files. +Examples: + +.ES +# For Visual Studio 7.0 or later (.NET). +env.MSVSProject(target = 'Foo.vcproj', + slnguid = '{SLNGUID}', + srcs = ['foo.cpp'], + incs = ['sdk.h'], + localincs = ['foo.h'], + resources = ['foo.rc'], + misc = ['readme.txt'], + buildtarget = 'Foo.exe', + variant = 'Release') + +# For earlier Visual Studio versions. +env.MSVSProject(target = 'Foo.dsp', + srcs = ['foo.cpp'], + incs = ['sdk.h'], + localincs = ['foo.h'], + resources = ['foo.rc'], + misc = ['readme.txt'], + buildtarget = 'Foo.exe', + variant = 'Release') +.EE + .IP RES Builds a Microsoft Visual C++ resource file. This builder is only provided @@ -2646,6 +2690,28 @@ General options passed to the linker. .IP LINKCOM The command line used to link object files into an executable. +.IP MSVSPROJECTCOM +The action used to generate Microsoft Visual Studio +project and solution files. + +.IP MSVSPROJECTSUFFIX +The suffix used for Microsoft Visual Studio project (DSP) files. +The default value is +.B .vcproj +when using Visual Studio version 7.x (.NET), +and +.B .dsp +when using earlier versions of Visual Studio. + +.IP MSVSSOLUTIONSUFFIX +The suffix used for Microsoft Visual Studio solution (DSW) files. +The default value is +.B .sln +when using Visual Studio version 7.x (.NET), +and +.B .dsw +when using earlier versions of Visual Studio. + .IP no_import_lib When set to non-zero, suppresses creation of a corresponding Win32 static import lib by the diff --git a/runtest.py b/runtest.py index b2176e9..049643a 100644 --- a/runtest.py +++ b/runtest.py @@ -309,6 +309,7 @@ elif scons_lib_dir: if scons_exec: os.environ['SCONS_EXEC'] = '1' +os.environ['SCONS_SCRIPT_DIR'] = scons_script_dir os.environ['SCONS_CWD'] = cwd os.environ['SCONS_VERSION'] = version diff --git a/src/CHANGES.txt b/src/CHANGES.txt index 1222d44..dd976ef 100644 --- a/src/CHANGES.txt +++ b/src/CHANGES.txt @@ -39,6 +39,16 @@ RELEASE 0.15 - XXX - Fix use of the SConf subsystem with SConscriptChdir(). + From Greg Spencer + + - Check for the existence of MS Visual Studio on disk before using it, + to avoid getting fooled by leftover junk in the registry. + + - Add support for MSVC++ .NET. + + - Add support for MS Visual Studio project files (DSP, DSW, + SLN and VCPROJ files). + RELEASE 0.14 - Wed, 21 May 2003 05:16:32 -0500 diff --git a/src/RELEASE.txt b/src/RELEASE.txt index 6d7916c..467633c 100644 --- a/src/RELEASE.txt +++ b/src/RELEASE.txt @@ -27,7 +27,12 @@ RELEASE 0.15 - XXX Please note the following important changes since release 0.14: - - + - SCons now tries to verify that Microsoft Visual Studio (including + Visual C++) is actually installed before using it, by checking that + the program directory exists. If SCons cannot find your copy of + Visual Studio, it is probably because it installed itself itself in + a default directory that we have not seen before. If this is the + case, please let us know so that we can update future versions. Please note the following important changes since release 0.13: diff --git a/src/engine/MANIFEST.in b/src/engine/MANIFEST.in index 65d6886..f1987e7 100644 --- a/src/engine/MANIFEST.in +++ b/src/engine/MANIFEST.in @@ -76,6 +76,7 @@ SCons/Tool/mingw.py SCons/Tool/mslib.py SCons/Tool/mslink.py SCons/Tool/msvc.py +SCons/Tool/msvs.py SCons/Tool/nasm.py SCons/Tool/pdflatex.py SCons/Tool/pdftex.py diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index ba5e279..0f5cb30 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -1,7 +1,12 @@ """SCons.Environment -XXX +Base class for construction Environments. These are +the primary objects used to communicate dependency and +construction information to the build engine. +Keyword arguments supplied when the construction Environment +is created are construction variables used to initialize the +Environment """ # @@ -33,6 +38,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import copy import os import os.path +import string import re import shutil from UserDict import UserDict @@ -271,6 +277,45 @@ class Environment: else: self._dict[key] = kw[key] + self._dict[key] + def PrependENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): + """Prepend path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + """ + + orig = '' + if self._dict.has_key(envname) and self._dict[envname].has_key(name): + orig = self._dict[envname][name] + + nv = SCons.Util.PrependPath(orig, newpath, sep) + + if not self._dict.has_key(envname): + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def AppendENVPath(self, name, newpath, envname = 'ENV', sep = os.pathsep): + """Append path elements to the path 'name' in the 'ENV' + dictionary for this environment. Will only add any particular + path once, and will normpath and normcase all paths to help + assure this. This can also handle the case where the env + variable is a list instead of a string. + """ + + orig = '' + if self._dict.has_key(envname) and self._dict[envname].has_key(name): + orig = self._dict[envname][name] + + nv = SCons.Util.AppendPath(orig, newpath, sep) + + if not self._dict.has_key(envname): + self._dict[envname] = {} + + self._dict[envname][name] = nv + + def Depends(self, target, dependency): """Explicity specify that 'target's depend on 'dependency'.""" tlist = SCons.Node.arg2nodes(target, self.fs.File) diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index aed3a9e..d8b4f9d 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -474,6 +474,54 @@ class EnvironmentTestCase(unittest.TestCase): assert hasattr(env4, 'z1') assert hasattr(env4, 'z2') + def test_PrependENVPath(self): + """Test prepending to an ENV path.""" + env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'}, + MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}) + # have to include the pathsep here so that the test will work on UNIX too. + env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') + env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';') + env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';') + env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';') + assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one') + assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two') + + def test_AppendENVPath(self): + """Test appending to an ENV path.""" + env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'}, + MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}) + # have to include the pathsep here so that the test will work on UNIX too. + env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';') + env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';') + env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';') + env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';') + assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') + assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one') + + def test_AppendENVPath(self): + """Test prepending to an ENV path.""" + env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'}, + MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}) + # have to include the pathsep here so that the test will work on UNIX too. + env1.PrependENVPath('PATH',r'C:\dir\num\two',sep = ';') + env1.PrependENVPath('PATH',r'C:\dir\num\three',sep = ';') + env1.PrependENVPath('MYPATH',r'C:\mydir\num\three','MYENV',sep = ';') + env1.PrependENVPath('MYPATH',r'C:\mydir\num\one','MYENV',sep = ';') + assert(env1['ENV']['PATH'] == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one') + assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two') + + def test_AppendENVPath(self): + """Test appending to an ENV path.""" + env1 = Environment(ENV = {'PATH': r'C:\dir\num\one;C:\dir\num\two'}, + MYENV = {'MYPATH': r'C:\mydir\num\one;C:\mydir\num\two'}) + # have to include the pathsep here so that the test will work on UNIX too. + env1.AppendENVPath('PATH',r'C:\dir\num\two', sep = ';') + env1.AppendENVPath('PATH',r'C:\dir\num\three', sep = ';') + env1.AppendENVPath('MYPATH',r'C:\mydir\num\three','MYENV', sep = ';') + env1.AppendENVPath('MYPATH',r'C:\mydir\num\one','MYENV', sep = ';') + assert(env1['ENV']['PATH'] == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') + assert(env1['MYENV']['MYPATH'] == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one') + def test_Depends(self): """Test the explicit Depends method.""" env = Environment() diff --git a/src/engine/SCons/Platform/win32.py b/src/engine/SCons/Platform/win32.py index 6874d01..0803004 100644 --- a/src/engine/SCons/Platform/win32.py +++ b/src/engine/SCons/Platform/win32.py @@ -68,11 +68,7 @@ class TempFileMunge: # a bug in Win32 that will use a forward slash as a path # delimiter. Win32's link mistakes that for a command line # switch and barfs. - # - # We use the .lnk suffix for the benefit of the Phar Lap - # linkloc linker, which likes to append an .lnk suffix if - # none is given. - tmp = os.path.normpath(tempfile.mktemp('.lnk')) + tmp = os.path.normpath(tempfile.mktemp()) native_tmp = SCons.Util.get_native_path(tmp) # The sh shell will try to escape the backslashes in the @@ -160,10 +156,18 @@ def spawn(sh, escape, cmd, args, env): sys.stderr.write("scons: %s: %s\n" % (cmd, e[1])) return ret -# Windows does not allow special characters in file names -# anyway, so no need for an escape function, we will just quote -# the arg. -escape = lambda x: '"' + x + '"' +# We just quote the arg here, but since the escape for a double +# quote in the command processor (I hesitate to call it a shell :-) is +# to double it (i.e. '""' => '"' in the command processor), we have to +# make sure not to double any double quotes on the ends. +def escape(x): + first = '"' + last = '"' + if x and x[0] == '"': + first = '" ' + if x and x[-1] == '"': + last = ' "' + return first + x + last # Get the windows system directory name def get_system_root(): @@ -233,10 +237,30 @@ def generate(env): cmd_interp = os.path.join(val, 'command.com') except: pass + + # For the special case of not having access to the registry, we + # use a temporary path and pathext to attempt to find the command + # interpreter. If we fail, we try to find the interpreter through + # the env's PATH. The problem with that is that it might not + # contain an ENV and a PATH. + if not cmd_interp: + systemroot = r'C:\Windows' + if os.environ.has_key('SYSTEMROOT'): + systemroot = os.environ['SYSTEMROOT'] + tmp_path = systemroot + os.pathsep + \ + os.path.join(systemroot,'System32') + tmp_pathext = '.com;.exe;.bat;.cmd' + if os.environ.has_key('PATHEXT'): + tmp_pathext = os.environ['PATHEXT'] + cmd_interp = SCons.Util.WhereIs('cmd', tmp_path, tmp_pathext) + if not cmd_interp: + cmd_interp = SCons.Util.WhereIs('command', tmp_path, tmp_pathext) + if not cmd_interp: cmd_interp = env.Detect('cmd') if not cmd_interp: cmd_interp = env.Detect('command') + if not env.has_key('ENV'): env['ENV'] = {} diff --git a/src/engine/SCons/Script/SConscript.py b/src/engine/SCons/Script/SConscript.py index 5258d90..1e14658 100644 --- a/src/engine/SCons/Script/SConscript.py +++ b/src/engine/SCons/Script/SConscript.py @@ -112,11 +112,16 @@ def compute_exports(exports): class Frame: """A frame on the SConstruct/SConscript call stack""" - def __init__(self, exports): + def __init__(self, exports, sconscript): self.globals = BuildDefaultGlobals() self.retval = None self.prev_dir = SCons.Node.FS.default_fs.getcwd() self.exports = compute_exports(exports) # exports from the calling SConscript + # make sure the sconscript attr is a Node. + if isinstance(sconscript, SCons.Node.Node): + self.sconscript = sconscript + else: + self.sconscript = SCons.Node.FS.default_fs.File(str(sconscript)) # the SConstruct/SConscript call stack: stack = [] @@ -215,7 +220,7 @@ def SConscript(*ls, **kw): # evaluate each SConscript file results = [] for fn in files: - stack.append(Frame(exports)) + stack.append(Frame(exports,fn)) old_sys_path = sys.path try: if fn == "-": diff --git a/src/engine/SCons/Tool/__init__.py b/src/engine/SCons/Tool/__init__.py index 7cf9b29..b039460 100644 --- a/src/engine/SCons/Tool/__init__.py +++ b/src/engine/SCons/Tool/__init__.py @@ -228,7 +228,7 @@ def tool_list(platform, env): other_tools = FindAllTools(['BitKeeper', 'CVS', 'dvipdf', 'dvips', 'gs', 'jar', 'javac', 'javah', - 'latex', 'lex', 'midl', + 'latex', 'lex', 'midl', 'msvs', 'pdflatex', 'pdftex', 'Perforce', 'RCS', 'rmic', 'SCCS', # 'Subversion', diff --git a/src/engine/SCons/Tool/mslib.py b/src/engine/SCons/Tool/mslib.py index 4180e05..93a36cc 100644 --- a/src/engine/SCons/Tool/mslib.py +++ b/src/engine/SCons/Tool/mslib.py @@ -34,15 +34,32 @@ selection method. __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.Defaults +import SCons.Tool.msvs +import SCons.Tool.msvc def generate(env): """Add Builders and construction variables for lib to an Environment.""" env['BUILDERS']['Library'] = SCons.Defaults.StaticLibrary env['BUILDERS']['StaticLibrary'] = SCons.Defaults.StaticLibrary - + + version = SCons.Tool.msvs.get_default_visualstudio_version(env) + + if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(version) + else: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(version) + + # since other tools can set this, we just make sure that the + # relevant stuff from MSVS is in there somewhere. + env.PrependENVPath('PATH', exe_path) + env['AR'] = 'lib' env['ARFLAGS'] = '/nologo' env['ARCOM'] = "${TEMPFILE('$AR $ARFLAGS /OUT:$TARGET $SOURCES')}" def exists(env): - return env.Detect('lib') + if not SCons.Util.can_read_reg or not SCons.Tool.msvs.get_visualstudio_versions(): + return env.Detect('lib') + else: + # there's at least one version of MSVS installed. + return True diff --git a/src/engine/SCons/Tool/mslink.py b/src/engine/SCons/Tool/mslink.py index 8aa6f09..5a8a729 100644 --- a/src/engine/SCons/Tool/mslink.py +++ b/src/engine/SCons/Tool/mslink.py @@ -40,9 +40,9 @@ import SCons.Action import SCons.Defaults import SCons.Errors import SCons.Util -import msvc - -from SCons.Tool.msvc import get_msdev_paths +import SCons.Tool.msvs +import SCons.Tool.msvc +import SCons.Platform.win32 def pdbGenerator(env, target, source, for_signature): if target and env.has_key('PDB') and env['PDB']: @@ -73,7 +73,7 @@ def win32ShlinkSources(target, source, env, for_signature): return listCmd def win32LibEmitter(target, source, env): - msvc.validate_vars(env) + SCons.Tool.msvc.validate_vars(env) dll = env.FindIxes(target, "SHLIBPREFIX", "SHLIBSUFFIX") no_import_lib = env.get('no_import_lib', 0) @@ -107,7 +107,7 @@ def win32LibEmitter(target, source, env): return (target, source) def prog_emitter(target, source, env): - msvc.validate_vars(env) + SCons.Tool.msvc.validate_vars(env) if env.has_key('PDB') and env['PDB']: env.SideEffect(env['PDB'], target) @@ -159,14 +159,26 @@ def generate(env): env['WIN32EXPSUFFIX'] = '.exp' env['REGSVRACTION'] = regServerCheck - env['REGSVR'] = 'regsvr32' + env['REGSVR'] = os.path.join(SCons.Platform.win32.get_system_root(),'System32','regsvr32') env['REGSVRFLAGS'] = '/s ' env['REGSVRCOM'] = '$REGSVR $REGSVRFLAGS $TARGET' - if SCons.Util.can_read_reg: - include_path, lib_path, exe_path = get_msdev_paths() - env['ENV']['LIB'] = lib_path - env['ENV']['PATH'] = exe_path + version = SCons.Tool.msvs.get_default_visualstudio_version(env) + + if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_default_paths(version) + else: + include_path, lib_path, exe_path = SCons.Tool.msvc.get_msvc_paths(version) + + # since other tools can set these, we just make sure that the + # relevant stuff from MSVS is in there somewhere. + env.PrependENVPath('INCLUDE', include_path) + env.PrependENVPath('LIB', lib_path) + env.PrependENVPath('PATH', exe_path) def exists(env): - return env.Detect('link') + if not SCons.Util.can_read_reg or not SCons.Tool.msvs.get_visualstudio_versions(): + return env.Detect('link') + else: + # there's at least one version of MSVS installed. + return True diff --git a/src/engine/SCons/Tool/msvc.py b/src/engine/SCons/Tool/msvc.py index 6f0c516..f936535 100644 --- a/src/engine/SCons/Tool/msvc.py +++ b/src/engine/SCons/Tool/msvc.py @@ -35,59 +35,121 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import os.path import string +import types +import re import SCons.Action import SCons.Tool import SCons.Errors +import SCons.Warnings import SCons.Builder import SCons.Util import SCons.Platform.win32 +import SCons.Tool.msvs CSuffixes = ['.c', '.C'] CXXSuffixes = ['.cc', '.cpp', '.cxx', '.c++', '.C++'] -def get_devstudio_versions(): - """ - Get list of devstudio versions from the Windows registry. Return a - list of strings containing version numbers; an exception will be raised - if we were unable to access the registry (eg. couldn't import - a registry-access module) or the appropriate registry keys weren't - found. - """ - +def _parse_msvc7_overrides(version): + """ Parse any overridden defaults for MSVS directory locations in MSVS .NET. """ + + # First, we get the shell folder for this user: if not SCons.Util.can_read_reg: raise SCons.Errors.InternalError, "No Windows registry module was found" - K = 'Software\\Microsoft\\Devstudio' - L = [] - for base in (SCons.Util.HKEY_CLASSES_ROOT, - SCons.Util.HKEY_LOCAL_MACHINE, - SCons.Util.HKEY_CURRENT_USER, - SCons.Util.HKEY_USERS): + comps = "" + try: + (comps, t) = SCons.Util.RegGetValue(SCons.Util.HKEY_CURRENT_USER, + r'Software\Microsoft\Windows\CurrentVersion' +\ + r'\Explorer\Shell Folders\Local AppData') + except SCons.Util.RegError: + raise SCons.Errors.InternalError, "The Local AppData directory was not found in the registry." + + comps = comps + '\\Microsoft\\VisualStudio\\' + version + '\\VSComponents.dat' + dirs = {} + + if os.path.exists(comps): + # now we parse the directories from this file, if it exists. + # We only look for entries after: [VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories], + # since this file could contain a number of things... + f = open(comps,'r') + line = f.readline() + found = 0 + while line: + line.strip() + if found == 1: + (key, val) = line.split('=',1) + key = key.replace(' Dirs','') + dirs[key.upper()] = val + if line.find(r'[VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories]') >= 0: + found = 1 + if line == '': + found = 0 + line = f.readline() + f.close() + else: + # since the file didn't exist, we have only the defaults in + # the registry to work with. try: - k = SCons.Util.RegOpenKeyEx(base,K) + K = 'SOFTWARE\\Microsoft\\VisualStudio\\' + version + K = K + r'\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories' + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,K) i = 0 while 1: try: - p = SCons.Util.RegEnumKey(k,i) - if p[0] in '123456789' and p not in L: - L.append(p) + (key,val,t) = SCons.Util.RegEnumValue(k,i) + key = key.replace(' Dirs','') + dirs[key.upper()] = val + i = i + 1 except SCons.Util.RegError: break - i = i + 1 except SCons.Util.RegError: - pass + # if we got here, then we didn't find the registry entries: + raise SCons.Errors.InternalError, "Unable to find MSVC paths in the registry." + return dirs + +def _get_msvc7_path(path, version, platform): + """ + Get Visual Studio directories from version 7 (MSVS .NET) + (it has a different registry structure than versions before it) + """ + # first, look for a customization of the default values in the + # registry: These are sometimes stored in the Local Settings area + # for Visual Studio, in a file, so we have to parse it. + dirs = _parse_msvc7_overrides(version) + + if dirs.has_key(path): + p = dirs[path] + else: + raise SCons.Errors.InternalError, "Unable to retrieve the %s path from MS VC++."%path + + # collect some useful information for later expansions... + paths = SCons.Tool.msvs.get_msvs_install_dirs(version) + + # expand the directory path variables that we support. If there + # is a variable we don't support, then replace that entry with + # "---Unknown Location VSInstallDir---" or something similar, to clue + # people in that we didn't find something, and so env expansion doesn't + # do weird things with the $(xxx)'s + s = re.compile('\$\(([a-zA-Z0-9_]+?)\)') + + def repl(match): + key = string.upper(match.group(1)) + if paths.has_key(key): + return paths[key] + else: + return '---Unknown Location %s---' % match.group() - if not L: - raise SCons.Errors.InternalError, "DevStudio was not found." + rv = [] + for entry in p.split(os.pathsep): + entry = s.sub(repl,entry) + rv.append(entry) - L.sort() - L.reverse() - return L + return string.join(rv,os.pathsep) def get_msvc_path (path, version, platform='x86'): """ - Get a list of devstudio directories (include, lib or path). Return + Get a list of visualstudio directories (include, lib or path). Return a string delimited by ';'. An exception will be raised if unable to access the registry or appropriate registry keys not found. """ @@ -95,16 +157,22 @@ def get_msvc_path (path, version, platform='x86'): if not SCons.Util.can_read_reg: raise SCons.Errors.InternalError, "No Windows registry module was found" - if path=='lib': - path= 'Library' + # normalize the case for comparisons (since the registry is case + # insensitive) + path = string.upper(path) + + if path=='LIB': + path= 'LIBRARY' + + if float(version) >= 7.0: + return _get_msvc7_path(path, version, platform) + path = string.upper(path + ' Dirs') K = ('Software\\Microsoft\\Devstudio\\%s\\' + 'Build System\\Components\\Platforms\\Win32 (%s)\\Directories') % \ (version,platform) - for base in (SCons.Util.HKEY_CLASSES_ROOT, - SCons.Util.HKEY_LOCAL_MACHINE, - SCons.Util.HKEY_CURRENT_USER, - SCons.Util.HKEY_USERS): + for base in (SCons.Util.HKEY_CURRENT_USER, + SCons.Util.HKEY_LOCAL_MACHINE): try: k = SCons.Util.RegOpenKeyEx(base,K) i = 0 @@ -120,77 +188,127 @@ def get_msvc_path (path, version, platform='x86'): pass # if we got here, then we didn't find the registry entries: - raise SCons.Errors.InternalError, "%s was not found in the registry."%path - -def get_msdev_dir(version): - """Returns the root directory of the MSDev installation from the - registry if it can be found, otherwise we guess.""" - if SCons.Util.can_read_reg: - K = ('Software\\Microsoft\\Devstudio\\%s\\' + - 'Products\\Microsoft Visual C++') % \ - version - for base in (SCons.Util.HKEY_LOCAL_MACHINE, - SCons.Util.HKEY_CURRENT_USER): - try: - k = SCons.Util.RegOpenKeyEx(base,K) - val, tok = SCons.Util.RegQueryValueEx(k, 'ProductDir') - return os.path.split(val)[0] - except SCons.Util.RegError: - pass - -def get_msdev_paths(version=None): + raise SCons.Errors.InternalError, "The %s path was not found in the registry."%path + +def _get_msvc6_default_paths(version): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those + three environment variables that should be set in order to execute + the MSVC 6.0 tools properly, if the information wasn't available + from the registry.""" + MVSdir = None + paths = {} + exe_path = '' + lib_path = '' + include_path = '' + try: + paths = SCons.Tool.msvs.get_msvs_install_dirs(version) + MVSdir = paths['VSINSTALLDIR'] + except (SCons.Util.RegError, SCons.Errors.InternalError): + if os.environ.has_key('MSDEVDIR'): + MVSdir = os.path.normpath(os.path.join(os.environ['MSDEVDIR'],'..','..')) + else: + MVSdir = r'C:\Program Files\Microsoft Visual Studio' + if MVSdir: + if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): + MVSVCdir = paths['VCINSTALLDIR'] + else: + MVSVCdir = os.path.join(MVSdir,'VC98') + + MVSCommondir = r'%s\Common' % MVSdir + include_path = r'%s\ATL\include;%s\MFC\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir) + lib_path = r'%s\MFC\lib;%s\lib' % (MVSVCdir, MVSVCdir) + exe_path = r'%s\MSDev98\bin;%s\bin' % (MVSCommondir, MVSVCdir) + return (include_path, lib_path, exe_path) + +def _get_msvc7_default_paths(version): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those + three environment variables that should be set in order to execute + the MSVC .NET tools properly, if the information wasn't available + from the registry.""" + + MVSdir = None + paths = {} + exe_path = '' + lib_path = '' + include_path = '' + try: + paths = SCons.Tool.msvs.get_msvs_install_dirs(version) + MVSdir = paths['VSINSTALLDIR'] + except (KeyError, SCons.Util.RegError, SCons.Errors.InternalError): + if os.environ.has_key('VSCOMNTOOLS'): + MVSdir = os.path.normpath(os.path.join(os.environ['VSCOMNTOOLS'],'..','..')) + else: + # last resort -- default install location + MVSdir = r'C:\Program Files\Microsoft Visual Studio .NET' + + if not MVSdir: + if SCons.Util.can_read_reg and paths.has_key('VCINSTALLDIR'): + MVSVCdir = paths['VCINSTALLDIR'] + else: + MVSVCdir = os.path.join(MVSdir,'Vc7') + + MVSCommondir = r'%s\Common7' % MVSdir + include_path = r'%s\atlmfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir) + lib_path = r'%s\atlmfc\lib;%s\lib' % (MVSVCdir, MVSVCdir) + exe_path = r'%s\Tools\bin;%s\Tools;%s\bin' % (MVSCommondir, MVSCommondir, MVSVCdir) + return (include_path, lib_path, exe_path) + +def get_msvc_paths(version=None): """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those three environment variables that should be set in order to execute the MSVC tools properly.""" exe_path = '' lib_path = '' include_path = '' + + if not version and not SCons.Util.can_read_reg: + version = '6.0' + try: if not version: - version = get_devstudio_versions()[0] #use highest version + version = get_visualstudio_versions()[0] #use highest version + include_path = get_msvc_path("include", version) lib_path = get_msvc_path("lib", version) - exe_path = get_msvc_path("path", version) + ";" + os.environ['PATH'] + exe_path = get_msvc_path("path", version) + except (SCons.Util.RegError, SCons.Errors.InternalError): - # Could not get the configured directories from the registry. - # However, the configured directories only appear if the user - # changes them from the default. Therefore, we'll see if - # we can get the path to the MSDev base installation from - # the registry and deduce the default directories. - MVSdir = None - if version: - MVSdir = get_msdev_dir(version) - if MVSdir: - MVSVCdir = r'%s\VC98' % MVSdir - MVSCommondir = r'%s\Common' % MVSdir - include_path = r'%s\atl\include;%s\mfc\include;%s\include' % (MVSVCdir, MVSVCdir, MVSVCdir) - lib_path = r'%s\mfc\lib;%s\lib' % (MVSVCdir, MVSVCdir) - try: - extra_path = os.pathsep + os.environ['PATH'] - except KeyError: - extra_path = '' - exe_path = (r'%s\MSDev98\Bin;%s\Bin' % (MVSCommondir, MVSVCdir)) + extra_path + # Could not get all the configured directories from the + # registry. However, some of the configured directories only + # appear if the user changes them from the default. + # Therefore, we'll see if we can get the path to the MSDev + # base installation from the registry and deduce the default + # directories. + if float(version) >= 7.0: + return _get_msvc7_default_paths(version) else: - # The DevStudio environment variables don't exist, - # so just use the variables from the source environment. - progfiles = SCons.Platform.win32.get_program_files_dir() - MVSdir = os.path.join(progfiles,r'Microsoft Visual Studio') - MVSVCdir = r'%s\VC98' % MVSdir - MVSCommondir = r'%s\Common' % MVSdir - try: - include_path = os.environ['INCLUDE'] - except KeyError: - include_path = '' - try: - lib_path = os.environ['LIB'] - except KeyError: - lib_path = '' - try: - exe_path = os.environ['PATH'] - except KeyError: - exe_path = '' + return _get_msvc6_default_paths(version) + return (include_path, lib_path, exe_path) +def get_msvc_default_paths(version = None): + """Return a 3-tuple of (INCLUDE, LIB, PATH) as the values of those + three environment variables that should be set in order to execute + the MSVC tools properly. This will only return the default + locations for the tools, not the values used by MSVS in their + directory setup area. This can help avoid problems with different + developers having different settings, and should allow the tools + to run in most cases.""" + + if not version and not SCons.Util.can_read_reg: + version = '6.0' + + try: + if not version: + version = get_visualstudio_versions()[0] #use highest version + except: + pass + + if float(version) >= 7.0: + return _get_msvc7_default_paths(version) + else: + return _get_msvc6_default_paths(version) + def validate_vars(env): """Validate the PDB, PCH, and PCHSTOP construction variables.""" if env.has_key('PCH') and env['PCH']: @@ -282,10 +400,18 @@ def generate(env): CScan.add_skey('.rc') env['BUILDERS']['RES'] = res_builder - if SCons.Util.can_read_reg: - include_path, lib_path, exe_path = get_msdev_paths() - env['ENV']['INCLUDE'] = include_path - env['ENV']['PATH'] = exe_path + version = SCons.Tool.msvs.get_default_visualstudio_version(env) + + if env.has_key('MSVS_IGNORE_IDE_PATHS') and env['MSVS_IGNORE_IDE_PATHS']: + include_path, lib_path, exe_path = get_msvc_default_paths(version) + else: + include_path, lib_path, exe_path = get_msvc_paths(version) + + # since other tools can set these, we just make sure that the + # relevant stuff from MSVS is in there somewhere. + env.PrependENVPath('INCLUDE', include_path) + env.PrependENVPath('LIB', lib_path) + env.PrependENVPath('PATH', exe_path) env['CFILESUFFIX'] = '.c' env['CXXFILESUFFIX'] = '.cc' @@ -294,4 +420,8 @@ def generate(env): env['BUILDERS']['PCH'] = pch_builder def exists(env): - return env.Detect('cl') + if not SCons.Util.can_read_reg or not SCons.Tool.msvs.get_visualstudio_versions(): + return env.Detect('cl') + else: + # there's at least one version of MSVS installed. + return True diff --git a/src/engine/SCons/Tool/msvs.py b/src/engine/SCons/Tool/msvs.py new file mode 100644 index 0000000..9b8eb41 --- /dev/null +++ b/src/engine/SCons/Tool/msvs.py @@ -0,0 +1,1030 @@ +"""SCons.Tool.msvs + +Tool-specific initialization for Microsoft Visual Studio project files. + +There normally shouldn't be any need to import this module directly. +It will usually be imported through the generic SCons.Tool.Tool() +selection method. + +""" + +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +import base64 +import md5 +import os.path +import pickle +import re +import string +import sys +import types + +import SCons.Builder +import SCons.Node.FS +import SCons.Platform.win32 +import SCons.Script.SConscript +import SCons.Util +import SCons.Warnings + +############################################################################## +# Below here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def _hexdigest(s): + """Return a string as a string of hex characters. + """ + # NOTE: This routine is a method in the Python 2.0 interface + # of the native md5 module, but we want SCons to operate all + # the way back to at least Python 1.5.2, which doesn't have it. + h = string.hexdigits + r = '' + for c in s: + i = ord(c) + r = r + h[(i >> 4) & 0xF] + h[i & 0xF] + return r + +def _generateGUID(slnfile, name): + """This generates a dummy GUID for the sln file to use. It is + based on the MD5 signatures of the sln filename plus the name of + the project. It basically just needs to be unique, and not + change with each invocation.""" + solution = _hexdigest(md5.new(str(slnfile)+str(name)).digest()).upper() + # convert most of the signature to GUID form (discard the rest) + solution = "{" + solution[:8] + "-" + solution[8:12] + "-" + solution[12:16] + "-" + solution[16:28] + "}" + return solution + +class Config: + pass + +class _DSPGenerator: + """ Base class for DSP generators """ + def __init__(self, dspfile, source, env): + if type(dspfile) == types.StringType: + self.dspfile = os.path.abspath(dspfile) + else: + self.dspfile = dspfile.get_abspath() + + try: + self.conspath = source[0].attributes.sconstruct.get_abspath() + except KeyError: + raise SCons.Errors.InternalError, \ + "Unable to determine where the SConstruct is" + + self.config = Config() + if env.has_key('variant'): + self.config.variant = env['variant'].capitalize() + else: + raise SCons.Errors.InternalError, \ + "You must specify a 'variant' argument (i.e. 'Debug' or " +\ + "'Release') to create an MSVSProject." + + if env.has_key('buildtarget'): + if type(env['buildtarget']) == types.StringType: + self.config.buildtarget = os.path.abspath(env['buildtarget']) + elif type(env['buildtarget']) == types.ListType: + self.config.buildtarget = env['buildtarget'][0].get_abspath() + else: + self.config.buildtarget = env['buildtarget'].get_abspath() + else: + raise SCons.Errors.InternalError, \ + "You must specify a target 'buildtarget' file argument (such as the target" +\ + " executable) to create an MSVSProject." + + self.config.outdir = os.path.dirname(self.config.buildtarget) + + if type(source[0]) == types.StringType: + self.source = os.path.abspath(source[0]) + else: + self.source = source[0].get_abspath() + + self.env = env + + if self.env.has_key('name'): + self.name = self.env['name'] + else: + self.name = os.path.basename(os.path.splitext(self.dspfile)[0]) + + print "Adding '" + self.name + ' - ' + self.config.variant + "' to Visual Studio Project '" + str(dspfile) + "'" + + sourcenames = [ + ' Source Files', + 'Header Files', + 'Local Headers', + 'Resource Files', + 'Other Files'] + + srcargs = [ + 'srcs', + 'incs', + 'localincs', + 'resources', + 'misc'] + + self.sources = {} + for n in sourcenames: + self.sources[n] = [] + + self.configs = {} + + if os.path.exists(self.dspfile): + self.Parse() + + for t in zip(sourcenames,srcargs): + if self.env.has_key(t[1]): + if type(self.env[t[1]]) == types.ListType: + for i in self.env[t[1]]: + if not i in self.sources[t[0]]: + self.sources[t[0]].append(i) + else: + if not self.env[t[1]] in self.sources[t[0]]: + self.sources[t[0]].append(self.env[t[1]]) + + for n in sourcenames: + self.sources[n].sort() + + self.configs[self.config.variant] = self.config + + def Build(self): + pass + +class _GenerateV6DSP(_DSPGenerator): + """Generates a Project file for MSVS 6.0""" + + def PrintHeader(self): + name = self.name + # pick a default config + confkeys = self.configs.keys() + confkeys.sort() + + self.file.write('# Microsoft Developer Studio Project File - Name="%s" - Package Owner=<4>\n' + '# Microsoft Developer Studio Generated Build File, Format Version 6.00\n' + '# ** DO NOT EDIT **\n\n' + '# TARGTYPE "Win32 (x86) External Target" 0x0106\n\n' + 'CFG=%s - Win32 %s\n' + '!MESSAGE This is not a valid makefile. To build this project using NMAKE,\n' + '!MESSAGE use the Export Makefile command and run\n' + '!MESSAGE \n' + '!MESSAGE NMAKE /f "%s.mak".\n' + '!MESSAGE \n' + '!MESSAGE You can specify a configuration when running NMAKE\n' + '!MESSAGE by defining the macro CFG on the command line. For example:\n' + '!MESSAGE \n' + '!MESSAGE NMAKE /f "%s.mak" CFG="%s - Win32 %s"\n' + '!MESSAGE \n' + '!MESSAGE Possible choices for configuration are:\n' + '!MESSAGE \n' % (name,name,confkeys[0],name,name,name,confkeys[0])) + + for kind in confkeys: + self.file.write('!MESSAGE "%s - Win32 %s" (based on "Win32 (x86) External Target")\n' % (name, kind)) + + self.file.write('!MESSAGE \n\n') + + def PrintProject(self): + name = self.name + self.file.write('# Begin Project\n' + '# PROP AllowPerConfigDependencies 0\n' + '# PROP Scc_ProjName ""\n' + '# PROP Scc_LocalPath ""\n\n') + + first = 1 + confkeys = self.configs.keys() + confkeys.sort() + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + if first == 1: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + first = 0 + else: + self.file.write('\n!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name, kind)) + + # have to write this twice, once with the BASE settings, and once without + for base in ("BASE ",""): + self.file.write('# PROP %sUse_MFC 0\n' + '# PROP %sUse_Debug_Libraries ' % (base, base)) + if kind.lower().find('debug') < 0: + self.file.write('0\n') + else: + self.file.write('1\n') + self.file.write('# PROP %sOutput_Dir "%s"\n' + '# PROP %sIntermediate_Dir "%s"\n' % (base,outdir,base,outdir)) + (d,c) = os.path.split(str(self.conspath)) + cmd = '%s %s -C %s -f %s %s' % (sys.executable, os.path.normpath(sys.argv[0]), d, c, buildtarget) + self.file.write('# PROP %sCmd_Line "%s"\n' + '# PROP %sRebuild_Opt "-c && %s"\n' + '# PROP %sTarget_File "%s"\n' + '# PROP %sBsc_Name ""\n' + '# PROP %sTarget_Dir ""\n'\ + %(base,cmd,base,cmd,base,buildtarget,base,base)) + + self.file.write('\n!ENDIF\n\n' + '# Begin Target\n\n') + for kind in confkeys: + self.file.write('# Name "%s - Win32 %s"\n' % (name,kind)) + self.file.write('\n') + first = 0 + for kind in confkeys: + if first == 0: + self.file.write('!IF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + first = 1 + else: + self.file.write('!ELSEIF "$(CFG)" == "%s - Win32 %s"\n\n' % (name,kind)) + self.file.write('!ENDIF \n\n') + self.PrintSourceFiles() + self.file.write('# End Target\n' + '# End Project\n') + + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,True) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + pdata = pickle.dumps(self.sources,True) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def PrintSourceFiles(self): + categories = {' Source Files': 'cpp|c|cxx|l|y|def|odl|idl|hpj|bat', + 'Header Files': 'h|hpp|hxx|hm|inl', + 'Local Headers': 'h|hpp|hxx|hm|inl', + 'Resource Files': 'r|rc|ico|cur|bmp|dlg|rc2|rct|bin|cnt|rtf|gif|jpg|jpeg|jpe', + 'Other Files': ''} + + cats = categories.keys() + cats.sort() + for kind in cats: + if not self.sources[kind]: + continue # skip empty groups + + self.file.write('# Begin Group "' + kind + '"\n\n') + typelist = categories[kind].replace('|',';') + self.file.write('# PROP Default_Filter "' + typelist + '"\n') + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + file + '"\n' + '# End Source File\n') + self.file.write('# End Group\n') + + # add the Conscript file outside of the groups + self.file.write('# Begin Source File\n\n' + 'SOURCE="' + str(self.source) + '"\n' + '# End Source File\n') + + def Parse(self): + try: + dspfile = file(self.dspfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find("# End Project") > -1: + break + line = dspfile.readline() + + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except: + return # unable to unpickle any data for some reason + + self.configs.update(data) + + data = None + line = dspfile.readline() + datas = line + while line and line != '\n': + line = dspfile.readline() + datas = datas + line + + # OK, we've found our little pickled cache of data. + # it has a "# " in front of it, so we strip that. + try: + datas = base64.decodestring(datas) + data = pickle.loads(datas) + except: + return # unable to unpickle any data for some reason + + self.sources.update(data) + + def Build(self): + try: + self.file = file(self.dspfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError, 'Unable to open "' + self.dspfile + '" for writing:' + str(detail) + else: + self.PrintHeader() + self.PrintProject() + self.file.close() + +class _GenerateV7DSP(_DSPGenerator): + """Generates a Project file for MSVS .NET""" + + def PrintHeader(self): + self.file.write('\n' + '\n' + ' \n' + ' \n' + ' \n' % self.name) + + def PrintProject(self): + + + self.file.write(' \n') + + first = 1 + confkeys = self.configs.keys() + confkeys.sort() + for kind in confkeys: + outdir = self.configs[kind].outdir + buildtarget = self.configs[kind].buildtarget + + (d,c) = os.path.split(str(self.conspath)) + cmd = '%s %s -C %s -f %s %s\n' % (sys.executable,\ + os.path.normpath(sys.argv[0]),\ + d,c,buildtarget) + + cleancmd = '%s %s -C %s -f %s -c %s' % (sys.executable,\ + os.path.normpath(sys.argv[0]),\ + d,c,buildtarget) + + self.file.write(' \n' + ' \n' + ' \n' % (kind.capitalize(),outdir,outdir,\ + cmd,cleancmd,cmd,buildtarget)) + + self.file.write(' \n') + + self.PrintSourceFiles() + + self.file.write('\n') + + # now we pickle some data and add it to the file -- MSDEV will ignore it. + pdata = pickle.dumps(self.configs,True) + pdata = base64.encodestring(pdata) + self.file.write('\n') + + def PrintSourceFiles(self): + categories = {' Source Files': 'cpp;c;cxx;l;y;def;odl;idl;hpj;bat', + 'Header Files': 'h;hpp;hxx;hm;inl', + 'Local Headers': 'h;hpp;hxx;hm;inl', + 'Resource Files': 'r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe', + 'Other Files': ''} + + self.file.write(' \n') + + cats = categories.keys() + cats.sort() + for kind in cats: + if not self.sources[kind]: + continue # skip empty groups + + self.file.write(' \n' % (kind, categories[kind])) + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write(' \n' + ' \n' % file) + + self.file.write(' \n') + + # add the Conscript file outside of the groups + self.file.write(' \n' + ' \n' + ' \n' + ' \n' + ' \n' % str(self.source)) + + def Parse(self): + try: + dspfile = file(self.dspfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dspfile.readline() + while line: + if line.find('