1# 2# Copyright (c) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012 The SCons Foundation 3# 4# Permission is hereby granted, free of charge, to any person obtaining 5# a copy of this software and associated documentation files (the 6# "Software"), to deal in the Software without restriction, including 7# without limitation the rights to use, copy, modify, merge, publish, 8# distribute, sublicense, and/or sell copies of the Software, and to 9# permit persons to whom the Software is furnished to do so, subject to 10# the following conditions: 11# 12# The above copyright notice and this permission notice shall be included 13# in all copies or substantial portions of the Software. 14# 15# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 16# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 17# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 18# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 19# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 20# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 21# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22# 23 24__revision__ = "src/engine/SCons/Tool/MSCommon/common.py issue-2856:2676:d23b7a2f45e8 2012/08/05 15:38:28 garyo" 25 26__doc__ = """ 27Common helper functions for working with the Microsoft tool chain. 28""" 29 30import copy 31import os 32import subprocess 33import re 34 35import SCons.Util 36 37 38logfile = os.environ.get('SCONS_MSCOMMON_DEBUG') 39if logfile == '-': 40 def debug(x): 41 print x 42elif logfile: 43 try: 44 import logging 45 except ImportError: 46 debug = lambda x: open(logfile, 'a').write(x + '\n') 47 else: 48 logging.basicConfig(filename=logfile, level=logging.DEBUG) 49 debug = logging.debug 50else: 51 debug = lambda x: None 52 53 54_is_win64 = None 55 56def is_win64(): 57 """Return true if running on windows 64 bits. 58 59 Works whether python itself runs in 64 bits or 32 bits.""" 60 # Unfortunately, python does not provide a useful way to determine 61 # if the underlying Windows OS is 32-bit or 64-bit. Worse, whether 62 # the Python itself is 32-bit or 64-bit affects what it returns, 63 # so nothing in sys.* or os.* help. 64 65 # Apparently the best solution is to use env vars that Windows 66 # sets. If PROCESSOR_ARCHITECTURE is not x86, then the python 67 # process is running in 64 bit mode (on a 64-bit OS, 64-bit 68 # hardware, obviously). 69 # If this python is 32-bit but the OS is 64, Windows will set 70 # ProgramW6432 and PROCESSOR_ARCHITEW6432 to non-null. 71 # (Checking for HKLM\Software\Wow6432Node in the registry doesn't 72 # work, because some 32-bit installers create it.) 73 global _is_win64 74 if _is_win64 is None: 75 # I structured these tests to make it easy to add new ones or 76 # add exceptions in the future, because this is a bit fragile. 77 _is_win64 = False 78 if os.environ.get('PROCESSOR_ARCHITECTURE','x86') != 'x86': 79 _is_win64 = True 80 if os.environ.get('PROCESSOR_ARCHITEW6432'): 81 _is_win64 = True 82 if os.environ.get('ProgramW6432'): 83 _is_win64 = True 84 return _is_win64 85 86 87def read_reg(value): 88 return SCons.Util.RegGetValue(SCons.Util.HKEY_LOCAL_MACHINE, value)[0] 89 90def has_reg(value): 91 """Return True if the given key exists in HKEY_LOCAL_MACHINE, False 92 otherwise.""" 93 try: 94 SCons.Util.RegOpenKeyEx(SCons.Util.HKEY_LOCAL_MACHINE, value) 95 ret = True 96 except WindowsError: 97 ret = False 98 return ret 99 100# Functions for fetching environment variable settings from batch files. 101 102def normalize_env(env, keys, force=False): 103 """Given a dictionary representing a shell environment, add the variables 104 from os.environ needed for the processing of .bat files; the keys are 105 controlled by the keys argument. 106 107 It also makes sure the environment values are correctly encoded. 108 109 If force=True, then all of the key values that exist are copied 110 into the returned dictionary. If force=false, values are only 111 copied if the key does not already exist in the copied dictionary. 112 113 Note: the environment is copied.""" 114 normenv = {} 115 if env: 116 for k in env.keys(): 117 normenv[k] = copy.deepcopy(env[k]).encode('mbcs') 118 119 for k in keys: 120 if k in os.environ and (force or not k in normenv): 121 normenv[k] = os.environ[k].encode('mbcs') 122 123 return normenv 124 125def get_output(vcbat, args = None, env = None): 126 """Parse the output of given bat file, with given args.""" 127 128 if env is None: 129 # Create a blank environment, for use in launching the tools 130 env = SCons.Environment.Environment(tools=[]) 131 132 # TODO: This is a hard-coded list of the variables that (may) need 133 # to be imported from os.environ[] for v[sc]*vars*.bat file 134 # execution to work. This list should really be either directly 135 # controlled by vc.py, or else derived from the common_tools_var 136 # settings in vs.py. 137 vars = [ 138 'COMSPEC', 139 'VS90COMNTOOLS', 140 'VS80COMNTOOLS', 141 'VS71COMNTOOLS', 142 'VS70COMNTOOLS', 143 'VS60COMNTOOLS', 144 ] 145 env['ENV'] = normalize_env(env['ENV'], vars, force=False) 146 147 if args: 148 debug("Calling '%s %s'" % (vcbat, args)) 149 popen = SCons.Action._subproc(env, 150 '"%s" %s & set' % (vcbat, args), 151 stdin = 'devnull', 152 stdout=subprocess.PIPE, 153 stderr=subprocess.PIPE) 154 else: 155 debug("Calling '%s'" % vcbat) 156 popen = SCons.Action._subproc(env, 157 '"%s" & set' % vcbat, 158 stdin = 'devnull', 159 stdout=subprocess.PIPE, 160 stderr=subprocess.PIPE) 161 162 # Use the .stdout and .stderr attributes directly because the 163 # .communicate() method uses the threading module on Windows 164 # and won't work under Pythons not built with threading. 165 stdout = popen.stdout.read() 166 stderr = popen.stderr.read() 167 if stderr: 168 # TODO: find something better to do with stderr; 169 # this at least prevents errors from getting swallowed. 170 import sys 171 sys.stderr.write(stderr) 172 if popen.wait() != 0: 173 raise IOError(stderr.decode("mbcs")) 174 175 output = stdout.decode("mbcs") 176 return output 177 178def parse_output(output, keep = ("INCLUDE", "LIB", "LIBPATH", "PATH")): 179 # dkeep is a dict associating key: path_list, where key is one item from 180 # keep, and pat_list the associated list of paths 181 182 dkeep = dict([(i, []) for i in keep]) 183 184 # rdk will keep the regex to match the .bat file output line starts 185 rdk = {} 186 for i in keep: 187 rdk[i] = re.compile('%s=(.*)' % i, re.I) 188 189 def add_env(rmatch, key, dkeep=dkeep): 190 plist = rmatch.group(1).split(os.pathsep) 191 for p in plist: 192 # Do not add empty paths (when a var ends with ;) 193 if p: 194 p = p.encode('mbcs') 195 # XXX: For some reason, VC98 .bat file adds "" around the PATH 196 # values, and it screws up the environment later, so we strip 197 # it. 198 p = p.strip('"') 199 dkeep[key].append(p) 200 201 for line in output.splitlines(): 202 for k,v in rdk.items(): 203 m = v.match(line) 204 if m: 205 add_env(m, k) 206 207 return dkeep 208 209# TODO(sgk): unused 210def output_to_dict(output): 211 """Given an output string, parse it to find env variables. 212 213 Return a dict where keys are variables names, and values their content""" 214 envlinem = re.compile(r'^([a-zA-z0-9]+)=([\S\s]*)$') 215 parsedenv = {} 216 for line in output.splitlines(): 217 m = envlinem.match(line) 218 if m: 219 parsedenv[m.group(1)] = m.group(2) 220 return parsedenv 221 222# TODO(sgk): unused 223def get_new(l1, l2): 224 """Given two list l1 and l2, return the items in l2 which are not in l1. 225 Order is maintained.""" 226 227 # We don't try to be smart: lists are small, and this is not the bottleneck 228 # is any case 229 new = [] 230 for i in l2: 231 if i not in l1: 232 new.append(i) 233 234 return new 235 236# Local Variables: 237# tab-width:4 238# indent-tabs-mode:nil 239# End: 240# vim: set expandtab tabstop=4 shiftwidth=4: 241