Support foreign language installs of Visual Studio (bug 6055, r=ds). default tip
authorDavid Anderson <dvander@alliedmods.net>
Sat Feb 15 20:57:39 2014 -0800 (2014-02-15)
changeset 243eaa3bec37766
parent 242 10388f39ac2b
Support foreign language installs of Visual Studio (bug 6055, r=ds).
ambuild2/frontend/amb2/gen.py
ambuild2/frontend/cpp.py
ambuild2/task.py
ambuild2/util.py
     1.1 --- a/ambuild2/frontend/amb2/gen.py	Sat Feb 15 12:24:48 2014 -0800
     1.2 +++ b/ambuild2/frontend/amb2/gen.py	Sat Feb 15 20:57:39 2014 -0800
     1.3 @@ -17,7 +17,6 @@
     1.4  import os
     1.5  from ambuild2 import util
     1.6  from ambuild2 import nodetypes
     1.7 -from ambuild2.frontend.cpp import DetectCompiler
     1.8  from ambuild2.frontend import base_gen
     1.9  from ambuild2 import database
    1.10  
    1.11 @@ -163,6 +162,18 @@
    1.12        'options': self.options,
    1.13        'args': self.args
    1.14      }
    1.15 +
    1.16 +    # Save any extra compiler info that must be communicated to the backend.
    1.17 +    if self.compiler is not None:
    1.18 +      compilers = [
    1.19 +        ('cc', self.compiler.cc),
    1.20 +        ('cxx', self.compiler.cxx),
    1.21 +      ]
    1.22 +      for prefix, comp in compilers:
    1.23 +        for prop_name in comp.extra_props:
    1.24 +          key = '{0}_{1}'.format(prefix, prop_name)
    1.25 +          vars[key] = comp.extra_props[prop_name]
    1.26 +
    1.27      with open(os.path.join(self.cacheFolder, 'vars'), 'wb') as fp:
    1.28        util.DiskPickle(vars, fp)
    1.29  
     2.1 --- a/ambuild2/frontend/cpp.py	Sat Feb 15 12:24:48 2014 -0800
     2.2 +++ b/ambuild2/frontend/cpp.py	Sat Feb 15 20:57:39 2014 -0800
     2.3 @@ -27,6 +27,7 @@
     2.4      self.command = command
     2.5      self.objSuffix = objSuffix
     2.6      self.debuginfo_argv = []
     2.7 +    self.extra_props = {}
     2.8  
     2.9  class MSVC(Vendor):
    2.10    def __init__(self, command, version):
    2.11 @@ -115,6 +116,18 @@
    2.12    }
    2.13  }
    2.14  
    2.15 +def DetectMicrosoftInclusionPattern(text):
    2.16 +  for line in [raw.strip() for raw in text.split('\n')]:
    2.17 +    m = re.match(r'(.*)\s+([A-Za-z]:\\.*stdio\.h)$', line)
    2.18 +    if m is None:
    2.19 +      continue
    2.20 +
    2.21 +    phrase = m.group(1)
    2.22 +    return re.escape(phrase) + r'\s+([A-Za-z]:\\.*)$'
    2.23 +
    2.24 +  raise Exception('Could not find compiler inclusion pattern')
    2.25 +
    2.26 +
    2.27  def DetectCompiler(cx, env, var):
    2.28    if var in env:
    2.29      trys = [env[var]]
    2.30 @@ -191,6 +204,12 @@
    2.31    # if vendor == 'gcc' and mode == 'CXX':
    2.32    #   args.extend(['-fno-exceptions', '-fno-rtti'])
    2.33    args.extend([filename, '-o', executable])
    2.34 +
    2.35 +  # For MSVC, we need to detect the inclusion pattern for foreign-language
    2.36 +  # systems.
    2.37 +  if vendor == 'msvc':
    2.38 +    args += ['-nologo', '-showIncludes']
    2.39 +
    2.40    util.con_out(
    2.41      util.ConsoleHeader,
    2.42      'Checking {0} compiler (vendor test {1})... '.format(mode, vendor),
    2.43 @@ -206,6 +225,10 @@
    2.44      print('failed with return code {0}'.format(p.returncode))
    2.45      return False
    2.46  
    2.47 +  inclusion_pattern = None
    2.48 +  if vendor == 'msvc':
    2.49 +    inclusion_pattern = DetectMicrosoftInclusionPattern(p.stdoutText)
    2.50 +
    2.51    exe = util.MakePath('.', executable)
    2.52    p = util.CreateProcess([executable], executable = exe)
    2.53    if p == None:
    2.54 @@ -235,6 +258,9 @@
    2.55      print('Unknown vendor {0}'.format(vendor))
    2.56      return False
    2.57  
    2.58 +  if inclusion_pattern is not None:
    2.59 +    v.extra_props['inclusion_pattern'] = inclusion_pattern
    2.60 +
    2.61    util.con_out(
    2.62      util.ConsoleHeader,
    2.63      'found {0} version {1}'.format(vendor, version),
     3.1 --- a/ambuild2/task.py	Sat Feb 15 12:24:48 2014 -0800
     3.2 +++ b/ambuild2/task.py	Sat Feb 15 20:57:39 2014 -0800
     3.3 @@ -44,10 +44,11 @@
     3.4      return (' '.join([arg for arg in self.data]))
     3.5  
     3.6  class WorkerChild(ChildProcessListener):
     3.7 -  def __init__(self, pump, channel, buildPath):
     3.8 +  def __init__(self, pump, channel, vars):
     3.9      super(WorkerChild, self).__init__(pump, channel)
    3.10 -    self.buildPath = buildPath
    3.11 +    self.buildPath = vars['buildPath']
    3.12      self.pid = os.getpid()
    3.13 +    self.vars = vars
    3.14      self.messageMap = {
    3.15        'task': lambda channel, message: self.receiveTask(channel, message)
    3.16      }
    3.17 @@ -200,7 +201,7 @@
    3.18        if cc_type == 'gcc':
    3.19          err, deps = util.ParseGCCDeps(err)
    3.20        elif cc_type == 'msvc':
    3.21 -        out, deps = util.ParseMSVCDeps(out)
    3.22 +        out, deps = util.ParseMSVCDeps(self.vars, out)
    3.23        elif cc_type == 'sun':
    3.24          err, deps = util.ParseSunDeps(err)
    3.25        else:
    3.26 @@ -226,7 +227,7 @@
    3.27      with util.FolderChanger(task_folder):
    3.28        # Includes go to stderr when we preprocess to stdout.
    3.29        p, out, err = util.Execute(cl_argv)
    3.30 -      out, deps = util.ParseMSVCDeps(err)
    3.31 +      out, deps = util.ParseMSVCDeps(self.vars, err)
    3.32        paths = self.rewriteDeps(deps)
    3.33  
    3.34        if p.returncode == 0:
    3.35 @@ -263,7 +264,7 @@
    3.36  
    3.37  # The TaskMasterChild is in the same process as the WorkerParent.
    3.38  class TaskMasterChild(ChildProcessListener):
    3.39 -  def __init__(self, pump, channel, task_graph, buildPath, num_processes):
    3.40 +  def __init__(self, pump, channel, task_graph, vars, num_processes):
    3.41      super(TaskMasterChild, self).__init__(pump, channel)
    3.42      self.task_graph = task_graph
    3.43      self.outstanding = {}
    3.44 @@ -279,7 +280,7 @@
    3.45        self.procman.spawn(
    3.46          WorkerParent(self),
    3.47          WorkerChild,
    3.48 -        args=(buildPath,)
    3.49 +        args=(vars,)
    3.50        )
    3.51  
    3.52      self.channel.send({
    3.53 @@ -456,7 +457,7 @@
    3.54      self.taskMaster = cx.procman.spawn(
    3.55        self,
    3.56        TaskMasterChild,
    3.57 -      args=(task_graph, cx.buildPath, num_processes)
    3.58 +      args=(task_graph, cx.vars, num_processes)
    3.59      )
    3.60  
    3.61    def receiveDone(self, message):
     4.1 --- a/ambuild2/util.py	Sat Feb 15 12:24:48 2014 -0800
     4.2 +++ b/ambuild2/util.py	Sat Feb 15 20:57:39 2014 -0800
     4.3 @@ -88,8 +88,8 @@
     4.4  
     4.5  def WaitForProcess(process):
     4.6    out, err = process.communicate()
     4.7 -  process.stdoutText = DecodeConsoleText(sys.stdout, process.stdout, out)
     4.8 -  process.stderrText = DecodeConsoleText(sys.stderr, process.stderr, err)
     4.9 +  process.stdoutText = DecodeConsoleText(sys.stdout, out)
    4.10 +  process.stderrText = DecodeConsoleText(sys.stderr, err)
    4.11    return process.returncode
    4.12  
    4.13  def CreateProcess(argv, executable = None):
    4.14 @@ -152,8 +152,8 @@
    4.15        shell=shell
    4.16    )
    4.17    stdout, stderr = p.communicate()
    4.18 -  out = stdout.decode('utf8')
    4.19 -  err = stderr.decode('utf8')
    4.20 +  out = DecodeConsoleText(sys.stdout, stdout)
    4.21 +  err = DecodeConsoleText(sys.stderr, stderr)
    4.22    return p, out, err
    4.23  
    4.24  def typeof(x):
    4.25 @@ -222,15 +222,22 @@
    4.26        new_text += line + '\n'
    4.27    return new_text, deps
    4.28  
    4.29 -def ParseMSVCDeps(out):
    4.30 +def ParseMSVCDeps(vars, out):
    4.31 +  if 'cc_inclusion_pattern' in vars:
    4.32 +    pattern = vars['cc_inclusion_pattern']
    4.33 +  elif 'cxx_inclusion_pattern' in vars:
    4.34 +    pattern = vars['cxx_inclusion_pattern']
    4.35 +  else:
    4.36 +    pattern = 'Note: including file:\s+(.+)$'
    4.37 +
    4.38    deps = []
    4.39    new_text = ''
    4.40    out = out.replace('\r\n', '\n')
    4.41    out = out.replace('\r', '\n')
    4.42    for line in out.split('\n'):
    4.43 -    m = re.match('Note: including file:\s+(.+)$', line)
    4.44 +    m = re.search(pattern, line)
    4.45      if m != None:
    4.46 -      file = m.groups()[0].strip()
    4.47 +      file = m.group(1).strip()
    4.48        deps.append(file)
    4.49      else:
    4.50        new_text += line + '\n'
    4.51 @@ -364,23 +371,19 @@
    4.52                ConsoleNormal)
    4.53        raise
    4.54  
    4.55 -def try_decode_with_pipe(pipe, text):
    4.56 +def DecodeConsoleText(origin, text):
    4.57    try:
    4.58 -    if hasattr(pipe, 'encoding') and pipe.encoding:
    4.59 -      return text.decode(pipe.encoding, 'replace')
    4.60 +    if origin.encoding:
    4.61 +      return text.decode(origin.encoding, 'replace')
    4.62    except:
    4.63 -    return None
    4.64 +    pass
    4.65  
    4.66 -def DecodeConsoleText(origin, pipe, text):
    4.67 -  result = try_decode_with_pipe(pipe, text)
    4.68 -  if result is None:
    4.69 -    result = try_decode_with_pipe(origin, text)
    4.70 -    if result is None:
    4.71 -      try:
    4.72 -        result = text.decode(locale.getpreferredencoding(), 'replace')
    4.73 -      except:
    4.74 -        result = text
    4.75 -  return result.encode('utf8')
    4.76 +  try:
    4.77 +    return text.decode(locale.getpreferredencoding(), 'replace')
    4.78 +  except:
    4.79 +    pass
    4.80 +
    4.81 +  return text.decode('utf8', 'replace')
    4.82  
    4.83  def WriteEncodedText(fd, text):
    4.84    if not hasattr(fd, 'encoding') or fd.encoding == None: