Creates temp symlink tree, runs program, and copies back outputs.

  Args:
    inputs: List of fake paths to real paths, which are used for symlink tree.
    program: List containing real path of program and its arguments. The
        execroot directory will be appended as the last argument.
    outputs: List of fake outputted paths to copy back to real paths.
  Returns:
    0 if succeeded or nonzero if failed.

def run(inputs, program, outputs):
  """Creates temp symlink tree, runs program, and copies back outputs.

  Args:
    inputs: List of fake paths to real paths, which are used for symlink tree.
    program: List containing real path of program and its arguments. The
        execroot directory will be appended as the last argument.
    outputs: List of fake outputted paths to copy back to real paths.
  Returns:
    0 if succeeded or nonzero if failed.
  """
  root = tempfile.mkdtemp()
  try:
    cwd = os.getcwd()
    for fake, real in inputs:
      parent = os.path.join(root, os.path.dirname(fake))
      if not os.path.exists(parent):
        os.makedirs(parent)
      # Use symlink if possible and not on Windows, since on Windows 10
      # symlinks exist but they require administrator privileges to use.
      if hasattr(os, 'symlink') and not os.name == 'nt':
        os.symlink(os.path.join(cwd, real), os.path.join(root, fake))
      else:
        shutil.copyfile(os.path.join(cwd, real), os.path.join(root, fake))
    if subprocess.call(program + [root]) != 0:
      return 1
    for fake, real in outputs:
      shutil.copyfile(os.path.join(root, fake), real)
    return 0
  finally:
    try:
      shutil.rmtree(root)
    except EnvironmentError:
      # Ignore "file in use" errors on Windows; ok since it's just a tmpdir.
      pass