diff options
author | Steven Knight <knight@baldmt.com> | 2003-06-08 13:22:57 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2003-06-08 13:22:57 (GMT) |
commit | 4d7ed00302f2f872f9719125b4fcb048e4ecb7ef (patch) | |
tree | 4afaba4669ddae8c3ce9972f79471d8387930e87 | |
parent | 8fc3c77d320fb435cb9268d31c34a58b18a58b54 (diff) | |
download | SCons-4d7ed00302f2f872f9719125b4fcb048e4ecb7ef.zip SCons-4d7ed00302f2f872f9719125b4fcb048e4ecb7ef.tar.gz SCons-4d7ed00302f2f872f9719125b4fcb048e4ecb7ef.tar.bz2 |
Add MSVS Project file support. (Greg Spencer)
-rw-r--r-- | bin/files | 1 | ||||
-rw-r--r-- | doc/man/scons.1 | 68 | ||||
-rw-r--r-- | runtest.py | 1 | ||||
-rw-r--r-- | src/CHANGES.txt | 10 | ||||
-rw-r--r-- | src/RELEASE.txt | 7 | ||||
-rw-r--r-- | src/engine/MANIFEST.in | 1 | ||||
-rw-r--r-- | src/engine/SCons/Environment.py | 47 | ||||
-rw-r--r-- | src/engine/SCons/EnvironmentTests.py | 48 | ||||
-rw-r--r-- | src/engine/SCons/Platform/win32.py | 42 | ||||
-rw-r--r-- | src/engine/SCons/Script/SConscript.py | 9 | ||||
-rw-r--r-- | src/engine/SCons/Tool/__init__.py | 2 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslib.py | 21 | ||||
-rw-r--r-- | src/engine/SCons/Tool/mslink.py | 34 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvc.py | 320 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvs.py | 1030 | ||||
-rw-r--r-- | src/engine/SCons/Tool/msvsTests.py | 476 | ||||
-rw-r--r-- | src/engine/SCons/Util.py | 88 | ||||
-rw-r--r-- | src/engine/SCons/UtilTests.py | 24 | ||||
-rw-r--r-- | test/import.py | 25 | ||||
-rw-r--r-- | test/midl.py | 2 | ||||
-rw-r--r-- | test/msvs.py | 415 |
21 files changed, 2546 insertions, 125 deletions
@@ -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 @@ -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('<?xml version="1.0" encoding = "Windows-1252"?>\n' + '<VisualStudioProject\n' + ' ProjectType="Visual C++"\n' + ' Version="7.00"\n' + ' Name="%s"\n' + ' SccProjectName=""\n' + ' SccLocalPath=""\n' + ' Keyword="MakeFileProj">\n' + ' <Platforms>\n' + ' <Platform\n' + ' Name="Win32"/>\n' + ' </Platforms>\n' % self.name) + + def PrintProject(self): + + + self.file.write(' <Configurations>\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(' <Configuration\n' + ' Name="%s|Win32"\n' + ' OutputDirectory="%s"\n' + ' IntermediateDirectory="%s"\n' + ' ConfigurationType="0"\n' + ' UseOfMFC="0"\n' + ' ATLMinimizesCRunTimeLibraryUsage="FALSE">\n' + ' <Tool\n' + ' Name="VCNMakeTool"\n' + ' BuildCommandLine="%s"\n' + ' CleanCommandLine="%s"\n' + ' RebuildCommandLine="%s"\n' + ' Output="%s"/>\n' + ' </Configuration>\n' % (kind.capitalize(),outdir,outdir,\ + cmd,cleancmd,cmd,buildtarget)) + + self.file.write(' </Configurations>\n') + + self.PrintSourceFiles() + + self.file.write('</VisualStudioProject>\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('<!-- SCons Data:\n' + 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': ''} + + self.file.write(' <Files>\n') + + cats = categories.keys() + cats.sort() + for kind in cats: + if not self.sources[kind]: + continue # skip empty groups + + self.file.write(' <Filter\n' + ' Name="%s"\n' + ' Filter="%s">\n' % (kind, categories[kind])) + + for file in self.sources[kind]: + file = os.path.normpath(file) + self.file.write(' <File\n' + ' RelativePath="%s">\n' + ' </File>\n' % file) + + self.file.write(' </Filter>\n') + + # add the Conscript file outside of the groups + self.file.write(' <File\n' + ' RelativePath="%s">\n' + ' </File>\n' + ' </Files>\n' + ' <Globals>\n' + ' </Globals>\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('<!-- SCons Data:') > -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. + 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 _DSWGenerator: + """ Base class for DSW generators """ + def __init__(self, dswfile, dspfile, source, env): + self.dswfile = os.path.normpath(str(dswfile)) + self.dspfile = os.path.abspath(str(dspfile)) + 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]) + + def Build(self): + pass + +class _GenerateV7DSW(_DSWGenerator): + """Generates a Solution file for MSVS .NET""" + def __init__(self, dswfile, dspfile, source, env): + _DSWGenerator.__init__(self, dswfile,dspfile,source,env) + + if env.has_key('slnguid') and env['slnguid']: + self.slnguid = env['slnguid'] + else: + self.slnguid = _generateGUID(dswfile, self.name) + + 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 MSVS Solution File." + + self.configs = {} + + if os.path.exists(self.dswfile): + self.Parse() + + self.configs[self.config.variant] = self.config + + def Parse(self): + try: + dswfile = file(self.dswfile,'r') + except IOError: + return # doesn't exist yet, so can't add anything to configs. + + line = dswfile.readline() + while line: + if line[:9] == "EndGlobal": + break + line = dswfile.readline() + + line = dswfile.readline() + datas = line + while line: + line = dswfile.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) + + def PrintSolution(self): + """Writes a solution file""" + self.file.write('Microsoft Visual Studio Solution File, Format Version 7.00\n' + # the next line has the GUID for an external makefile project. + 'Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "%s", "%s", "%s"\n' + 'EndProject\n' + 'Global\n' + ' GlobalSection(SolutionConfiguration) = preSolution\n'\ + % (self.name, os.path.basename(self.dspfile), self.slnguid)) + confkeys = self.configs.keys() + confkeys.sort() + cnt = 0 + for name in confkeys: + self.file.write(' ConfigName.%d = %s\n' % (cnt, name.capitalize())) + cnt = cnt + 1 + self.file.write(' EndGlobalSection\n' + ' GlobalSection(ProjectDependencies) = postSolution\n' + ' EndGlobalSection\n' + ' GlobalSection(ProjectConfiguration) = postSolution\n') + for name in confkeys: + name = name.capitalize() + self.file.write(' %s.%s.ActiveCfg = %s|Win32\n' + ' %s.%s.Build.0 = %s|Win32\n' %(self.slnguid,name,name,self.slnguid,name,name)) + self.file.write(' EndGlobalSection\n' + ' GlobalSection(ExtensibilityGlobals) = postSolution\n' + ' EndGlobalSection\n' + ' GlobalSection(ExtensibilityAddIns) = postSolution\n' + ' EndGlobalSection\n' + 'EndGlobal\n') + pdata = pickle.dumps(self.configs,True) + pdata = base64.encodestring(pdata) + self.file.write(pdata + '\n') + + def Build(self): + try: + self.file = file(self.dswfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail) + else: + self.PrintSolution() + self.file.close() + +class _GenerateV6DSW(_DSWGenerator): + """Generates a Workspace file for MSVS 6.0""" + + def PrintWorkspace(self): + """ writes a DSW file """ + self.file.write('Microsoft Developer Studio Workspace File, Format Version 6.00\n' + '# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!\n' + '\n' + '###############################################################################\n' + '\n' + 'Project: "%s"="%s" - Package Owner=<4>\n' + '\n' + 'Package=<5>\n' + '{{{\n' + '}}}\n' + '\n' + 'Package=<4>\n' + '{{{\n' + '}}}\n' + '\n' + '###############################################################################\n' + '\n' + 'Global:\n' + '\n' + 'Package=<5>\n' + '{{{\n' + '}}}\n' + '\n' + 'Package=<3>\n' + '{{{\n' + '}}}\n' + '\n' + '###############################################################################\n'\ + %(self.name,self.dspfile)) + + def Build(self): + try: + self.file = file(self.dswfile,'w') + except IOError, detail: + raise SCons.Errors.InternalError, 'Unable to open "' + self.dswfile + '" for writing:' + str(detail) + else: + self.PrintWorkspace() + self.file.close() + + +def GenerateDSP(dspfile, source, env): + """Generates a Project file based on the version of MSVS that is being used""" + + if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0: + g = _GenerateV7DSP(dspfile, source, env) + g.Build() + else: + g = _GenerateV6DSP(dspfile, source, env) + g.Build() + +def GenerateDSW(dswfile, dspfile, source, env): + """Generates a Solution/Workspace file based on the version of MSVS that is being used""" + + if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0: + g = _GenerateV7DSW(dswfile, dspfile, source, env) + g.Build() + else: + g = _GenerateV6DSW(dswfile, dspfile, source, env) + g.Build() + + +############################################################################## +# Above here are the classes and functions for generation of +# DSP/DSW/SLN/VCPROJ files. +############################################################################## + +def get_default_visualstudio_version(env): + """Returns the version set in the env, or the latest version + installed, if it can find it, or '6.0' if all else fails. Also + updated the environment with what it found.""" + + version = '6.0' + versions = [version] + if not env.has_key('MSVS') or type(env['MSVS']) != types.DictType: + env['MSVS'] = {} + + if env.has_key('MSVS_VERSION'): + version = env['MSVS_VERSION'] + versions = [version] + else: + if SCons.Util.can_read_reg: + versions = get_visualstudio_versions() + version = versions[0] #use highest version by default + + env['MSVS_VERSION'] = version + env['MSVS']['VERSIONS'] = versions + env['MSVS']['VERSION'] = version + + return version + +def get_visualstudio_versions(): + """ + Get list of visualstudio 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. + """ + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError, "No Windows registry module was found" + + HLM = SCons.Util.HKEY_LOCAL_MACHINE + K = r'Software\Microsoft\VisualStudio' + L = [] + try: + k = SCons.Util.RegOpenKeyEx(HLM, K) + i = 0 + while 1: + try: + p = SCons.Util.RegEnumKey(k,i) + except SCons.Util.RegError: + break + i = i + 1 + if not p[0] in '123456789' or p in L: + continue + # Only add this version number if there is a valid + # registry structure (includes the "Setup" key), + # and at least some of the correct directories + # exist. Sometimes VS uninstall leaves around + # some registry/filesystem turds that we don't + # want to trip over. Also, some valid registry + # entries are MSDN entries, not MSVS ('7.1', + # notably), and we want to skip those too. + try: + tst = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p + '\\Setup') + except SCons.Util.RegError: + continue + + id = [] + idk = SCons.Util.RegOpenKeyEx(HLM, K + '\\' + p) + # This is not always here -- it only exists if the + # user installed into a non-standard location (at + # least in VS6 it works that way -- VS7 seems to + # always write it) + try: + id = SCons.Util.RegQueryValueEx(idk, 'InstallDir') + except SCons.Util.RegError: + pass + + # If the InstallDir key doesn't exist, + # then we check the default locations. + if not id or not id[0]: + files_dir = SCons.Platform.win32.get_program_files_dir() + if float(p) < 7.0: + vs = r'Microsoft Visual Studio\Common\MSDev98' + else: + vs = r'Microsoft Visual Studio .NET\Common7\IDE' + id = [ os.path.join(files_dir, vs) ] + if os.path.exists(id[0]): + L.append(p) + except SCons.Util.RegError: + pass + + if not L: + raise SCons.Errors.InternalError, "Microsoft Visual Studio was not found." + + L.sort() + L.reverse() + + return L + +def get_msvs_install_dirs(version = None): + """ + Get installed locations for various msvc-related products, like the .NET SDK + and the Platform SDK. + """ + + if not SCons.Util.can_read_reg: + raise SCons.Errors.InternalError, "No Windows registry module was found" + + if not version: + version = get_visualstudio_versions()[0] #use highest version by default + + K = 'Software\\Microsoft\\VisualStudio\\' + version + + # vc++ install dir + rv = {} + try: + if (float(version) < 7.0): + (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + K + r'\Setup\Microsoft Visual C++\ProductDir') + else: + (rv['VCINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + K + r'\Setup\VC\ProductDir') + except SCons.Util.RegError: + pass + + # visual studio install dir + if (float(version) < 7.0): + try: + (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + K + r'\Setup\Microsoft Visual Studio\ProductDir') + except SCons.Util.RegError: + pass + + if not rv.has_key('VSINSTALLDIR') or not rv['VSINSTALLDIR']: + if rv.has_key('VCINSTALLDIR') and rv['VCINSTALLDIR']: + rv['VSINSTALLDIR'] = os.path.dirname(rv['VCINSTALLDIR']) + else: + rv['VSINSTALLDIR'] = os.path.join(SCons.Platform.win32.get_program_files_dir(),'Microsoft Visual Studio') + else: + try: + (rv['VSINSTALLDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + K + r'\Setup\VS\ProductDir') + except SCons.Util.RegError: + pass + + # .NET framework install dir + try: + (rv['FRAMEWORKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + r'Software\Microsoft\.NETFramework\InstallRoot') + except SCons.Util.RegError: + pass + + if rv.has_key('FRAMEWORKDIR'): + # try and enumerate the installed versions of the .NET framework. + contents = os.listdir(rv['FRAMEWORKDIR']) + l = re.compile('v[0-9]+.*') + versions = [] + for entry in contents: + if l.match(entry): + versions.append(entry) + + def versrt(a,b): + # since version numbers aren't really floats... + aa = a[1:] + bb = b[1:] + aal = aa.split('.') + bbl = bb.split('.') + c = int(bbl[0]) - int(aal[0]) + if c == 0: + c = int(bbl[1]) - int(aal[1]) + if c == 0: + c = int(bbl[2]) - int(aal[2]) + return c + + versions.sort(versrt) + + rv['FRAMEWORKVERSIONS'] = versions + # assume that the highest version is the latest version installed + rv['FRAMEWORKVERSION'] = versions[0] + + # .NET framework SDK install dir + try: + (rv['FRAMEWORKSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + r'Software\Microsoft\.NETFramework\sdkInstallRoot') + except SCons.Util.RegError: + pass + + # MS Platform SDK dir + try: + (rv['PLATFORMSDKDIR'], t) = SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, + r'Software\Microsoft\MicrosoftSDK\Directories\Install Dir') + except SCons.Util.RegError: + pass + + if rv.has_key('PLATFORMSDKDIR'): + # if we have a platform SDK, try and get some info on it. + vers = {} + try: + loc = r'Software\Microsoft\MicrosoftSDK\InstalledSDKs' + k = SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE,loc) + i = 0 + while 1: + try: + key = SCons.Util.RegEnumKey(k,i) + sdk = SCons.Util.RegOpenKeyEx(k,key) + j = 0 + name = '' + date = '' + version = '' + while 1: + try: + (vk,vv,t) = SCons.Util.RegEnumValue(sdk,j) + if vk.lower() == 'keyword': + name = vv + if vk.lower() == 'propagation_date': + date = vv + if vk.lower() == 'version': + version = vv + j = j + 1 + except SCons.Util.RegError: + break + if name: + vers[name] = (date, version) + i = i + 1 + except SCons.Util.RegError: + break + rv['PLATFORMSDK_MODULES'] = vers + except SCons.Util.RegError: + pass + + return rv; + +def GenerateProject(target, source, env): + # generate the dsp file, according to the version of MSVS. + builddspfile = target[0] + builddswfile = target[1] + dswfile = builddswfile.srcnode() + dspfile = builddspfile.srcnode() + +# print "SConscript :",str(source[0]) +# print "DSW file :",dswfile +# print "DSP file :",dspfile +# print "Build DSW file:",builddswfile +# print "Build DSP file:",builddspfile + + # this detects whether or not we're using a BuildDir + if os.path.abspath(os.path.normcase(str(dspfile))) != \ + os.path.abspath(os.path.normcase(str(builddspfile))): + try: + bdsp = file(str(builddspfile), "w+") + except IOError, detail: + print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' + raise + + bdsp.write("This is just a placeholder file.\nThe real project file is here:\n%s\n" % dspfile.get_abspath()) + + try: + bdsw = file(str(builddswfile), "w+") + except IOError, detail: + print 'Unable to open "' + str(dspfile) + '" for writing:',detail,'\n' + raise + + bdsw.write("This is just a placeholder file.\nThe real workspace file is here:\n%s\n" % dswfile.get_abspath()) + + GenerateDSP(dspfile, source, env) + GenerateDSW(dswfile, dspfile, source, env) + +def projectEmitter(target, source, env): + """Sets up the DSP and DSW dependencies for an SConscript file.""" + + if source[0] == target[0]: + source = [] + + # make sure the suffix is correct for the version of MSVS we're running. + (base, suff) = os.path.splitext(str(target[0])) + suff = env['MSVSPROJECTSUFFIX'] + target[0] = base + suff + + dspfile = SCons.Node.FS.default_fs.File(target[0]).srcnode() + dswfile = SCons.Node.FS.default_fs.File(os.path.splitext(str(dspfile))[0] + env['MSVSSOLUTIONSUFFIX']) + + if not source: + source = [SCons.Script.SConscript.stack[-1].sconscript.srcnode()] + + source[0].attributes.sconstruct = SCons.Script.SConscript.stack[0].sconscript + + bdswpath = os.path.splitext(str(target[0]))[0] + env['MSVSSOLUTIONSUFFIX'] + bdswfile = SCons.Node.FS.default_fs.File(bdswpath) + + # only make these side effects if they're + # not the same file. + if os.path.abspath(os.path.normcase(str(dspfile))) != \ + os.path.abspath(os.path.normcase(str(target[0]))): + env.SideEffect(dspfile, target[0]) + env.Precious(dspfile) + # dswfile isn't precious -- it can be blown away and rewritten each time. + env.SideEffect(dswfile, target[0]) + + return ([target[0],bdswfile], source) + +projectGeneratorAction = SCons.Action.Action(GenerateProject, None) + +projectBuilder = SCons.Builder.Builder(action = '$MSVSPROJECTCOM', + suffix = '$MSVSPROJECTSUFFIX', + emitter = projectEmitter) + +def generate(env): + """Add Builders and construction variables for Microsoft Visual + Studio project files to an Environment.""" + try: + bld = env['BUILDERS']['MSVSProject'] + except KeyError: + env['BUILDERS']['MSVSProject'] = projectBuilder + + env['MSVSPROJECTCOM'] = projectGeneratorAction + + version = get_default_visualstudio_version(env) + + # keep a record of some of the MSVS info so the user can use it. + try: + dirs = get_msvs_install_dirs(version) + env['MSVS'].update(dirs) + except (SCons.Util.RegError, SCons.Errors.InternalError): + # we don't care if we can't do this -- if we can't, it's + # because we don't have access to the registry, or because the + # tools aren't installed. In either case, the user will have to + # find them on their own. + pass + + if (float(env['MSVS_VERSION']) < 7.0): + env['MSVSPROJECTSUFFIX'] = '.dsp' + env['MSVSSOLUTIONSUFFIX'] = '.dsw' + else: + env['MSVSPROJECTSUFFIX'] = '.vcproj' + env['MSVSSOLUTIONSUFFIX'] = '.sln' + +def exists(env): + if not SCons.Util.can_read_reg or not get_visualstudio_versions(): + if env.has_key('MSVS_VERSION') and float(env['MSVS_VERSION']) >= 7.0: + return env.Detect('devenv') + else: + return env.Detect('msdev') + else: + # there's at least one version of MSVS installed. + return True + diff --git a/src/engine/SCons/Tool/msvsTests.py b/src/engine/SCons/Tool/msvsTests.py new file mode 100644 index 0000000..731cc2d --- /dev/null +++ b/src/engine/SCons/Tool/msvsTests.py @@ -0,0 +1,476 @@ +# +# __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 os +import string +import sys +import TestCmd +import unittest + +from SCons.Tool.msvs import * +import SCons.Util +import SCons.Warnings + +regdata_6a = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks] +"sp3"="" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup] +"VsCommonDir"="C:\Program Files\Microsoft Visual Studio\Common" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Developer Network Library - Visual Studio 6.0a] +"ProductDir"="C:\Program Files\Microsoft Visual Studio\MSDN98\98VSa\1033" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++] +"ProductDir"="C:\Program Files\Microsoft Visual Studio\VC98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] +"ProgramFilesDir"="C:\Program Files" +"CommonFilesDir"="C:\Program Files\Common Files" +"MediaPath"="C:\WINDOWS\Media" +''','\n') + +regdata_6b = string.split(r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0] +"InstallDir"="C:\VS6\Common\IDE\IDE98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks] +"sp5"="" +"latest"=dword:00000005 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup] +"VsCommonDir"="C:\VS6\Common" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic] +"ProductDir"="C:\VS6\VB98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++] +"ProductDir"="C:\VS6\VC98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio] +"ProductDir"="C:\VS6" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft VSEE Client] +"ProductDir"="C:\VS6\Common\Tools" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Visual Studio 98] +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] +"ProgramFilesDir"="C:\Program Files" +"CommonFilesDir"="C:\Program Files\Common Files" +"MediaPath"="C:\WINDOWS\Media" +''','\n') + +regdata_7 = string.split(r''' +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0] +"InstallDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +"Source Directories"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\crt\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\atl\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\CrystalReports] +@="#15007" +"Package"="{F05E92C6-8346-11D3-B4AD-00A0C9B04E7B}" +"ProductDetails"="#15009" +"LogoID"="0" +"PID"="#15008" +"UseInterface"=dword:00000001 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual Basic.NET] +@="" +"DefaultProductAttribute"="VB" +"Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}" +"UseInterface"=dword:00000001 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual C#] +@="" +"Package"="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" +"UseInterface"=dword:00000001 +"DefaultProductAttribute"="C#" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\VisualC++] +"UseInterface"=dword:00000001 +"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}" +"DefaultProductAttribute"="VC" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup] +"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +"dw_dir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\MSDN] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Msdn\1033\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Servicing\SKU] +"Visual Studio .NET Professional - English"="{D0610409-7D65-11D5-A54F-0090278A1BB8}" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VB] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vb7\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC#] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\VC#\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Visual Studio .NET Professional - English] +"InstallSuccess"=dword:00000001 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS] +"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" +"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" +"MSMDir"="C:\Program Files\Common Files\Merge Modules\" +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\" +"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\" +"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\" +"VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET\Setup\VSUpdate\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\BuildNumber] +"1033"="7.0.9466" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\Pro] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32] +@="{A54AAE91-30C2-11D3-87BF-A04A4CC10000}" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories] +"Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\perl\bin;C:\cygwin\bin;c:\cygwin\usr\bin;C:\bin;C:\program files\perforce;C:\cygwin\usr\local\bin\i686-pc-cygwin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem" +"Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib" +"Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include" +"Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src" +"Reference Dirs"="" +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] +"ProgramFilesDir"="C:\Program Files" +"CommonFilesDir"="C:\Program Files\Common Files" +"MediaPath"="C:\WINDOWS\Media" +''','\n') + +regdata_67 = string.split(r''' +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0] +"InstallDir"="C:\VS6\Common\IDE\IDE98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\ServicePacks] +"sp5"="" +"latest"=dword:00000005 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup] +"VsCommonDir"="C:\VS6\Common" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Basic] +"ProductDir"="C:\VS6\VB98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++] +"ProductDir"="C:\VS6\VC98" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio] +"ProductDir"="C:\VS6" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Microsoft VSEE Client] +"ProductDir"="C:\VS6\Common\Tools" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\6.0\Setup\Visual Studio 98] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0] +"InstallDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +"Source Directories"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\crt\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\mfc\;C:\Program Files\Microsoft Visual Studio .NET\Vc7\atlmfc\src\atl\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\CrystalReports] +@="#15007" +"Package"="{F05E92C6-8346-11D3-B4AD-00A0C9B04E7B}" +"ProductDetails"="#15009" +"LogoID"="0" +"PID"="#15008" +"UseInterface"=dword:00000001 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual Basic.NET] +@="" +"DefaultProductAttribute"="VB" +"Package"="{164B10B9-B200-11D0-8C61-00A0C91E29D5}" +"UseInterface"=dword:00000001 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\Visual C#] +@="" +"Package"="{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}" +"UseInterface"=dword:00000001 +"DefaultProductAttribute"="C#" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\InstalledProducts\VisualC++] +"UseInterface"=dword:00000001 +"Package"="{F1C25864-3097-11D2-A5C5-00C04F7968B4}" +"DefaultProductAttribute"="VC" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup] +"Dbghelp_path"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +"dw_dir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\MSDN] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Msdn\1033\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Servicing\SKU] +"Visual Studio .NET Professional - English"="{D0610409-7D65-11D5-A54F-0090278A1BB8}" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VB] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vb7\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\Vc7\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VC#] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\VC#\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\Visual Studio .NET Professional - English] +"InstallSuccess"=dword:00000001 +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS] +"EnvironmentDirectory"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\" +"EnvironmentPath"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" +"VS7EnvironmentLocation"="C:\Program Files\Microsoft Visual Studio .NET\Common7\IDE\devenv.exe" +"MSMDir"="C:\Program Files\Common Files\Merge Modules\" +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\" +"VS7CommonBinDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\Tools\" +"VS7CommonDir"="C:\Program Files\Microsoft Visual Studio .NET\Common7\" +"VSUpdateDir"="C:\Program Files\Microsoft Visual Studio .NET\Setup\VSUpdate\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\BuildNumber] +"1033"="7.0.9466" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\Setup\VS\Pro] +"ProductDir"="C:\Program Files\Microsoft Visual Studio .NET\" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO] +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32] +@="{A54AAE91-30C2-11D3-87BF-A04A4CC10000}" +[HKEY_LOCAL_MACHINE\Software\Microsoft\VisualStudio\7.0\VC\VC_OBJECTS_PLATFORM_INFO\Win32\Directories] +"Path Dirs"="$(VCInstallDir)bin;$(VSInstallDir)Common7\Tools\bin\prerelease;$(VSInstallDir)Common7\Tools\bin;$(VSInstallDir)Common7\tools;$(VSInstallDir)Common7\ide;C:\Program Files\HTML Help Workshop\;$(FrameworkSDKDir)bin;$(FrameworkDir)$(FrameworkVersion);C:\perl\bin;C:\cygwin\bin;c:\cygwin\usr\bin;C:\bin;C:\program files\perforce;C:\cygwin\usr\local\bin\i686-pc-cygwin;C:\WINDOWS\system32;C:\WINDOWS;C:\WINDOWS\System32\Wbem" +"Library Dirs"="$(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(VCInstallDir)PlatformSDK\lib\prerelease;$(VCInstallDir)PlatformSDK\lib;$(FrameworkSDKDir)lib" +"Include Dirs"="$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(VCInstallDir)PlatformSDK\include\prerelease;$(VCInstallDir)PlatformSDK\include;$(FrameworkSDKDir)include" +"Source Dirs"="$(VCInstallDir)atlmfc\src\mfc;$(VCInstallDir)atlmfc\src\atl;$(VCInstallDir)crt\src" +"Reference Dirs"="" +[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] +"ProgramFilesDir"="C:\Program Files" +"CommonFilesDir"="C:\Program Files\Common Files" +"MediaPath"="C:\WINDOWS\Media" +''','\n') + + +class DummyEnv: + def __init__(self, dict=None): + if dict: + self.dict = dict + else: + self.dict = {} + + def Dictionary(self, key = None): + if not key: + return self.dict + return self.dict[key] + + def __setitem__(self,key,value): + self.dict[key] = value + + def __getitem__(self,key): + return self.dict[key] + + def has_key(self,name): + return self.dict.has_key(name) + +class RegKey: + """key class for storing an 'open' registry key""" + def __init__(self,key): + self.key = key + +class RegNode: + """node in the dummy registry""" + def __init__(self,name): + self.valdict = {} + self.keydict = {} + self.keyarray = [] + self.valarray = [] + self.name = name + + def value(self,val): + if self.valdict.has_key(val): + return (self.valdict[val],1) + else: + raise SCons.Util.RegError + + def addValue(self,name,val): + self.valdict[name] = val + self.valarray.append(name) + + def valindex(self,index): + rv = None + try: + rv = (self.valarray[index],self.valdict[self.valarray[index]],1) + except KeyError: + raise SCons.Util.RegError + return rv + + def key(self,key,sep = '\\'): + if key.find(sep) != -1: + keyname, subkeys = key.split(sep,1) + else: + keyname = key + subkeys = "" + try: + # recurse, and return the lowest level key node + if subkeys: + return self.keydict[keyname].key(subkeys) + else: + return self.keydict[keyname] + except KeyError: + raise SCons.Util.RegError + + def addKey(self,name,sep = '\\'): + if name.find(sep) != -1: + keyname, subkeys = name.split(sep,1) + else: + keyname = name + subkeys = "" + + if not self.keydict.has_key(keyname): + self.keydict[keyname] = RegNode(keyname) + self.keyarray.append(keyname) + + # recurse, and return the lowest level key node + if subkeys: + return self.keydict[keyname].addKey(subkeys) + else: + return self.keydict[keyname] + + def keyindex(self,index): + return self.keydict[self.keyarray[index]] + + def __str__(self): + return self._doStr() + + def _doStr(self, indent = ''): + rv = "" + for value in self.valarray: + rv = rv + '%s"%s" = "%s"\n' % (indent, value, self.valdict[value]) + for key in self.keyarray: + rv = rv + "%s%s: {\n"%(indent, key) + rv = rv + self.keydict[key]._doStr(indent + ' ') + rv = rv + indent + '}\n' + return rv + +class DummyRegistry: + """registry class for storing fake registry attributes""" + def __init__(self,data): + """parse input data into the fake registry""" + self.root = RegNode('REGISTRY') + self.root.addKey('HKEY_LOCAL_MACHINE') + self.root.addKey('HKEY_CURRENT_USER') + self.root.addKey('HKEY_USERS') + self.root.addKey('HKEY_CLASSES_ROOT') + + self.parse(data) + + def parse(self, data): + parent = self.root + keymatch = re.compile('^\[(.*)\]$') + valmatch = re.compile('^(?:"(.*)"|[@])="(.*)"$') + for line in data: + m1 = keymatch.match(line) + if m1: + # add a key, set it to current parent + parent = self.root.addKey(m1.group(1)) + else: + m2 = valmatch.match(line) + if m2: + parent.addValue(m2.group(1),m2.group(2)) + + def OpenKeyEx(self,root,key): + if root == SCons.Util.HKEY_CLASSES_ROOT: + mykey = 'HKEY_CLASSES_ROOT\\' + key + if root == SCons.Util.HKEY_USERS: + mykey = 'HKEY_USERS\\' + key + if root == SCons.Util.HKEY_CURRENT_USER: + mykey = 'HKEY_CURRENT_USER\\' + key + if root == SCons.Util.HKEY_LOCAL_MACHINE: + mykey = 'HKEY_LOCAL_MACHINE\\' + key + #print "Open Key",mykey + return self.root.key(mykey) + +def DummyOpenKeyEx(root, key): + return registry.OpenKeyEx(root,key) + +def DummyEnumKey(key, index): + rv = None + try: + rv = key.keyarray[index] + except IndexError: + raise SCons.Util.RegError +# print "Enum Key",key.name,"[",index,"] =>",rv + return rv + +def DummyEnumValue(key, index): + rv = key.valindex(index) +# print "Enum Value",key.name,"[",index,"] =>",rv + return rv + +def DummyQueryValue(key, value): + rv = key.value(value) +# print "Query Value",key.name+"\\"+value,"=>",rv + return rv + +def DummyExists(path): + return True + +class msvsTestCase(unittest.TestCase): + def test_get_default_visual_studio_version(self): + """Test retrieval of the default visual studio version""" + env = DummyEnv() + v1 = get_default_visualstudio_version(env) + assert env['MSVS_VERSION'] == default_version + assert env['MSVS']['VERSION'] == default_version + assert v1 == default_version + + env = DummyEnv({'MSVS_VERSION':'7.0'}) + v2 = get_default_visualstudio_version(env) + assert env['MSVS_VERSION'] == '7.0' + assert env['MSVS']['VERSION'] == '7.0' + assert v2 == '7.0' + + def test_get_visual_studio_versions(self): + """Test retrieval of the list of visual studio versions""" + v1 = get_visualstudio_versions() + assert v1[0] == highest_version + assert len(v1) == number_of_versions + + def test_get_msvs_install_dirs(self): + """Test retrieval of the list of visual studio installed locations""" + v1 = get_msvs_install_dirs() + v2 = get_msvs_install_dirs('7.0') + assert v1 == install_location1 + assert v2 == install_location2 + +if __name__ == "__main__": + + # only makes sense to test this on win32 + if sys.platform != 'win32': + sys.exit(0) + + SCons.Util.RegOpenKeyEx = DummyOpenKeyEx + SCons.Util.RegEnumKey = DummyEnumKey + SCons.Util.RegEnumValue = DummyEnumValue + SCons.Util.RegQueryValueEx = DummyQueryValue + os.path.exists = DummyExists # make sure all files exist :-) + + # try it for each possible setup. + suite = unittest.makeSuite(msvsTestCase, 'test_') + registry = DummyRegistry(regdata_6a) + default_version = '6.0' + highest_version = '6.0' + number_of_versions = 1 + install_location1 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio\\VC98'} + install_location2 = {} + # print str(registry.root) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) + + registry = DummyRegistry(regdata_6b) + default_version = '6.0' + highest_version = '6.0' + number_of_versions = 1 + install_location1 = {'VSINSTALLDIR': 'C:\\VS6', 'VCINSTALLDIR': 'C:\\VS6\\VC98'} + install_location2 = {} + # print str(registry.root) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) + + registry = DummyRegistry(regdata_67) + default_version = '7.0' + highest_version = '7.0' + number_of_versions = 2 + install_location1 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'} + install_location2 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'} + # print str(registry.root) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) + + registry = DummyRegistry(regdata_7) + default_version = '7.0' + highest_version = '7.0' + number_of_versions = 1 + install_location1 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'} + install_location2 = {'VSINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\', 'VCINSTALLDIR': 'C:\\Program Files\\Microsoft Visual Studio .NET\\Vc7\\'} + # print str(registry.root) + if not unittest.TextTestRunner().run(suite).wasSuccessful(): + sys.exit(1) diff --git a/src/engine/SCons/Util.py b/src/engine/SCons/Util.py index 2ebe0d9..06a3dac 100644 --- a/src/engine/SCons/Util.py +++ b/src/engine/SCons/Util.py @@ -737,6 +737,16 @@ if can_read_reg: HKEY_CURRENT_USER = hkey_mod.HKEY_CURRENT_USER HKEY_USERS = hkey_mod.HKEY_USERS + def RegGetValue(root, key): + """Returns a value in the registry without + having to open the key first.""" + # I would use os.path.split here, but it's not a filesystem + # path... + p = key.rfind('\\') + 1 + keyp = key[:p] + val = key[p:] + k = SCons.Util.RegOpenKeyEx(root, keyp) + return SCons.Util.RegQueryValueEx(k,val) if sys.platform == 'win32': @@ -803,6 +813,84 @@ else: return os.path.normpath(f) return None +def PrependPath(oldpath, newpath, sep = os.pathsep): + """Prepend newpath elements to the given oldpath. Will only add + any particular path once (leaving the first one it encounters and + ignoring the rest, to preserve path order), and will normpath and + normcase all paths to help assure this. This can also handle the + case where the given oldpath variable is a list instead of a + string, in which case a list will be returned instead of a string. + """ + + orig = oldpath + is_list = 1 + paths = orig + if not SCons.Util.is_List(orig): + paths = string.split(paths, sep) + is_list = 0 + + if SCons.Util.is_List(newpath): + newpaths = newpath + else: + newpaths = string.split(newpath, sep) + + newpaths = newpaths + paths # prepend new paths + + normpaths = [] + paths = [] + # now we add them only of they are unique + for path in newpaths: + normpath = os.path.normpath(os.path.normcase(path)) + if path and not normpath in normpaths: + paths.append(path) + normpaths.append(normpath) + + if is_list: + return paths + else: + return string.join(paths, sep) + +def AppendPath(oldpath, newpath, sep = os.pathsep): + """Append newpath elements to the given oldpath. Will only add + any particular path once (leaving the first one it encounters and + ignoring the rest, to preserve path order), and will normpath and + normcase all paths to help assure this. This can also handle the + case where the given oldpath variable is a list instead of a + string, in which case a list will be returned instead of a string. + """ + + orig = oldpath + is_list = 1 + paths = orig + if not SCons.Util.is_List(orig): + paths = string.split(paths, sep) + is_list = 0 + + if SCons.Util.is_List(newpath): + newpaths = newpath + else: + newpaths = string.split(newpath, sep) + + newpaths = paths + newpaths # append new paths + newpaths.reverse() + + normpaths = [] + paths = [] + # now we add them only of they are unique + for path in newpaths: + normpath = os.path.normpath(os.path.normcase(path)) + if path and not normpath in normpaths: + paths.append(path) + normpaths.append(normpath) + + paths.reverse() + + if is_list: + return paths + else: + return string.join(paths, sep) + + def ParseConfig(env, command, function=None): """Use the specified function to parse the output of the command in order to modify the specified environment. The 'command' can be a string or a diff --git a/src/engine/SCons/UtilTests.py b/src/engine/SCons/UtilTests.py index 7f5f166..b7b41be 100644 --- a/src/engine/SCons/UtilTests.py +++ b/src/engine/SCons/UtilTests.py @@ -815,6 +815,30 @@ class UtilTestCase(unittest.TestCase): SOURCES.sort() assert SOURCES == ['rstr-s4', 's3'], d['SOURCES'] + def test_PrependPath(self): + """Test prepending to a path""" + p1 = r'C:\dir\num\one;C:\dir\num\two' + p2 = r'C:\mydir\num\one;C:\mydir\num\two' + # have to include the pathsep here so that the test will work on UNIX too. + p1 = PrependPath(p1,r'C:\dir\num\two',sep = ';') + p1 = PrependPath(p1,r'C:\dir\num\three',sep = ';') + p2 = PrependPath(p2,r'C:\mydir\num\three',sep = ';') + p2 = PrependPath(p2,r'C:\mydir\num\one',sep = ';') + assert(p1 == r'C:\dir\num\three;C:\dir\num\two;C:\dir\num\one') + assert(p2 == r'C:\mydir\num\one;C:\mydir\num\three;C:\mydir\num\two') + + def test_AppendPath(self): + """Test appending to a path.""" + p1 = r'C:\dir\num\one;C:\dir\num\two' + p2 = r'C:\mydir\num\one;C:\mydir\num\two' + # have to include the pathsep here so that the test will work on UNIX too. + p1 = AppendPath(p1,r'C:\dir\num\two',sep = ';') + p1 = AppendPath(p1,r'C:\dir\num\three',sep = ';') + p2 = AppendPath(p2,r'C:\mydir\num\three',sep = ';') + p2 = AppendPath(p2,r'C:\mydir\num\one',sep = ';') + assert(p1 == r'C:\dir\num\one;C:\dir\num\two;C:\dir\num\three') + assert(p2 == r'C:\mydir\num\two;C:\mydir\num\three;C:\mydir\num\one') + def test_NodeList(self): """Test NodeList class""" class TestClass: diff --git a/test/import.py b/test/import.py index d66658b..8dc3682 100644 --- a/test/import.py +++ b/test/import.py @@ -45,38 +45,61 @@ x = SCons.Platform.%s.generate tools = [ # Can't import '386asm' directly due to initial '3' syntax error... + 'aixcc', + 'aixf77', + 'aixlink', 'ar', 'as', + 'BitKeeper', 'cc', + 'CVS', 'default', 'dvipdf', 'dvips', + 'f77', + # Can't import 'g++' directly due to '+' syntax error... 'g77', 'gas', 'gcc', 'gnulink', - # Can't import 'g++' directly due to '+' syntax error... + 'gs', + 'hpcc', + 'hplink', 'icc', 'ifl', 'ilink', + 'jar', + 'javac', + 'javah', 'latex', 'lex', 'link', # Can't import 'linkloc' everywhere due to Windows registry dependency... 'masm', + 'midl', 'mingw', 'mslib', 'mslink', 'msvc', + 'msvs', 'nasm', 'pdflatex', 'pdftex', + 'Perforce', + 'RCS', + 'rmic', + 'SCCS', 'sgiar', 'sgicc', 'sgilink', + 'sunar', + 'suncc', + 'sunlink', + 'Subversion', 'tar', 'tex', 'yacc', + 'zip', ] for tool in tools: diff --git a/test/midl.py b/test/midl.py index 59fba57..2e48db2 100644 --- a/test/midl.py +++ b/test/midl.py @@ -63,7 +63,7 @@ local = env.Copy(WIN32_INSERT_DEF = 1) barsrc = [ 'BarObject.cpp', 'bar.cpp', - local.RES('bar.rc', RCFLAGS= '/I\"${SOURCE.srcdir}\"'), + local.RES('bar.rc', RCFLAGS= '/I${SOURCE.srcdir}'), ] local.TypeLibrary('bar.idl') diff --git a/test/msvs.py b/test/msvs.py new file mode 100644 index 0000000..03f08cb --- /dev/null +++ b/test/msvs.py @@ -0,0 +1,415 @@ +#!/usr/bin/env python +# +# __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 TestSCons +import sys +import re +import os.path +import os +import TestCmd +import time + +expected_dspfile = '''\ +# Microsoft Developer Studio Project File - Name="Test" - Package Owner=<4> +# Microsoft Developer Studio Generated Build File, Format Version 6.00 +# ** DO NOT EDIT ** + +# TARGTYPE "Win32 (x86) External Target" 0x0106 + +CFG=Test - Win32 Release +!MESSAGE This is not a valid makefile. To build this project using NMAKE, +!MESSAGE use the Export Makefile command and run +!MESSAGE +!MESSAGE NMAKE /f "Test.mak". +!MESSAGE +!MESSAGE You can specify a configuration when running NMAKE +!MESSAGE by defining the macro CFG on the command line. For example: +!MESSAGE +!MESSAGE NMAKE /f "Test.mak" CFG="Test - Win32 Release" +!MESSAGE +!MESSAGE Possible choices for configuration are: +!MESSAGE +!MESSAGE "Test - Win32 Release" (based on "Win32 (x86) External Target") +!MESSAGE + +# Begin Project +# PROP AllowPerConfigDependencies 0 +# PROP Scc_ProjName "" +# PROP Scc_LocalPath "" + +!IF "$(CFG)" == "Test - Win32 Release" + +# PROP BASE Use_MFC 0 +# PROP BASE Use_Debug_Libraries 0 +# PROP BASE Output_Dir "<WORKPATH>" +# PROP BASE Intermediate_Dir "<WORKPATH>" +# PROP BASE Cmd_Line "<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe" +# PROP BASE Rebuild_Opt "-c && <PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe" +# PROP BASE Target_File "<WORKPATH>\Test.exe" +# PROP BASE Bsc_Name "" +# PROP BASE Target_Dir "" +# PROP Use_MFC 0 +# PROP Use_Debug_Libraries 0 +# PROP Output_Dir "<WORKPATH>" +# PROP Intermediate_Dir "<WORKPATH>" +# PROP Cmd_Line "<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe" +# PROP Rebuild_Opt "-c && <PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe" +# PROP Target_File "<WORKPATH>\Test.exe" +# PROP Bsc_Name "" +# PROP Target_Dir "" + +!ENDIF + +# Begin Target + +# Name "Test - Win32 Release" + +!IF "$(CFG)" == "Test - Win32 Release" + +!ENDIF + +# Begin Group " Source Files" + +# PROP Default_Filter "cpp;c;cxx;l;y;def;odl;idl;hpj;bat" +# Begin Source File + +SOURCE="test.cpp" +# End Source File +# End Group +# Begin Group "Header Files" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE="sdk.h" +# End Source File +# End Group +# Begin Group "Local Headers" + +# PROP Default_Filter "h;hpp;hxx;hm;inl" +# Begin Source File + +SOURCE="test.h" +# End Source File +# End Group +# Begin Group "Other Files" + +# PROP Default_Filter "" +# Begin Source File + +SOURCE="readme.txt" +# End Source File +# End Group +# Begin Group "Resource Files" + +# PROP Default_Filter "r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe" +# Begin Source File + +SOURCE="test.rc" +# End Source File +# End Group +# Begin Source File + +SOURCE="<WORKPATH>\SConstruct" +# End Source File +# End Target +# End Project +''' + +expected_dswfile = '''\ +Microsoft Developer Studio Workspace File, Format Version 6.00 +# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE! + +############################################################################### + +Project: "Test"="<WORKPATH>\Test.dsp" - Package Owner=<4> + +Package=<5> +{{{ +}}} + +Package=<4> +{{{ +}}} + +############################################################################### + +Global: + +Package=<5> +{{{ +}}} + +Package=<3> +{{{ +}}} + +############################################################################### +''' + +expected_slnfile = '''\ +Microsoft Visual Studio Solution File, Format Version 7.00 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Test", "Test.vcproj", "{SLNGUID}" +EndProject +Global + GlobalSection(SolutionConfiguration) = preSolution + ConfigName.0 = Release + EndGlobalSection + GlobalSection(ProjectDependencies) = postSolution + EndGlobalSection + GlobalSection(ProjectConfiguration) = postSolution + {SLNGUID}.Release.ActiveCfg = Release|Win32 + {SLNGUID}.Release.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + EndGlobalSection + GlobalSection(ExtensibilityAddIns) = postSolution + EndGlobalSection +EndGlobal +''' + +expected_vcprojfile = '''\ +<?xml version="1.0" encoding = "Windows-1252"?> +<VisualStudioProject + ProjectType="Visual C++" + Version="7.00" + Name="Test" + SccProjectName="" + SccLocalPath="" + Keyword="MakeFileProj"> + <Platforms> + <Platform + Name="Win32"/> + </Platforms> + <Configurations> + <Configuration + Name="Release|Win32" + OutputDirectory="<WORKPATH>" + IntermediateDirectory="<WORKPATH>" + ConfigurationType="0" + UseOfMFC="0" + ATLMinimizesCRunTimeLibraryUsage="FALSE"> + <Tool + Name="VCNMakeTool" + BuildCommandLine="<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe +" + CleanCommandLine="<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct -c <WORKPATH>\Test.exe" + RebuildCommandLine="<PYTHON> <SCONS> -C <WORKPATH> -f SConstruct <WORKPATH>\Test.exe +" + Output="<WORKPATH>\Test.exe"/> + </Configuration> + </Configurations> + <Files> + <Filter + Name=" Source Files" + Filter="cpp;c;cxx;l;y;def;odl;idl;hpj;bat"> + <File + RelativePath="test.cpp"> + </File> + </Filter> + <Filter + Name="Header Files" + Filter="h;hpp;hxx;hm;inl"> + <File + RelativePath="sdk.h"> + </File> + </Filter> + <Filter + Name="Local Headers" + Filter="h;hpp;hxx;hm;inl"> + <File + RelativePath="test.h"> + </File> + </Filter> + <Filter + Name="Other Files" + Filter=""> + <File + RelativePath="readme.txt"> + </File> + </Filter> + <Filter + Name="Resource Files" + Filter="r;rc;ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"> + <File + RelativePath="test.rc"> + </File> + </Filter> + <File + RelativePath="<WORKPATH>\SConstruct"> + </File> + </Files> + <Globals> + </Globals> +</VisualStudioProject> +''' + +test = TestSCons.TestSCons(match = TestCmd.match_re) + +if sys.platform != 'win32': + test.pass_test() + +#### +# Determine which environments are installed on the test machine. +test.write('SConstruct',''' +env = Environment() + +f = open('versions','w') +f.write('versions = ' + str(env['MSVS']['VERSIONS'])) +f.close() +''') + +test.run() +versions = [] +execfile(test.workpath('versions')) + +##### +# Test v6.0 output + +if '6.0' in versions: + test.write('SConstruct',''' +env=Environment(MSVS_VERSION = '6.0') + +testsrc = ['test.cpp'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.dsp', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') + ''') + + test.run(arguments="Test.dsp") + + test.fail_test(not os.path.exists(test.workpath('Test.dsp'))) + test.fail_test(not os.path.exists(test.workpath('Test.dsw'))) + + # check to see that we got what we expected: + expected_dspfile = expected_dspfile.replace(r'<WORKPATH>',test.workpath()) + expected_dspfile = expected_dspfile.replace(r'<PYTHON>',sys.executable) + expected_dspfile = expected_dspfile.replace(r'<SCONS>',os.path.join(os.environ['SCONS_SCRIPT_DIR'],'scons.py')) + expected_dswfile = expected_dswfile.replace(r'<WORKPATH>',test.workpath()) + + f = open(test.workpath('Test.dsp')) + dsp = f.read() + f.close() + + # don't compare the pickled data + assert dsp[:len(expected_dspfile)] == expected_dspfile + + f = open(test.workpath('Test.dsw')) + dsw = f.read() + f.close() + assert dsw == expected_dswfile + + test.run(arguments='-c .') + + test.fail_test(os.path.exists(test.workpath('Test.dsp'))) + test.fail_test(os.path.exists(test.workpath('Test.dsw'))) + + test.run(arguments='Test.dsp') + + test.fail_test(not os.path.exists(test.workpath('Test.dsp'))) + test.fail_test(not os.path.exists(test.workpath('Test.dsw'))) + + test.run(arguments='-c Test.dsw') + + test.fail_test(os.path.exists(test.workpath('Test.dsp'))) + test.fail_test(os.path.exists(test.workpath('Test.dsw'))) + +##### +# Test .NET output + +if '7.0' in versions: + test.write('SConstruct',''' +env=Environment(MSVS_VERSION = '7.0') + +testsrc = ['test.cpp'] +testincs = ['sdk.h'] +testlocalincs = ['test.h'] +testresources = ['test.rc'] +testmisc = ['readme.txt'] + +env.MSVSProject(target = 'Test.vcproj', + slnguid = '{SLNGUID}', + srcs = testsrc, + incs = testincs, + localincs = testlocalincs, + resources = testresources, + misc = testmisc, + buildtarget = 'Test.exe', + variant = 'Release') + ''') + + test.run(arguments="Test.vcproj") + + test.fail_test(not os.path.exists(test.workpath('Test.vcproj'))) + test.fail_test(not os.path.exists(test.workpath('Test.sln'))) + + f = open(test.workpath('Test.vcproj')) + vcproj = f.read() + f.close() + expected_vcprojfile = expected_vcprojfile.replace(r'<WORKPATH>',test.workpath()) + expected_vcprojfile = expected_vcprojfile.replace(r'<PYTHON>',sys.executable) + expected_vcprojfile = expected_vcprojfile.replace(r'<SCONS>',os.path.join(os.environ['SCONS_SCRIPT_DIR'],'scons.py')) + + # don't compare the pickled data + assert vcproj[:len(expected_vcprojfile)] == expected_vcprojfile + + f = open(test.workpath('Test.sln')) + sln = f.read() + f.close() + + assert sln[:len(expected_slnfile)] == expected_slnfile + + test.run(arguments='-c .') + + test.fail_test(os.path.exists(test.workpath('Test.vcproj'))) + test.fail_test(os.path.exists(test.workpath('Test.sln'))) + + test.run(arguments='Test.vcproj') + + test.fail_test(not os.path.exists(test.workpath('Test.vcproj'))) + test.fail_test(not os.path.exists(test.workpath('Test.sln'))) + + test.run(arguments='-c Test.sln') + + test.fail_test(os.path.exists(test.workpath('Test.vcproj'))) + test.fail_test(os.path.exists(test.workpath('Test.sln'))) + +test.pass_test() + + + + + |