diff options
Diffstat (limited to 'engine/SCons/Tool/MSCommon')
-rw-r--r-- | engine/SCons/Tool/MSCommon/__init__.py | 56 | ||||
-rw-r--r-- | engine/SCons/Tool/MSCommon/arch.py | 61 | ||||
-rw-r--r-- | engine/SCons/Tool/MSCommon/common.py | 195 | ||||
-rw-r--r-- | engine/SCons/Tool/MSCommon/netframework.py | 84 | ||||
-rw-r--r-- | engine/SCons/Tool/MSCommon/sdk.py | 321 | ||||
-rw-r--r-- | engine/SCons/Tool/MSCommon/vc.py | 367 | ||||
-rw-r--r-- | engine/SCons/Tool/MSCommon/vs.py | 497 |
7 files changed, 1581 insertions, 0 deletions
diff --git a/engine/SCons/Tool/MSCommon/__init__.py b/engine/SCons/Tool/MSCommon/__init__.py new file mode 100644 index 0000000..4f56a4a --- /dev/null +++ b/engine/SCons/Tool/MSCommon/__init__.py @@ -0,0 +1,56 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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__ = "src/engine/SCons/Tool/MSCommon/__init__.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """ +Common functions for Microsoft Visual Studio and Visual C/C++. +""" + +import copy +import os +import re +import subprocess + +import SCons.Errors +import SCons.Platform.win32 +import SCons.Util + +from SCons.Tool.MSCommon.sdk import mssdk_exists, \ + mssdk_setup_env + +from SCons.Tool.MSCommon.vc import msvc_exists, \ + msvc_setup_env, \ + msvc_setup_env_once + +from SCons.Tool.MSCommon.vs import get_default_version, \ + get_vs_by_version, \ + merge_default_version, \ + msvs_exists, \ + query_versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/arch.py b/engine/SCons/Tool/MSCommon/arch.py new file mode 100644 index 0000000..ce214c2 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/arch.py @@ -0,0 +1,61 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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__ = "src/engine/SCons/Tool/MSCommon/arch.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """Module to define supported Windows chip architectures. +""" + +import os + +class ArchDefinition: + """ + A class for defining architecture-specific settings and logic. + """ + def __init__(self, arch, synonyms=[]): + self.arch = arch + self.synonyms = synonyms + +SupportedArchitectureList = [ + ArchitectureDefinition( + 'x86', + ['i386', 'i486', 'i586', 'i686'], + ), + + ArchitectureDefinition( + 'x86_64', + ['AMD64', 'amd64', 'em64t', 'EM64T', 'x86_64'], + ), + + ArchitectureDefinition( + 'ia64', + ['IA64'], + ), +] + +SupportedArchitectureMap = {} +for a in SupportedArchitectureList: + SupportedArchitectureMap[a.arch] = a + for s in a.synonyms: + SupportedArchitectureMap[s] = a + diff --git a/engine/SCons/Tool/MSCommon/common.py b/engine/SCons/Tool/MSCommon/common.py new file mode 100644 index 0000000..219e680 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/common.py @@ -0,0 +1,195 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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__ = "src/engine/SCons/Tool/MSCommon/common.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """ +Common helper functions for working with the Microsoft tool chain. +""" + +import copy +import os +import subprocess +import re + +import SCons.Util + + +logfile = os.environ.get('SCONS_MSCOMMON_DEBUG') +if logfile == '-': + def debug(x): + print x +elif logfile: + try: + import logging + except ImportError: + debug = lambda x: open(logfile, 'a').write(x + '\n') + else: + logging.basicConfig(filename=logfile, level=logging.DEBUG) + debug = logging.debug +else: + debug = lambda x: None + + +_is_win64 = None + +def is_win64(): + """Return true if running on windows 64 bits. + + Works whether python itself runs in 64 bits or 32 bits.""" + # Unfortunately, python does not provide a useful way to determine + # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether + # the Python itself is 32-bit or 64-bit affects what it returns, + # so nothing in sys.* or os.* help. So we go to the registry to + # look directly for a clue from Windows, caching the result to + # avoid repeated registry calls. + global _is_win64 + if _is_win64 is None: + _is_win64 = has_reg(r"Software\Wow6432Node") + return _is_win64 + + +def read_reg(value): + return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] + +def has_reg(value): + """Return True if the given key exists in HKEY_LOCAL_MACHINE, False + otherwise.""" + try: + SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) + ret = True + except WindowsError: + ret = False + return ret + +# Functions for fetching environment variable settings from batch files. + +def normalize_env(env, keys): + """Given a dictionary representing a shell environment, add the variables + from os.environ needed for the processing of .bat files; the keys are + controlled by the keys argument. + + It also makes sure the environment values are correctly encoded. + + Note: the environment is copied""" + normenv = {} + if env: + for k in env.keys(): + normenv[k] = copy.deepcopy(env[k]).encode('mbcs') + + for k in keys: + if os.environ.has_key(k): + normenv[k] = os.environ[k].encode('mbcs') + + return normenv + +def get_output(vcbat, args = None, env = None): + """Parse the output of given bat file, with given args.""" + if args: + debug("Calling '%s %s'" % (vcbat, args)) + popen = subprocess.Popen('"%s" %s & set' % (vcbat, args), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env) + else: + debug("Calling '%s'" % vcbat) + popen = subprocess.Popen('"%s" & set' % vcbat, + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + env=env) + + # Use the .stdout and .stderr attributes directly because the + # .communicate() method uses the threading module on Windows + # and won't work under Pythons not built with threading. + stdout = popen.stdout.read() + if popen.wait() != 0: + raise IOError(popen.stderr.read().decode("mbcs")) + + output = stdout.decode("mbcs") + return output + +def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): + # dkeep is a dict associating key: path_list, where key is one item from + # keep, and pat_list the associated list of paths + + # TODO(1.5): replace with the following list comprehension: + #dkeep = dict([(i, []) for i in keep]) + dkeep = dict(map(lambda i: (i, []), keep)) + + # rdk will keep the regex to match the .bat file output line starts + rdk = {} + for i in keep: + rdk[i] = re.compile('%s=(.*)' % i, re.I) + + def add_env(rmatch, key, dkeep=dkeep): + plist = rmatch.group(1).split(os.pathsep) + for p in plist: + # Do not add empty paths (when a var ends with ;) + if p: + p = p.encode('mbcs') + # XXX: For some reason, VC98 .bat file adds "" around the PATH + # values, and it screws up the environment later, so we strip + # it. + p = p.strip('"') + dkeep[key].append(p) + + for line in output.splitlines(): + for k,v in rdk.items(): + m = v.match(line) + if m: + add_env(m, k) + + return dkeep + +# TODO(sgk): unused +def output_to_dict(output): + """Given an output string, parse it to find env variables. + + Return a dict where keys are variables names, and values their content""" + envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') + parsedenv = {} + for line in output.splitlines(): + m = envlinem.match(line) + if m: + parsedenv[m.group(1)] = m.group(2) + return parsedenv + +# TODO(sgk): unused +def get_new(l1, l2): + """Given two list l1 and l2, return the items in l2 which are not in l1. + Order is maintained.""" + + # We don't try to be smart: lists are small, and this is not the bottleneck + # is any case + new = [] + for i in l2: + if i not in l1: + new.append(i) + + return new + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/netframework.py b/engine/SCons/Tool/MSCommon/netframework.py new file mode 100644 index 0000000..300f662 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/netframework.py @@ -0,0 +1,84 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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__ = "src/engine/SCons/Tool/MSCommon/netframework.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """ +""" + +import os +import re +import string + +from common import read_reg, debug + +# Original value recorded by dcournapeau +_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\.NETFramework\InstallRoot' +# On SGK's system +_FRAMEWORKDIR_HKEY_ROOT = r'Software\Microsoft\Microsoft SDKs\.NETFramework\v2.0\InstallationFolder' + +def find_framework_root(): + # XXX: find it from environment (FrameworkDir) + try: + froot = read_reg(_FRAMEWORKDIR_HKEY_ROOT) + debug("Found framework install root in registry: %s" % froot) + except WindowsError, e: + debug("Could not read reg key %s" % _FRAMEWORKDIR_HKEY_ROOT) + return None + + if not os.path.exists(froot): + debug("%s not found on fs" % froot) + return None + + return froot + +def query_versions(): + froot = find_framework_root() + if froot: + contents = os.listdir(froot) + + l = re.compile('v[0-9]+.*') + versions = filter(lambda e, l=l: l.match(e), contents) + + def versrt(a,b): + # since version numbers aren't really floats... + aa = a[1:] + bb = b[1:] + aal = string.split(aa, '.') + bbl = string.split(bb, '.') + # sequence comparison in python is lexicographical + # which is exactly what we want. + # Note we sort backwards so the highest version is first. + return cmp(bbl,aal) + + versions.sort(versrt) + else: + versions = [] + + return versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/sdk.py b/engine/SCons/Tool/MSCommon/sdk.py new file mode 100644 index 0000000..f1345e4 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/sdk.py @@ -0,0 +1,321 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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__ = "src/engine/SCons/Tool/MSCommon/sdk.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """Module to detect the Platform/Windows SDK + +PSDK 2003 R1 is the earliest version detected. +""" + +import os + +import SCons.Errors +import SCons.Util + +import common + +debug = common.debug + +# SDK Checks. This is of course a mess as everything else on MS platforms. Here +# is what we do to detect the SDK: +# +# For Windows SDK >= 6.0: just look into the registry entries: +# HKLM\Software\Microsoft\Microsoft SDKs\Windows +# All the keys in there are the available versions. +# +# For Platform SDK before 6.0 (2003 server R1 and R2, etc...), there does not +# seem to be any sane registry key, so the precise location is hardcoded. +# +# For versions below 2003R1, it seems the PSDK is included with Visual Studio? +# +# Also, per the following: +# http://benjamin.smedbergs.us/blog/tag/atl/ +# VC++ Professional comes with the SDK, VC++ Express does not. + +# Location of the SDK (checked for 6.1 only) +_CURINSTALLED_SDK_HKEY_ROOT = \ + r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" + + +class SDKDefinition: + """ + An abstract base class for trying to find installed SDK directories. + """ + def __init__(self, version, **kw): + self.version = version + self.__dict__.update(kw) + + def find_sdk_dir(self): + """Try to find the MS SDK from the registry. + + Return None if failed or the directory does not exist. + """ + if not SCons.Util.can_read_reg: + debug('find_sdk_dir(): can not read registry') + return None + + hkey = self.HKEY_FMT % self.hkey_data + + try: + sdk_dir = common.read_reg(hkey) + except WindowsError, e: + debug('find_sdk_dir(): no SDK registry key %s' % repr(hkey)) + return None + + if not os.path.exists(sdk_dir): + debug('find_sdk_dir(): %s not on file system' % sdk_dir) + return None + + ftc = os.path.join(sdk_dir, self.sanity_check_file) + if not os.path.exists(ftc): + debug("find_sdk_dir(): sanity check %s not found" % ftc) + return None + + return sdk_dir + + def get_sdk_dir(self): + """Return the MSSSDK given the version string.""" + try: + return self._sdk_dir + except AttributeError: + sdk_dir = self.find_sdk_dir() + self._sdk_dir = sdk_dir + return sdk_dir + +class WindowsSDK(SDKDefinition): + """ + A subclass for trying to find installed Windows SDK directories. + """ + HKEY_FMT = r'Software\Microsoft\Microsoft SDKs\Windows\v%s\InstallationFolder' + def __init__(self, *args, **kw): + apply(SDKDefinition.__init__, (self,)+args, kw) + self.hkey_data = self.version + +class PlatformSDK(SDKDefinition): + """ + A subclass for trying to find installed Platform SDK directories. + """ + HKEY_FMT = r'Software\Microsoft\MicrosoftSDK\InstalledSDKS\%s\Install Dir' + def __init__(self, *args, **kw): + apply(SDKDefinition.__init__, (self,)+args, kw) + self.hkey_data = self.uuid + +# The list of support SDKs which we know how to detect. +# +# The first SDK found in the list is the one used by default if there +# are multiple SDKs installed. Barring good reasons to the contrary, +# this means we should list SDKs with from most recent to oldest. +# +# If you update this list, update the documentation in Tool/mssdk.xml. +SupportedSDKList = [ + WindowsSDK('6.1', + sanity_check_file=r'bin\SetEnv.Cmd', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + ), + + WindowsSDK('6.0A', + sanity_check_file=r'include\windows.h', + include_subdir='include', + lib_subdir={ + 'x86' : ['lib'], + 'x86_64' : [r'lib\x64'], + 'ia64' : [r'lib\ia64'], + }, + ), + + WindowsSDK('6.0', + sanity_check_file=r'bin\gacutil.exe', + include_subdir='include', + lib_subdir='lib', + ), + + PlatformSDK('2003R2', + sanity_check_file=r'SetEnv.Cmd', + uuid="D2FF9F89-8AA2-4373-8A31-C838BF4DBBE1" + ), + + PlatformSDK('2003R1', + sanity_check_file=r'SetEnv.Cmd', + uuid="8F9E5EF3-A9A5-491B-A889-C58EFFECE8B3", + ), +] + +SupportedSDKMap = {} +for sdk in SupportedSDKList: + SupportedSDKMap[sdk.version] = sdk + + +# Finding installed SDKs isn't cheap, because it goes not only to the +# registry but also to the disk to sanity-check that there is, in fact, +# an SDK installed there and that the registry entry isn't just stale. +# Find this information once, when requested, and cache it. + +InstalledSDKList = None +InstalledSDKMap = None + +def get_installed_sdks(): + global InstalledSDKList + global InstalledSDKMap + if InstalledSDKList is None: + InstalledSDKList = [] + InstalledSDKMap = {} + for sdk in SupportedSDKList: + debug('trying to find SDK %s' % sdk.version) + if sdk.get_sdk_dir(): + debug('found SDK %s' % sdk.version) + InstalledSDKList.append(sdk) + InstalledSDKMap[sdk.version] = sdk + return InstalledSDKList + + +# We may be asked to update multiple construction environments with +# SDK information. When doing this, we check on-disk for whether +# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk +# is expensive, cache results by directory. + +SDKEnvironmentUpdates = {} + +def set_sdk_by_directory(env, sdk_dir): + global SDKEnvironmentUpdates + try: + env_tuple_list = SDKEnvironmentUpdates[sdk_dir] + except KeyError: + env_tuple_list = [] + SDKEnvironmentUpdates[sdk_dir] = env_tuple_list + + include_path = os.path.join(sdk_dir, 'include') + mfc_path = os.path.join(include_path, 'mfc') + atl_path = os.path.join(include_path, 'atl') + + if os.path.exists(mfc_path): + env_tuple_list.append(('INCLUDE', mfc_path)) + if os.path.exists(atl_path): + env_tuple_list.append(('INCLUDE', atl_path)) + env_tuple_list.append(('INCLUDE', include_path)) + + env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) + env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) + env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) + + for variable, directory in env_tuple_list: + env.PrependENVPath(variable, directory) + + +# TODO(sgk): currently unused; remove? +def get_cur_sdk_dir_from_reg(): + """Try to find the platform sdk directory from the registry. + + Return None if failed or the directory does not exist""" + if not SCons.Util.can_read_reg: + debug('SCons cannot read registry') + return None + + try: + val = common.read_reg(_CURINSTALLED_SDK_HKEY_ROOT) + debug("Found current sdk dir in registry: %s" % val) + except WindowsError, e: + debug("Did not find current sdk in registry") + return None + + if not os.path.exists(val): + debug("Current sdk dir %s not on fs" % val) + return None + + return val + +def get_sdk_by_version(mssdk): + if not SupportedSDKMap.has_key(mssdk): + msg = "SDK version %s is not supported" % repr(mssdk) + raise SCons.Errors.UserError, msg + get_installed_sdks() + return InstalledSDKMap.get(mssdk) + +def get_default_sdk(): + """Set up the default Platform/Windows SDK.""" + get_installed_sdks() + if not InstalledSDKList: + return None + return InstalledSDKList[0] + +def mssdk_setup_env(env): + debug('msvs_setup_env()') + if env.has_key('MSSDK_DIR'): + sdk_dir = env['MSSDK_DIR'] + if sdk_dir is None: + return + sdk_dir = env.subst(sdk_dir) + elif env.has_key('MSSDK_VERSION'): + sdk_version = env['MSSDK_VERSION'] + if sdk_version is None: + msg = "SDK version %s is not installed" % repr(mssdk) + raise SCons.Errors.UserError, msg + sdk_version = env.subst(sdk_version) + mssdk = get_sdk_by_version(sdk_version) + sdk_dir = mssdk.get_sdk_dir() + elif env.has_key('MSVS_VERSION'): + msvs_version = env['MSVS_VERSION'] + debug('Getting MSVS_VERSION from env:%s'%msvs_version) + if msvs_version is None: + return + msvs_version = env.subst(msvs_version) + import vs + msvs = vs.get_vs_by_version(msvs_version) + debug('msvs is :%s'%msvs) + if not msvs: + return + sdk_version = msvs.sdk_version + if not sdk_version: + return + mssdk = get_sdk_by_version(sdk_version) + if not mssdk: + mssdk = get_default_sdk() + if not mssdk: + return + sdk_dir = mssdk.get_sdk_dir() + else: + mssdk = get_default_sdk() + if not mssdk: + return + sdk_dir = mssdk.get_sdk_dir() + + set_sdk_by_directory(env, sdk_dir) + + #print "No MSVS_VERSION: this is likely to be a bug" + +def mssdk_exists(version=None): + sdks = get_installed_sdks() + if version is None: + return len(sdks) > 0 + return sdks.has_key(version) + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/engine/SCons/Tool/MSCommon/vc.py b/engine/SCons/Tool/MSCommon/vc.py new file mode 100644 index 0000000..c02f15c --- /dev/null +++ b/engine/SCons/Tool/MSCommon/vc.py @@ -0,0 +1,367 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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. +# + +# TODO: +# * supported arch for versions: for old versions of batch file without +# argument, giving bogus argument cannot be detected, so we have to hardcode +# this here +# * print warning when msvc version specified but not found +# * find out why warning do not print +# * test on 64 bits XP + VS 2005 (and VS 6 if possible) +# * SDK +# * Assembly +__revision__ = "src/engine/SCons/Tool/MSCommon/vc.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """Module for Visual C/C++ detection and configuration. +""" +import SCons.compat + +import os +import platform + +import SCons.Warnings + +import common + +debug = common.debug + +class VisualCException(Exception): + pass + +class UnsupportedVersion(VisualCException): + pass + +class UnsupportedArch(VisualCException): + pass + +class MissingConfiguration(VisualCException): + pass + +class NoVersionFound(VisualCException): + pass + +class BatchFileExecutionError(VisualCException): + pass + +# Dict to 'canonalize' the arch +_ARCH_TO_CANONICAL = { + "x86": "x86", + "amd64": "amd64", + "i386": "x86", + "emt64": "amd64", + "x86_64": "amd64", + "itanium": "ia64", + "ia64": "ia64", +} + +# Given a (host, target) tuple, return the argument for the bat file. Both host +# and targets should be canonalized. +_HOST_TARGET_ARCH_TO_BAT_ARCH = { + ("x86", "x86"): "x86", + ("x86", "amd64"): "x86_amd64", + ("amd64", "amd64"): "amd64", + ("amd64", "x86"): "x86", + ("x86", "ia64"): "x86_ia64" +} + +def get_host_target(env): + host_platform = env.get('HOST_ARCH') + if not host_platform: + host_platform = platform.machine() + # TODO(2.5): the native Python platform.machine() function returns + # '' on all Python versions before 2.6, after which it also uses + # PROCESSOR_ARCHITECTURE. + if not host_platform: + host_platform = os.environ.get('PROCESSOR_ARCHITECTURE', '') + target_platform = env.get('TARGET_ARCH') + if not target_platform: + target_platform = host_platform + + try: + host = _ARCH_TO_CANONICAL[host_platform] + except KeyError, e: + msg = "Unrecognized host architecture %s" + raise ValueError(msg % repr(host_platform)) + + try: + target = _ARCH_TO_CANONICAL[target_platform] + except KeyError, e: + raise ValueError("Unrecognized target architecture %s" % target_platform) + + return (host, target) + +_VCVER = ["10.0", "9.0", "8.0", "7.1", "7.0", "6.0"] + +_VCVER_TO_PRODUCT_DIR = { + '10.0': [ + r'Microsoft\VisualStudio\10.0\Setup\VC\ProductDir'], + '9.0': [ + r'Microsoft\VisualStudio\9.0\Setup\VC\ProductDir', + r'Microsoft\VCExpress\9.0\Setup\VC\ProductDir'], + '8.0': [ + r'Microsoft\VisualStudio\8.0\Setup\VC\ProductDir', + r'Microsoft\VCExpress\8.0\Setup\VC\ProductDir'], + '7.1': [ + r'Microsoft\VisualStudio\7.1\Setup\VC\ProductDir'], + '7.0': [ + r'Microsoft\VisualStudio\7.0\Setup\VC\ProductDir'], + '6.0': [ + r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual C++\ProductDir'] +} + +def msvc_version_to_maj_min(msvc_version): + t = msvc_version.split(".") + if not len(t) == 2: + raise ValueError("Unrecognized version %s" % msvc_version) + try: + maj = int(t[0]) + min = int(t[1]) + return maj, min + except ValueError, e: + raise ValueError("Unrecognized version %s" % msvc_version) + +def is_host_target_supported(host_target, msvc_version): + """Return True if the given (host, target) tuple is supported given the + msvc version. + + Parameters + ---------- + host_target: tuple + tuple of (canonalized) host-target, e.g. ("x86", "amd64") for cross + compilation from 32 bits windows to 64 bits. + msvc_version: str + msvc version (major.minor, e.g. 10.0) + + Note + ---- + This only check whether a given version *may* support the given (host, + target), not that the toolchain is actually present on the machine. + """ + # We assume that any Visual Studio version supports x86 as a target + if host_target[1] != "x86": + maj, min = msvc_version_to_maj_min(msvc_version) + if maj < 8: + return False + + return True + +def find_vc_pdir(msvc_version): + """Try to find the product directory for the given + version. + + Note + ---- + If for some reason the requested version could not be found, an + exception which inherits from VisualCException will be raised.""" + root = 'Software\\' + if common.is_win64(): + root = root + 'Wow6432Node\\' + try: + hkeys = _VCVER_TO_PRODUCT_DIR[msvc_version] + except KeyError: + debug("Unknown version of MSVC: %s" % msvc_version) + raise UnsupportedVersion("Unknown version %s" % msvc_version) + + for key in hkeys: + key = root + key + try: + comps = common.read_reg(key) + except WindowsError, e: + debug('find_vc_dir(): no VC registry key %s' % repr(key)) + else: + debug('find_vc_dir(): found VC in registry: %s' % comps) + if os.path.exists(comps): + return comps + else: + debug('find_vc_dir(): reg says dir is %s, but it does not exist. (ignoring)'\ + % comps) + raise MissingConfiguration("registry dir %s not found on the filesystem" % comps) + return None + +def find_batch_file(msvc_version): + pdir = find_vc_pdir(msvc_version) + if pdir is None: + raise NoVersionFound("No version of Visual Studio found") + + vernum = float(msvc_version) + if 7 <= vernum < 8: + pdir = os.path.join(pdir, os.pardir, "Common7", "Tools") + batfilename = os.path.join(pdir, "vsvars32.bat") + elif vernum < 7: + pdir = os.path.join(pdir, "Bin") + batfilename = os.path.join(pdir, "vcvars32.bat") + else: # >= 8 + batfilename = os.path.join(pdir, "vcvarsall.bat") + + if os.path.exists(batfilename): + return batfilename + else: + debug("Not found: %s" % batfilename) + return None + +__INSTALLED_VCS_RUN = None + +def cached_get_installed_vcs(): + global __INSTALLED_VCS_RUN + + if __INSTALLED_VCS_RUN is None: + ret = get_installed_vcs() + __INSTALLED_VCS_RUN = ret + + return __INSTALLED_VCS_RUN + +def get_installed_vcs(): + installed_versions = [] + for ver in _VCVER: + debug('trying to find VC %s' % ver) + try: + if find_vc_pdir(ver): + debug('found VC %s' % ver) + installed_versions.append(ver) + else: + debug('find_vc_pdir return None for ver %s' % ver) + except VisualCException, e: + debug('did not find VC %s: caught exception %s' % (ver, str(e))) + return installed_versions + +def reset_installed_vcs(): + """Make it try again to find VC. This is just for the tests.""" + __INSTALLED_VCS_RUN = None + +def script_env(script, args=None): + stdout = common.get_output(script, args) + # Stupid batch files do not set return code: we take a look at the + # beginning of the output for an error message instead + olines = stdout.splitlines() + if olines[0].startswith("The specified configuration type is missing"): + raise BatchFileExecutionError("\n".join(olines[:2])) + + return common.parse_output(stdout) + +def get_default_version(env): + debug('get_default_version()') + + msvc_version = env.get('MSVC_VERSION') + msvs_version = env.get('MSVS_VERSION') + + if msvs_version and not msvc_version: + SCons.Warnings.warn( + SCons.Warnings.DeprecatedWarning, + "MSVS_VERSION is deprecated: please use MSVC_VERSION instead ") + return msvs_version + elif msvc_version and msvs_version: + if not msvc_version == msvs_version: + SCons.Warnings.warn( + SCons.Warnings.VisualVersionMismatch, + "Requested msvc version (%s) and msvs version (%s) do " \ + "not match: please use MSVC_VERSION only to request a " \ + "visual studio version, MSVS_VERSION is deprecated" \ + % (msvc_version, msvs_version)) + return msvs_version + if not msvc_version: + installed_vcs = cached_get_installed_vcs() + debug('installed_vcs:%s' % installed_vcs) + if not installed_vcs: + msg = 'No installed VCs' + debug('msv %s\n' % repr(msg)) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, msg) + return None + msvc_version = installed_vcs[0] + debug('msvc_setup_env: using default installed MSVC version %s\n' % repr(msvc_version)) + + return msvc_version + +def msvc_setup_env_once(env): + try: + has_run = env["MSVC_SETUP_RUN"] + except KeyError: + has_run = False + + if not has_run: + msvc_setup_env(env) + env["MSVC_SETUP_RUN"] = True + +def msvc_setup_env(env): + debug('msvc_setup_env()') + + version = get_default_version(env) + if version is None: + warn_msg = "No version of Visual Studio compiler found - C/C++ " \ + "compilers most likely not set correctly" + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + debug('msvc_setup_env: using specified MSVC version %s\n' % repr(version)) + + # XXX: we set-up both MSVS version for backward + # compatibility with the msvs tool + env['MSVC_VERSION'] = version + env['MSVS_VERSION'] = version + env['MSVS'] = {} + + try: + script = find_batch_file(version) + except VisualCException, e: + msg = str(e) + debug('Caught exception while looking for batch file (%s)' % msg) + warn_msg = "VC version %s not installed. " + \ + "C/C++ compilers are most likely not set correctly.\n" + \ + " Installed versions are: %s" + warn_msg = warn_msg % (version, cached_get_installed_vcs()) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + + use_script = env.get('MSVC_USE_SCRIPT', True) + if SCons.Util.is_String(use_script): + debug('use_script 1 %s\n' % repr(use_script)) + d = script_env(use_script) + elif use_script: + host_platform, target_platform = get_host_target(env) + host_target = (host_platform, target_platform) + if not is_host_target_supported(host_target, version): + warn_msg = "host, target = %s not supported for MSVC version %s" % \ + (host_target, version) + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + arg = _HOST_TARGET_ARCH_TO_BAT_ARCH[host_target] + debug('use_script 2 %s, args:%s\n' % (repr(script), arg)) + try: + d = script_env(script, args=arg) + except BatchFileExecutionError, e: + msg = "MSVC error while executing %s with args %s (error was %s)" % \ + (script, arg, str(e)) + raise SCons.Errors.UserError(msg) + else: + debug('MSVC_USE_SCRIPT set to False') + warn_msg = "MSVC_USE_SCRIPT set to False, assuming environment " \ + "set correctly." + SCons.Warnings.warn(SCons.Warnings.VisualCMissingWarning, warn_msg) + return None + + for k, v in d.items(): + env.PrependENVPath(k, v, delete_existing=True) + +def msvc_exists(version=None): + vcs = cached_get_installed_vcs() + if version is None: + return len(vcs) > 0 + return version in vcs + diff --git a/engine/SCons/Tool/MSCommon/vs.py b/engine/SCons/Tool/MSCommon/vs.py new file mode 100644 index 0000000..f9531c0 --- /dev/null +++ b/engine/SCons/Tool/MSCommon/vs.py @@ -0,0 +1,497 @@ +# +# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009 The SCons Foundation +# +# 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__ = "src/engine/SCons/Tool/MSCommon/vs.py 4577 2009/12/27 19:43:56 scons" + +__doc__ = """Module to detect Visual Studio and/or Visual C/C++ +""" + +import os + +import SCons.Errors +import SCons.Util + +from common import debug, \ + get_output, \ + is_win64, \ + normalize_env, \ + parse_output, \ + read_reg + +import SCons.Tool.MSCommon.vc + +class VisualStudio: + """ + An abstract base class for trying to find installed versions of + Visual Studio. + """ + def __init__(self, version, **kw): + self.version = version + kw['vc_version'] = kw.get('vc_version', version) + kw['sdk_version'] = kw.get('sdk_version', version) + self.__dict__.update(kw) + self._cache = {} + + # + + def find_batch_file(self): + vs_dir = self.get_vs_dir() + if not vs_dir: + debug('find_executable(): no vs_dir') + return None + batch_file = os.path.join(vs_dir, self.batch_file_path) + batch_file = os.path.normpath(batch_file) + if not os.path.isfile(batch_file): + debug('find_batch_file(): %s not on file system' % batch_file) + return None + return batch_file + + def find_vs_dir_by_vc(self): + SCons.Tool.MSCommon.vc.get_installed_vcs() + dir = SCons.Tool.MSCommon.vc.find_vc_pdir(self.vc_version) + if not dir: + debug('find_vs_dir(): no installed VC %s' % self.vc_version) + return None + return dir + + def find_vs_dir_by_reg(self): + root = 'Software\\' + + if is_win64(): + root = root + 'Wow6432Node\\' + for key in self.hkeys: + if key=='use_dir': + return self.find_vs_dir_by_vc() + key = root + key + try: + comps = read_reg(key) + except WindowsError, e: + debug('find_vs_dir_by_reg(): no VS registry key %s' % repr(key)) + else: + debug('find_vs_dir_by_reg(): found VS in registry: %s' % comps) + return comps + return None + + def find_vs_dir(self): + """ Can use registry or location of VC to find vs dir + First try to find by registry, and if that fails find via VC dir + """ + + + if True: + vs_dir=self.find_vs_dir_by_reg() + return vs_dir + else: + return self.find_vs_dir_by_vc() + + def find_executable(self): + vs_dir = self.get_vs_dir() + if not vs_dir: + debug('find_executable(): no vs_dir (%s)'%vs_dir) + return None + executable = os.path.join(vs_dir, self.executable_path) + executable = os.path.normpath(executable) + if not os.path.isfile(executable): + debug('find_executable(): %s not on file system' % executable) + return None + return executable + + # + + def get_batch_file(self): + try: + return self._cache['batch_file'] + except KeyError: + batch_file = self.find_batch_file() + self._cache['batch_file'] = batch_file + return batch_file + + def get_executable(self): + try: + debug('get_executable using cache:%s'%self._cache['executable']) + return self._cache['executable'] + except KeyError: + executable = self.find_executable() + self._cache['executable'] = executable + debug('get_executable not in cache:%s'%executable) + return executable + + def get_vs_dir(self): + try: + return self._cache['vs_dir'] + except KeyError: + vs_dir = self.find_vs_dir() + self._cache['vs_dir'] = vs_dir + return vs_dir + + def get_supported_arch(self): + try: + return self._cache['supported_arch'] + except KeyError: + # RDEVE: for the time being use hardcoded lists + # supported_arch = self.find_supported_arch() + self._cache['supported_arch'] = self.supported_arch + return self.supported_arch + + def reset(self): + self._cache = {} + +# The list of supported Visual Studio versions we know how to detect. +# +# How to look for .bat file ? +# - VS 2008 Express (x86): +# * from registry key productdir, gives the full path to vsvarsall.bat. In +# HKEY_LOCAL_MACHINE): +# Software\Microsoft\VCEpress\9.0\Setup\VC\productdir +# * from environmnent variable VS90COMNTOOLS: the path is then ..\..\VC +# relatively to the path given by the variable. +# +# - VS 2008 Express (WoW6432: 32 bits on windows x64): +# Software\Wow6432Node\Microsoft\VCEpress\9.0\Setup\VC\productdir +# +# - VS 2005 Express (x86): +# * from registry key productdir, gives the full path to vsvarsall.bat. In +# HKEY_LOCAL_MACHINE): +# Software\Microsoft\VCEpress\8.0\Setup\VC\productdir +# * from environmnent variable VS80COMNTOOLS: the path is then ..\..\VC +# relatively to the path given by the variable. +# +# - VS 2005 Express (WoW6432: 32 bits on windows x64): does not seem to have a +# productdir ? +# +# - VS 2003 .Net (pro edition ? x86): +# * from registry key productdir. The path is then ..\Common7\Tools\ +# relatively to the key. The key is in HKEY_LOCAL_MACHINE): +# Software\Microsoft\VisualStudio\7.1\Setup\VC\productdir +# * from environmnent variable VS71COMNTOOLS: the path is the full path to +# vsvars32.bat +# +# - VS 98 (VS 6): +# * from registry key productdir. The path is then Bin +# relatively to the key. The key is in HKEY_LOCAL_MACHINE): +# Software\Microsoft\VisualStudio\6.0\Setup\VC98\productdir +# +# The first version found in the list is the one used by default if +# there are multiple versions installed. Barring good reasons to +# the contrary, this means we should list versions from most recent +# to oldest. Pro versions get listed before Express versions on the +# assumption that, by default, you'd rather use the version you paid +# good money for in preference to whatever Microsoft makes available +# for free. +# +# If you update this list, update the documentation in Tool/msvs.xml. + +SupportedVSList = [ + # Visual Studio 2010 + # TODO: find the settings, perhaps from someone with a CTP copy? + #VisualStudio('TBD', + # hkey_root=r'TBD', + # common_tools_var='TBD', + # executable_path=r'TBD', + # default_dirname='TBD', + #), + + # Visual Studio 2008 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('9.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VisualStudio\9.0\Setup\VS\ProductDir'], + common_tools_var='VS90COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 9', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2008 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('9.0Exp', + vc_version='9.0', + sdk_version='6.1', + hkeys=[r'Microsoft\VCExpress\9.0\Setup\VS\ProductDir'], + common_tools_var='VS90COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 9', + supported_arch=['x86'], + ), + + # Visual Studio 2005 + # The batch file we look for is in the VC directory, + # so the devenv.com executable is up in ..\..\Common7\IDE. + VisualStudio('8.0', + sdk_version='6.0A', + hkeys=[r'Microsoft\VisualStudio\8.0\Setup\VS\ProductDir'], + common_tools_var='VS80COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 8', + supported_arch=['x86', 'amd64'], + ), + + # Visual C++ 2005 Express Edition + # The batch file we look for is in the VC directory, + # so the VCExpress.exe executable is up in ..\..\Common7\IDE. + VisualStudio('8.0Exp', + vc_version='8.0', + sdk_version='6.0A', + hkeys=[r'Microsoft\VCExpress\8.0\Setup\VS\ProductDir'], + common_tools_var='VS80COMNTOOLS', + executable_path=r'Common7\IDE\VCExpress.exe', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio 8', + supported_arch=['x86'], + ), + + # Visual Studio .NET 2003 + # The batch file we look for is in the Common7\Tools directory, + # so the devenv.com executable is next door in ..\IDE. + VisualStudio('7.1', + sdk_version='6.0', + hkeys=[r'Microsoft\VisualStudio\7.1\Setup\VS\ProductDir'], + common_tools_var='VS71COMNTOOLS', + executable_path=r'Common7\IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio .NET 2003', + supported_arch=['x86'], + ), + + # Visual Studio .NET + # The batch file we look for is in the Common7\Tools directory, + # so the devenv.com executable is next door in ..\IDE. + VisualStudio('7.0', + sdk_version='2003R2', + hkeys=[r'Microsoft\VisualStudio\7.0\Setup\VS\ProductDir'], + common_tools_var='VS70COMNTOOLS', + executable_path=r'IDE\devenv.com', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio .NET', + supported_arch=['x86'], + ), + + # Visual Studio 6.0 + VisualStudio('6.0', + sdk_version='2003R1', + hkeys=[r'Microsoft\VisualStudio\6.0\Setup\Microsoft Visual Studio\ProductDir', + 'use_dir'], + common_tools_var='VS60COMNTOOLS', + executable_path=r'Common\MSDev98\Bin\MSDEV.COM', + batch_file_path=r'Common7\Tools\vsvars32.bat', + default_dirname='Microsoft Visual Studio', + supported_arch=['x86'], + ), +] + +SupportedVSMap = {} +for vs in SupportedVSList: + SupportedVSMap[vs.version] = vs + + +# Finding installed versions of Visual Studio isn't cheap, because it +# goes not only to the registry but also to the disk to sanity-check +# that there is, in fact, a Visual Studio directory there and that the +# registry entry isn't just stale. Find this information once, when +# requested, and cache it. + +InstalledVSList = None +InstalledVSMap = None + +def get_installed_visual_studios(): + global InstalledVSList + global InstalledVSMap + if InstalledVSList is None: + InstalledVSList = [] + InstalledVSMap = {} + for vs in SupportedVSList: + debug('trying to find VS %s' % vs.version) + if vs.get_executable(): + debug('found VS %s' % vs.version) + InstalledVSList.append(vs) + InstalledVSMap[vs.version] = vs + return InstalledVSList + +def reset_installed_visual_studios(): + global InstalledVSList + global InstalledVSMap + InstalledVSList = None + InstalledVSMap = None + for vs in SupportedVSList: + vs.reset() + + # Need to clear installed VC's as well as they are used in finding + # installed VS's + SCons.Tool.MSCommon.vc.reset_installed_vcs() + + +# We may be asked to update multiple construction environments with +# SDK information. When doing this, we check on-disk for whether +# the SDK has 'mfc' and 'atl' subdirectories. Since going to disk +# is expensive, cache results by directory. + +#SDKEnvironmentUpdates = {} +# +#def set_sdk_by_directory(env, sdk_dir): +# global SDKEnvironmentUpdates +# try: +# env_tuple_list = SDKEnvironmentUpdates[sdk_dir] +# except KeyError: +# env_tuple_list = [] +# SDKEnvironmentUpdates[sdk_dir] = env_tuple_list +# +# include_path = os.path.join(sdk_dir, 'include') +# mfc_path = os.path.join(include_path, 'mfc') +# atl_path = os.path.join(include_path, 'atl') +# +# if os.path.exists(mfc_path): +# env_tuple_list.append(('INCLUDE', mfc_path)) +# if os.path.exists(atl_path): +# env_tuple_list.append(('INCLUDE', atl_path)) +# env_tuple_list.append(('INCLUDE', include_path)) +# +# env_tuple_list.append(('LIB', os.path.join(sdk_dir, 'lib'))) +# env_tuple_list.append(('LIBPATH', os.path.join(sdk_dir, 'lib'))) +# env_tuple_list.append(('PATH', os.path.join(sdk_dir, 'bin'))) +# +# for variable, directory in env_tuple_list: +# env.PrependENVPath(variable, directory) + +def msvs_exists(): + return (len(get_installed_visual_studios()) > 0) + +def get_vs_by_version(msvs): + global InstalledVSMap + global SupportedVSMap + + if not SupportedVSMap.has_key(msvs): + msg = "Visual Studio version %s is not supported" % repr(msvs) + raise SCons.Errors.UserError, msg + get_installed_visual_studios() + vs = InstalledVSMap.get(msvs) + debug('InstalledVSMap:%s'%InstalledVSMap) + # Some check like this would let us provide a useful error message + # if they try to set a Visual Studio version that's not installed. + # However, we also want to be able to run tests (like the unit + # tests) on systems that don't, or won't ever, have it installed. + # It might be worth resurrecting this, with some configurable + # setting that the tests can use to bypass the check. + #if not vs: + # msg = "Visual Studio version %s is not installed" % repr(msvs) + # raise SCons.Errors.UserError, msg + return vs + +def get_default_version(env): + """Returns the default version string to use for MSVS. + + If no version was requested by the user through the MSVS environment + variable, query all the available the visual studios through + query_versions, and take the highest one. + + Return + ------ + version: str + the default version. + """ + if not env.has_key('MSVS') or not SCons.Util.is_Dict(env['MSVS']): + # TODO(1.5): + #versions = [vs.version for vs in get_installed_visual_studios()] + versions = map(lambda vs: vs.version, get_installed_visual_studios()) + env['MSVS'] = {'VERSIONS' : versions} + else: + versions = env['MSVS'].get('VERSIONS', []) + + if not env.has_key('MSVS_VERSION'): + if versions: + env['MSVS_VERSION'] = versions[0] #use highest version by default + else: + env['MSVS_VERSION'] = SupportedVSList[0].version + + env['MSVS']['VERSION'] = env['MSVS_VERSION'] + + return env['MSVS_VERSION'] + +def get_default_arch(env): + """Return the default arch to use for MSVS + + if no version was requested by the user through the MSVS_ARCH environment + variable, select x86 + + Return + ------ + arch: str + """ + arch = env.get('MSVS_ARCH', 'x86') + + msvs = InstalledVSMap.get(env['MSVS_VERSION']) + + if not msvs: + arch = 'x86' + elif not arch in msvs.get_supported_arch(): + fmt = "Visual Studio version %s does not support architecture %s" + raise SCons.Errors.UserError, fmt % (env['MSVS_VERSION'], arch) + + return arch + +def merge_default_version(env): + version = get_default_version(env) + arch = get_default_arch(env) + +def msvs_setup_env(env): + batfilename = msvs.get_batch_file() + msvs = get_vs_by_version(version) + if msvs is None: + return + + # XXX: I think this is broken. This will silently set a bogus tool instead + # of failing, but there is no other way with the current scons tool + # framework + if batfilename is not None: + + vars = ('LIB', 'LIBPATH', 'PATH', 'INCLUDE') + + msvs_list = get_installed_visual_studios() + # TODO(1.5): + #vscommonvarnames = [ vs.common_tools_var for vs in msvs_list ] + vscommonvarnames = map(lambda vs: vs.common_tools_var, msvs_list) + nenv = normalize_env(env['ENV'], vscommonvarnames + ['COMSPEC']) + output = get_output(batfilename, arch, env=nenv) + vars = parse_output(output, vars) + + for k, v in vars.items(): + env.PrependENVPath(k, v, delete_existing=1) + +def query_versions(): + """Query the system to get available versions of VS. A version is + considered when a batfile is found.""" + msvs_list = get_installed_visual_studios() + # TODO(1.5) + #versions = [ msvs.version for msvs in msvs_list ] + versions = map(lambda msvs: msvs.version, msvs_list) + return versions + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: |