Update prebuilt Clang to r416183b1 (12.0.7).

clang 12.0.7 (based on r416183b1) from build 7485623.

Bug: http://b/189328402
Test: N/A
Change-Id: I378644ffa040e2c6f30674ce5173e473d3839010
Merged-In: I378644ffa040e2c6f30674ce5173e473d3839010
(cherry picked from commit 42276f8be3ab82a9a71957ca4943872498bdcdee)
diff --git a/bin/bisect_driver.py b/bin/bisect_driver.py
new file mode 100644
index 0000000..95d0ad1
--- /dev/null
+++ b/bin/bisect_driver.py
@@ -0,0 +1,337 @@
+# Copyright 2016 Google Inc. All Rights Reserved.
+#
+# This script is used to help the compiler wrapper in the Android build system
+# bisect for bad object files.
+"""Utilities for bisection of Android object files.
+
+This module contains a set of utilities to allow bisection between
+two sets (good and bad) of object files. Mostly used to find compiler
+bugs.
+
+Reference page:
+https://sites.google.com/a/google.com/chromeos-toolchain-team-home2/home/team-tools-and-scripts/bisecting-chromeos-compiler-problems/bisection-compiler-wrapper
+
+Design doc:
+https://docs.google.com/document/d/1yDgaUIa2O5w6dc3sSTe1ry-1ehKajTGJGQCbyn0fcEM
+"""
+
+from __future__ import print_function
+
+import contextlib
+import fcntl
+import os
+import shutil
+import subprocess
+import sys
+
+VALID_MODES = ['POPULATE_GOOD', 'POPULATE_BAD', 'TRIAGE']
+GOOD_CACHE = 'good'
+BAD_CACHE = 'bad'
+LIST_FILE = os.path.join(GOOD_CACHE, '_LIST')
+
+CONTINUE_ON_MISSING = os.environ.get('BISECT_CONTINUE_ON_MISSING', None) == '1'
+WRAPPER_SAFE_MODE = os.environ.get('BISECT_WRAPPER_SAFE_MODE', None) == '1'
+
+
+class Error(Exception):
+  """The general compiler wrapper error class."""
+  pass
+
+
+@contextlib.contextmanager
+def lock_file(path, mode):
+  """Lock file and block if other process has lock on file.
+
+  Acquire exclusive lock for file. Only blocks other processes if they attempt
+  to also acquire lock through this method. If only reading (modes 'r' and 'rb')
+  then the lock is shared (i.e. many reads can happen concurrently, but only one
+  process may write at a time).
+
+  This function is a contextmanager, meaning it's meant to be used with the
+  "with" statement in Python. This is so cleanup and setup happens automatically
+  and cleanly. Execution of the outer "with" statement happens at the "yield"
+  statement. Execution resumes after the yield when the outer "with" statement
+  ends.
+
+  Args:
+    path: path to file being locked
+    mode: mode to open file with ('w', 'r', etc.)
+  """
+  with open(path, mode) as f:
+    # Share the lock if just reading, make lock exclusive if writing
+    if f.mode == 'r' or f.mode == 'rb':
+      lock_type = fcntl.LOCK_SH
+    else:
+      lock_type = fcntl.LOCK_EX
+
+    try:
+      fcntl.lockf(f, lock_type)
+      yield f
+      f.flush()
+    except:
+      raise
+    finally:
+      fcntl.lockf(f, fcntl.LOCK_UN)
+
+
+def log_to_file(path, execargs, link_from=None, link_to=None):
+  """Common logging function.
+
+  Log current working directory, current execargs, and a from-to relationship
+  between files.
+  """
+  with lock_file(path, 'a') as log:
+    log.write('cd: %s; %s\n' % (os.getcwd(), ' '.join(execargs)))
+    if link_from and link_to:
+      log.write('%s -> %s\n' % (link_from, link_to))
+
+
+def exec_and_return(execargs):
+  """Execute process and return.
+
+  Execute according to execargs and return immediately. Don't inspect
+  stderr or stdout.
+  """
+  return subprocess.call(execargs)
+
+
+def which_cache(obj_file):
+  """Determine which cache an object belongs to.
+
+  The binary search tool creates two files for each search iteration listing
+  the full set of bad objects and full set of good objects. We use this to
+  determine where an object file should be linked from (good or bad).
+  """
+  bad_set_file = os.environ.get('BISECT_BAD_SET')
+  ret = subprocess.call(['grep', '-x', '-q', obj_file, bad_set_file])
+  if ret == 0:
+    return BAD_CACHE
+  else:
+    return GOOD_CACHE
+
+
+def makedirs(path):
+  """Try to create directories in path."""
+  try:
+    os.makedirs(path)
+  except os.error:
+    if not os.path.isdir(path):
+      raise
+
+
+def get_obj_path(execargs):
+  """Get the object path for the object file in the list of arguments.
+
+  Returns:
+    Absolute object path from execution args (-o argument). If no object being
+    outputted or output doesn't end in ".o" then return empty string.
+  """
+  try:
+    i = execargs.index('-o')
+  except ValueError:
+    return ''
+
+  obj_path = execargs[i + 1]
+  if not obj_path.endswith(('.o',)):
+    # TODO: what suffixes do we need to contemplate
+    # TODO: add this as a warning
+    # TODO: need to handle -r compilations
+    return ''
+
+  return os.path.abspath(obj_path)
+
+
+def get_dep_path(execargs):
+  """Get the dep file path for the dep file in the list of arguments.
+
+  Returns:
+    Absolute path of dependency file path from execution args (-o argument). If
+    no dependency being outputted then return empty string.
+  """
+  if '-MD' not in execargs and '-MMD' not in execargs:
+    return ''
+
+  # If -MF given this is the path of the dependency file. Otherwise the
+  # dependency file is the value of -o but with a .d extension
+  if '-MF' in execargs:
+    i = execargs.index('-MF')
+    dep_path = execargs[i + 1]
+    return os.path.abspath(dep_path)
+
+  full_obj_path = get_obj_path(execargs)
+  if not full_obj_path:
+    return ''
+
+  return full_obj_path[:-2] + '.d'
+
+
+def get_dwo_path(execargs):
+  """Get the dwo file path for the dwo file in the list of arguments.
+
+  Returns:
+    Absolute dwo file path from execution args (-gsplit-dwarf argument) If no
+    dwo file being outputted then return empty string.
+  """
+  if '-gsplit-dwarf' not in execargs:
+    return ''
+
+  full_obj_path = get_obj_path(execargs)
+  if not full_obj_path:
+    return ''
+
+  return full_obj_path[:-2] + '.dwo'
+
+
+def in_object_list(obj_name, list_filename):
+  """Check if object file name exist in file with object list."""
+  if not obj_name:
+    return False
+
+  with lock_file(list_filename, 'r') as list_file:
+    for line in list_file:
+      if line.strip() == obj_name:
+        return True
+
+    return False
+
+
+def get_side_effects(execargs):
+  """Determine side effects generated by compiler
+
+  Returns:
+    List of paths of objects that the compiler generates as side effects.
+  """
+  side_effects = []
+
+  # Cache dependency files
+  full_dep_path = get_dep_path(execargs)
+  if full_dep_path:
+    side_effects.append(full_dep_path)
+
+  # Cache dwo files
+  full_dwo_path = get_dwo_path(execargs)
+  if full_dwo_path:
+    side_effects.append(full_dwo_path)
+
+  return side_effects
+
+
+def cache_file(execargs, bisect_dir, cache, abs_file_path):
+  """Cache compiler output file (.o/.d/.dwo)."""
+  # os.path.join fails with absolute paths, use + instead
+  bisect_path = os.path.join(bisect_dir, cache) + abs_file_path
+  bisect_path_dir = os.path.dirname(bisect_path)
+  makedirs(bisect_path_dir)
+  pop_log = os.path.join(bisect_dir, cache, '_POPULATE_LOG')
+  log_to_file(pop_log, execargs, abs_file_path, bisect_path)
+
+  try:
+    if os.path.exists(abs_file_path):
+      shutil.copy2(abs_file_path, bisect_path)
+  except Exception:
+    print('Could not cache file %s' % abs_file_path, file=sys.stderr)
+    raise
+
+
+def restore_file(bisect_dir, cache, abs_file_path):
+  """Restore file from cache (.o/.d/.dwo)."""
+  # os.path.join fails with absolute paths, use + instead
+  cached_path = os.path.join(bisect_dir, cache) + abs_file_path
+  if os.path.exists(cached_path):
+    if os.path.exists(abs_file_path):
+      os.remove(abs_file_path)
+    try:
+      os.link(cached_path, abs_file_path)
+    except OSError:
+      shutil.copyfile(cached_path, abs_file_path)
+  else:
+    raise Error(('%s is missing from %s cache! Unsure how to proceed. Make '
+                 'will now crash.' % (cache, cached_path)))
+
+
+def bisect_populate(execargs, bisect_dir, population_name):
+  """Add necessary information to the bisect cache for the given execution.
+
+  Extract the necessary information for bisection from the compiler
+  execution arguments and put it into the bisection cache. This
+  includes copying the created object file, adding the object
+  file path to the cache list and keeping a log of the execution.
+
+  Args:
+    execargs: compiler execution arguments.
+    bisect_dir: bisection directory.
+    population_name: name of the cache being populated (good/bad).
+  """
+  retval = exec_and_return(execargs)
+  if retval:
+    return retval
+
+  full_obj_path = get_obj_path(execargs)
+  # If not a normal compiler call then just exit
+  if not full_obj_path:
+    return
+
+  cache_file(execargs, bisect_dir, population_name, full_obj_path)
+
+  population_dir = os.path.join(bisect_dir, population_name)
+  with lock_file(os.path.join(population_dir, '_LIST'), 'a') as object_list:
+    object_list.write('%s\n' % full_obj_path)
+
+  for side_effect in get_side_effects(execargs):
+    cache_file(execargs, bisect_dir, population_name, side_effect)
+
+
+def bisect_triage(execargs, bisect_dir):
+  full_obj_path = get_obj_path(execargs)
+  obj_list = os.path.join(bisect_dir, LIST_FILE)
+
+  # If the output isn't an object file just call compiler
+  if not full_obj_path:
+    return exec_and_return(execargs)
+
+  # If this isn't a bisected object just call compiler
+  # This shouldn't happen!
+  if not in_object_list(full_obj_path, obj_list):
+    if CONTINUE_ON_MISSING:
+      log_file = os.path.join(bisect_dir, '_MISSING_CACHED_OBJ_LOG')
+      log_to_file(log_file, execargs, '? compiler', full_obj_path)
+      return exec_and_return(execargs)
+    else:
+      raise Error(('%s is missing from cache! To ignore export '
+                   'BISECT_CONTINUE_ON_MISSING=1. See documentation for more '
+                   'details on this option.' % full_obj_path))
+
+  cache = which_cache(full_obj_path)
+
+  # If using safe WRAPPER_SAFE_MODE option call compiler and overwrite the
+  # result from the good/bad cache. This option is safe and covers all compiler
+  # side effects, but is very slow!
+  if WRAPPER_SAFE_MODE:
+    retval = exec_and_return(execargs)
+    if retval:
+      return retval
+    os.remove(full_obj_path)
+    restore_file(bisect_dir, cache, full_obj_path)
+    return
+
+  # Generate compiler side effects. Trick Make into thinking compiler was
+  # actually executed.
+  for side_effect in get_side_effects(execargs):
+    restore_file(bisect_dir, cache, side_effect)
+
+  # If generated object file happened to be pruned/cleaned by Make then link it
+  # over from cache again.
+  if not os.path.exists(full_obj_path):
+    restore_file(bisect_dir, cache, full_obj_path)
+
+
+def bisect_driver(bisect_stage, bisect_dir, execargs):
+  """Call appropriate bisection stage according to value in bisect_stage."""
+  if bisect_stage == 'POPULATE_GOOD':
+    bisect_populate(execargs, bisect_dir, GOOD_CACHE)
+  elif bisect_stage == 'POPULATE_BAD':
+    bisect_populate(execargs, bisect_dir, BAD_CACHE)
+  elif bisect_stage == 'TRIAGE':
+    bisect_triage(execargs, bisect_dir)
+  else:
+    raise ValueError('wrong value for BISECT_STAGE: %s' % bisect_stage)
diff --git a/bin/clang b/bin/clang
new file mode 100755
index 0000000..aed5473
--- /dev/null
+++ b/bin/clang
Binary files differ
diff --git a/bin/clang++ b/bin/clang++
new file mode 100755
index 0000000..aed5473
--- /dev/null
+++ b/bin/clang++
Binary files differ
diff --git a/bin/clang++.real b/bin/clang++.real
new file mode 120000
index 0000000..5f658f3
--- /dev/null
+++ b/bin/clang++.real
@@ -0,0 +1 @@
+clang.real
\ No newline at end of file
diff --git a/bin/clang-12 b/bin/clang-12
new file mode 100755
index 0000000..46c168c
--- /dev/null
+++ b/bin/clang-12
Binary files differ
diff --git a/bin/clang-check b/bin/clang-check
new file mode 100755
index 0000000..b5faa9b
--- /dev/null
+++ b/bin/clang-check
Binary files differ
diff --git a/bin/clang-cl b/bin/clang-cl
new file mode 120000
index 0000000..5f658f3
--- /dev/null
+++ b/bin/clang-cl
@@ -0,0 +1 @@
+clang.real
\ No newline at end of file
diff --git a/bin/clang-format b/bin/clang-format
new file mode 100755
index 0000000..52d890a
--- /dev/null
+++ b/bin/clang-format
Binary files differ
diff --git a/bin/clang-tidy b/bin/clang-tidy
new file mode 100755
index 0000000..aed5473
--- /dev/null
+++ b/bin/clang-tidy
Binary files differ
diff --git a/bin/clang-tidy.real b/bin/clang-tidy.real
new file mode 100755
index 0000000..6385c4d
--- /dev/null
+++ b/bin/clang-tidy.real
Binary files differ
diff --git a/bin/clang.real b/bin/clang.real
new file mode 120000
index 0000000..057f242
--- /dev/null
+++ b/bin/clang.real
@@ -0,0 +1 @@
+clang-12
\ No newline at end of file
diff --git a/bin/clangd b/bin/clangd
new file mode 100755
index 0000000..a23ac3d
--- /dev/null
+++ b/bin/clangd
Binary files differ
diff --git a/bin/dsymutil b/bin/dsymutil
new file mode 100755
index 0000000..94f2dfe
--- /dev/null
+++ b/bin/dsymutil
Binary files differ
diff --git a/bin/git-clang-format b/bin/git-clang-format
new file mode 100755
index 0000000..ccd2f50
--- /dev/null
+++ b/bin/git-clang-format
@@ -0,0 +1,586 @@
+#!/usr/bin/env python
+#
+#===- git-clang-format - ClangFormat Git Integration ---------*- python -*--===#
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+#===------------------------------------------------------------------------===#
+
+r"""
+clang-format git integration
+============================
+
+This file provides a clang-format integration for git. Put it somewhere in your
+path and ensure that it is executable. Then, "git clang-format" will invoke
+clang-format on the changes in current files or a specific commit.
+
+For further details, run:
+git clang-format -h
+
+Requires Python 2.7 or Python 3
+"""
+
+from __future__ import absolute_import, division, print_function
+import argparse
+import collections
+import contextlib
+import errno
+import os
+import re
+import subprocess
+import sys
+
+usage = 'git clang-format [OPTIONS] [<commit>] [<commit>] [--] [<file>...]'
+
+desc = '''
+If zero or one commits are given, run clang-format on all lines that differ
+between the working directory and <commit>, which defaults to HEAD.  Changes are
+only applied to the working directory.
+
+If two commits are given (requires --diff), run clang-format on all lines in the
+second <commit> that differ from the first <commit>.
+
+The following git-config settings set the default of the corresponding option:
+  clangFormat.binary
+  clangFormat.commit
+  clangFormat.extensions
+  clangFormat.style
+'''
+
+# Name of the temporary index file in which save the output of clang-format.
+# This file is created within the .git directory.
+temp_index_basename = 'clang-format-index'
+
+
+Range = collections.namedtuple('Range', 'start, count')
+
+
+def main():
+  config = load_git_config()
+
+  # In order to keep '--' yet allow options after positionals, we need to
+  # check for '--' ourselves.  (Setting nargs='*' throws away the '--', while
+  # nargs=argparse.REMAINDER disallows options after positionals.)
+  argv = sys.argv[1:]
+  try:
+    idx = argv.index('--')
+  except ValueError:
+    dash_dash = []
+  else:
+    dash_dash = argv[idx:]
+    argv = argv[:idx]
+
+  default_extensions = ','.join([
+      # From clang/lib/Frontend/FrontendOptions.cpp, all lower case
+      'c', 'h',  # C
+      'm',  # ObjC
+      'mm',  # ObjC++
+      'cc', 'cp', 'cpp', 'c++', 'cxx', 'hh', 'hpp', 'hxx',  # C++
+      'cu', 'cuh',  # CUDA
+      # Other languages that clang-format supports
+      'proto', 'protodevel',  # Protocol Buffers
+      'java',  # Java
+      'js',  # JavaScript
+      'ts',  # TypeScript
+      'cs',  # C Sharp
+      ])
+
+  p = argparse.ArgumentParser(
+    usage=usage, formatter_class=argparse.RawDescriptionHelpFormatter,
+    description=desc)
+  p.add_argument('--binary',
+                 default=config.get('clangformat.binary', 'clang-format'),
+                 help='path to clang-format'),
+  p.add_argument('--commit',
+                 default=config.get('clangformat.commit', 'HEAD'),
+                 help='default commit to use if none is specified'),
+  p.add_argument('--diff', action='store_true',
+                 help='print a diff instead of applying the changes')
+  p.add_argument('--extensions',
+                 default=config.get('clangformat.extensions',
+                                    default_extensions),
+                 help=('comma-separated list of file extensions to format, '
+                       'excluding the period and case-insensitive')),
+  p.add_argument('-f', '--force', action='store_true',
+                 help='allow changes to unstaged files')
+  p.add_argument('-p', '--patch', action='store_true',
+                 help='select hunks interactively')
+  p.add_argument('-q', '--quiet', action='count', default=0,
+                 help='print less information')
+  p.add_argument('--style',
+                 default=config.get('clangformat.style', None),
+                 help='passed to clang-format'),
+  p.add_argument('-v', '--verbose', action='count', default=0,
+                 help='print extra information')
+  # We gather all the remaining positional arguments into 'args' since we need
+  # to use some heuristics to determine whether or not <commit> was present.
+  # However, to print pretty messages, we make use of metavar and help.
+  p.add_argument('args', nargs='*', metavar='<commit>',
+                 help='revision from which to compute the diff')
+  p.add_argument('ignored', nargs='*', metavar='<file>...',
+                 help='if specified, only consider differences in these files')
+  opts = p.parse_args(argv)
+
+  opts.verbose -= opts.quiet
+  del opts.quiet
+
+  commits, files = interpret_args(opts.args, dash_dash, opts.commit)
+  if len(commits) > 1:
+    if not opts.diff:
+      die('--diff is required when two commits are given')
+  else:
+    if len(commits) > 2:
+      die('at most two commits allowed; %d given' % len(commits))
+  changed_lines = compute_diff_and_extract_lines(commits, files)
+  if opts.verbose >= 1:
+    ignored_files = set(changed_lines)
+  filter_by_extension(changed_lines, opts.extensions.lower().split(','))
+  if opts.verbose >= 1:
+    ignored_files.difference_update(changed_lines)
+    if ignored_files:
+      print('Ignoring changes in the following files (wrong extension):')
+      for filename in ignored_files:
+        print('    %s' % filename)
+    if changed_lines:
+      print('Running clang-format on the following files:')
+      for filename in changed_lines:
+        print('    %s' % filename)
+  if not changed_lines:
+    if opts.verbose >= 0:
+      print('no modified files to format')
+    return
+  # The computed diff outputs absolute paths, so we must cd before accessing
+  # those files.
+  cd_to_toplevel()
+  if len(commits) > 1:
+    old_tree = commits[1]
+    new_tree = run_clang_format_and_save_to_tree(changed_lines,
+                                                 revision=commits[1],
+                                                 binary=opts.binary,
+                                                 style=opts.style)
+  else:
+    old_tree = create_tree_from_workdir(changed_lines)
+    new_tree = run_clang_format_and_save_to_tree(changed_lines,
+                                                 binary=opts.binary,
+                                                 style=opts.style)
+  if opts.verbose >= 1:
+    print('old tree: %s' % old_tree)
+    print('new tree: %s' % new_tree)
+  if old_tree == new_tree:
+    if opts.verbose >= 0:
+      print('clang-format did not modify any files')
+  elif opts.diff:
+    print_diff(old_tree, new_tree)
+  else:
+    changed_files = apply_changes(old_tree, new_tree, force=opts.force,
+                                  patch_mode=opts.patch)
+    if (opts.verbose >= 0 and not opts.patch) or opts.verbose >= 1:
+      print('changed files:')
+      for filename in changed_files:
+        print('    %s' % filename)
+
+
+def load_git_config(non_string_options=None):
+  """Return the git configuration as a dictionary.
+
+  All options are assumed to be strings unless in `non_string_options`, in which
+  is a dictionary mapping option name (in lower case) to either "--bool" or
+  "--int"."""
+  if non_string_options is None:
+    non_string_options = {}
+  out = {}
+  for entry in run('git', 'config', '--list', '--null').split('\0'):
+    if entry:
+      if '\n' in entry:
+        name, value = entry.split('\n', 1)
+      else:
+        # A setting with no '=' ('\n' with --null) is implicitly 'true'
+        name = entry
+        value = 'true'
+      if name in non_string_options:
+        value = run('git', 'config', non_string_options[name], name)
+      out[name] = value
+  return out
+
+
+def interpret_args(args, dash_dash, default_commit):
+  """Interpret `args` as "[commits] [--] [files]" and return (commits, files).
+
+  It is assumed that "--" and everything that follows has been removed from
+  args and placed in `dash_dash`.
+
+  If "--" is present (i.e., `dash_dash` is non-empty), the arguments to its
+  left (if present) are taken as commits.  Otherwise, the arguments are checked
+  from left to right if they are commits or files.  If commits are not given,
+  a list with `default_commit` is used."""
+  if dash_dash:
+    if len(args) == 0:
+      commits = [default_commit]
+    else:
+      commits = args
+    for commit in commits:
+      object_type = get_object_type(commit)
+      if object_type not in ('commit', 'tag'):
+        if object_type is None:
+          die("'%s' is not a commit" % commit)
+        else:
+          die("'%s' is a %s, but a commit was expected" % (commit, object_type))
+    files = dash_dash[1:]
+  elif args:
+    commits = []
+    while args:
+      if not disambiguate_revision(args[0]):
+        break
+      commits.append(args.pop(0))
+    if not commits:
+      commits = [default_commit]
+    files = args
+  else:
+    commits = [default_commit]
+    files = []
+  return commits, files
+
+
+def disambiguate_revision(value):
+  """Returns True if `value` is a revision, False if it is a file, or dies."""
+  # If `value` is ambiguous (neither a commit nor a file), the following
+  # command will die with an appropriate error message.
+  run('git', 'rev-parse', value, verbose=False)
+  object_type = get_object_type(value)
+  if object_type is None:
+    return False
+  if object_type in ('commit', 'tag'):
+    return True
+  die('`%s` is a %s, but a commit or filename was expected' %
+      (value, object_type))
+
+
+def get_object_type(value):
+  """Returns a string description of an object's type, or None if it is not
+  a valid git object."""
+  cmd = ['git', 'cat-file', '-t', value]
+  p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+  stdout, stderr = p.communicate()
+  if p.returncode != 0:
+    return None
+  return convert_string(stdout.strip())
+
+
+def compute_diff_and_extract_lines(commits, files):
+  """Calls compute_diff() followed by extract_lines()."""
+  diff_process = compute_diff(commits, files)
+  changed_lines = extract_lines(diff_process.stdout)
+  diff_process.stdout.close()
+  diff_process.wait()
+  if diff_process.returncode != 0:
+    # Assume error was already printed to stderr.
+    sys.exit(2)
+  return changed_lines
+
+
+def compute_diff(commits, files):
+  """Return a subprocess object producing the diff from `commits`.
+
+  The return value's `stdin` file object will produce a patch with the
+  differences between the working directory and the first commit if a single
+  one was specified, or the difference between both specified commits, filtered
+  on `files` (if non-empty).  Zero context lines are used in the patch."""
+  git_tool = 'diff-index'
+  if len(commits) > 1:
+    git_tool = 'diff-tree'
+  cmd = ['git', git_tool, '-p', '-U0'] + commits + ['--']
+  cmd.extend(files)
+  p = subprocess.Popen(cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
+  p.stdin.close()
+  return p
+
+
+def extract_lines(patch_file):
+  """Extract the changed lines in `patch_file`.
+
+  The return value is a dictionary mapping filename to a list of (start_line,
+  line_count) pairs.
+
+  The input must have been produced with ``-U0``, meaning unidiff format with
+  zero lines of context.  The return value is a dict mapping filename to a
+  list of line `Range`s."""
+  matches = {}
+  for line in patch_file:
+    line = convert_string(line)
+    match = re.search(r'^\+\+\+\ [^/]+/(.*)', line)
+    if match:
+      filename = match.group(1).rstrip('\r\n')
+    match = re.search(r'^@@ -[0-9,]+ \+(\d+)(,(\d+))?', line)
+    if match:
+      start_line = int(match.group(1))
+      line_count = 1
+      if match.group(3):
+        line_count = int(match.group(3))
+      if line_count > 0:
+        matches.setdefault(filename, []).append(Range(start_line, line_count))
+  return matches
+
+
+def filter_by_extension(dictionary, allowed_extensions):
+  """Delete every key in `dictionary` that doesn't have an allowed extension.
+
+  `allowed_extensions` must be a collection of lowercase file extensions,
+  excluding the period."""
+  allowed_extensions = frozenset(allowed_extensions)
+  for filename in list(dictionary.keys()):
+    base_ext = filename.rsplit('.', 1)
+    if len(base_ext) == 1 and '' in allowed_extensions:
+        continue
+    if len(base_ext) == 1 or base_ext[1].lower() not in allowed_extensions:
+      del dictionary[filename]
+
+
+def cd_to_toplevel():
+  """Change to the top level of the git repository."""
+  toplevel = run('git', 'rev-parse', '--show-toplevel')
+  os.chdir(toplevel)
+
+
+def create_tree_from_workdir(filenames):
+  """Create a new git tree with the given files from the working directory.
+
+  Returns the object ID (SHA-1) of the created tree."""
+  return create_tree(filenames, '--stdin')
+
+
+def run_clang_format_and_save_to_tree(changed_lines, revision=None,
+                                      binary='clang-format', style=None):
+  """Run clang-format on each file and save the result to a git tree.
+
+  Returns the object ID (SHA-1) of the created tree."""
+  def iteritems(container):
+      try:
+          return container.iteritems() # Python 2
+      except AttributeError:
+          return container.items() # Python 3
+  def index_info_generator():
+    for filename, line_ranges in iteritems(changed_lines):
+      if revision:
+        git_metadata_cmd = ['git', 'ls-tree',
+                            '%s:%s' % (revision, os.path.dirname(filename)),
+                            os.path.basename(filename)]
+        git_metadata = subprocess.Popen(git_metadata_cmd, stdin=subprocess.PIPE,
+                                        stdout=subprocess.PIPE)
+        stdout = git_metadata.communicate()[0]
+        mode = oct(int(stdout.split()[0], 8))
+      else:
+        mode = oct(os.stat(filename).st_mode)
+      # Adjust python3 octal format so that it matches what git expects
+      if mode.startswith('0o'):
+          mode = '0' + mode[2:]
+      blob_id = clang_format_to_blob(filename, line_ranges,
+                                     revision=revision,
+                                     binary=binary,
+                                     style=style)
+      yield '%s %s\t%s' % (mode, blob_id, filename)
+  return create_tree(index_info_generator(), '--index-info')
+
+
+def create_tree(input_lines, mode):
+  """Create a tree object from the given input.
+
+  If mode is '--stdin', it must be a list of filenames.  If mode is
+  '--index-info' is must be a list of values suitable for "git update-index
+  --index-info", such as "<mode> <SP> <sha1> <TAB> <filename>".  Any other mode
+  is invalid."""
+  assert mode in ('--stdin', '--index-info')
+  cmd = ['git', 'update-index', '--add', '-z', mode]
+  with temporary_index_file():
+    p = subprocess.Popen(cmd, stdin=subprocess.PIPE)
+    for line in input_lines:
+      p.stdin.write(to_bytes('%s\0' % line))
+    p.stdin.close()
+    if p.wait() != 0:
+      die('`%s` failed' % ' '.join(cmd))
+    tree_id = run('git', 'write-tree')
+    return tree_id
+
+
+def clang_format_to_blob(filename, line_ranges, revision=None,
+                         binary='clang-format', style=None):
+  """Run clang-format on the given file and save the result to a git blob.
+
+  Runs on the file in `revision` if not None, or on the file in the working
+  directory if `revision` is None.
+
+  Returns the object ID (SHA-1) of the created blob."""
+  clang_format_cmd = [binary]
+  if style:
+    clang_format_cmd.extend(['-style='+style])
+  clang_format_cmd.extend([
+      '-lines=%s:%s' % (start_line, start_line+line_count-1)
+      for start_line, line_count in line_ranges])
+  if revision:
+    clang_format_cmd.extend(['-assume-filename='+filename])
+    git_show_cmd = ['git', 'cat-file', 'blob', '%s:%s' % (revision, filename)]
+    git_show = subprocess.Popen(git_show_cmd, stdin=subprocess.PIPE,
+                                stdout=subprocess.PIPE)
+    git_show.stdin.close()
+    clang_format_stdin = git_show.stdout
+  else:
+    clang_format_cmd.extend([filename])
+    git_show = None
+    clang_format_stdin = subprocess.PIPE
+  try:
+    clang_format = subprocess.Popen(clang_format_cmd, stdin=clang_format_stdin,
+                                    stdout=subprocess.PIPE)
+    if clang_format_stdin == subprocess.PIPE:
+      clang_format_stdin = clang_format.stdin
+  except OSError as e:
+    if e.errno == errno.ENOENT:
+      die('cannot find executable "%s"' % binary)
+    else:
+      raise
+  clang_format_stdin.close()
+  hash_object_cmd = ['git', 'hash-object', '-w', '--path='+filename, '--stdin']
+  hash_object = subprocess.Popen(hash_object_cmd, stdin=clang_format.stdout,
+                                 stdout=subprocess.PIPE)
+  clang_format.stdout.close()
+  stdout = hash_object.communicate()[0]
+  if hash_object.returncode != 0:
+    die('`%s` failed' % ' '.join(hash_object_cmd))
+  if clang_format.wait() != 0:
+    die('`%s` failed' % ' '.join(clang_format_cmd))
+  if git_show and git_show.wait() != 0:
+    die('`%s` failed' % ' '.join(git_show_cmd))
+  return convert_string(stdout).rstrip('\r\n')
+
+
+@contextlib.contextmanager
+def temporary_index_file(tree=None):
+  """Context manager for setting GIT_INDEX_FILE to a temporary file and deleting
+  the file afterward."""
+  index_path = create_temporary_index(tree)
+  old_index_path = os.environ.get('GIT_INDEX_FILE')
+  os.environ['GIT_INDEX_FILE'] = index_path
+  try:
+    yield
+  finally:
+    if old_index_path is None:
+      del os.environ['GIT_INDEX_FILE']
+    else:
+      os.environ['GIT_INDEX_FILE'] = old_index_path
+    os.remove(index_path)
+
+
+def create_temporary_index(tree=None):
+  """Create a temporary index file and return the created file's path.
+
+  If `tree` is not None, use that as the tree to read in.  Otherwise, an
+  empty index is created."""
+  gitdir = run('git', 'rev-parse', '--git-dir')
+  path = os.path.join(gitdir, temp_index_basename)
+  if tree is None:
+    tree = '--empty'
+  run('git', 'read-tree', '--index-output='+path, tree)
+  return path
+
+
+def print_diff(old_tree, new_tree):
+  """Print the diff between the two trees to stdout."""
+  # We use the porcelain 'diff' and not plumbing 'diff-tree' because the output
+  # is expected to be viewed by the user, and only the former does nice things
+  # like color and pagination.
+  #
+  # We also only print modified files since `new_tree` only contains the files
+  # that were modified, so unmodified files would show as deleted without the
+  # filter.
+  subprocess.check_call(['git', 'diff', '--diff-filter=M', old_tree, new_tree,
+                         '--'])
+
+
+def apply_changes(old_tree, new_tree, force=False, patch_mode=False):
+  """Apply the changes in `new_tree` to the working directory.
+
+  Bails if there are local changes in those files and not `force`.  If
+  `patch_mode`, runs `git checkout --patch` to select hunks interactively."""
+  changed_files = run('git', 'diff-tree', '--diff-filter=M', '-r', '-z',
+                      '--name-only', old_tree,
+                      new_tree).rstrip('\0').split('\0')
+  if not force:
+    unstaged_files = run('git', 'diff-files', '--name-status', *changed_files)
+    if unstaged_files:
+      print('The following files would be modified but '
+                'have unstaged changes:', file=sys.stderr)
+      print(unstaged_files, file=sys.stderr)
+      print('Please commit, stage, or stash them first.', file=sys.stderr)
+      sys.exit(2)
+  if patch_mode:
+    # In patch mode, we could just as well create an index from the new tree
+    # and checkout from that, but then the user will be presented with a
+    # message saying "Discard ... from worktree".  Instead, we use the old
+    # tree as the index and checkout from new_tree, which gives the slightly
+    # better message, "Apply ... to index and worktree".  This is not quite
+    # right, since it won't be applied to the user's index, but oh well.
+    with temporary_index_file(old_tree):
+      subprocess.check_call(['git', 'checkout', '--patch', new_tree])
+    index_tree = old_tree
+  else:
+    with temporary_index_file(new_tree):
+      run('git', 'checkout-index', '-a', '-f')
+  return changed_files
+
+
+def run(*args, **kwargs):
+  stdin = kwargs.pop('stdin', '')
+  verbose = kwargs.pop('verbose', True)
+  strip = kwargs.pop('strip', True)
+  for name in kwargs:
+    raise TypeError("run() got an unexpected keyword argument '%s'" % name)
+  p = subprocess.Popen(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                       stdin=subprocess.PIPE)
+  stdout, stderr = p.communicate(input=stdin)
+
+  stdout = convert_string(stdout)
+  stderr = convert_string(stderr)
+
+  if p.returncode == 0:
+    if stderr:
+      if verbose:
+        print('`%s` printed to stderr:' % ' '.join(args), file=sys.stderr)
+      print(stderr.rstrip(), file=sys.stderr)
+    if strip:
+      stdout = stdout.rstrip('\r\n')
+    return stdout
+  if verbose:
+    print('`%s` returned %s' % (' '.join(args), p.returncode), file=sys.stderr)
+  if stderr:
+    print(stderr.rstrip(), file=sys.stderr)
+  sys.exit(2)
+
+
+def die(message):
+  print('error:', message, file=sys.stderr)
+  sys.exit(2)
+
+
+def to_bytes(str_input):
+    # Encode to UTF-8 to get binary data.
+    if isinstance(str_input, bytes):
+        return str_input
+    return str_input.encode('utf-8')
+
+
+def to_string(bytes_input):
+    if isinstance(bytes_input, str):
+        return bytes_input
+    return bytes_input.encode('utf-8')
+
+
+def convert_string(bytes_input):
+    try:
+        return to_string(bytes_input.decode('utf-8'))
+    except AttributeError: # 'str' object has no attribute 'decode'.
+        return str(bytes_input)
+    except UnicodeError:
+        return str(bytes_input)
+
+if __name__ == '__main__':
+  main()
diff --git a/bin/ld.lld b/bin/ld.lld
new file mode 120000
index 0000000..02416ac
--- /dev/null
+++ b/bin/ld.lld
@@ -0,0 +1 @@
+lld
\ No newline at end of file
diff --git a/bin/ld64.lld b/bin/ld64.lld
new file mode 120000
index 0000000..02416ac
--- /dev/null
+++ b/bin/ld64.lld
@@ -0,0 +1 @@
+lld
\ No newline at end of file
diff --git a/bin/lld b/bin/lld
new file mode 100755
index 0000000..a4380c1
--- /dev/null
+++ b/bin/lld
Binary files differ
diff --git a/bin/lld-link b/bin/lld-link
new file mode 120000
index 0000000..02416ac
--- /dev/null
+++ b/bin/lld-link
@@ -0,0 +1 @@
+lld
\ No newline at end of file
diff --git a/bin/lldb b/bin/lldb
new file mode 100755
index 0000000..8410a7e
--- /dev/null
+++ b/bin/lldb
Binary files differ
diff --git a/bin/lldb-argdumper b/bin/lldb-argdumper
new file mode 100755
index 0000000..6f58f6f
--- /dev/null
+++ b/bin/lldb-argdumper
Binary files differ
diff --git a/bin/lldb.sh b/bin/lldb.sh
new file mode 100755
index 0000000..fc21c26
--- /dev/null
+++ b/bin/lldb.sh
@@ -0,0 +1,5 @@
+#!/bin/bash
+CURDIR=$(cd $(dirname $0) && pwd)
+export PYTHONHOME="$CURDIR/../python3"
+export LD_LIBRARY_PATH="$CURDIR/../python3/lib:$LD_LIBRARY_PATH"
+"$CURDIR/lldb" "$@"
diff --git a/bin/llvm-addr2line b/bin/llvm-addr2line
new file mode 120000
index 0000000..51f8041
--- /dev/null
+++ b/bin/llvm-addr2line
@@ -0,0 +1 @@
+llvm-symbolizer
\ No newline at end of file
diff --git a/bin/llvm-ar b/bin/llvm-ar
new file mode 100755
index 0000000..238abff
--- /dev/null
+++ b/bin/llvm-ar
Binary files differ
diff --git a/bin/llvm-as b/bin/llvm-as
new file mode 100755
index 0000000..14776d9
--- /dev/null
+++ b/bin/llvm-as
Binary files differ
diff --git a/bin/llvm-cfi-verify b/bin/llvm-cfi-verify
new file mode 100755
index 0000000..5dbeaaf
--- /dev/null
+++ b/bin/llvm-cfi-verify
Binary files differ
diff --git a/bin/llvm-config b/bin/llvm-config
new file mode 100755
index 0000000..ec11122
--- /dev/null
+++ b/bin/llvm-config
Binary files differ
diff --git a/bin/llvm-cov b/bin/llvm-cov
new file mode 100755
index 0000000..38bbbf1
--- /dev/null
+++ b/bin/llvm-cov
Binary files differ
diff --git a/bin/llvm-cxxfilt b/bin/llvm-cxxfilt
new file mode 100755
index 0000000..967fd7e
--- /dev/null
+++ b/bin/llvm-cxxfilt
Binary files differ
diff --git a/bin/llvm-dis b/bin/llvm-dis
new file mode 100755
index 0000000..bef780a
--- /dev/null
+++ b/bin/llvm-dis
Binary files differ
diff --git a/bin/llvm-dwarfdump b/bin/llvm-dwarfdump
new file mode 100755
index 0000000..51b95d8
--- /dev/null
+++ b/bin/llvm-dwarfdump
Binary files differ
diff --git a/bin/llvm-dwp b/bin/llvm-dwp
new file mode 100755
index 0000000..0191ddf
--- /dev/null
+++ b/bin/llvm-dwp
Binary files differ
diff --git a/bin/llvm-lib b/bin/llvm-lib
new file mode 120000
index 0000000..3b94d1a
--- /dev/null
+++ b/bin/llvm-lib
@@ -0,0 +1 @@
+llvm-ar
\ No newline at end of file
diff --git a/bin/llvm-link b/bin/llvm-link
new file mode 100755
index 0000000..40ed828
--- /dev/null
+++ b/bin/llvm-link
Binary files differ
diff --git a/bin/llvm-lipo b/bin/llvm-lipo
new file mode 100755
index 0000000..4c418b4
--- /dev/null
+++ b/bin/llvm-lipo
Binary files differ
diff --git a/bin/llvm-modextract b/bin/llvm-modextract
new file mode 100755
index 0000000..b9952b1
--- /dev/null
+++ b/bin/llvm-modextract
Binary files differ
diff --git a/bin/llvm-nm b/bin/llvm-nm
new file mode 100755
index 0000000..8cc47e3
--- /dev/null
+++ b/bin/llvm-nm
Binary files differ
diff --git a/bin/llvm-objcopy b/bin/llvm-objcopy
new file mode 100755
index 0000000..0b11364
--- /dev/null
+++ b/bin/llvm-objcopy
Binary files differ
diff --git a/bin/llvm-objdump b/bin/llvm-objdump
new file mode 100755
index 0000000..78bc5e7
--- /dev/null
+++ b/bin/llvm-objdump
Binary files differ
diff --git a/bin/llvm-profdata b/bin/llvm-profdata
new file mode 100755
index 0000000..f1bd8ff
--- /dev/null
+++ b/bin/llvm-profdata
Binary files differ
diff --git a/bin/llvm-ranlib b/bin/llvm-ranlib
new file mode 120000
index 0000000..3b94d1a
--- /dev/null
+++ b/bin/llvm-ranlib
@@ -0,0 +1 @@
+llvm-ar
\ No newline at end of file
diff --git a/bin/llvm-rc b/bin/llvm-rc
new file mode 100755
index 0000000..974773c
--- /dev/null
+++ b/bin/llvm-rc
Binary files differ
diff --git a/bin/llvm-readelf b/bin/llvm-readelf
new file mode 120000
index 0000000..a39a921
--- /dev/null
+++ b/bin/llvm-readelf
@@ -0,0 +1 @@
+llvm-readobj
\ No newline at end of file
diff --git a/bin/llvm-readobj b/bin/llvm-readobj
new file mode 100755
index 0000000..6cdf2eb
--- /dev/null
+++ b/bin/llvm-readobj
Binary files differ
diff --git a/bin/llvm-size b/bin/llvm-size
new file mode 100755
index 0000000..1b9f59c
--- /dev/null
+++ b/bin/llvm-size
Binary files differ
diff --git a/bin/llvm-strings b/bin/llvm-strings
new file mode 100755
index 0000000..39453b5
--- /dev/null
+++ b/bin/llvm-strings
Binary files differ
diff --git a/bin/llvm-strip b/bin/llvm-strip
new file mode 120000
index 0000000..caea5a7
--- /dev/null
+++ b/bin/llvm-strip
@@ -0,0 +1 @@
+llvm-objcopy
\ No newline at end of file
diff --git a/bin/llvm-symbolizer b/bin/llvm-symbolizer
new file mode 100755
index 0000000..1bcec10
--- /dev/null
+++ b/bin/llvm-symbolizer
Binary files differ
diff --git a/bin/remote_toolchain_inputs b/bin/remote_toolchain_inputs
new file mode 100644
index 0000000..d00934d
--- /dev/null
+++ b/bin/remote_toolchain_inputs
@@ -0,0 +1,14 @@
+clang
+clang++
+clang.real
+clang++.real
+clang-tidy
+clang-tidy.real
+../lib64/libc++.so.1
+lld
+ld64.lld
+ld.lld
+../lib64/clang/12.0.7/share
+../lib64/clang/12.0.7/lib/linux
+../lib64/clang/12.0.7/include
+../lib64/libxml2.so.2.9.10
diff --git a/bin/sancov b/bin/sancov
new file mode 100755
index 0000000..b6a59a0
--- /dev/null
+++ b/bin/sancov
Binary files differ
diff --git a/bin/sanstats b/bin/sanstats
new file mode 100755
index 0000000..34dd5b6
--- /dev/null
+++ b/bin/sanstats
Binary files differ
diff --git a/bin/scan-build b/bin/scan-build
new file mode 100755
index 0000000..645f550
--- /dev/null
+++ b/bin/scan-build
@@ -0,0 +1,2010 @@
+#!/usr/bin/env perl
+#
+# Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+# See https://llvm.org/LICENSE.txt for license information.
+# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+#
+##===----------------------------------------------------------------------===##
+#
+# A script designed to wrap a build so that all calls to gcc are intercepted
+# and piped to the static analyzer.
+#
+##===----------------------------------------------------------------------===##
+
+use strict;
+use warnings;
+use FindBin qw($RealBin);
+use Digest::MD5;
+use File::Basename;
+use File::Find;
+use File::Copy qw(copy);
+use File::Path qw( rmtree mkpath );
+use Term::ANSIColor;
+use Term::ANSIColor qw(:constants);
+use Cwd qw/ getcwd abs_path /;
+use Sys::Hostname;
+use Hash::Util qw(lock_keys);
+
+my $Prog = "scan-build";
+my $BuildName;
+my $BuildDate;
+
+my $TERM = $ENV{'TERM'};
+my $UseColor = (defined $TERM and $TERM =~ 'xterm-.*color' and -t STDOUT
+                and defined $ENV{'SCAN_BUILD_COLOR'});
+
+# Portability: getpwuid is not implemented for Win32 (see Perl language
+# reference, perlport), use getlogin instead.
+my $UserName = HtmlEscape(getlogin() || getpwuid($<) || 'unknown');
+my $HostName = HtmlEscape(hostname() || 'unknown');
+my $CurrentDir = HtmlEscape(getcwd());
+
+my $CmdArgs;
+
+my $Date = localtime();
+
+# Command-line/config arguments.
+my %Options = (
+  Verbose => 0,              # Verbose output from this script.
+  AnalyzeHeaders => 0,
+  OutputDir => undef,        # Parent directory to store HTML files.
+  HtmlTitle => basename($CurrentDir)." - scan-build results",
+  IgnoreErrors => 0,         # Ignore build errors.
+  KeepCC => 0,               # Do not override CC and CXX make variables
+  ViewResults => 0,          # View results when the build terminates.
+  ExitStatusFoundBugs => 0,  # Exit status reflects whether bugs were found
+  ShowDescription => 0,      # Display the description of the defect in the list
+  KeepEmpty => 0,            # Don't remove output directory even with 0 results.
+  EnableCheckers => {},
+  DisableCheckers => {},
+  SilenceCheckers => {},
+  Excludes => [],
+  UseCC => undef,            # C compiler to use for compilation.
+  UseCXX => undef,           # C++ compiler to use for compilation.
+  AnalyzerTarget => undef,
+  StoreModel => undef,
+  ConstraintsModel => undef,
+  InternalStats => undef,
+  OutputFormat => "html",
+  ConfigOptions => [],       # Options to pass through to the analyzer's -analyzer-config flag.
+  ReportFailures => undef,
+  AnalyzerStats => 0,
+  MaxLoop => 0,
+  PluginsToLoad => [],
+  AnalyzerDiscoveryMethod => undef,
+  OverrideCompiler => 0,     # The flag corresponding to the --override-compiler command line option.
+  ForceAnalyzeDebugCode => 0,
+  GenerateIndex => 0         # Skip the analysis, only generate index.html.
+);
+lock_keys(%Options);
+
+##----------------------------------------------------------------------------##
+# Diagnostics
+##----------------------------------------------------------------------------##
+
+sub Diag {
+  if ($UseColor) {
+    print BOLD, MAGENTA "$Prog: @_";
+    print RESET;
+  }
+  else {
+    print "$Prog: @_";
+  }
+}
+
+sub ErrorDiag {
+  if ($UseColor) {
+    print STDERR BOLD, RED "$Prog: ";
+    print STDERR RESET, RED @_;
+    print STDERR RESET;
+  } else {
+    print STDERR "$Prog: @_";
+  }
+}
+
+sub DiagCrashes {
+  my $Dir = shift;
+  Diag ("The analyzer encountered problems on some source files.\n");
+  Diag ("Preprocessed versions of these sources were deposited in '$Dir/failures'.\n");
+  Diag ("Please consider submitting a bug report using these files:\n");
+  Diag ("  http://clang-analyzer.llvm.org/filing_bugs.html\n")
+}
+
+sub DieDiag {
+  if ($UseColor) {
+    print STDERR BOLD, RED "$Prog: ";
+    print STDERR RESET, RED @_;
+    print STDERR RESET;
+  }
+  else {
+    print STDERR "$Prog: ", @_;
+  }
+  exit 1;
+}
+
+##----------------------------------------------------------------------------##
+# Print default checker names
+##----------------------------------------------------------------------------##
+
+if (grep /^--help-checkers$/, @ARGV) {
+    my @options = qx($0 -h);
+    foreach (@options) {
+    next unless /^ \+/;
+    s/^\s*//;
+    my ($sign, $name, @text) = split ' ', $_;
+    print $name, $/ if $sign eq '+';
+    }
+    exit 0;
+}
+
+##----------------------------------------------------------------------------##
+# Declaration of Clang options.  Populated later.
+##----------------------------------------------------------------------------##
+
+my $Clang;
+my $ClangSB;
+my $ClangCXX;
+my $ClangVersion;
+
+##----------------------------------------------------------------------------##
+# GetHTMLRunDir - Construct an HTML directory name for the current sub-run.
+##----------------------------------------------------------------------------##
+
+sub GetHTMLRunDir {
+  die "Not enough arguments." if (@_ == 0);
+  my $Dir = shift @_;
+  my $TmpMode = 0;
+  if (!defined $Dir) {
+    $Dir = $ENV{'TMPDIR'} || $ENV{'TEMP'} || $ENV{'TMP'} || "/tmp";
+    $TmpMode = 1;
+  }
+
+  # Chop off any trailing '/' characters.
+  while ($Dir =~ /\/$/) { chop $Dir; }
+
+  # Get current date and time.
+  my @CurrentTime = localtime();
+  my $year  = $CurrentTime[5] + 1900;
+  my $day   = $CurrentTime[3];
+  my $month = $CurrentTime[4] + 1;
+  my $hour =  $CurrentTime[2];
+  my $min =   $CurrentTime[1];
+  my $sec =   $CurrentTime[0];
+
+  my $TimeString = sprintf("%02d%02d%02d", $hour, $min, $sec);
+  my $DateString = sprintf("%d-%02d-%02d-%s-$$",
+                           $year, $month, $day, $TimeString);
+
+  # Determine the run number.
+  my $RunNumber;
+
+  if (-d $Dir) {
+    if (! -r $Dir) {
+      DieDiag("directory '$Dir' exists but is not readable.\n");
+    }
+    # Iterate over all files in the specified directory.
+    my $max = 0;
+    opendir(DIR, $Dir);
+    my @FILES = grep { -d "$Dir/$_" } readdir(DIR);
+    closedir(DIR);
+
+    foreach my $f (@FILES) {
+      # Strip the prefix '$Prog-' if we are dumping files to /tmp.
+      if ($TmpMode) {
+        next if (!($f =~ /^$Prog-(.+)/));
+        $f = $1;
+      }
+
+      my @x = split/-/, $f;
+      next if (scalar(@x) != 4);
+      next if ($x[0] != $year);
+      next if ($x[1] != $month);
+      next if ($x[2] != $day);
+      next if ($x[3] != $TimeString);
+      next if ($x[4] != $$);
+
+      if ($x[5] > $max) {
+        $max = $x[5];
+      }
+    }
+
+    $RunNumber = $max + 1;
+  }
+  else {
+
+    if (-x $Dir) {
+      DieDiag("'$Dir' exists but is not a directory.\n");
+    }
+
+    if ($TmpMode) {
+      DieDiag("The directory '/tmp' does not exist or cannot be accessed.\n");
+    }
+
+    # $Dir does not exist.  It will be automatically created by the
+    # clang driver.  Set the run number to 1.
+
+    $RunNumber = 1;
+  }
+
+  die "RunNumber must be defined!" if (!defined $RunNumber);
+
+  # Append the run number.
+  my $NewDir;
+  if ($TmpMode) {
+    $NewDir = "$Dir/$Prog-$DateString-$RunNumber";
+  }
+  else {
+    $NewDir = "$Dir/$DateString-$RunNumber";
+  }
+
+  # Make sure that the directory does not exist in order to avoid hijack.
+  if (-e $NewDir) {
+      DieDiag("The directory '$NewDir' already exists.\n");
+  }
+
+  mkpath($NewDir);
+  return $NewDir;
+}
+
+sub SetHtmlEnv {
+
+  die "Wrong number of arguments." if (scalar(@_) != 2);
+
+  my $Args = shift;
+  my $Dir = shift;
+
+  die "No build command." if (scalar(@$Args) == 0);
+
+  my $Cmd = $$Args[0];
+
+  if ($Cmd =~ /configure/ || $Cmd =~ /autogen/) {
+    return;
+  }
+
+  if ($Options{Verbose}) {
+    Diag("Emitting reports for this run to '$Dir'.\n");
+  }
+
+  $ENV{'CCC_ANALYZER_HTML'} = $Dir;
+}
+
+##----------------------------------------------------------------------------##
+# ComputeDigest - Compute a digest of the specified file.
+##----------------------------------------------------------------------------##
+
+sub ComputeDigest {
+  my $FName = shift;
+  DieDiag("Cannot read $FName to compute Digest.\n") if (! -r $FName);
+
+  # Use Digest::MD5.  We don't have to be cryptographically secure.  We're
+  # just looking for duplicate files that come from a non-malicious source.
+  # We use Digest::MD5 because it is a standard Perl module that should
+  # come bundled on most systems.
+  open(FILE, $FName) or DieDiag("Cannot open $FName when computing Digest.\n");
+  binmode FILE;
+  my $Result = Digest::MD5->new->addfile(*FILE)->hexdigest;
+  close(FILE);
+
+  # Return the digest.
+  return $Result;
+}
+
+##----------------------------------------------------------------------------##
+#  UpdatePrefix - Compute the common prefix of files.
+##----------------------------------------------------------------------------##
+
+my $Prefix;
+
+sub UpdatePrefix {
+  my $x = shift;
+  my $y = basename($x);
+  $x =~ s/\Q$y\E$//;
+
+  if (!defined $Prefix) {
+    $Prefix = $x;
+    return;
+  }
+
+  chop $Prefix while (!($x =~ /^\Q$Prefix/));
+}
+
+sub GetPrefix {
+  return $Prefix;
+}
+
+##----------------------------------------------------------------------------##
+#  UpdateInFilePath - Update the path in the report file.
+##----------------------------------------------------------------------------##
+
+sub UpdateInFilePath {
+  my $fname = shift;
+  my $regex = shift;
+  my $newtext = shift;
+
+  open (RIN, $fname) or die "cannot open $fname";
+  open (ROUT, ">", "$fname.tmp") or die "cannot open $fname.tmp";
+
+  while (<RIN>) {
+    s/$regex/$newtext/;
+    print ROUT $_;
+  }
+
+  close (ROUT);
+  close (RIN);
+  rename("$fname.tmp", $fname)
+}
+
+##----------------------------------------------------------------------------##
+# AddStatLine - Decode and insert a statistics line into the database.
+##----------------------------------------------------------------------------##
+
+sub AddStatLine {
+  my $Line  = shift;
+  my $Stats = shift;
+  my $File  = shift;
+
+  print $Line . "\n";
+
+  my $Regex = qr/(.*?)\ ->\ Total\ CFGBlocks:\ (\d+)\ \|\ Unreachable
+      \ CFGBlocks:\ (\d+)\ \|\ Exhausted\ Block:\ (yes|no)\ \|\ Empty\ WorkList:
+      \ (yes|no)/x;
+
+  if ($Line !~ $Regex) {
+    return;
+  }
+
+  # Create a hash of the interesting fields
+  my $Row = {
+    Filename    => $File,
+    Function    => $1,
+    Total       => $2,
+    Unreachable => $3,
+    Aborted     => $4,
+    Empty       => $5
+  };
+
+  # Add them to the stats array
+  push @$Stats, $Row;
+}
+
+##----------------------------------------------------------------------------##
+# ScanFile - Scan a report file for various identifying attributes.
+##----------------------------------------------------------------------------##
+
+# Sometimes a source file is scanned more than once, and thus produces
+# multiple error reports.  We use a cache to solve this problem.
+
+my %AlreadyScanned;
+
+sub ScanFile {
+
+  my $Index = shift;
+  my $Dir = shift;
+  my $FName = shift;
+  my $Stats = shift;
+
+  # Compute a digest for the report file.  Determine if we have already
+  # scanned a file that looks just like it.
+
+  my $digest = ComputeDigest("$Dir/$FName");
+
+  if (defined $AlreadyScanned{$digest}) {
+    # Redundant file.  Remove it.
+    unlink("$Dir/$FName");
+    return;
+  }
+
+  $AlreadyScanned{$digest} = 1;
+
+  # At this point the report file is not world readable.  Make it happen.
+  chmod(0644, "$Dir/$FName");
+
+  # Scan the report file for tags.
+  open(IN, "$Dir/$FName") or DieDiag("Cannot open '$Dir/$FName'\n");
+
+  my $BugType        = "";
+  my $BugFile        = "";
+  my $BugFunction    = "";
+  my $BugCategory    = "";
+  my $BugDescription = "";
+  my $BugPathLength  = 1;
+  my $BugLine        = 0;
+
+  while (<IN>) {
+    last if (/<!-- BUGMETAEND -->/);
+
+    if (/<!-- BUGTYPE (.*) -->$/) {
+      $BugType = $1;
+    }
+    elsif (/<!-- BUGFILE (.*) -->$/) {
+      $BugFile = abs_path($1);
+      if (!defined $BugFile) {
+         # The file no longer exists: use the original path.
+         $BugFile = $1;
+      }
+
+      # Get just the path
+      my $p = dirname($BugFile);
+      # Check if the path is found in the list of exclude
+      if (grep { $p =~ m/$_/ } @{$Options{Excludes}}) {
+         if ($Options{Verbose}) {
+             Diag("File '$BugFile' deleted: part of an ignored directory.\n");
+         }
+
+       # File in an ignored directory. Remove it
+       unlink("$Dir/$FName");
+       return;
+      }
+
+      UpdatePrefix($BugFile);
+    }
+    elsif (/<!-- BUGPATHLENGTH (.*) -->$/) {
+      $BugPathLength = $1;
+    }
+    elsif (/<!-- BUGLINE (.*) -->$/) {
+      $BugLine = $1;
+    }
+    elsif (/<!-- BUGCATEGORY (.*) -->$/) {
+      $BugCategory = $1;
+    }
+    elsif (/<!-- BUGDESC (.*) -->$/) {
+      $BugDescription = $1;
+    }
+    elsif (/<!-- FUNCTIONNAME (.*) -->$/) {
+      $BugFunction = $1;
+    }
+
+  }
+
+
+  close(IN);
+
+  if (!defined $BugCategory) {
+    $BugCategory = "Other";
+  }
+
+  # Don't add internal statistics to the bug reports
+  if ($BugCategory =~ /statistics/i) {
+    AddStatLine($BugDescription, $Stats, $BugFile);
+    return;
+  }
+
+  push @$Index,[ $FName, $BugCategory, $BugType, $BugFile, $BugFunction, $BugLine,
+                 $BugPathLength ];
+
+  if ($Options{ShowDescription}) {
+      push @{ $Index->[-1] }, $BugDescription
+  }
+}
+
+##----------------------------------------------------------------------------##
+# CopyFiles - Copy resource files to target directory.
+##----------------------------------------------------------------------------##
+
+sub CopyFiles {
+
+  my $Dir = shift;
+
+  my $JS = Cwd::realpath("$RealBin/../share/scan-build/sorttable.js");
+
+  DieDiag("Cannot find 'sorttable.js'.\n")
+    if (! -r $JS);
+
+  copy($JS, "$Dir");
+
+  DieDiag("Could not copy 'sorttable.js' to '$Dir'.\n")
+    if (! -r "$Dir/sorttable.js");
+
+  my $CSS = Cwd::realpath("$RealBin/../share/scan-build/scanview.css");
+
+  DieDiag("Cannot find 'scanview.css'.\n")
+    if (! -r $CSS);
+
+  copy($CSS, "$Dir");
+
+  DieDiag("Could not copy 'scanview.css' to '$Dir'.\n")
+    if (! -r $CSS);
+}
+
+##----------------------------------------------------------------------------##
+# CalcStats - Calculates visitation statistics and returns the string.
+##----------------------------------------------------------------------------##
+
+sub CalcStats {
+  my $Stats = shift;
+
+  my $TotalBlocks = 0;
+  my $UnreachedBlocks = 0;
+  my $TotalFunctions = scalar(@$Stats);
+  my $BlockAborted = 0;
+  my $WorkListAborted = 0;
+  my $Aborted = 0;
+
+  # Calculate the unique files
+  my $FilesHash = {};
+
+  foreach my $Row (@$Stats) {
+    $FilesHash->{$Row->{Filename}} = 1;
+    $TotalBlocks += $Row->{Total};
+    $UnreachedBlocks += $Row->{Unreachable};
+    $BlockAborted++ if $Row->{Aborted} eq 'yes';
+    $WorkListAborted++ if $Row->{Empty} eq 'no';
+    $Aborted++ if $Row->{Aborted} eq 'yes' || $Row->{Empty} eq 'no';
+  }
+
+  my $TotalFiles = scalar(keys(%$FilesHash));
+
+  # Calculations
+  my $PercentAborted = sprintf("%.2f", $Aborted / $TotalFunctions * 100);
+  my $PercentBlockAborted = sprintf("%.2f", $BlockAborted / $TotalFunctions
+      * 100);
+  my $PercentWorkListAborted = sprintf("%.2f", $WorkListAborted /
+      $TotalFunctions * 100);
+  my $PercentBlocksUnreached = sprintf("%.2f", $UnreachedBlocks / $TotalBlocks
+      * 100);
+
+  my $StatsString = "Analyzed $TotalBlocks blocks in $TotalFunctions functions"
+    . " in $TotalFiles files\n"
+    . "$Aborted functions aborted early ($PercentAborted%)\n"
+    . "$BlockAborted had aborted blocks ($PercentBlockAborted%)\n"
+    . "$WorkListAborted had unfinished worklists ($PercentWorkListAborted%)\n"
+    . "$UnreachedBlocks blocks were never reached ($PercentBlocksUnreached%)\n";
+
+  return $StatsString;
+}
+
+##----------------------------------------------------------------------------##
+# Postprocess - Postprocess the results of an analysis scan.
+##----------------------------------------------------------------------------##
+
+my @filesFound;
+my $baseDir;
+sub FileWanted {
+    my $baseDirRegEx = quotemeta $baseDir;
+    my $file = $File::Find::name;
+
+    # The name of the file is generated by clang binary (HTMLDiagnostics.cpp)
+    if ($file =~ /report-.*\.html$/) {
+       my $relative_file = $file;
+       $relative_file =~ s/$baseDirRegEx//g;
+       push @filesFound, $relative_file;
+    }
+}
+
+sub Postprocess {
+
+  my $Dir           = shift;
+  my $BaseDir       = shift;
+  my $AnalyzerStats = shift;
+  my $KeepEmpty     = shift;
+
+  die "No directory specified." if (!defined $Dir);
+
+  if (! -d $Dir) {
+    Diag("No bugs found.\n");
+    return 0;
+  }
+
+  $baseDir = $Dir . "/";
+  find({ wanted => \&FileWanted, follow => 0}, $Dir);
+
+  if (scalar(@filesFound) == 0 and ! -e "$Dir/failures") {
+    if (! $KeepEmpty) {
+      Diag("Removing directory '$Dir' because it contains no reports.\n");
+      rmtree($Dir) or die "Cannot rmtree '$Dir' : $!";
+    }
+    Diag("No bugs found.\n");
+    return 0;
+  }
+
+  # Scan each report file, in alphabetical order, and build an index.
+  my @Index;
+  my @Stats;
+
+  @filesFound = sort @filesFound;
+  foreach my $file (@filesFound) { ScanFile(\@Index, $Dir, $file, \@Stats); }
+
+  # Scan the failures directory and use the information in the .info files
+  # to update the common prefix directory.
+  my @failures;
+  my @attributes_ignored;
+  if (-d "$Dir/failures") {
+    opendir(DIR, "$Dir/failures");
+    @failures = grep { /[.]info.txt$/ && !/attribute_ignored/; } readdir(DIR);
+    closedir(DIR);
+    opendir(DIR, "$Dir/failures");
+    @attributes_ignored = grep { /^attribute_ignored/; } readdir(DIR);
+    closedir(DIR);
+    foreach my $file (@failures) {
+      open IN, "$Dir/failures/$file" or DieDiag("cannot open $file\n");
+      my $Path = <IN>;
+      if (defined $Path) { UpdatePrefix($Path); }
+      close IN;
+    }
+  }
+
+  # Generate an index.html file.
+  my $FName = "$Dir/index.html";
+  open(OUT, ">", $FName) or DieDiag("Cannot create file '$FName'\n");
+
+  # Print out the header.
+
+print OUT <<ENDTEXT;
+<html>
+<head>
+<title>${Options{HtmlTitle}}</title>
+<link type="text/css" rel="stylesheet" href="scanview.css"/>
+<script src="sorttable.js"></script>
+<script language='javascript' type="text/javascript">
+function SetDisplay(RowClass, DisplayVal)
+{
+  var Rows = document.getElementsByTagName("tr");
+  for ( var i = 0 ; i < Rows.length; ++i ) {
+    if (Rows[i].className == RowClass) {
+      Rows[i].style.display = DisplayVal;
+    }
+  }
+}
+
+function CopyCheckedStateToCheckButtons(SummaryCheckButton) {
+  var Inputs = document.getElementsByTagName("input");
+  for ( var i = 0 ; i < Inputs.length; ++i ) {
+    if (Inputs[i].type == "checkbox") {
+      if(Inputs[i] != SummaryCheckButton) {
+        Inputs[i].checked = SummaryCheckButton.checked;
+        Inputs[i].onclick();
+      }
+    }
+  }
+}
+
+function returnObjById( id ) {
+    if (document.getElementById)
+        var returnVar = document.getElementById(id);
+    else if (document.all)
+        var returnVar = document.all[id];
+    else if (document.layers)
+        var returnVar = document.layers[id];
+    return returnVar;
+}
+
+var NumUnchecked = 0;
+
+function ToggleDisplay(CheckButton, ClassName) {
+  if (CheckButton.checked) {
+    SetDisplay(ClassName, "");
+    if (--NumUnchecked == 0) {
+      returnObjById("AllBugsCheck").checked = true;
+    }
+  }
+  else {
+    SetDisplay(ClassName, "none");
+    NumUnchecked++;
+    returnObjById("AllBugsCheck").checked = false;
+  }
+}
+</script>
+<!-- SUMMARYENDHEAD -->
+</head>
+<body>
+<h1>${Options{HtmlTitle}}</h1>
+
+<table>
+<tr><th>User:</th><td>${UserName}\@${HostName}</td></tr>
+<tr><th>Working Directory:</th><td>${CurrentDir}</td></tr>
+<tr><th>Command Line:</th><td>${CmdArgs}</td></tr>
+<tr><th>Clang Version:</th><td>${ClangVersion}</td></tr>
+<tr><th>Date:</th><td>${Date}</td></tr>
+ENDTEXT
+
+print OUT "<tr><th>Version:</th><td>${BuildName} (${BuildDate})</td></tr>\n"
+  if (defined($BuildName) && defined($BuildDate));
+
+print OUT <<ENDTEXT;
+</table>
+ENDTEXT
+
+  if (scalar(@filesFound)) {
+    # Print out the summary table.
+    my %Totals;
+
+    for my $row ( @Index ) {
+      my $bug_type = ($row->[2]);
+      my $bug_category = ($row->[1]);
+      my $key = "$bug_category:$bug_type";
+
+      if (!defined $Totals{$key}) { $Totals{$key} = [1,$bug_category,$bug_type]; }
+      else { $Totals{$key}->[0]++; }
+    }
+
+    print OUT "<h2>Bug Summary</h2>";
+
+    if (defined $BuildName) {
+      print OUT "\n<p>Results in this analysis run are based on analyzer build <b>$BuildName</b>.</p>\n"
+    }
+
+  my $TotalBugs = scalar(@Index);
+print OUT <<ENDTEXT;
+<table>
+<thead><tr><td>Bug Type</td><td>Quantity</td><td class="sorttable_nosort">Display?</td></tr></thead>
+<tr style="font-weight:bold"><td class="SUMM_DESC">All Bugs</td><td class="Q">$TotalBugs</td><td><center><input type="checkbox" id="AllBugsCheck" onClick="CopyCheckedStateToCheckButtons(this);" checked/></center></td></tr>
+ENDTEXT
+
+    my $last_category;
+
+    for my $key (
+      sort {
+        my $x = $Totals{$a};
+        my $y = $Totals{$b};
+        my $res = $x->[1] cmp $y->[1];
+        $res = $x->[2] cmp $y->[2] if ($res == 0);
+        $res
+      } keys %Totals )
+    {
+      my $val = $Totals{$key};
+      my $category = $val->[1];
+      if (!defined $last_category or $last_category ne $category) {
+        $last_category = $category;
+        print OUT "<tr><th>$category</th><th colspan=2></th></tr>\n";
+      }
+      my $x = lc $key;
+      $x =~ s/[ ,'":\/()]+/_/g;
+      print OUT "<tr><td class=\"SUMM_DESC\">";
+      print OUT $val->[2];
+      print OUT "</td><td class=\"Q\">";
+      print OUT $val->[0];
+      print OUT "</td><td><center><input type=\"checkbox\" onClick=\"ToggleDisplay(this,'bt_$x');\" checked/></center></td></tr>\n";
+    }
+
+  # Print out the table of errors.
+
+print OUT <<ENDTEXT;
+</table>
+<h2>Reports</h2>
+
+<table class="sortable" style="table-layout:automatic">
+<thead><tr>
+  <td>Bug Group</td>
+  <td class="sorttable_sorted">Bug Type<span id="sorttable_sortfwdind">&nbsp;&#x25BE;</span></td>
+  <td>File</td>
+  <td>Function/Method</td>
+  <td class="Q">Line</td>
+  <td class="Q">Path Length</td>
+ENDTEXT
+
+if ($Options{ShowDescription}) {
+print OUT <<ENDTEXT;
+    <td class="Q">Description</td>
+ENDTEXT
+}
+
+print OUT <<ENDTEXT;
+  <td class="sorttable_nosort"></td>
+  <!-- REPORTBUGCOL -->
+</tr></thead>
+<tbody>
+ENDTEXT
+
+    my $prefix = GetPrefix();
+    my $regex;
+    my $InFileRegex;
+    my $InFilePrefix = "File:</td><td>";
+
+    if (defined $prefix) {
+      $regex = qr/^\Q$prefix\E/is;
+      $InFileRegex = qr/\Q$InFilePrefix$prefix\E/is;
+    }
+
+    for my $row ( sort { $a->[2] cmp $b->[2] } @Index ) {
+      my $x = "$row->[1]:$row->[2]";
+      $x = lc $x;
+      $x =~ s/[ ,'":\/()]+/_/g;
+
+      my $ReportFile = $row->[0];
+
+      print OUT "<tr class=\"bt_$x\">";
+      print OUT "<td class=\"DESC\">";
+      print OUT $row->[1]; # $BugCategory
+      print OUT "</td>";
+      print OUT "<td class=\"DESC\">";
+      print OUT $row->[2]; # $BugType
+      print OUT "</td>";
+
+      # Update the file prefix.
+      my $fname = $row->[3];
+
+      if (defined $regex) {
+        $fname =~ s/$regex//;
+        UpdateInFilePath("$Dir/$ReportFile", $InFileRegex, $InFilePrefix)
+      }
+
+      print OUT "<td>";
+      my @fname = split /\//,$fname;
+      if ($#fname > 0) {
+        while ($#fname >= 0) {
+          my $x = shift @fname;
+          print OUT $x;
+          if ($#fname >= 0) {
+            print OUT "/";
+          }
+        }
+      }
+      else {
+        print OUT $fname;
+      }
+      print OUT "</td>";
+
+      print OUT "<td class=\"DESC\">";
+      print OUT $row->[4]; # Function
+      print OUT "</td>";
+
+      # Print out the quantities.
+      for my $j ( 5 .. 6 ) { # Line & Path length
+        print OUT "<td class=\"Q\">$row->[$j]</td>";
+      }
+
+      # Print the rest of the columns.
+      for (my $j = 7; $j <= $#{$row}; ++$j) {
+        print OUT "<td>$row->[$j]</td>"
+      }
+
+      # Emit the "View" link.
+      print OUT "<td><a href=\"$ReportFile#EndPath\">View Report</a></td>";
+
+      # Emit REPORTBUG markers.
+      print OUT "\n<!-- REPORTBUG id=\"$ReportFile\" -->\n";
+
+      # End the row.
+      print OUT "</tr>\n";
+    }
+
+    print OUT "</tbody>\n</table>\n\n";
+  }
+
+  if (scalar (@failures) || scalar(@attributes_ignored)) {
+    print OUT "<h2>Analyzer Failures</h2>\n";
+
+    if (scalar @attributes_ignored) {
+      print OUT "The analyzer's parser ignored the following attributes:<p>\n";
+      print OUT "<table>\n";
+      print OUT "<thead><tr><td>Attribute</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
+      foreach my $file (sort @attributes_ignored) {
+        die "cannot demangle attribute name\n" if (! ($file =~ /^attribute_ignored_(.+).txt/));
+        my $attribute = $1;
+        # Open the attribute file to get the first file that failed.
+        next if (!open (ATTR, "$Dir/failures/$file"));
+        my $ppfile = <ATTR>;
+        chomp $ppfile;
+        close ATTR;
+        next if (! -e "$Dir/failures/$ppfile");
+        # Open the info file and get the name of the source file.
+        open (INFO, "$Dir/failures/$ppfile.info.txt") or
+          die "Cannot open $Dir/failures/$ppfile.info.txt\n";
+        my $srcfile = <INFO>;
+        chomp $srcfile;
+        close (INFO);
+        # Print the information in the table.
+        my $prefix = GetPrefix();
+        if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
+        print OUT "<tr><td>$attribute</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
+        my $ppfile_clang = $ppfile;
+        $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
+        print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
+      }
+      print OUT "</table>\n";
+    }
+
+    if (scalar @failures) {
+      print OUT "<p>The analyzer had problems processing the following files:</p>\n";
+      print OUT "<table>\n";
+      print OUT "<thead><tr><td>Problem</td><td>Source File</td><td>Preprocessed File</td><td>STDERR Output</td></tr></thead>\n";
+      foreach my $file (sort @failures) {
+        $file =~ /(.+).info.txt$/;
+        # Get the preprocessed file.
+        my $ppfile = $1;
+        # Open the info file and get the name of the source file.
+        open (INFO, "$Dir/failures/$file") or
+          die "Cannot open $Dir/failures/$file\n";
+        my $srcfile = <INFO>;
+        chomp $srcfile;
+        my $problem = <INFO>;
+        chomp $problem;
+        close (INFO);
+        # Print the information in the table.
+        my $prefix = GetPrefix();
+        if (defined $prefix) { $srcfile =~ s/^\Q$prefix//; }
+        print OUT "<tr><td>$problem</td><td>$srcfile</td><td><a href=\"failures/$ppfile\">$ppfile</a></td><td><a href=\"failures/$ppfile.stderr.txt\">$ppfile.stderr.txt</a></td></tr>\n";
+        my $ppfile_clang = $ppfile;
+        $ppfile_clang =~ s/[.](.+)$/.clang.$1/;
+        print OUT "  <!-- REPORTPROBLEM src=\"$srcfile\" file=\"failures/$ppfile\" clangfile=\"failures/$ppfile_clang\" stderr=\"failures/$ppfile.stderr.txt\" info=\"failures/$ppfile.info.txt\" -->\n";
+      }
+      print OUT "</table>\n";
+    }
+    print OUT "<p>Please consider submitting preprocessed files as <a href=\"http://clang-analyzer.llvm.org/filing_bugs.html\">bug reports</a>. <!-- REPORTCRASHES --> </p>\n";
+  }
+
+  print OUT "</body></html>\n";
+  close(OUT);
+  CopyFiles($Dir);
+
+  # Make sure $Dir and $BaseDir are world readable/executable.
+  chmod(0755, $Dir);
+  if (defined $BaseDir) { chmod(0755, $BaseDir); }
+
+  # Print statistics
+  print CalcStats(\@Stats) if $AnalyzerStats;
+
+  my $Num = scalar(@Index);
+  if ($Num == 1) {
+    Diag("$Num bug found.\n");
+  } else {
+    Diag("$Num bugs found.\n");
+  }
+  if ($Num > 0 && -r "$Dir/index.html") {
+    Diag("Run 'scan-view $Dir' to examine bug reports.\n");
+  }
+
+  DiagCrashes($Dir) if (scalar @failures || scalar @attributes_ignored);
+
+  return $Num;
+}
+
+sub Finalize {
+  my $BaseDir = shift;
+  my $ExitStatus = shift;
+
+  Diag "Analysis run complete.\n";
+  if (defined $Options{OutputFormat}) {
+    if ($Options{OutputFormat} =~ /plist/ ||
+        $Options{OutputFormat} =~ /sarif/) {
+      Diag "Analysis results (" .
+        ($Options{OutputFormat} =~ /plist/ ? "plist" : "sarif") .
+        " files) deposited in '$Options{OutputDir}'\n";
+    }
+    if ($Options{OutputFormat} =~ /html/) {
+      # Postprocess the HTML directory.
+      my $NumBugs = Postprocess($Options{OutputDir}, $BaseDir,
+                                $Options{AnalyzerStats}, $Options{KeepEmpty});
+
+      if ($Options{ViewResults} and -r "$Options{OutputDir}/index.html") {
+        Diag "Viewing analysis results in '$Options{OutputDir}' using scan-view.\n";
+        my $ScanView = Cwd::realpath("$RealBin/scan-view");
+        if (! -x $ScanView) { $ScanView = "scan-view"; }
+        if (! -x $ScanView) { $ScanView = Cwd::realpath("$RealBin/../../scan-view/bin/scan-view"); }
+        if (! -x $ScanView) { $ScanView = `which scan-view`; chomp $ScanView; }
+        exec $ScanView, "$Options{OutputDir}";
+      }
+
+      if ($Options{ExitStatusFoundBugs}) {
+        exit 1 if ($NumBugs > 0);
+        exit $ExitStatus;
+      }
+    }
+  }
+
+  exit $ExitStatus;
+}
+
+##----------------------------------------------------------------------------##
+# RunBuildCommand - Run the build command.
+##----------------------------------------------------------------------------##
+
+sub AddIfNotPresent {
+  my $Args = shift;
+  my $Arg = shift;
+  my $found = 0;
+
+  foreach my $k (@$Args) {
+    if ($k eq $Arg) {
+      $found = 1;
+      last;
+    }
+  }
+
+  if ($found == 0) {
+    push @$Args, $Arg;
+  }
+}
+
+sub SetEnv {
+  my $EnvVars = shift @_;
+  foreach my $var ('CC', 'CXX', 'CLANG', 'CLANG_CXX',
+                   'CCC_ANALYZER_ANALYSIS', 'CCC_ANALYZER_PLUGINS',
+                   'CCC_ANALYZER_CONFIG') {
+    die "$var is undefined\n" if (!defined $var);
+    $ENV{$var} = $EnvVars->{$var};
+  }
+  foreach my $var ('CCC_ANALYZER_STORE_MODEL',
+                   'CCC_ANALYZER_CONSTRAINTS_MODEL',
+                   'CCC_ANALYZER_INTERNAL_STATS',
+                   'CCC_ANALYZER_OUTPUT_FORMAT',
+                   'CCC_CC',
+                   'CCC_CXX',
+                   'CCC_REPORT_FAILURES',
+                   'CLANG_ANALYZER_TARGET',
+                   'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE') {
+    my $x = $EnvVars->{$var};
+    if (defined $x) { $ENV{$var} = $x }
+  }
+  my $Verbose = $EnvVars->{'VERBOSE'};
+  if ($Verbose >= 2) {
+    $ENV{'CCC_ANALYZER_VERBOSE'} = 1;
+  }
+  if ($Verbose >= 3) {
+    $ENV{'CCC_ANALYZER_LOG'} = 1;
+  }
+}
+
+sub RunXcodebuild {
+  my $Args = shift;
+  my $IgnoreErrors = shift;
+  my $CCAnalyzer = shift;
+  my $CXXAnalyzer = shift;
+  my $EnvVars = shift;
+
+  if ($IgnoreErrors) {
+    AddIfNotPresent($Args,"-PBXBuildsContinueAfterErrors=YES");
+  }
+
+  # Detect the version of Xcode.  If Xcode 4.6 or higher, use new
+  # in situ support for analyzer interposition without needed to override
+  # the compiler.
+  open(DETECT_XCODE, "-|", $Args->[0], "-version") or
+    die "error: cannot detect version of xcodebuild\n";
+
+  my $oldBehavior = 1;
+
+  while(<DETECT_XCODE>) {
+    if (/^Xcode (.+)$/) {
+      my $ver = $1;
+      if ($ver =~ /^([0-9]+[.][0-9]+)[^0-9]?/) {
+        if ($1 >= 4.6) {
+          $oldBehavior = 0;
+          last;
+        }
+      }
+    }
+  }
+  close(DETECT_XCODE);
+
+  # If --override-compiler is explicitly requested, resort to the old
+  # behavior regardless of Xcode version.
+  if ($Options{OverrideCompiler}) {
+    $oldBehavior = 1;
+  }
+
+  if ($oldBehavior == 0) {
+    my $OutputDir = $EnvVars->{"OUTPUT_DIR"};
+    my $CLANG = $EnvVars->{"CLANG"};
+    my $OtherFlags = $EnvVars->{"CCC_ANALYZER_ANALYSIS"};
+    push @$Args,
+        "RUN_CLANG_STATIC_ANALYZER=YES",
+        "CLANG_ANALYZER_OUTPUT=plist-html",
+        "CLANG_ANALYZER_EXEC=$CLANG",
+        "CLANG_ANALYZER_OUTPUT_DIR=$OutputDir",
+        "CLANG_ANALYZER_OTHER_FLAGS=$OtherFlags";
+
+    return (system(@$Args) >> 8);
+  }
+
+  # Default to old behavior where we insert a bogus compiler.
+  SetEnv($EnvVars);
+
+  # Check if using iPhone SDK 3.0 (simulator).  If so the compiler being
+  # used should be gcc-4.2.
+  if (!defined $ENV{"CCC_CC"}) {
+    for (my $i = 0 ; $i < scalar(@$Args); ++$i) {
+      if ($Args->[$i] eq "-sdk" && $i + 1 < scalar(@$Args)) {
+        if (@$Args[$i+1] =~ /^iphonesimulator3/) {
+          $ENV{"CCC_CC"} = "gcc-4.2";
+          $ENV{"CCC_CXX"} = "g++-4.2";
+        }
+      }
+    }
+  }
+
+  # Disable PCH files until clang supports them.
+  AddIfNotPresent($Args,"GCC_PRECOMPILE_PREFIX_HEADER=NO");
+
+  # When 'CC' is set, xcodebuild uses it to do all linking, even if we are
+  # linking C++ object files.  Set 'LDPLUSPLUS' so that xcodebuild uses 'g++'
+  # (via c++-analyzer) when linking such files.
+  $ENV{"LDPLUSPLUS"} = $CXXAnalyzer;
+
+  return (system(@$Args) >> 8);
+}
+
+sub RunBuildCommand {
+  my $Args = shift;
+  my $IgnoreErrors = shift;
+  my $KeepCC = shift;
+  my $Cmd = $Args->[0];
+  my $CCAnalyzer = shift;
+  my $CXXAnalyzer = shift;
+  my $EnvVars = shift;
+
+  if ($Cmd =~ /\bxcodebuild$/) {
+    return RunXcodebuild($Args, $IgnoreErrors, $CCAnalyzer, $CXXAnalyzer, $EnvVars);
+  }
+
+  # Setup the environment.
+  SetEnv($EnvVars);
+
+  if ($Cmd =~ /(.*\/?gcc[^\/]*$)/ or
+      $Cmd =~ /(.*\/?cc[^\/]*$)/ or
+      $Cmd =~ /(.*\/?llvm-gcc[^\/]*$)/ or
+      $Cmd =~ /(.*\/?clang[^\/]*$)/ or
+      $Cmd =~ /(.*\/?ccc-analyzer[^\/]*$)/) {
+
+    if (!($Cmd =~ /ccc-analyzer/) and !defined $ENV{"CCC_CC"}) {
+      $ENV{"CCC_CC"} = $1;
+    }
+
+    shift @$Args;
+    unshift @$Args, $CCAnalyzer;
+  }
+  elsif ($Cmd =~ /(.*\/?g\+\+[^\/]*$)/ or
+        $Cmd =~ /(.*\/?c\+\+[^\/]*$)/ or
+        $Cmd =~ /(.*\/?llvm-g\+\+[^\/]*$)/ or
+        $Cmd =~ /(.*\/?clang\+\+$)/ or
+        $Cmd =~ /(.*\/?c\+\+-analyzer[^\/]*$)/) {
+    if (!($Cmd =~ /c\+\+-analyzer/) and !defined $ENV{"CCC_CXX"}) {
+      $ENV{"CCC_CXX"} = $1;
+    }
+    shift @$Args;
+    unshift @$Args, $CXXAnalyzer;
+  }
+  elsif ($Cmd eq "make" or $Cmd eq "gmake" or $Cmd eq "mingw32-make") {
+    if (!$KeepCC) {
+      AddIfNotPresent($Args, "CC=$CCAnalyzer");
+      AddIfNotPresent($Args, "CXX=$CXXAnalyzer");
+    }
+    if ($IgnoreErrors) {
+      AddIfNotPresent($Args,"-k");
+      AddIfNotPresent($Args,"-i");
+    }
+  }
+
+  return (system(@$Args) >> 8);
+}
+
+##----------------------------------------------------------------------------##
+# DisplayHelp - Utility function to display all help options.
+##----------------------------------------------------------------------------##
+
+sub DisplayHelp {
+
+  my $ArgClangNotFoundErrMsg = shift;
+print <<ENDTEXT;
+USAGE: $Prog [options] <build command> [build options]
+
+ENDTEXT
+
+  if (defined $BuildName) {
+    print "ANALYZER BUILD: $BuildName ($BuildDate)\n\n";
+  }
+
+print <<ENDTEXT;
+OPTIONS:
+
+ -analyze-headers
+
+   Also analyze functions in #included files.  By default, such functions
+   are skipped unless they are called by functions within the main source file.
+
+ --force-analyze-debug-code
+
+   Tells analyzer to enable assertions in code even if they were disabled
+   during compilation to enable more precise results.
+
+ -o <output location>
+
+   Specifies the output directory for analyzer reports. Subdirectories will be
+   created as needed to represent separate "runs" of the analyzer. If this
+   option is not specified, a directory is created in /tmp (TMPDIR on Mac OS X)
+   to store the reports.
+
+ -h
+ --help
+
+   Display this message.
+
+ -k
+ --keep-going
+
+   Add a "keep on going" option to the specified build command. This option
+   currently supports make and xcodebuild. This is a convenience option; one
+   can specify this behavior directly using build options.
+
+ --keep-cc
+
+   Do not override CC and CXX make variables. Useful when running make in
+   autoconf-based (and similar) projects where configure can add extra flags
+   to those variables.
+
+ --html-title [title]
+ --html-title=[title]
+
+   Specify the title used on generated HTML pages. If not specified, a default
+   title will be used.
+
+ --show-description
+
+   Display the description of defects in the list
+
+ -sarif
+
+  By default the output of scan-build is a set of HTML files. This option
+  outputs the results in SARIF format.
+ 
+ -plist
+
+   By default the output of scan-build is a set of HTML files. This option
+   outputs the results as a set of .plist files.
+
+ -plist-html
+
+   By default the output of scan-build is a set of HTML files. This option
+   outputs the results as a set of HTML and .plist files.
+
+ --status-bugs
+
+   By default, the exit status of scan-build is the same as the executed build
+   command. Specifying this option causes the exit status of scan-build to be 1
+   if it found potential bugs and the exit status of the build itself otherwise.
+
+ --exclude <path>
+
+   Do not run static analyzer against files found in this
+   directory (You can specify this option multiple times).
+   Could be useful when project contains 3rd party libraries.
+
+ --use-cc [compiler path]
+ --use-cc=[compiler path]
+
+   scan-build analyzes a project by interposing a "fake compiler", which
+   executes a real compiler for compilation and the static analyzer for analysis.
+   Because of the current implementation of interposition, scan-build does not
+   know what compiler your project normally uses.  Instead, it simply overrides
+   the CC environment variable, and guesses your default compiler.
+
+   In the future, this interposition mechanism to be improved, but if you need
+   scan-build to use a specific compiler for *compilation* then you can use
+   this option to specify a path to that compiler.
+
+   If the given compiler is a cross compiler, you may also need to provide
+   --analyzer-target option to properly analyze the source code because static
+   analyzer runs as if the code is compiled for the host machine by default.
+
+ --use-c++ [compiler path]
+ --use-c++=[compiler path]
+
+   This is the same as "--use-cc" but for C++ code.
+
+ --analyzer-target [target triple name for analysis]
+ --analyzer-target=[target triple name for analysis]
+
+   This provides target triple information to clang static analyzer.
+   It only changes the target for analysis but doesn't change the target of a
+   real compiler given by --use-cc and --use-c++ options.
+
+ -v
+
+   Enable verbose output from scan-build. A second and third '-v' increases
+   verbosity.
+
+ -V
+ --view
+
+   View analysis results in a web browser when the build completes.
+
+ --generate-index-only <output location>
+
+   Do not perform the analysis, but only regenerate the index.html file
+   from existing report.html files. Useful for making a custom Static Analyzer
+   integration into a build system that isn't otherwise supported by scan-build.
+
+ADVANCED OPTIONS:
+
+ -no-failure-reports
+
+   Do not create a 'failures' subdirectory that includes analyzer crash reports
+   and preprocessed source files.
+
+ -stats
+
+   Generates visitation statistics for the project being analyzed.
+
+ -maxloop <loop count>
+
+   Specify the number of times a block can be visited before giving up.
+   Default is 4. Increase for more comprehensive coverage at a cost of speed.
+
+ -internal-stats
+
+   Generate internal analyzer statistics.
+
+ --use-analyzer [Xcode|path to clang]
+ --use-analyzer=[Xcode|path to clang]
+
+   scan-build uses the 'clang' executable relative to itself for static
+   analysis. One can override this behavior with this option by using the
+   'clang' packaged with Xcode (on OS X) or from the PATH.
+
+ --keep-empty
+
+   Don't remove the build results directory even if no issues were reported.
+
+ --override-compiler
+   Always resort to the ccc-analyzer even when better interposition methods
+   are available.
+
+ -analyzer-config <options>
+
+   Provide options to pass through to the analyzer's -analyzer-config flag.
+   Several options are separated with comma: 'key1=val1,key2=val2'
+
+   Available options:
+     * stable-report-filename=true or false (default)
+       Switch the page naming to:
+       report-<filename>-<function/method name>-<id>.html
+       instead of report-XXXXXX.html
+
+CONTROLLING CHECKERS:
+
+ A default group of checkers are always run unless explicitly disabled.
+ Checkers may be enabled/disabled using the following options:
+
+ -enable-checker [checker name]
+ -disable-checker [checker name]
+
+LOADING CHECKERS:
+
+ Loading external checkers using the clang plugin interface:
+
+ -load-plugin [plugin library]
+ENDTEXT
+
+  if (defined $Clang && -x $Clang) {
+    # Query clang for list of checkers that are enabled.
+
+    # create a list to load the plugins via the 'Xclang' command line
+    # argument
+    my @PluginLoadCommandline_xclang;
+    foreach my $param ( @{$Options{PluginsToLoad}} ) {
+      push ( @PluginLoadCommandline_xclang, "-Xclang" );
+      push ( @PluginLoadCommandline_xclang, "-load" );
+      push ( @PluginLoadCommandline_xclang, "-Xclang" );
+      push ( @PluginLoadCommandline_xclang, $param );
+    }
+
+    my %EnabledCheckers;
+    foreach my $lang ("c", "objective-c", "objective-c++", "c++") {
+      my $ExecLine = join(' ', qq/"$Clang"/, @PluginLoadCommandline_xclang, "--analyze", "-x", $lang, "-", "-###", "2>&1", "|");
+      open(PS, $ExecLine);
+      while (<PS>) {
+        foreach my $val (split /\s+/) {
+          $val =~ s/\"//g;
+          if ($val =~ /-analyzer-checker\=([^\s]+)/) {
+            $EnabledCheckers{$1} = 1;
+          }
+        }
+      }
+    }
+
+    # Query clang for complete list of checkers.
+    my @PluginLoadCommandline;
+    foreach my $param ( @{$Options{PluginsToLoad}} ) {
+      push ( @PluginLoadCommandline, "-load" );
+      push ( @PluginLoadCommandline, $param );
+    }
+
+    my $ExecLine = join(' ', qq/"$Clang"/, "-cc1", @PluginLoadCommandline, "-analyzer-checker-help", "2>&1", "|");
+    open(PS, $ExecLine);
+    my $foundCheckers = 0;
+    while (<PS>) {
+      if (/CHECKERS:/) {
+        $foundCheckers = 1;
+        last;
+      }
+    }
+    if (!$foundCheckers) {
+      print "  *** Could not query Clang for the list of available checkers.";
+    }
+    else {
+      print("\nAVAILABLE CHECKERS:\n\n");
+      my $skip = 0;
+       while(<PS>) {
+        if (/experimental/) {
+          $skip = 1;
+          next;
+        }
+        if ($skip) {
+          next if (!/^\s\s[^\s]/);
+          $skip = 0;
+        }
+        s/^\s\s//;
+        if (/^([^\s]+)/) {
+          # Is the checker enabled?
+          my $checker = $1;
+          my $enabled = 0;
+          my $aggregate = "";
+          foreach my $domain (split /\./, $checker) {
+            $aggregate .= $domain;
+            if ($EnabledCheckers{$aggregate}) {
+              $enabled =1;
+              last;
+            }
+            # append a dot, if an additional domain is added in the next iteration
+            $aggregate .= ".";
+          }
+
+          if ($enabled) {
+            print " + ";
+          }
+          else {
+            print "   ";
+          }
+        }
+        else {
+          print "   ";
+        }
+        print $_;
+      }
+      print "\nNOTE: \"+\" indicates that an analysis is enabled by default.\n";
+    }
+    close PS;
+  }
+  else {
+    print "  *** Could not query Clang for the list of available checkers.\n";
+    if (defined  $ArgClangNotFoundErrMsg) {
+      print "  *** Reason: $ArgClangNotFoundErrMsg\n";
+    }
+  }
+
+print <<ENDTEXT
+
+BUILD OPTIONS
+
+ You can specify any build option acceptable to the build command.
+
+EXAMPLE
+
+ scan-build -o /tmp/myhtmldir make -j4
+
+The above example causes analysis reports to be deposited into a subdirectory
+of "/tmp/myhtmldir" and to run "make" with the "-j4" option. A different
+subdirectory is created each time scan-build analyzes a project. The analyzer
+should support most parallel builds, but not distributed builds.
+
+ENDTEXT
+}
+
+##----------------------------------------------------------------------------##
+# HtmlEscape - HTML entity encode characters that are special in HTML
+##----------------------------------------------------------------------------##
+
+sub HtmlEscape {
+  # copy argument to new variable so we don't clobber the original
+  my $arg = shift || '';
+  my $tmp = $arg;
+  $tmp =~ s/&/&amp;/g;
+  $tmp =~ s/</&lt;/g;
+  $tmp =~ s/>/&gt;/g;
+  return $tmp;
+}
+
+##----------------------------------------------------------------------------##
+# ShellEscape - backslash escape characters that are special to the shell
+##----------------------------------------------------------------------------##
+
+sub ShellEscape {
+  # copy argument to new variable so we don't clobber the original
+  my $arg = shift || '';
+  if ($arg =~ /["\s]/) { return "'" . $arg . "'"; }
+  return $arg;
+}
+
+##----------------------------------------------------------------------------##
+# FindXcrun - searches for the 'xcrun' executable. Returns "" if not found.
+##----------------------------------------------------------------------------##
+
+sub FindXcrun {
+  my $xcrun = `which xcrun`;
+  chomp $xcrun;
+  return $xcrun;
+}
+
+##----------------------------------------------------------------------------##
+# FindClang - searches for 'clang' executable.
+##----------------------------------------------------------------------------##
+
+sub FindClang {
+  if (!defined $Options{AnalyzerDiscoveryMethod}) {
+    $Clang = Cwd::realpath("$RealBin/bin/clang") if (-f "$RealBin/bin/clang");
+    if (!defined $Clang || ! -x $Clang) {
+      $Clang = Cwd::realpath("$RealBin/clang") if (-f "$RealBin/clang");
+      if (!defined $Clang || ! -x $Clang) {
+        # When an Xcode toolchain is present, look for a clang in the sibling bin
+        # of the parent of the bin directory. So if scan-build is at
+        # $TOOLCHAIN/usr/local/bin/scan-build look for clang at
+        # $TOOLCHAIN/usr/bin/clang.
+        my $has_xcode_toolchain = FindXcrun() ne "";
+        if ($has_xcode_toolchain && -f "$RealBin/../../bin/clang") {
+          $Clang = Cwd::realpath("$RealBin/../../bin/clang");
+        }
+      }
+    }
+    if (!defined $Clang || ! -x $Clang) {
+      return "error: Cannot find an executable 'clang' relative to" .
+             " scan-build. Consider using --use-analyzer to pick a version of" .
+             " 'clang' to use for static analysis.\n";
+    }
+  }
+  else {
+    if ($Options{AnalyzerDiscoveryMethod} =~ /^[Xx]code$/) {
+      my $xcrun = FindXcrun();
+      if ($xcrun eq "") {
+        return "Cannot find 'xcrun' to find 'clang' for analysis.\n";
+      }
+      $Clang = `$xcrun -toolchain XcodeDefault -find clang`;
+      chomp $Clang;
+      if ($Clang eq "") {
+        return "No 'clang' executable found by 'xcrun'\n";
+      }
+    }
+    else {
+      $Clang = $Options{AnalyzerDiscoveryMethod};
+      if (!defined $Clang or not -x $Clang) {
+        return "Cannot find an executable clang at '$Options{AnalyzerDiscoveryMethod}'\n";
+      }
+    }
+  }
+  return undef;
+}
+
+##----------------------------------------------------------------------------##
+# Process command-line arguments.
+##----------------------------------------------------------------------------##
+
+my $RequestDisplayHelp = 0;
+my $ForceDisplayHelp = 0;
+
+sub ProcessArgs {
+  my $Args = shift;
+  my $NumArgs = 0;
+
+  while (@$Args) {
+
+    $NumArgs++;
+
+    # Scan for options we recognize.
+
+    my $arg = $Args->[0];
+
+    if ($arg eq "-h" or $arg eq "--help") {
+      $RequestDisplayHelp = 1;
+      shift @$Args;
+      next;
+    }
+
+    if ($arg eq '-analyze-headers') {
+      shift @$Args;
+      $Options{AnalyzeHeaders} = 1;
+      next;
+    }
+
+    if ($arg eq "-o") {
+      if (defined($Options{OutputDir})) {
+        DieDiag("Only one of '-o' or '--generate-index-only' can be specified.\n");
+      }
+
+      shift @$Args;
+
+      if (!@$Args) {
+        DieDiag("'-o' option requires a target directory name.\n");
+      }
+
+      # Construct an absolute path.  Uses the current working directory
+      # as a base if the original path was not absolute.
+      my $OutDir = shift @$Args;
+      mkpath($OutDir) unless (-e $OutDir);  # abs_path wants existing dir
+      $Options{OutputDir} = abs_path($OutDir);
+
+      next;
+    }
+
+    if ($arg eq "--generate-index-only") {
+      if (defined($Options{OutputDir})) {
+        DieDiag("Only one of '-o' or '--generate-index-only' can be specified.\n");
+      }
+
+      shift @$Args;
+
+      if (!@$Args) {
+        DieDiag("'--generate-index-only' option requires a target directory name.\n");
+      }
+
+      # Construct an absolute path.  Uses the current working directory
+      # as a base if the original path was not absolute.
+      my $OutDir = shift @$Args;
+      mkpath($OutDir) unless (-e $OutDir);  # abs_path wants existing dir
+      $Options{OutputDir} = abs_path($OutDir);
+      $Options{GenerateIndex} = 1;
+
+      next;
+    }
+
+    if ($arg =~ /^--html-title(=(.+))?$/) {
+      shift @$Args;
+
+      if (!defined $2 || $2 eq '') {
+        if (!@$Args) {
+          DieDiag("'--html-title' option requires a string.\n");
+        }
+
+        $Options{HtmlTitle} = shift @$Args;
+      } else {
+        $Options{HtmlTitle} = $2;
+      }
+
+      next;
+    }
+
+    if ($arg eq "-k" or $arg eq "--keep-going") {
+      shift @$Args;
+      $Options{IgnoreErrors} = 1;
+      next;
+    }
+
+    if ($arg eq "--keep-cc") {
+      shift @$Args;
+      $Options{KeepCC} = 1;
+      next;
+    }
+
+    if ($arg =~ /^--use-cc(=(.+))?$/) {
+      shift @$Args;
+      my $cc;
+
+      if (!defined $2 || $2 eq "") {
+        if (!@$Args) {
+          DieDiag("'--use-cc' option requires a compiler executable name.\n");
+        }
+        $cc = shift @$Args;
+      }
+      else {
+        $cc = $2;
+      }
+
+      $Options{UseCC} = $cc;
+      next;
+    }
+
+    if ($arg =~ /^--use-c\+\+(=(.+))?$/) {
+      shift @$Args;
+      my $cxx;
+
+      if (!defined $2 || $2 eq "") {
+        if (!@$Args) {
+          DieDiag("'--use-c++' option requires a compiler executable name.\n");
+        }
+        $cxx = shift @$Args;
+      }
+      else {
+        $cxx = $2;
+      }
+
+      $Options{UseCXX} = $cxx;
+      next;
+    }
+
+    if ($arg =~ /^--analyzer-target(=(.+))?$/) {
+      shift @ARGV;
+      my $AnalyzerTarget;
+
+      if (!defined $2 || $2 eq "") {
+        if (!@ARGV) {
+          DieDiag("'--analyzer-target' option requires a target triple name.\n");
+        }
+        $AnalyzerTarget = shift @ARGV;
+      }
+      else {
+        $AnalyzerTarget = $2;
+      }
+
+      $Options{AnalyzerTarget} = $AnalyzerTarget;
+      next;
+    }
+
+    if ($arg eq "-v") {
+      shift @$Args;
+      $Options{Verbose}++;
+      next;
+    }
+
+    if ($arg eq "-V" or $arg eq "--view") {
+      shift @$Args;
+      $Options{ViewResults} = 1;
+      next;
+    }
+
+    if ($arg eq "--status-bugs") {
+      shift @$Args;
+      $Options{ExitStatusFoundBugs} = 1;
+      next;
+    }
+
+    if ($arg eq "--show-description") {
+      shift @$Args;
+      $Options{ShowDescription} = 1;
+      next;
+    }
+
+    if ($arg eq "-store") {
+      shift @$Args;
+      $Options{StoreModel} = shift @$Args;
+      next;
+    }
+
+    if ($arg eq "-constraints") {
+      shift @$Args;
+      $Options{ConstraintsModel} = shift @$Args;
+      next;
+    }
+
+    if ($arg eq "-internal-stats") {
+      shift @$Args;
+      $Options{InternalStats} = 1;
+      next;
+    }
+
+    if ($arg eq "-sarif") {
+      shift @$Args;
+      $Options{OutputFormat} = "sarif";
+      next;
+    }
+
+    if ($arg eq "-plist") {
+      shift @$Args;
+      $Options{OutputFormat} = "plist";
+      next;
+    }
+
+    if ($arg eq "-plist-html") {
+      shift @$Args;
+      $Options{OutputFormat} = "plist-html";
+      next;
+    }
+
+    if ($arg eq "-analyzer-config") {
+      shift @$Args;
+      push @{$Options{ConfigOptions}}, shift @$Args;
+      next;
+    }
+
+    if ($arg eq "-no-failure-reports") {
+      shift @$Args;
+      $Options{ReportFailures} = 0;
+      next;
+    }
+
+    if ($arg eq "-stats") {
+      shift @$Args;
+      $Options{AnalyzerStats} = 1;
+      next;
+    }
+
+    if ($arg eq "-maxloop") {
+      shift @$Args;
+      $Options{MaxLoop} = shift @$Args;
+      next;
+    }
+
+    if ($arg eq "-enable-checker") {
+      shift @$Args;
+      my $Checker = shift @$Args;
+      # Store $NumArgs to preserve the order the checkers were enabled.
+      $Options{EnableCheckers}{$Checker} = $NumArgs;
+      delete $Options{DisableCheckers}{$Checker};
+      next;
+    }
+
+    if ($arg eq "-disable-checker") {
+      shift @$Args;
+      my $Checker = shift @$Args;
+      # Store $NumArgs to preserve the order the checkers are disabled/silenced.
+      # See whether it is a core checker to disable. That means we do not want
+      # to emit a report from that checker so we have to silence it.
+      if (index($Checker, "core") == 0) {
+        $Options{SilenceCheckers}{$Checker} = $NumArgs;
+      } else {
+        $Options{DisableCheckers}{$Checker} = $NumArgs;
+        delete $Options{EnableCheckers}{$Checker};
+      }
+      next;
+    }
+
+    if ($arg eq "--exclude") {
+      shift @$Args;
+      my $arg = shift @$Args;
+      # Remove the trailing slash if any
+      $arg =~ s|/$||;
+      push @{$Options{Excludes}}, $arg;
+      next;
+    }
+
+    if ($arg eq "-load-plugin") {
+      shift @$Args;
+      push @{$Options{PluginsToLoad}}, shift @$Args;
+      next;
+    }
+
+    if ($arg eq "--use-analyzer") {
+      shift @$Args;
+      $Options{AnalyzerDiscoveryMethod} = shift @$Args;
+      next;
+    }
+
+    if ($arg =~ /^--use-analyzer=(.+)$/) {
+      shift @$Args;
+      $Options{AnalyzerDiscoveryMethod} = $1;
+      next;
+    }
+
+    if ($arg eq "--keep-empty") {
+      shift @$Args;
+      $Options{KeepEmpty} = 1;
+      next;
+    }
+
+    if ($arg eq "--override-compiler") {
+      shift @$Args;
+      $Options{OverrideCompiler} = 1;
+      next;
+    }
+
+    if ($arg eq "--force-analyze-debug-code") {
+      shift @$Args;
+      $Options{ForceAnalyzeDebugCode} = 1;
+      next;
+    }
+
+    DieDiag("unrecognized option '$arg'\n") if ($arg =~ /^-/);
+
+    $NumArgs--;
+    last;
+  }
+  return $NumArgs;
+}
+
+if (!@ARGV) {
+  $ForceDisplayHelp = 1
+}
+
+ProcessArgs(\@ARGV);
+# All arguments are now shifted from @ARGV. The rest is a build command, if any.
+
+my $ClangNotFoundErrMsg = FindClang();
+
+if ($ForceDisplayHelp || $RequestDisplayHelp) {
+  DisplayHelp($ClangNotFoundErrMsg);
+  exit $ForceDisplayHelp;
+}
+
+$CmdArgs = HtmlEscape(join(' ', map(ShellEscape($_), @ARGV)));
+
+if ($Options{GenerateIndex}) {
+  $ClangVersion = "unknown";
+  Finalize($Options{OutputDir}, 0);
+}
+
+# Make sure to use "" to handle paths with spaces.
+$ClangVersion = HtmlEscape(`"$Clang" --version`);
+
+if (!@ARGV and !$RequestDisplayHelp) {
+  ErrorDiag("No build command specified.\n\n");
+  $ForceDisplayHelp = 1;
+}
+
+# Determine the output directory for the HTML reports.
+my $BaseDir = $Options{OutputDir};
+$Options{OutputDir} = GetHTMLRunDir($Options{OutputDir});
+
+DieDiag($ClangNotFoundErrMsg) if (defined $ClangNotFoundErrMsg);
+
+$ClangCXX = $Clang;
+if ($Clang !~ /\+\+(\.exe)?$/) {
+  # If $Clang holds the name of the clang++ executable then we leave
+  # $ClangCXX and $Clang equal, otherwise construct the name of the clang++
+  # executable from the clang executable name.
+
+  # Determine operating system under which this copy of Perl was built.
+  my $IsWinBuild = ($^O =~/msys|cygwin|MSWin32/);
+  if($IsWinBuild) {
+    $ClangCXX =~ s/.exe$/++.exe/;
+  }
+  else {
+    $ClangCXX =~ s/\-\d+(\.\d+)?$//;
+    $ClangCXX .= "++";
+  }
+}
+
+# Determine the location of ccc-analyzer.
+my $AbsRealBin = Cwd::realpath($RealBin);
+my $Cmd = "$AbsRealBin/../libexec/ccc-analyzer";
+my $CmdCXX = "$AbsRealBin/../libexec/c++-analyzer";
+
+# Portability: use less strict but portable check -e (file exists) instead of
+# non-portable -x (file is executable). On some windows ports -x just checks
+# file extension to determine if a file is executable (see Perl language
+# reference, perlport)
+if (!defined $Cmd || ! -e $Cmd) {
+  $Cmd = "$AbsRealBin/ccc-analyzer";
+  DieDiag("'ccc-analyzer' does not exist at '$Cmd'\n") if(! -e $Cmd);
+}
+if (!defined $CmdCXX || ! -e $CmdCXX) {
+  $CmdCXX = "$AbsRealBin/c++-analyzer";
+  DieDiag("'c++-analyzer' does not exist at '$CmdCXX'\n") if(! -e $CmdCXX);
+}
+
+Diag("Using '$Clang' for static analysis\n");
+
+SetHtmlEnv(\@ARGV, $Options{OutputDir});
+
+my @AnalysesToRun;
+foreach (sort { $Options{EnableCheckers}{$a} <=> $Options{EnableCheckers}{$b} }
+         keys %{$Options{EnableCheckers}}) {
+  # Push checkers in order they were enabled.
+  push @AnalysesToRun, "-analyzer-checker", $_;
+}
+foreach (sort { $Options{DisableCheckers}{$a} <=> $Options{DisableCheckers}{$b} }
+         keys %{$Options{DisableCheckers}}) {
+  # Push checkers in order they were disabled.
+  push @AnalysesToRun, "-analyzer-disable-checker", $_;
+}
+if ($Options{AnalyzeHeaders}) { push @AnalysesToRun, "-analyzer-opt-analyze-headers"; }
+if ($Options{AnalyzerStats}) { push @AnalysesToRun, '-analyzer-checker=debug.Stats'; }
+if ($Options{MaxLoop} > 0) { push @AnalysesToRun, "-analyzer-max-loop $Options{MaxLoop}"; }
+
+# Delay setting up other environment variables in case we can do true
+# interposition.
+my $CCC_ANALYZER_ANALYSIS = join ' ', @AnalysesToRun;
+my $CCC_ANALYZER_PLUGINS = join ' ', map { "-load ".$_ } @{$Options{PluginsToLoad}};
+my $CCC_ANALYZER_CONFIG = join ' ', map { "-analyzer-config ".$_ } @{$Options{ConfigOptions}};
+
+if (%{$Options{SilenceCheckers}}) {
+  $CCC_ANALYZER_CONFIG =
+      $CCC_ANALYZER_CONFIG." -analyzer-config silence-checkers="
+                          .join(';', sort {
+                                            $Options{SilenceCheckers}{$a} <=>
+                                            $Options{SilenceCheckers}{$b}
+                                          } keys %{$Options{SilenceCheckers}});
+}
+
+my %EnvVars = (
+  'CC' => $Cmd,
+  'CXX' => $CmdCXX,
+  'CLANG' => $Clang,
+  'CLANG_CXX' => $ClangCXX,
+  'VERBOSE' => $Options{Verbose},
+  'CCC_ANALYZER_ANALYSIS' => $CCC_ANALYZER_ANALYSIS,
+  'CCC_ANALYZER_PLUGINS' => $CCC_ANALYZER_PLUGINS,
+  'CCC_ANALYZER_CONFIG' => $CCC_ANALYZER_CONFIG,
+  'OUTPUT_DIR' => $Options{OutputDir},
+  'CCC_CC' => $Options{UseCC},
+  'CCC_CXX' => $Options{UseCXX},
+  'CCC_REPORT_FAILURES' => $Options{ReportFailures},
+  'CCC_ANALYZER_STORE_MODEL' => $Options{StoreModel},
+  'CCC_ANALYZER_CONSTRAINTS_MODEL' => $Options{ConstraintsModel},
+  'CCC_ANALYZER_INTERNAL_STATS' => $Options{InternalStats},
+  'CCC_ANALYZER_OUTPUT_FORMAT' => $Options{OutputFormat},
+  'CLANG_ANALYZER_TARGET' => $Options{AnalyzerTarget},
+  'CCC_ANALYZER_FORCE_ANALYZE_DEBUG_CODE' => $Options{ForceAnalyzeDebugCode}
+);
+
+# Run the build.
+my $ExitStatus = RunBuildCommand(\@ARGV, $Options{IgnoreErrors}, $Options{KeepCC},
+	                        $Cmd, $CmdCXX, \%EnvVars);
+
+Finalize($BaseDir, $ExitStatus);
diff --git a/bin/scan-view b/bin/scan-view
new file mode 100755
index 0000000..6165432
--- /dev/null
+++ b/bin/scan-view
@@ -0,0 +1,150 @@
+#!/usr/bin/env python
+
+from __future__ import print_function
+
+"""The clang static analyzer results viewer.
+"""
+
+import sys
+import imp
+import os
+import posixpath
+import threading
+import time
+try:
+    from urllib.request import urlopen
+except ImportError:
+    from urllib2 import urlopen
+import webbrowser
+
+# How long to wait for server to start.
+kSleepTimeout = .05
+kMaxSleeps = int(60 / kSleepTimeout)
+
+# Default server parameters
+
+kDefaultHost = '127.0.0.1'
+kDefaultPort = 8181
+kMaxPortsToTry = 100
+
+###
+
+
+def url_is_up(url):
+    try:
+        o = urlopen(url)
+    except IOError:
+        return False
+    o.close()
+    return True
+
+
+def start_browser(port, options):
+    import webbrowser
+
+    url = 'http://%s:%d' % (options.host, port)
+
+    # Wait for server to start...
+    if options.debug:
+        sys.stderr.write('%s: Waiting for server.' % sys.argv[0])
+        sys.stderr.flush()
+    for i in range(kMaxSleeps):
+        if url_is_up(url):
+            break
+        if options.debug:
+            sys.stderr.write('.')
+            sys.stderr.flush()
+        time.sleep(kSleepTimeout)
+    else:
+        print('WARNING: Unable to detect that server started.', file=sys.stderr) 
+
+    if options.debug:
+        print('%s: Starting webbrowser...' % sys.argv[0], file=sys.stderr)
+    webbrowser.open(url)
+
+
+def run(port, options, root):
+    # Prefer to look relative to the installed binary
+    share = os.path.dirname(__file__) + "/../share/scan-view"
+    if not os.path.isdir(share):
+        # Otherwise look relative to the source
+        share = os.path.dirname(__file__) + "/../../scan-view/share"
+    sys.path.append(share)
+
+    import ScanView
+    try:
+        print('Starting scan-view at: http://%s:%d' % (options.host,
+                                                       port))
+        print('  Use Ctrl-C to exit.')
+        httpd = ScanView.create_server((options.host, port),
+                                       options, root)
+        httpd.serve_forever()
+    except KeyboardInterrupt:
+        pass
+
+
+def port_is_open(port):
+    try:
+        import socketserver
+    except ImportError:
+        import SocketServer as socketserver
+    try:
+        t = socketserver.TCPServer((kDefaultHost, port), None)
+    except:
+        return False
+    t.server_close()
+    return True
+
+
+def main():
+    import argparse
+    parser = argparse.ArgumentParser(description="The clang static analyzer "
+                                                 "results viewer.")
+    parser.add_argument("root", metavar="<results directory>", type=str)
+    parser.add_argument(
+        '--host', dest="host", default=kDefaultHost, type=str,
+        help="Host interface to listen on. (default=%s)" % kDefaultHost)
+    parser.add_argument('--port', dest="port", default=None, type=int,
+                        help="Port to listen on. (default=%s)" % kDefaultPort)
+    parser.add_argument("--debug", dest="debug", default=0,
+                        action="count",
+                        help="Print additional debugging information.")
+    parser.add_argument("--auto-reload", dest="autoReload", default=False,
+                        action="store_true",
+                        help="Automatically update module for each request.")
+    parser.add_argument("--no-browser", dest="startBrowser", default=True,
+                        action="store_false",
+                        help="Don't open a webbrowser on startup.")
+    parser.add_argument("--allow-all-hosts", dest="onlyServeLocal",
+                        default=True, action="store_false",
+                        help='Allow connections from any host (access '
+                             'restricted to "127.0.0.1" by default)')
+    args = parser.parse_args()
+
+    # Make sure this directory is in a reasonable state to view.
+    if not posixpath.exists(posixpath.join(args.root, 'index.html')):
+        parser.error('Invalid directory, analysis results not found!')
+
+    # Find an open port. We aren't particularly worried about race
+    # conditions here. Note that if the user specified a port we only
+    # use that one.
+    if args.port is not None:
+        port = args.port
+    else:
+        for i in range(kMaxPortsToTry):
+            if port_is_open(kDefaultPort + i):
+                port = kDefaultPort + i
+                break
+        else:
+            parser.error('Unable to find usable port in [%d,%d)' %
+                         (kDefaultPort, kDefaultPort+kMaxPortsToTry))
+
+    # Kick off thread to wait for server and start web browser, if
+    # requested.
+    if args.startBrowser:
+        threading.Thread(target=start_browser, args=(port, args)).start()
+
+    run(port, args, args.root)
+
+if __name__ == '__main__':
+    main()