# # Copyright (c) 2001 - 2015 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 rel_2.4.0:3365:9259ea1c13d7 2015/09/21 14:03:43 bdbaddog" __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. # Apparently the best solution is to use env vars that Windows # sets. If PROCESSOR_ARCHITECTURE is not x86, then the python # process is running in 64 bit mode (on a 64-bit OS, 64-bit # hardware, obviously). # If this python is 32-bit but the OS is 64, Windows will set # ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. # (Checking for HKLM\Software\Wow6432Node in the registry doesn't # work, because some 32-bit installers create it.) global _is_win64 if _is_win64 is None: # I structured these tests to make it easy to add new ones or # add exceptions in the future, because this is a bit fragile. _is_win64 = False if os.environ.get('PROCESSOR_ARCHITECTURE','x86') != 'x86': _is_win64 = True if os.environ.get('PROCESSOR_ARCHITEW6432'): _is_win64 = True if os.environ.get('ProgramW6432'): _is_win64 = True 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, force=False): """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. If force=True, then all of the key values that exist are copied into the returned dictionary. If force=false, values are only copied if the key does not already exist in the copied dictionary. 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 k in os.environ and (force or not k in normenv): normenv[k] = os.environ[k].encode('mbcs') # This shouldn't be necessary, since the default environment should include system32, # but keep this here to be safe, since it's needed to find reg.exe which the MSVC # bat scripts use. sys32_dir = os.path.join(os.environ.get("SystemRoot", os.environ.get("windir",r"C:\Windows\system32")),"System32") if sys32_dir not in normenv['PATH']: normenv['PATH'] = normenv['PATH'] + os.pathsep + sys32_dir debug("PATH: %s"%normenv['PATH']) return normenv def get_output(vcbat, args = None, env = None): """Parse the output of given bat file, with given args.""" if env is None: # Create a blank environment, for use in launching the tools env = SCons.Environment.Environment(tools=[]) # TODO: This is a hard-coded list of the variables that (may) need # to be imported from os.environ[] for v[sc]*vars*.bat file # execution to work. This list should really be either directly # controlled by vc.py, or else derived from the common_tools_var # settings in vs.py. vars = [ 'COMSPEC', # VS100 and VS110: Still set, but modern MSVC setup scripts will # discard these if registry has values. However Intel compiler setup # script still requires these as of 2013/2014. 'VS110COMNTOOLS', 'VS100COMNTOOLS', 'VS90COMNTOOLS', 'VS80COMNTOOLS', 'VS71COMNTOOLS', 'VS70COMNTOOLS', 'VS60COMNTOOLS', ] env['ENV'] = normalize_env(env['ENV'], vars, force=False) if args: debug("Calling '%s %s'" % (vcbat, args)) popen = SCons.Action._subproc(env, '"%s" %s & set' % (vcbat, args), stdin = 'devnull', stdout=subprocess.PIPE, stderr=subprocess.PIPE) else: debug("Calling '%s'" % vcbat) popen = SCons.Action._subproc(env, '"%s" & set' % vcbat, stdin = 'devnull', stdout=subprocess.PIPE, stderr=subprocess.PIPE) # 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() stderr = popen.stderr.read() # Extra debug logic, uncomment if necessar # debug('get_output():stdout:%s'%stdout) # debug('get_output():stderr:%s'%stderr) if stderr: # TODO: find something better to do with stderr; # this at least prevents errors from getting swallowed. import sys sys.stderr.write(stderr) if popen.wait() != 0: raise IOError(stderr.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 dkeep = dict([(i, []) for i in 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: