//===--- Driver.cpp - Swift compiler driver -------------------------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
//
// This file contains implementations of parts of the compiler driver.
//
//===----------------------------------------------------------------------===//

#include "swift/Driver/Driver.h"

#include "ToolChains.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsDriver.h"
#include "swift/AST/DiagnosticsFrontend.h"
#include "swift/Basic/Assertions.h"
#include "swift/Basic/LLVM.h"
#include "swift/Basic/LangOptions.h"
#include "swift/Basic/OutputFileMap.h"
#include "swift/Basic/Platform.h"
#include "swift/Basic/Range.h"
#include "swift/Basic/Statistic.h"
#include "swift/Basic/TaskQueue.h"
#include "swift/Basic/Version.h"
#include "swift/Config.h"
#include "swift/Driver/Action.h"
#include "swift/Driver/Compilation.h"
#include "swift/Driver/Job.h"
#include "swift/Driver/PrettyStackTrace.h"
#include "swift/Driver/ToolChain.h"
#include "swift/Option/Options.h"
#include "swift/Option/SanitizerOptions.h"
#include "swift/Parse/Lexer.h"
#include "swift/Strings.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/DenseSet.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringSwitch.h"
#include "llvm/Config/config.h"
#include "llvm/Option/Arg.h"
#include "llvm/Option/ArgList.h"
#include "llvm/Option/OptTable.h"
#include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MD5.h"
#include "llvm/Support/Path.h"
#include "llvm/Support/PrettyStackTrace.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/TargetParser/Host.h"

#include <memory>

using namespace swift;
using namespace swift::driver;
using namespace llvm::opt;

Driver::Driver(StringRef DriverExecutable,
               StringRef Name,
               ArrayRef<const char *> Args,
               DiagnosticEngine &Diags)
  : Opts(createSwiftOptTable()), Diags(Diags),
    Name(Name), DriverExecutable(DriverExecutable),
    DefaultTargetTriple(llvm::sys::getDefaultTargetTriple()) {
      
  // The driver kind must be parsed prior to parsing arguments, since that
  // affects how arguments are parsed.
  parseDriverKind(Args.slice(1));
}

Driver::~Driver() = default;

void Driver::parseDriverKind(ArrayRef<const char *> Args) {
  // The default driver kind is determined by Name.
  StringRef DriverName = Name;

  StringRef OptName;
  // However, the driver kind may be overridden if the first argument is
  // --driver-mode.
  if (!Args.empty()) {
    OptName = getOpts().getOption(options::OPT_driver_mode).getPrefixedName();

    StringRef FirstArg(Args[0]);
    if (FirstArg.starts_with(OptName))
      DriverName = FirstArg.drop_front(OptName.size());
  }

  std::optional<DriverKind> Kind =
      llvm::StringSwitch<std::optional<DriverKind>>(DriverName)
          .Case("swift", DriverKind::Interactive)
          .Case("swiftc", DriverKind::Standard)
          .Case("swift-legacy-driver", DriverKind::Interactive)
          .Case("swiftc-legacy-driver", DriverKind::Standard)
          .Case("sil-opt", DriverKind::SILOpt)
          .Case("sil-func-extractor", DriverKind::SILFuncExtractor)
          .Case("sil-nm", DriverKind::SILNM)
          .Case("sil-llvm-gen", DriverKind::SILLLVMGen)
          .Case("sil-passpipeline-dumper", DriverKind::SILPassPipelineDumper)
          .Case("swift-dependency-tool", DriverKind::SwiftDependencyTool)
          .Case("swift-llvm-opt", DriverKind::SwiftLLVMOpt)
          .Case("swift-autolink-extract", DriverKind::AutolinkExtract)
          .Case("swift-symbolgraph-extract", DriverKind::SymbolGraph)
          .Case("swift-api-digester", DriverKind::APIDigester)
          .Case("swift-cache-tool", DriverKind::CacheTool)
          .Case("swift-parse-test", DriverKind::ParseTest)
          .Case("swift-synthesize-interface", DriverKind::SynthesizeInterface)
          .Default(std::nullopt);

  if (Kind.has_value())
    driverKind = Kind.value();
  else if (!OptName.empty())
    Diags.diagnose({}, diag::error_invalid_arg_value, OptName, DriverName);
}

ArrayRef<const char *> Driver::getArgsWithoutProgramNameAndDriverMode(
                                      ArrayRef<const char *> Args) const {
  Args = Args.slice(1);
  if (Args.empty())
    return Args;

  StringRef OptName =
    getOpts().getOption(options::OPT_driver_mode).getPrefixedName();
  if (StringRef(Args[0]).starts_with(OptName))
    Args = Args.slice(1);
  return Args;
}

static void validateLegacyUnsupportedArgs(DiagnosticEngine &diags,
                                          const ArgList &args) {
  if (args.hasArg(options::OPT_incremental)) {
    diags.diagnose({}, diag::warning_unsupported_driver_option, "-incremental");
  }
  return;
}

static void validateBridgingHeaderArgs(DiagnosticEngine &diags,
                                       const ArgList &args) {
  if (!args.hasArgNoClaim(options::OPT_import_bridging_header,
                          options::OPT_internal_import_bridging_header))
    return;

  if (args.hasArgNoClaim(options::OPT_import_underlying_module))
    diags.diagnose({}, diag::error_framework_bridging_header);

  if (args.hasArgNoClaim(options::OPT_emit_module_interface,
                         options::OPT_emit_module_interface_path)) {
    diags.diagnose({}, diag::error_bridging_header_module_interface);
  }
}

static void validateWarningControlArgs(DiagnosticEngine &diags,
                                       const ArgList &args) {
  if (args.hasArg(options::OPT_suppress_warnings)) {
    if (args.hasFlag(options::OPT_warnings_as_errors,
                     options::OPT_no_warnings_as_errors, false)) {
      diags.diagnose(SourceLoc(), diag::error_conflicting_options,
                     "-warnings-as-errors", "-suppress-warnings");
    }
    if (args.hasArg(options::OPT_Wwarning)) {
      diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-Wwarning",
                     "-suppress-warnings");
    }
    if (args.hasArg(options::OPT_Werror)) {
      diags.diagnose(SourceLoc(), diag::error_conflicting_options, "-Werror",
                     "-suppress-warnings");
    }
  }
}

static void validateProfilingArgs(DiagnosticEngine &diags,
                                  const ArgList &args) {
  const Arg *ProfileGenerate = args.getLastArg(options::OPT_profile_generate);
  const Arg *ProfileUse = args.getLastArg(options::OPT_profile_use);
  if (ProfileGenerate && ProfileUse) {
    diags.diagnose(SourceLoc(), diag::error_conflicting_options,
                   "-profile-generate", "-profile-use");
  }

  // Check if the profdata is missing
  if (ProfileUse && !llvm::sys::fs::exists(ProfileUse->getValue())) {
    diags.diagnose(SourceLoc(), diag::error_profile_missing,
                   ProfileUse->getValue());
  }
}

static void validateDependencyScanningArgs(DiagnosticEngine &diags,
                                           const ArgList &args) {
  const Arg *ScanDependencies = args.getLastArg(options::OPT_scan_dependencies);
  const Arg *Prescan = args.getLastArg(options::OPT_import_prescan);

  const Arg *SerializeCache =
      args.getLastArg(options::OPT_serialize_dependency_scan_cache);
  const Arg *ReuseCache =
      args.getLastArg(options::OPT_reuse_dependency_scan_cache);
  const Arg *CacheSerializationPath =
      args.getLastArg(options::OPT_dependency_scan_cache_path);
  const Arg *ValidatePriorCache =
      args.getLastArg(options::OPT_validate_prior_dependency_scan_cache);

  if (Prescan && !ScanDependencies) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-import-prescan", "-scan-dependencies");
  }
  if (Prescan && !ScanDependencies) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-import-prescan", "-scan-dependencies");
  }
  if (SerializeCache && !ScanDependencies) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-serialize-dependency-scan-cache", "-scan-dependencies");
  }
  if (ReuseCache && !ScanDependencies) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-load-dependency-scan-cache", "-scan-dependencies");
  }
  if (SerializeCache && !CacheSerializationPath) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-serialize-dependency-scan-cache",
                   "-dependency-scan-cache-path");
  }
  if (ReuseCache && !CacheSerializationPath) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-serialize-dependency-scan-cache",
                   "-dependency-scan-cache-path");
  }
  if (ValidatePriorCache && !ReuseCache) {
    diags.diagnose(SourceLoc(), diag::error_requirement_not_met,
                   "-validate-prior-dependency-scan-cache",
                   "-load-dependency-scan-cache");
  }
}

static void validateDebugInfoArgs(DiagnosticEngine &diags,
                                  const ArgList &args) {
  // Check for missing debug option when verifying debug info.
  if (args.hasArg(options::OPT_verify_debug_info)) {
    Arg *debugOpt = args.getLastArg(swift::options::OPT_g_Group);
    if (!debugOpt || debugOpt->getOption().matches(swift::options::OPT_gnone)) {
      diags.diagnose(SourceLoc(),
                     diag::verify_debug_info_requires_debug_option);
    }
  }

  // Check for any -*-prefix-map options that aren't of the form
  // 'original=remapped' (either side can be empty, however).
  for (const Arg *A : args.filtered(options::OPT_debug_prefix_map,
                                    options::OPT_coverage_prefix_map,
                                    options::OPT_file_prefix_map)) {
    StringRef val = A->getValue();
    if (val.find('=') == StringRef::npos)
      diags.diagnose(SourceLoc(), diag::error_opt_invalid_mapping,
                     A->getOption().getPrefixedName(), val);
  }
}

static void validateCompilationConditionArgs(DiagnosticEngine &diags,
                                             const ArgList &args) {
  for (const Arg *A : args.filtered(options::OPT_D)) {
    StringRef name = A->getValue();
    if (name.contains('=')) {
      diags.diagnose(SourceLoc(),
                     diag::cannot_assign_value_to_conditional_compilation_flag,
                     name);
    } else if (name.starts_with("-D")) {
      diags.diagnose(SourceLoc(), diag::redundant_prefix_compilation_flag,
                     name);
    } else if (!Lexer::isIdentifier(name)) {
      diags.diagnose(SourceLoc(), diag::invalid_conditional_compilation_flag,
                     name);
    }
  }
}

static void validateSearchPathArgs(DiagnosticEngine &diags,
                                   const ArgList &args) {
  for (const Arg *A : args.filtered(options::OPT_F, options::OPT_Fsystem)) {
    StringRef name = A->getValue();
    if (name.ends_with(".framework") || name.ends_with(".framework/"))
      diags.diagnose(SourceLoc(),
                     diag::framework_search_path_includes_framework_extension,
                     name);
  }
}

static void validateLinkArgs(DiagnosticEngine &diags, const ArgList &args) {
  if (args.hasArg(options::OPT_experimental_hermetic_seal_at_link)) {
    if (args.hasArg(options::OPT_enable_library_evolution)) {
      diags.diagnose(SourceLoc(),
                     diag::error_hermetic_seal_cannot_have_library_evolution);
    }

    bool ltoOk = false;
    if (const Arg *A = args.getLastArg(options::OPT_lto)) {
      StringRef name = A->getValue();
      if (name == "llvm-thin" || name == "llvm-full") {
        ltoOk = true;
      }
    }

    if (!ltoOk) {
      diags.diagnose(SourceLoc(),
                     diag::error_hermetic_seal_requires_lto);
    }
  }
}

/// Perform miscellaneous early validation of arguments.
static void validateArgs(DiagnosticEngine &diags, const ArgList &args,
                         const llvm::Triple &T) {
  validateLegacyUnsupportedArgs(diags, args);
  validateBridgingHeaderArgs(diags, args);
  validateWarningControlArgs(diags, args);
  validateProfilingArgs(diags, args);
  validateDependencyScanningArgs(diags, args);
  validateDebugInfoArgs(diags, args);
  validateCompilationConditionArgs(diags, args);
  validateSearchPathArgs(diags, args);
  validateLinkArgs(diags, args);
}

std::unique_ptr<ToolChain>
Driver::buildToolChain(const llvm::opt::InputArgList &ArgList) {

  if (const Arg *A = ArgList.getLastArg(options::OPT_target)) {
    DefaultTargetTriple = llvm::Triple::normalize(A->getValue());
  }

  llvm::Triple target(DefaultTargetTriple);

  // Backward compatibility hack: infer "simulator" environment for x86
  // iOS/tvOS/watchOS.
  if (tripleInfersSimulatorEnvironment(target)) {
    // Set the simulator environment.
    target.setEnvironment(llvm::Triple::EnvironmentType::Simulator);

    auto newTargetTriple = target.normalize();
    Diags.diagnose(SourceLoc(), diag::warning_inferred_simulator_target,
                   DefaultTargetTriple, newTargetTriple);

    DefaultTargetTriple = newTargetTriple;
  }

  switch (target.getOS()) {
  case llvm::Triple::XROS:
  case llvm::Triple::IOS:
  case llvm::Triple::TvOS:
  case llvm::Triple::WatchOS:
  case llvm::Triple::Darwin:
  case llvm::Triple::MacOSX: {
    std::optional<llvm::Triple> targetVariant;
    if (const Arg *A = ArgList.getLastArg(options::OPT_target_variant))
      targetVariant = llvm::Triple(llvm::Triple::normalize(A->getValue()));

    return std::make_unique<toolchains::Darwin>(*this, target, targetVariant);
  }
  case llvm::Triple::Linux:
    if (target.isAndroid())
      return std::make_unique<toolchains::Android>(*this, target);
    return std::make_unique<toolchains::GenericUnix>(*this, target);
  case llvm::Triple::FreeBSD:
    return std::make_unique<toolchains::FreeBSD>(*this, target);
  case llvm::Triple::OpenBSD:
    return std::make_unique<toolchains::OpenBSD>(*this, target);
  case llvm::Triple::Win32:
    if (target.isWindowsCygwinEnvironment())
      return std::make_unique<toolchains::Cygwin>(*this, target);
    return std::make_unique<toolchains::Windows>(*this, target);
  case llvm::Triple::Haiku:
    return std::make_unique<toolchains::GenericUnix>(*this, target);
  case llvm::Triple::WASI:
    return std::make_unique<toolchains::WebAssembly>(*this, target);
  case llvm::Triple::UnknownOS:
    return std::make_unique<toolchains::GenericUnix>(*this, target);
  default:
    Diags.diagnose(SourceLoc(), diag::error_unknown_target,
                   ArgList.getLastArg(options::OPT_target)->getValue());
    break;
  }
  return nullptr;
}

std::unique_ptr<sys::TaskQueue> Driver::buildTaskQueue(const Compilation &C) {
  const auto &ArgList = C.getArgs();
  unsigned NumberOfParallelCommands = 1;
  if (const Arg *A = ArgList.getLastArg(options::OPT_j)) {
    if (StringRef(A->getValue()).getAsInteger(10, NumberOfParallelCommands)) {
      Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
                     A->getAsString(ArgList), A->getValue());
      return nullptr;
    }
  }
  if (environmentVariableRequestedMaximumDeterminism()) {
      NumberOfParallelCommands = 1;
      Diags.diagnose(SourceLoc(), diag::remark_max_determinism_overriding,
                     "-j");
  }

  const bool DriverSkipExecution =
    ArgList.hasArg(options::OPT_driver_skip_execution,
                   options::OPT_driver_print_jobs);
  if (DriverSkipExecution) {
    return std::make_unique<sys::DummyTaskQueue>(NumberOfParallelCommands);
  } else {
    return std::make_unique<sys::TaskQueue>(NumberOfParallelCommands,
                                             C.getStatsReporter());
  }
}

// warn if -embed-bitcode is set and the output type is not an object
static void validateEmbedBitcode(DerivedArgList &Args, const OutputInfo &OI,
                                 DiagnosticEngine &Diags) {
  if (Args.hasArg(options::OPT_embed_bitcode) &&
      OI.CompilerOutputType != file_types::TY_Object) {
    Diags.diagnose(SourceLoc(), diag::warn_ignore_embed_bitcode);
    Args.eraseArg(options::OPT_embed_bitcode);
  }

  if (Args.hasArg(options::OPT_embed_bitcode_marker) &&
      OI.CompilerOutputType != file_types::TY_Object) {
    Diags.diagnose(SourceLoc(), diag::warn_ignore_embed_bitcode_marker);
    Args.eraseArg(options::OPT_embed_bitcode_marker);
  }
}

/// Gets the filelist threshold to use. Diagnoses and returns true on error.
static bool getFilelistThreshold(DerivedArgList &Args, size_t &FilelistThreshold,
                                   DiagnosticEngine &Diags) {
  FilelistThreshold = 128;

  // claim and diagnose deprecated -driver-use-filelists
  bool HasUseFilelists = Args.hasArg(options::OPT_driver_use_filelists);
  if (HasUseFilelists)
    Diags.diagnose(SourceLoc(), diag::warn_use_filelists_deprecated);

  if (const Arg *A = Args.getLastArg(options::OPT_driver_filelist_threshold)) {
    // Use the supplied threshold
    if (StringRef(A->getValue()).getAsInteger(10, FilelistThreshold)) {
      Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
                     A->getAsString(Args), A->getValue());
      return true;
    }
  } else if (HasUseFilelists) {
    // Treat -driver-use-filelists as -driver-filelist-threshold=0
    FilelistThreshold = 0;
  } // else stick with the default

  return false;
}

static std::string
computeWorkingDirectory(const llvm::opt::InputArgList *ArgList) {
  if (auto *A = ArgList->getLastArg(options::OPT_working_directory)) {
    SmallString<128> workingDirectory;
    workingDirectory = A->getValue();
    llvm::sys::fs::make_absolute(workingDirectory);
    std::string result = workingDirectory.str().str();
    return result;
  }
  return std::string();
}

static std::unique_ptr<UnifiedStatsReporter>
createStatsReporter(const llvm::opt::InputArgList *ArgList,
                    const InputFileList &Inputs, const OutputInfo OI,
                    StringRef DefaultTargetTriple) {
  const Arg *A = ArgList->getLastArgNoClaim(options::OPT_stats_output_dir);
  if (!A)
    return nullptr;

  StringRef OptType;
  if (const Arg *OptA = ArgList->getLastArgNoClaim(options::OPT_O_Group)) {
    OptType = OptA->getSpelling();
  }
  StringRef InputName;
  if (Inputs.size() == 1) {
    InputName = Inputs[0].second->getSpelling();
  }
  StringRef OutputType = file_types::getExtension(OI.CompilerOutputType);
  return std::make_unique<UnifiedStatsReporter>("swift-driver",
                                                 OI.ModuleName,
                                                 InputName,
                                                 DefaultTargetTriple,
                                                 OutputType,
                                                 OptType,
                                                 A->getValue(),
                                                 nullptr,
                                                 nullptr,
                                                 false,
                                                 false,
                                                 false,
                                                 false,
                                                 false);
}

static bool
computeContinueBuildingAfterErrors(const llvm::opt::InputArgList *ArgList) {
  // FIXME: We don't really need (or want) a full ContinueBuildingAfterErrors.
  // If we fail to precompile a bridging header, for example, there's no need
  // to go on to compilation of source files, and if compilation of source files
  // fails, we shouldn't try to link. Instead, we'd want to let all jobs finish
  // but not schedule any new ones.
  return ArgList->hasArg(options::OPT_continue_building_after_errors);
}

std::unique_ptr<Compilation>
Driver::buildCompilation(const ToolChain &TC,
                         std::unique_ptr<llvm::opt::InputArgList> ArgList,
                         bool AllowErrors) {
  llvm::PrettyStackTraceString CrashInfo("Compilation construction");

  // Claim --driver-mode here, since it's already been handled.
  (void) ArgList->hasArg(options::OPT_driver_mode);

  DriverPrintBindings = ArgList->hasArg(options::OPT_driver_print_bindings);

  const std::string workingDirectory = computeWorkingDirectory(ArgList.get());

  std::unique_ptr<DerivedArgList> TranslatedArgList(
      translateInputAndPathArgs(*ArgList, workingDirectory));

  validateArgs(Diags, *TranslatedArgList, TC.getTriple());
    
  // Perform toolchain specific args validation.
  TC.validateArguments(Diags, *TranslatedArgList, DefaultTargetTriple);

  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  if (!handleImmediateArgs(*TranslatedArgList, TC)) {
    return nullptr;
  }

  // Construct the list of inputs.
  InputFileList Inputs;
  buildInputs(TC, *TranslatedArgList, Inputs);

  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  // Determine the OutputInfo for the driver.
  OutputInfo OI;
  OI.CompilerMode = computeCompilerMode(*TranslatedArgList, Inputs);
  buildOutputInfo(TC, *TranslatedArgList, Inputs, OI);

  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  assert(OI.CompilerOutputType != file_types::ID::TY_INVALID &&
         "buildOutputInfo() must set a valid output type!");

  TC.validateOutputInfo(Diags, OI);
  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  validateEmbedBitcode(*TranslatedArgList, OI, Diags);

  if (OI.CompilerMode == OutputInfo::Mode::REPL)
    // REPL mode expects no input files, so suppress the error.
    SuppressNoInputFilesError = true;

  std::optional<OutputFileMap> OFM =
      buildOutputFileMap(*TranslatedArgList, workingDirectory);

  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  if (ArgList->hasArg(options::OPT_driver_print_output_file_map)) {
    if (OFM)
      OFM->dump(llvm::errs(), true);
    else
      Diags.diagnose(SourceLoc(), diag::error_no_output_file_map_specified);
    if (!AllowErrors) {
      return nullptr;
    }
  }

  size_t DriverFilelistThreshold;
  if (getFilelistThreshold(*TranslatedArgList, DriverFilelistThreshold, Diags))
    return nullptr;

  OutputLevel Level = OutputLevel::Normal;
  if (const Arg *A =
          ArgList->getLastArg(options::OPT_driver_print_jobs, options::OPT_v,
                              options::OPT_parseable_output)) {
    if (A->getOption().matches(options::OPT_driver_print_jobs))
      Level = OutputLevel::PrintJobs;
    else if (A->getOption().matches(options::OPT_v))
      Level = OutputLevel::Verbose;
    else if (A->getOption().matches(options::OPT_parseable_output))
      Level = OutputLevel::Parseable;
    else
      llvm_unreachable("Unknown OutputLevel argument!");
  }

  
  // About to move argument list, so capture some flags that will be needed
  // later.
  const bool DriverPrintActions =
      ArgList->hasArg(options::OPT_driver_print_actions);
  const bool DriverPrintDerivedOutputFileMap =
      ArgList->hasArg(options::OPT_driver_print_derived_output_file_map);
  const bool ContinueBuildingAfterErrors =
      computeContinueBuildingAfterErrors(ArgList.get());
  const bool ShowJobLifecycle =
      ArgList->hasArg(options::OPT_driver_show_job_lifecycle);

  // In order to confine the values below, while still moving the argument
  // list, and preserving the interface to Compilation, enclose the call to the
  // constructor in a block:
  std::unique_ptr<Compilation> C;
  {
    const bool SaveTemps = ArgList->hasArg(options::OPT_save_temps);
    const bool ShowDriverTimeCompilation =
        ArgList->hasArg(options::OPT_driver_time_compilation);
    std::unique_ptr<UnifiedStatsReporter> StatsReporter =
        createStatsReporter(ArgList.get(), Inputs, OI, DefaultTargetTriple);

    const bool OnlyOneDependencyFile =
        ArgList->hasFlag(options::OPT_enable_only_one_dependency_file,
                         options::OPT_disable_only_one_dependency_file, false);

    // clang-format off
    C = std::make_unique<Compilation>(
        Diags, TC, OI, Level,
        std::move(ArgList),
        std::move(TranslatedArgList),
        std::move(Inputs),
        DriverFilelistThreshold,
        SaveTemps,
        ShowDriverTimeCompilation,
        std::move(StatsReporter),
        OnlyOneDependencyFile);
    // clang-format on
  }

  // Construct the graph of Actions.
  SmallVector<const Action *, 8> TopLevelActions;
  buildActions(TopLevelActions, TC, OI, *C);

  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  if (DriverPrintActions) {
    printActions(*C);
    return nullptr;
  }

  buildJobs(TopLevelActions, OI, OFM ? &*OFM : nullptr,
            workingDirectory, TC, *C);

  if (DriverPrintDerivedOutputFileMap) {
    C->getDerivedOutputFileMap().dump(llvm::outs(), true);
    return nullptr;
  }

  // For getting bulk fixits, or for when users explicitly request to continue
  // building despite errors.
  if (ContinueBuildingAfterErrors)
    C->setContinueBuildingAfterErrors();

  if (ShowJobLifecycle)
    C->setShowJobLifecycle();

  if (Diags.hadAnyError() && !AllowErrors)
    return nullptr;

  if (DriverPrintBindings)
    return nullptr;

  return C;
}

static Arg *makeInputArg(const DerivedArgList &Args, OptTable &Opts,
                         StringRef Value) {
  Arg *A = new Arg(Opts.getOption(options::OPT_INPUT), Value,
                   Args.getBaseArgs().MakeIndex(Value), Value.data());
  A->claim();
  return A;
}

using RemainingArgsHandler = llvm::function_ref<void(InputArgList &, unsigned)>;

std::unique_ptr<InputArgList>
parseArgsUntil(const llvm::opt::OptTable& Opts,
               const char *const *ArgBegin,
               const char *const *ArgEnd,
               unsigned &MissingArgIndex,
               unsigned &MissingArgCount,
               unsigned FlagsToInclude,
               unsigned FlagsToExclude,
               llvm::opt::OptSpecifier UntilOption,
               RemainingArgsHandler RemainingHandler) {
  auto Args = std::make_unique<InputArgList>(ArgBegin, ArgEnd);

  // FIXME: Handle '@' args (or at least error on them).

  bool CheckUntil = UntilOption != options::OPT_INVALID;
  MissingArgIndex = MissingArgCount = 0;
  unsigned Index = 0, End = ArgEnd - ArgBegin;
  while (Index < End) {
    // Ignore empty arguments (other things may still take them as arguments).
    StringRef Str = Args->getArgString(Index);
    if (Str == "") {
      ++Index;
      continue;
    }

    unsigned Prev = Index;
    Arg *A = Opts.ParseOneArg(*Args, Index, FlagsToInclude, FlagsToExclude)
                 .release();
    assert(Index > Prev && "Parser failed to consume argument.");

    // Check for missing argument error.
    if (!A) {
      assert(Index >= End && "Unexpected parser error.");
      assert(Index - Prev - 1 && "No missing arguments!");
      MissingArgIndex = Prev;
      MissingArgCount = Index - Prev - 1;
      break;
    }

    Args->append(A);

    if (CheckUntil && A->getOption().matches(UntilOption)) {
      if (Index < End)
        RemainingHandler(*Args, Index);
      return Args;
    }
  }

  return Args;
}

// Parse all args until we see an input, and then collect the remaining
// arguments into a synthesized "--" option.
static std::unique_ptr<InputArgList>
parseArgStringsForInteractiveDriver(const llvm::opt::OptTable& Opts,
                                    ArrayRef<const char *> Args,
                                    unsigned &MissingArgIndex,
                                    unsigned &MissingArgCount,
                                    unsigned FlagsToInclude,
                                    unsigned FlagsToExclude) {
  return parseArgsUntil(Opts, Args.begin(), Args.end(), MissingArgIndex,
                        MissingArgCount, FlagsToInclude, FlagsToExclude,
                        options::OPT_INPUT,
                        [&](InputArgList &Args, unsigned NextIndex) {
    assert(NextIndex < Args.getNumInputArgStrings());
    // Synthesize -- remaining args...
    Arg *Remaining =
        new Arg(Opts.getOption(options::OPT__DASH_DASH), "--", NextIndex);
    for (unsigned N = Args.getNumInputArgStrings(); NextIndex != N;
         ++NextIndex) {
      Remaining->getValues().push_back(Args.getArgString(NextIndex));
    }
    Args.append(Remaining);
  });
}

std::unique_ptr<InputArgList>
Driver::parseArgStrings(ArrayRef<const char *> Args) {
  unsigned IncludedFlagsBitmask = 0;
  unsigned ExcludedFlagsBitmask = options::NoDriverOption;
  unsigned MissingArgIndex, MissingArgCount;
  std::unique_ptr<InputArgList> ArgList;

  if (driverKind == DriverKind::Interactive) {
    ArgList = parseArgStringsForInteractiveDriver(getOpts(), Args,
        MissingArgIndex, MissingArgCount, IncludedFlagsBitmask,
        ExcludedFlagsBitmask);

  } else {
    ArgList = std::make_unique<InputArgList>(
        getOpts().ParseArgs(Args, MissingArgIndex, MissingArgCount,
                            IncludedFlagsBitmask, ExcludedFlagsBitmask));
  }

  assert(ArgList && "no argument list");

  // Check for missing argument error.
  if (MissingArgCount) {
    Diags.diagnose(SourceLoc(), diag::error_missing_arg_value,
                   ArgList->getArgString(MissingArgIndex), MissingArgCount);
    return nullptr;
  }

  // Check for unknown arguments.
  for (const Arg *A :  ArgList->filtered(options::OPT_UNKNOWN)) {
    Diags.diagnose(SourceLoc(), diag::error_unknown_arg,
                   A->getAsString(*ArgList));
  }

  // Check for unsupported options
  unsigned UnsupportedFlag = 0;
  if (driverKind == DriverKind::Interactive)
    UnsupportedFlag = options::NoInteractiveOption;
  else if (driverKind == DriverKind::Standard)
    UnsupportedFlag = options::NoBatchOption;

  if (UnsupportedFlag)
    for (const Arg *A : *ArgList)
      if (A->getOption().hasFlag(UnsupportedFlag))
        Diags.diagnose(SourceLoc(), diag::error_unsupported_option,
            ArgList->getArgString(A->getIndex()), Name,
            UnsupportedFlag == options::NoBatchOption ? "swift" : "swiftc");

  return ArgList;
}

DerivedArgList *
Driver::translateInputAndPathArgs(const InputArgList &ArgList,
                                  StringRef workingDirectory) const {
  DerivedArgList *DAL = new DerivedArgList(ArgList);

  auto addPath = [workingDirectory, DAL](Arg *A) {
    assert(A->getNumValues() == 1 && "multiple values not handled");
    StringRef path = A->getValue();
    if (workingDirectory.empty() || path == "-" ||
        llvm::sys::path::is_absolute(path)) {
      DAL->append(A);
      return;
    }

    SmallString<64> fullPath{workingDirectory};
    llvm::sys::path::append(fullPath, path);
    unsigned index = DAL->getBaseArgs().MakeIndex(fullPath);
    Arg *newArg = new Arg(A->getOption(), A->getSpelling(), index,
                          DAL->getBaseArgs().getArgString(index), A);
    DAL->AddSynthesizedArg(newArg);
    DAL->append(newArg);
  };

  for (Arg *A : ArgList) {
    if (A->getOption().hasFlag(options::ArgumentIsPath) ||
        A->getOption().matches(options::OPT_INPUT)) {
      addPath(A);
      continue;
    }

    // If we're not in immediate mode, pick up inputs via the -- option.
    if (driverKind != DriverKind::Interactive && A->getOption().matches(options::OPT__DASH_DASH)) {
      A->claim();
      for (unsigned i = 0, e = A->getNumValues(); i != e; ++i) {
        addPath(makeInputArg(*DAL, *Opts, A->getValue(i)));
      }
      continue;
    }

    DAL->append(A);
  }

  return DAL;
}

/// Check that the file referenced by \p Input exists. If it doesn't,
/// issue a diagnostic and return false.
static bool checkInputExistence(const Driver &D, const DerivedArgList &Args,
                                DiagnosticEngine &Diags, StringRef Input) {
  if (!D.getCheckInputFilesExist())
    return true;

  // stdin always exists.
  if (Input == "-")
    return true;

  if (llvm::sys::fs::exists(Input))
    return true;

  Diags.diagnose(SourceLoc(), diag::error_no_such_file_or_directory, Input);
  return false;
}

void Driver::buildInputs(const ToolChain &TC,
                         const DerivedArgList &Args,
                         InputFileList &Inputs) const {
  llvm::DenseMap<StringRef, StringRef> SourceFileNames;
  bool HasVFS = Args.hasArg(options::OPT_vfsoverlay);

  for (Arg *A : Args) {
    if (A->getOption().getKind() == Option::InputClass) {
      StringRef Value = A->getValue();
      file_types::ID Ty = file_types::TY_INVALID;

      // stdin must be handled specially.
      if (Value == "-") {
        // By default, treat stdin as Swift input.
        Ty = file_types::TY_Swift;
      } else {
        // Otherwise lookup by extension.
        Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value));

        if (Ty == file_types::TY_INVALID) {
          // By default, treat inputs with no extension, or with an
          // extension that isn't recognized, as object files.
          Ty = file_types::TY_Object;
        }
      }

      if (HasVFS || checkInputExistence(*this, Args, Diags, Value))
        Inputs.push_back(std::make_pair(Ty, A));

      if (Ty == file_types::TY_Swift) {
        StringRef Basename = llvm::sys::path::filename(Value);
        if (!SourceFileNames.insert({Basename, Value}).second) {
          Diags.diagnose(SourceLoc(), diag::error_two_files_same_name,
                         Basename, SourceFileNames[Basename], Value);
          Diags.diagnose(SourceLoc(), diag::note_explain_two_files_same_name);
        }
      }
    }
  }
}

static bool maybeBuildingExecutable(const OutputInfo &OI,
                                    const DerivedArgList &Args,
                                    const InputFileList &Inputs) {
  switch (OI.LinkAction) {
  case LinkKind::Executable:
    return true;
  case LinkKind::DynamicLibrary:
    return false;
  case LinkKind::StaticLibrary:
    return false;
  case LinkKind::None:
    break;
  }

  if (Args.hasArg(options::OPT_parse_as_library, options::OPT_parse_stdlib))
    return false;
  return Inputs.size() == 1;
}

static void diagnoseOutputModeArg(DiagnosticEngine &diags, const Arg *arg,
                                  bool hasInputs, const DerivedArgList &args,
                                  bool isInteractiveDriver,
                                  StringRef driverName) {
  switch (arg->getOption().getID()) {

  case options::OPT_i:
    diags.diagnose(SourceLoc(), diag::error_i_mode,
                   isInteractiveDriver ? driverName : "swift");
    break;

  case options::OPT_repl:
    if (isInteractiveDriver && !hasInputs)
      diags.diagnose(SourceLoc(), diag::warning_unnecessary_repl_mode,
                     args.getArgString(arg->getIndex()), driverName);
    break;

  default:
    break;
  }
}

static bool isSDKTooOld(StringRef sdkPath, llvm::VersionTuple minVersion,
                        StringRef firstPrefix, StringRef secondPrefix = {}) {
  // FIXME: This is a hack.
  // We should be looking at the SDKSettings.plist.
  StringRef sdkDirName = llvm::sys::path::filename(sdkPath);

  size_t versionStart = sdkDirName.rfind(firstPrefix);
  if (versionStart != StringRef::npos) {
    versionStart += firstPrefix.size();
  } else if (!secondPrefix.empty()) {
    versionStart = sdkDirName.rfind(secondPrefix);
    if (versionStart != StringRef::npos)
      versionStart += secondPrefix.size();
  }
  if (versionStart == StringRef::npos)
    return false;

  size_t versionEnd = sdkDirName.rfind(".Internal");
  if (versionEnd == StringRef::npos)
    versionEnd = sdkDirName.rfind(".sdk");
  if (versionEnd == StringRef::npos)
    return false;

  llvm::VersionTuple version;
  if (version.tryParse(sdkDirName.slice(versionStart, versionEnd)))
    return false;
  return version < minVersion;
}

/// Returns true if the given SDK path points to an SDK that is too old for
/// the given target.
static bool isSDKTooOld(StringRef sdkPath, const llvm::Triple &target) {
  if (target.isMacOSX()) {
    return isSDKTooOld(sdkPath, llvm::VersionTuple(10, 15), "OSX");

  } else if (target.isiOS()) {
    // Includes both iOS and TVOS.
    return isSDKTooOld(sdkPath, llvm::VersionTuple(13, 0), "Simulator", "OS");

  } else if (target.isWatchOS()) {
    return isSDKTooOld(sdkPath, llvm::VersionTuple(6, 0), "Simulator", "OS");

  } else {
    return false;
  }
}

void Driver::buildOutputInfo(const ToolChain &TC, const DerivedArgList &Args,
                             const InputFileList &Inputs,
                             OutputInfo &OI) const {

  if (const Arg *A = Args.getLastArg(options::OPT_lto)) {
    auto LTOVariant =
        llvm::StringSwitch<std::optional<OutputInfo::LTOKind>>(A->getValue())
            .Case("llvm-thin", OutputInfo::LTOKind::LLVMThin)
            .Case("llvm-full", OutputInfo::LTOKind::LLVMFull)
            .Default(std::nullopt);
    if (LTOVariant)
      OI.LTOVariant = LTOVariant.value();
    else
      Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
                     A->getAsString(Args), A->getValue());
  }
  
  if (const Arg *A = Args.getLastArg(options::OPT_lto_library)) {
    OI.LibLTOPath = A->getValue();
  } else {
    OI.LibLTOPath = "";
  }

  auto CompilerOutputType = OI.LTOVariant != OutputInfo::LTOKind::None
                             ? file_types::TY_LLVM_BC
                             : file_types::TY_Object;
  // By default, the driver does not link its output; this will be updated
  // appropriately below if linking is required.

  OI.CompilerOutputType = driverKind == DriverKind::Interactive
                              ? file_types::TY_Nothing
                              : CompilerOutputType;

  if (const Arg *A = Args.getLastArg(options::OPT_num_threads)) {
    if (StringRef(A->getValue()).getAsInteger(10, OI.numThreads)) {
      Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
                     A->getAsString(Args), A->getValue());
    }
  }

  const Arg *const OutputModeArg = Args.getLastArg(options::OPT_modes_Group);

  if (!OutputModeArg) {
    if (Args.hasArg(options::OPT_emit_module, options::OPT_emit_module_path)) {
      OI.CompilerOutputType = file_types::TY_SwiftModuleFile;
    } else if (driverKind != DriverKind::Interactive) {
      OI.LinkAction = LinkKind::Executable;
    }
  } else {
    diagnoseOutputModeArg(Diags, OutputModeArg, !Inputs.empty(), Args,
                          driverKind == DriverKind::Interactive, Name);

    switch (OutputModeArg->getOption().getID()) {
    case options::OPT_emit_executable:
      if (Args.hasArg(options::OPT_static))
        Diags.diagnose(SourceLoc(),
                       diag::error_static_emit_executable_disallowed);
                       
      OI.LinkAction = LinkKind::Executable;
      OI.CompilerOutputType = CompilerOutputType;
      break;

    case options::OPT_emit_library:
      OI.LinkAction = Args.hasArg(options::OPT_static) ?
                      LinkKind::StaticLibrary :
                      LinkKind::DynamicLibrary;
      OI.CompilerOutputType = CompilerOutputType;
      break;

    case options::OPT_static:
      break;

    case options::OPT_emit_object:
      OI.CompilerOutputType = file_types::TY_Object;
      if (OI.LTOVariant != OutputInfo::LTOKind::None)
        OI.CompilerOutputType = file_types::TY_LLVM_BC;
      break;

    case options::OPT_emit_assembly:
      OI.CompilerOutputType = file_types::TY_Assembly;
      break;

    case options::OPT_emit_sil:
      OI.CompilerOutputType = file_types::TY_SIL;
      break;

    case options::OPT_emit_silgen:
      OI.CompilerOutputType = file_types::TY_RawSIL;
      break;

    case options::OPT_emit_lowered_sil:
      OI.CompilerOutputType = file_types::TY_LoweredSIL;
      break;

    case options::OPT_emit_sib:
      OI.CompilerOutputType = file_types::TY_SIB;
      break;

    case options::OPT_emit_sibgen:
      OI.CompilerOutputType = file_types::TY_RawSIB;
      break;

    case options::OPT_emit_irgen:
      OI.CompilerOutputType = file_types::TY_RawLLVM_IR;
      break;

    case options::OPT_emit_ir:
      OI.CompilerOutputType = file_types::TY_LLVM_IR;
      break;

    case options::OPT_emit_bc:
      OI.CompilerOutputType = file_types::TY_LLVM_BC;
      break;

    case options::OPT_dump_ast:
      OI.CompilerOutputType = file_types::TY_ASTDump;
      break;

    case options::OPT_emit_pch:
      OI.CompilerMode = OutputInfo::Mode::SingleCompile;
      OI.CompilerOutputType = file_types::TY_PCH;
      break;

    case options::OPT_emit_pcm:
      OI.CompilerMode = OutputInfo::Mode::SingleCompile;
      OI.CompilerOutputType = file_types::TY_ClangModuleFile;
      break;

    case options::OPT_emit_imported_modules:
      OI.CompilerOutputType = file_types::TY_ImportedModules;
      // We want the imported modules from the module as a whole, not individual
      // files, so let's do it in one invocation rather than having to collate
      // later.
      OI.CompilerMode = OutputInfo::Mode::SingleCompile;
      break;

    case options::OPT_scan_dependencies:
      OI.CompilerOutputType = file_types::TY_JSONDependencies;
      // We want the imported modules from the module as a whole, not individual
      // files, so let's do it in one invocation rather than having to collate
      // later.
      OI.CompilerMode = OutputInfo::Mode::SingleCompile;
      break;

    case options::OPT_index_file:
      OI.CompilerMode = OutputInfo::Mode::SingleCompile;
      OI.CompilerOutputType = file_types::TY_IndexData;
      break;

    case options::OPT_update_code:
      OI.CompilerOutputType = file_types::TY_Remapping;
      OI.LinkAction = LinkKind::None;
      break;

    case options::OPT_parse:
    case options::OPT_resolve_imports:
    case options::OPT_experimental_lazy_typecheck:
    case options::OPT_typecheck:
    case options::OPT_dump_parse:
    case options::OPT_print_ast:
    case options::OPT_dump_availability_scopes:
    case options::OPT_dump_scope_maps:
    case options::OPT_dump_interface_hash:
    case options::OPT_dump_type_info:
    case options::OPT_verify_debug_info:
      OI.CompilerOutputType = file_types::TY_Nothing;
      break;

    case options::OPT_dump_pcm:
      OI.CompilerMode = OutputInfo::Mode::SingleCompile;
      OI.CompilerOutputType = file_types::TY_Nothing;
      break;

    case options::OPT_i:
      // Keep the default output/mode; this flag was removed and should already
      // have been diagnosed above.
      assert(Diags.hadAnyError() && "-i flag was removed");
      break;

    case options::OPT_repl:
    case options::OPT_deprecated_integrated_repl:
    case options::OPT_lldb_repl:
      OI.CompilerOutputType = file_types::TY_Nothing;
      OI.CompilerMode = OutputInfo::Mode::REPL;
      break;

    default:
      llvm_unreachable("unknown mode");
    }
  }

  assert(OI.CompilerOutputType != file_types::ID::TY_INVALID);

  if (const Arg *A = Args.getLastArg(options::OPT_g_Group)) {
    if (A->getOption().matches(options::OPT_g))
      OI.DebugInfoLevel = IRGenDebugInfoLevel::Normal;
    else if (A->getOption().matches(options::OPT_gline_tables_only))
      OI.DebugInfoLevel = IRGenDebugInfoLevel::LineTables;
    else if (A->getOption().matches(options::OPT_gdwarf_types))
      OI.DebugInfoLevel = IRGenDebugInfoLevel::DwarfTypes;
    else
      assert(A->getOption().matches(options::OPT_gnone) &&
             "unknown -g<kind> option");
  }

  if (const Arg *A = Args.getLastArg(options::OPT_debug_info_format)) {
    if (strcmp(A->getValue(), "dwarf") == 0)
      OI.DebugInfoFormat = IRGenDebugInfoFormat::DWARF;
    else if (strcmp(A->getValue(), "codeview") == 0)
      OI.DebugInfoFormat = IRGenDebugInfoFormat::CodeView;
    else
      Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
                     A->getAsString(Args), A->getValue());
  } else if (OI.DebugInfoLevel > IRGenDebugInfoLevel::None) {
    // If -g was specified but not -debug-info-format, DWARF is assumed.
    OI.DebugInfoFormat = IRGenDebugInfoFormat::DWARF;
  }
  if (Args.hasArg(options::OPT_debug_info_format) &&
      !Args.hasArg(options::OPT_g_Group)) {
    const Arg *debugFormatArg = Args.getLastArg(options::OPT_debug_info_format);
    Diags.diagnose(SourceLoc(), diag::error_option_missing_required_argument,
                   debugFormatArg->getAsString(Args), "-g");
  }
  if (OI.DebugInfoFormat == IRGenDebugInfoFormat::CodeView &&
      (OI.DebugInfoLevel == IRGenDebugInfoLevel::LineTables ||
       OI.DebugInfoLevel == IRGenDebugInfoLevel::DwarfTypes)) {
    const Arg *debugFormatArg = Args.getLastArg(options::OPT_debug_info_format);
    Diags.diagnose(SourceLoc(), diag::error_argument_not_allowed_with,
                   debugFormatArg->getAsString(Args),
                   OI.DebugInfoLevel == IRGenDebugInfoLevel::LineTables
                     ? "-gline-tables-only"
                     : "-gdwarf_types");
  }

  if (const Arg *A = Args.getLastArg(options::OPT_dwarf_version)) {
    unsigned vers;
    if (!StringRef(A->getValue()).getAsInteger(10, vers) && vers >= 2 &&
        vers <= 5)
      OI.DWARFVersion = vers;
    else
      Diags.diagnose(SourceLoc(), diag::error_invalid_arg_value,
                     A->getAsString(Args), A->getValue());
  }

  if (Args.hasArg(options::OPT_emit_module, options::OPT_emit_module_path)) {
    // The user has requested a module, so generate one and treat it as
    // top-level output.
    OI.ShouldGenerateModule = true;
    OI.ShouldTreatModuleAsTopLevelOutput = true;
  } else if (OI.DebugInfoLevel > IRGenDebugInfoLevel::LineTables &&
             OI.shouldLink()) {
    // An option has been passed which requires a module, but the user hasn't
    // requested one. Generate a module, but treat it as an intermediate output.
    OI.ShouldGenerateModule = true;
    OI.ShouldTreatModuleAsTopLevelOutput = false;
  } else if (Args.hasArg(options::OPT_emit_objc_header,
                         options::OPT_emit_objc_header_path,
                         options::OPT_emit_module_interface,
                         options::OPT_emit_module_interface_path,
                         options::OPT_emit_private_module_interface_path,
                         options::OPT_emit_package_module_interface_path) &&
             OI.CompilerMode != OutputInfo::Mode::SingleCompile) {
    // An option has been passed which requires whole-module knowledge, but we
    // don't have that. Generate a module, but treat it as an intermediate
    // output.
    OI.ShouldGenerateModule = true;
    OI.ShouldTreatModuleAsTopLevelOutput = false;
  } else {
    // No options require a module, so don't generate one.
    OI.ShouldGenerateModule = false;
    OI.ShouldTreatModuleAsTopLevelOutput = false;
  }

  if (OI.ShouldGenerateModule &&
      (OI.CompilerMode == OutputInfo::Mode::REPL ||
       OI.CompilerMode == OutputInfo::Mode::Immediate)) {
    Diags.diagnose(SourceLoc(), diag::error_mode_cannot_emit_module);
    return;
  }

  if (const Arg *A = Args.getLastArg(options::OPT_module_name)) {
    OI.ModuleName = A->getValue();
  } else if (OI.CompilerMode == OutputInfo::Mode::REPL) {
    // REPL mode should always use the REPL module.
    OI.ModuleName = "REPL";
  } else if (const Arg *A = Args.getLastArg(options::OPT_o)) {
    OI.ModuleName = llvm::sys::path::stem(A->getValue()).str();
    if ((OI.LinkAction == LinkKind::DynamicLibrary ||
         OI.LinkAction == LinkKind::StaticLibrary) &&
        !llvm::sys::path::extension(A->getValue()).empty() &&
        StringRef(OI.ModuleName).starts_with("lib")) {
      // Chop off a "lib" prefix if we're building a library.
      OI.ModuleName.erase(0, strlen("lib"));
    }
  } else if (Inputs.size() == 1) {
    OI.ModuleName =
        llvm::sys::path::stem(Inputs.front().second->getValue()).str();
  }

  if (!Lexer::isIdentifier(OI.ModuleName) ||
      (OI.ModuleName == STDLIB_NAME &&
       !Args.hasArg(options::OPT_parse_stdlib))) {
    OI.ModuleNameIsFallback = true;
    if (OI.CompilerOutputType == file_types::TY_Nothing ||
        maybeBuildingExecutable(OI, Args, Inputs))
      OI.ModuleName = "main";
    else if (!Inputs.empty() || OI.CompilerMode == OutputInfo::Mode::REPL) {
      // Having an improper module name is only bad if we have inputs or if
      // we're in REPL mode.
      auto DID = (OI.ModuleName == STDLIB_NAME) ? diag::error_stdlib_module_name
                                                : diag::error_bad_module_name;
      Diags.diagnose(SourceLoc(), DID,
                     OI.ModuleName, !Args.hasArg(options::OPT_module_name));
      OI.ModuleName = "__bad__";
    }
  }

  {
    if (const Arg *A = Args.getLastArg(options::OPT_sdk)) {
      OI.SDKPath = A->getValue();
    } else if (const char *SDKROOT = getenv("SDKROOT")) {
      OI.SDKPath = SDKROOT;
    } else if (OI.CompilerMode == OutputInfo::Mode::Immediate ||
               OI.CompilerMode == OutputInfo::Mode::REPL) {
      if (TC.getTriple().isMacOSX()) {
        // In immediate modes, use the SDK provided by xcrun.
        // This will prefer the SDK alongside the Swift found by "xcrun swift".
        // We don't do this in compilation modes because defaulting to the
        // latest SDK may not be intended.
        auto xcrunPath = llvm::sys::findProgramByName("xcrun");
        if (!xcrunPath.getError()) {
          const char *args[] = {
            "--show-sdk-path", "--sdk", "macosx", nullptr
          };
          sys::TaskQueue queue;
          queue.addTask(xcrunPath->c_str(), args);
          queue.execute(nullptr,
                        [&OI](sys::ProcessId PID, int returnCode,
                              StringRef output, StringRef errors,
                              sys::TaskProcessInformation ProcInfo,
                              void *unused) -> sys::TaskFinishedResponse {
            if (returnCode == 0) {
              output = output.rtrim();
              auto lastLineStart = output.find_last_of("\n\r");
              if (lastLineStart != StringRef::npos)
                output = output.substr(lastLineStart+1);
              if (output.empty())
                OI.SDKPath = "/";
              else
                OI.SDKPath = output.str();
            }
            return sys::TaskFinishedResponse::ContinueExecution;
          });
        }
      }
    }

    if (!OI.SDKPath.empty()) {
      // Delete a trailing /.
      if (OI.SDKPath.size() > 1 &&
          llvm::sys::path::is_separator(OI.SDKPath.back())) {
        OI.SDKPath.erase(OI.SDKPath.end()-1);
      }

      if (!llvm::sys::fs::exists(OI.SDKPath)) {
        Diags.diagnose(SourceLoc(), diag::warning_no_such_sdk, OI.SDKPath);
      } else if (isSDKTooOld(OI.SDKPath, TC.getTriple())) {
        Diags.diagnose(SourceLoc(), diag::error_sdk_too_old,
                       llvm::sys::path::filename(OI.SDKPath));
      }
    }
  }

  if (const Arg *A = Args.getLastArg(options::OPT_sanitize_EQ))
    OI.SelectedSanitizers = parseSanitizerArgValues(
        Args, A, TC.getTriple(), Diags,
        [&](StringRef sanitizerName, bool shared) {
          return TC.sanitizerRuntimeLibExists(Args, sanitizerName, shared);
        });

  if (const Arg *A = Args.getLastArg(options::OPT_sanitize_recover_EQ)) {
    // Just validate the args. The frontend will parse these again and actually
    // use them. To avoid emitting warnings multiple times we suppress warnings
    // here but not in the frontend.
    (void)parseSanitizerRecoverArgValues(A, OI.SelectedSanitizers, Diags,
                                         /*emitWarnings=*/false);
  }

  if (const Arg *A = Args.getLastArg(options::OPT_sanitize_coverage_EQ)) {

    // Check that the sanitizer coverage flags are supported if supplied.
    // Dismiss the output, as we will grab the value later.
    (void)parseSanitizerCoverageArgValue(A, TC.getTriple(), Diags,
                                         OI.SelectedSanitizers);

  }

  if (const Arg *A = Args.getLastArg(options::OPT_sanitize_stable_abi_EQ)) {
    OI.SanitizerUseStableABI =
        parseSanitizerUseStableABI(A, OI.SelectedSanitizers, Diags);
  }

  if (TC.getTriple().isOSWindows()) {
    if (const Arg *A = Args.getLastArg(options::OPT_libc)) {
      OI.RuntimeVariant =
          llvm::StringSwitch<std::optional<OutputInfo::MSVCRuntime>>(
              A->getValue())
              .Cases("MD", "MultiThreadedDLL", "shared-ucrt",
                     OutputInfo::MSVCRuntime::MultiThreadedDLL)
              .Cases("MDd", "MultiThreadedDebugDLL", "shared-debug-ucrt",
                     OutputInfo::MSVCRuntime::MultiThreadedDebugDLL)
              .Cases("MT", "MultiThreaded", "static-ucrt",
                     OutputInfo::MSVCRuntime::MultiThreaded)
              .Cases("MTd", "MultiThreadedDebug", "static-debug-ucrt",
                     OutputInfo::MSVCRuntime::MultiThreadedDebug)
              .Default(std::nullopt);
      if (!OI.RuntimeVariant)
        Diags.diagnose({}, diag::error_invalid_arg_value, A->getSpelling(),
                       A->getValue());
    } else {
      // NOTE: default to `/MD`.  This is different from `cl`'s default
      // behaviour of `/MT` on the command line, however, Visual Studio 2015 and
      // newer will default `/MD` as well.  Furthermore, this is far more useful
      // of a mode since the `/MT` mode requires that everything is statically
      // linked.
      OI.RuntimeVariant = OutputInfo::MSVCRuntime::MultiThreadedDLL;
    }
  }
}

OutputInfo::Mode
Driver::computeCompilerMode(const DerivedArgList &Args,
                            const InputFileList &Inputs) const {

  if (driverKind == Driver::DriverKind::Interactive)
    return Inputs.empty() ? OutputInfo::Mode::REPL
                          : OutputInfo::Mode::Immediate;

  bool UseWMO = Args.hasFlag(options::OPT_whole_module_optimization,
                             options::OPT_no_whole_module_optimization,
                             false);

  const Arg *ArgRequiringSingleCompile = Args.getLastArg(
      options::OPT_index_file,
      UseWMO ? options::OPT_whole_module_optimization : llvm::opt::OptSpecifier());

  // AST dump doesn't work with `-wmo`. Since it's not common to want to dump
  // the AST, we assume that's the priority and ignore `-wmo`, but we warn the
  // user about this decision.
  // FIXME: AST dump also doesn't work with `-index-file`, but that fix is a bit
  // more complicated than this.
  if (UseWMO && Args.hasArg(options::OPT_dump_ast)) {
    Diags.diagnose(SourceLoc(), diag::warn_ignoring_wmo);
    return OutputInfo::Mode::StandardCompile;
  }

  if (ArgRequiringSingleCompile)
    return OutputInfo::Mode::SingleCompile;
  return OutputInfo::Mode::StandardCompile;
}

namespace {
/// Encapsulates the computation of input jobs that are relevant to the
/// merge-modules job the scheduler can insert if we are not in a single compile
/// mode.
class ModuleInputs final {
private:
  SmallVector<const Action *, 2> AllModuleInputs;

public:
  explicit ModuleInputs() {}

public:
  void addInput(const Action *inputAction) {
    AllModuleInputs.push_back(inputAction);
  }

public:
  /// Returns \c true if no inputs have been registered with this instance.
  bool empty() const { return AllModuleInputs.empty(); }

public:
  /// Consumes this \c ModuleInputs instance and returns a merge-modules action
  /// from the list of input actions and status it has computed thus far.
  JobAction *intoAction(Compilation &C) && {
    return C.createAction<MergeModuleJobAction>(AllModuleInputs);
  }
};
} // namespace

void Driver::buildActions(SmallVectorImpl<const Action *> &TopLevelActions,
                          const ToolChain &TC, const OutputInfo &OI,
                          Compilation &C) const {
  const DerivedArgList &Args = C.getArgs();
  ArrayRef<InputPair> Inputs = C.getInputFiles();

  if (!SuppressNoInputFilesError && Inputs.empty()) {
    Diags.diagnose(SourceLoc(), diag::error_no_input_files);
    return;
  }

  ModuleInputs AllModuleInputs;
  SmallVector<const Action *, 2> AllLinkerInputs;

  switch (OI.CompilerMode) {
  case OutputInfo::Mode::StandardCompile: {

    // If the user is importing a textual (.h) bridging header and we're in
    // standard-compile (non-WMO) mode, we take the opportunity to precompile
    // the header into a temporary PCH, and replace the import argument with the
    // PCH in the subsequent frontend jobs.
    JobAction *PCH = nullptr;
    if (Args.hasFlag(options::OPT_enable_bridging_pch,
                     options::OPT_disable_bridging_pch,
                     true)) {
      if (Arg *A = Args.getLastArg(
              options::OPT_import_bridging_header,
              options::OPT_internal_import_bridging_header)) {
        StringRef Value = A->getValue();
        auto Ty = TC.lookupTypeForExtension(llvm::sys::path::extension(Value));
        if (Ty == file_types::TY_ClangHeader) {
          auto *HeaderInput = C.createAction<InputAction>(*A, Ty);
          StringRef PersistentPCHDir;
          if (const Arg *A = Args.getLastArg(options::OPT_pch_output_dir)) {
            PersistentPCHDir = A->getValue();
          }
          PCH = C.createAction<GeneratePCHJobAction>(HeaderInput,
                                                     PersistentPCHDir);
        }
      }
    }

    for (const InputPair &Input : Inputs) {
      file_types::ID InputType = Input.first;
      const Arg *InputArg = Input.second;

      Action *Current = C.createAction<InputAction>(*InputArg, InputType);
      switch (InputType) {
      case file_types::TY_Swift:
      case file_types::TY_SIL:
      case file_types::TY_LoweredSIL:
      case file_types::TY_SIB: {
        // Source inputs always need to be compiled.
        assert(file_types::isPartOfSwiftCompilation(InputType));
        if (Args.hasArg(options::OPT_embed_bitcode)) {
          Current = C.createAction<CompileJobAction>(
              Current, file_types::TY_LLVM_BC);
          if (PCH)
            cast<JobAction>(Current)->addInput(PCH);
          AllModuleInputs.addInput(Current);
          Current = C.createAction<BackendJobAction>(Current,
                                                     OI.CompilerOutputType, 0);
        } else {
          Current = C.createAction<CompileJobAction>(Current,
                                                     OI.CompilerOutputType);
          if (PCH)
            cast<JobAction>(Current)->addInput(PCH);
          AllModuleInputs.addInput(Current);
        }
        AllLinkerInputs.push_back(Current);
        break;
      }
      case file_types::TY_SwiftModuleFile:
      case file_types::TY_SwiftModuleDocFile:
      case file_types::TY_SwiftSourceInfoFile:
        if (OI.ShouldGenerateModule && !OI.shouldLink()) {
          // When generating a .swiftmodule as a top-level output (as opposed
          // to, for example, linking an image), treat .swiftmodule files as
          // inputs to a MergeModule action.
          AllModuleInputs.addInput(Current);
          break;
        } else if (OI.shouldLink()) {
          // Otherwise, if linking, pass .swiftmodule files as inputs to the
          // linker, so that their debug info is available.
          AllLinkerInputs.push_back(Current);
          break;
        } else {
          Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file,
                         InputArg->getValue());
          continue;
        }
      case file_types::TY_AutolinkFile:
      case file_types::TY_Object:
      case file_types::TY_TBD:
        // Object inputs are only okay if linking.
        if (OI.shouldLink()) {
          AllLinkerInputs.push_back(Current);
          break;
        }
        LLVM_FALLTHROUGH;
      case file_types::TY_ASTDump:
      case file_types::TY_Image:
      case file_types::TY_dSYM:
      case file_types::TY_Dependencies:
      case file_types::TY_Assembly:
      case file_types::TY_RawLLVM_IR:
      case file_types::TY_LLVM_IR:
      case file_types::TY_LLVM_BC:
      case file_types::TY_SerializedDiagnostics:
      case file_types::TY_ClangHeader:
      case file_types::TY_ClangModuleFile:
      case file_types::TY_SwiftDeps:
      case file_types::TY_ExternalSwiftDeps:
      case file_types::TY_Remapping:
      case file_types::TY_IndexData:
      case file_types::TY_PCH:
      case file_types::TY_ImportedModules:
      case file_types::TY_ModuleTrace:
      case file_types::TY_FineModuleTrace:
      case file_types::TY_YAMLOptRecord:
      case file_types::TY_BitstreamOptRecord:
      case file_types::TY_SwiftModuleInterfaceFile:
      case file_types::TY_PrivateSwiftModuleInterfaceFile:
      case file_types::TY_PackageSwiftModuleInterfaceFile:
      case file_types::TY_SwiftModuleSummaryFile:
      case file_types::TY_SwiftCrossImportDir:
      case file_types::TY_SwiftOverlayFile:
      case file_types::TY_JSONDependencies:
      case file_types::TY_JSONArguments:
      case file_types::TY_SwiftABIDescriptor:
      case file_types::TY_SwiftAPIDescriptor:
      case file_types::TY_ConstValues:
      case file_types::TY_SwiftFixIt:
      case file_types::TY_ModuleSemanticInfo:
      case file_types::TY_CachedDiagnostics:
      case file_types::TY_SymbolGraphFile:
        // We could in theory handle assembly or LLVM input, but let's not.
        // FIXME: What about LTO?
        Diags.diagnose(SourceLoc(), diag::error_unexpected_input_file,
                       InputArg->getValue());
        continue;
      case file_types::TY_RawSIB:
      case file_types::TY_RawSIL:
      case file_types::TY_Nothing:
      case file_types::TY_IndexUnitOutputPath:
      case file_types::TY_INVALID:
        llvm_unreachable("these types should never be inferred");
      }
    }
    break;
  }
  case OutputInfo::Mode::SingleCompile: {
    if (Inputs.empty()) break;
    if (Args.hasArg(options::OPT_emit_pcm) && Inputs.size() != 1) {
      // -emit-pcm mode requires exactly one input (the module map).
      Diags.diagnose(SourceLoc(), diag::error_mode_requires_one_input_file);
      return;
    }
    if (Args.hasArg(options::OPT_embed_bitcode)) {
      // Make sure we can handle the inputs.
      bool HandledHere = true;
      for (const InputPair &Input : Inputs) {
        file_types::ID InputType = Input.first;
        if (!file_types::isPartOfSwiftCompilation(InputType)) {
          HandledHere = false;
          break;
        }
      }
      if (HandledHere) {
        // Create a single CompileJobAction and a single BackendJobAction.
        JobAction *CA =
            C.createAction<CompileJobAction>(file_types::TY_LLVM_BC);
        AllModuleInputs.addInput(CA);

        int InputIndex = 0;
        for (const InputPair &Input : Inputs) {
          file_types::ID InputType = Input.first;
          const Arg *InputArg = Input.second;

          CA->addInput(C.createAction<InputAction>(*InputArg, InputType));
          if (OI.isMultiThreading()) {
            // With multi-threading we need a backend job for each output file
            // of the compilation.
            auto *BJA = C.createAction<BackendJobAction>(CA,
                                                         OI.CompilerOutputType,
                                                         InputIndex);
            AllLinkerInputs.push_back(BJA);
          }
          ++InputIndex;
        }
        if (!OI.isMultiThreading()) {
          // No multi-threading: the compilation only produces a single output
          // file.
          CA = C.createAction<BackendJobAction>(CA, OI.CompilerOutputType, 0);
          AllLinkerInputs.push_back(CA);
        }
        break;
      }
    }

    // Create a single CompileJobAction for all of the driver's inputs.
    auto *CA = C.createAction<CompileJobAction>(OI.CompilerOutputType);
    for (const InputPair &Input : Inputs) {
      file_types::ID InputType = Input.first;
      const Arg *InputArg = Input.second;

      CA->addInput(C.createAction<InputAction>(*InputArg, InputType));
    }
    AllModuleInputs.addInput(CA);
    AllLinkerInputs.push_back(CA);
    break;
  }
  case OutputInfo::Mode::Immediate: {
    if (Inputs.empty())
      return;

    assert(OI.CompilerOutputType == file_types::TY_Nothing);
    auto *CA = C.createAction<InterpretJobAction>();
    for (const InputPair &Input : Inputs) {
      file_types::ID InputType = Input.first;
      const Arg *InputArg = Input.second;

      CA->addInput(C.createAction<InputAction>(*InputArg, InputType));
    }
    TopLevelActions.push_back(CA);
    return;
  }
  case OutputInfo::Mode::REPL: {
    if (!Inputs.empty()) {
      // REPL mode requires no inputs.
      Diags.diagnose(SourceLoc(), diag::error_repl_requires_no_input_files);
      return;
    }

    REPLJobAction::Mode Mode = REPLJobAction::Mode::PreferLLDB;
    if (const Arg *A = Args.getLastArg(options::OPT_lldb_repl,
                                       options::OPT_deprecated_integrated_repl)) {
      if (A->getOption().matches(options::OPT_lldb_repl))
        Mode = REPLJobAction::Mode::RequireLLDB;
      else
        Mode = REPLJobAction::Mode::Integrated;
    }

    TopLevelActions.push_back(C.createAction<REPLJobAction>(Mode));
    return;
  }
  }

  JobAction *MergeModuleAction = nullptr;
  if (OI.ShouldGenerateModule &&
      OI.CompilerMode != OutputInfo::Mode::SingleCompile &&
      !AllModuleInputs.empty()) {
    // We're performing multiple compilations; set up a merge module step
    // so we generate a single swiftmodule as output.
    MergeModuleAction = std::move(AllModuleInputs).intoAction(C);
  }

  bool shouldPerformLTO = OI.LTOVariant != OutputInfo::LTOKind::None;
  if (OI.shouldLink() && !AllLinkerInputs.empty()) {
    JobAction *LinkAction = nullptr;

    if (OI.LinkAction == LinkKind::StaticLibrary) {
      LinkAction =
          C.createAction<StaticLinkJobAction>(AllLinkerInputs, OI.LinkAction);
    } else {
      LinkAction = C.createAction<DynamicLinkJobAction>(
          AllLinkerInputs, OI.LinkAction, shouldPerformLTO);
    }

    // On ELF platforms there's no built in autolinking mechanism, so we
    // pull the info we need from the .o files directly and pass them as an
    // argument input file to the linker.
    const auto &Triple = TC.getTriple();
    SmallVector<const Action *, 2> AutolinkExtractInputs;
    for (const Action *A : AllLinkerInputs)
      if (A->getType() == file_types::TY_Object) {
        // Shared objects on ELF platforms don't have a swift1_autolink_entries
        // section in them because the section in the .o files is marked as
        // SHF_EXCLUDE.
        if (auto *IA = dyn_cast<InputAction>(A)) {
          StringRef ObjectName = IA->getInputArg().getValue();
          if (Triple.getObjectFormat() == llvm::Triple::ELF &&
              ObjectName.ends_with(".so"))
            continue;
        }
        AutolinkExtractInputs.push_back(A);
      }
    const bool AutolinkExtractRequired =
        ((Triple.getObjectFormat() == llvm::Triple::ELF && !Triple.isPS4()) ||
         Triple.getObjectFormat() == llvm::Triple::Wasm ||
         Triple.isOSCygMing()) &&
        !shouldPerformLTO;
    if (!AutolinkExtractInputs.empty() && AutolinkExtractRequired) {
      auto *AutolinkExtractAction =
          C.createAction<AutolinkExtractJobAction>(AutolinkExtractInputs);
      // Takes the same inputs as the linker...
      // ...and gives its output to the linker.
      LinkAction->addInput(AutolinkExtractAction);
    }

    if (MergeModuleAction) {
      if (OI.DebugInfoLevel == IRGenDebugInfoLevel::Normal) {
        const bool ModuleWrapRequired =
            Triple.getObjectFormat() != llvm::Triple::MachO;
        if (ModuleWrapRequired) {
          auto *ModuleWrapAction =
              C.createAction<ModuleWrapJobAction>(MergeModuleAction);
          LinkAction->addInput(ModuleWrapAction);
        } else {
          LinkAction->addInput(MergeModuleAction);
        }
        // FIXME: Adding the MergeModuleAction as top-level regardless would
        // allow us to get rid of the special case flag for that.
      } else {
        TopLevelActions.push_back(MergeModuleAction);
      }
    }
    TopLevelActions.push_back(LinkAction);

    if (TC.getTriple().isOSDarwin()) {
      switch (OI.LinkAction) {
      case LinkKind::Executable:
      case LinkKind::DynamicLibrary:
        if (OI.DebugInfoLevel > IRGenDebugInfoLevel::None) {
          auto *dSYMAction = C.createAction<GenerateDSYMJobAction>(LinkAction);
          TopLevelActions.push_back(dSYMAction);
          if (Args.hasArg(options::OPT_verify_debug_info)) {
            TopLevelActions.push_back(
                C.createAction<VerifyDebugInfoJobAction>(dSYMAction));
          }
        }
        break;

      case LinkKind::None:
        LLVM_FALLTHROUGH;
      case LinkKind::StaticLibrary:
        // Cannot build the DSYM bundle for non-image targets.
        break;
      }
    }
  } else {
    // We can't rely on the merge module action being the only top-level
    // action that needs to run. There may be other actions (e.g.
    // BackendJobActions) that are not merge-module inputs but should be run
    // anyway.
    if (MergeModuleAction)
      TopLevelActions.push_back(MergeModuleAction);
    TopLevelActions.append(AllLinkerInputs.begin(), AllLinkerInputs.end());
  }

#ifdef NDEBUG
  bool verifyInterfacesByDefault = false;
#else
  bool verifyInterfacesByDefault = true;
#endif

  if (MergeModuleAction
      && Args.hasArg(options::OPT_enable_library_evolution)
      && Args.hasFlag(options::OPT_verify_emitted_module_interface,
                      options::OPT_no_verify_emitted_module_interface,
                      verifyInterfacesByDefault)) {
    if (Args.hasArgNoClaim(options::OPT_emit_module_interface,
                           options::OPT_emit_module_interface_path)) {
      TopLevelActions.push_back(
          C.createAction<VerifyModuleInterfaceJobAction>(MergeModuleAction,
              file_types::TY_SwiftModuleInterfaceFile));
    }

    if (Args.hasArgNoClaim(options::OPT_emit_private_module_interface_path)) {
      TopLevelActions.push_back(
          C.createAction<VerifyModuleInterfaceJobAction>(MergeModuleAction,
              file_types::TY_PrivateSwiftModuleInterfaceFile));
    }
    if (Args.hasArg(options::OPT_package_name) &&
        Args.hasArgNoClaim(options::OPT_emit_package_module_interface_path)) {
      TopLevelActions.push_back(
          C.createAction<VerifyModuleInterfaceJobAction>(MergeModuleAction,
              file_types::TY_PackageSwiftModuleInterfaceFile));
    }
  }
}

bool Driver::handleImmediateArgs(const ArgList &Args, const ToolChain &TC) {
  if (Args.hasArg(options::OPT_help)) {
    printHelp(false);
    return false;
  }

  if (Args.hasArg(options::OPT_help_hidden)) {
    printHelp(true);
    return false;
  }

  if (Args.hasArg(options::OPT_compiler_assertions)) {
    CONDITIONAL_ASSERT_Global_enable_flag = 1;
  }

  if (Args.hasArg(options::OPT_version)) {
    // Follow gcc/clang behavior and use stdout for --version and stderr for -v.
    printVersion(TC, llvm::outs());
    return false;
  }

  if (Args.hasArg(options::OPT_v)) {
    printVersion(TC, llvm::errs());
    SuppressNoInputFilesError = true;
  }

  if (const Arg *A = Args.getLastArg(options::OPT_driver_use_frontend_path)) {
    DriverExecutable = A->getValue();
    std::string commandString =
        Args.getLastArgValue(options::OPT_driver_use_frontend_path).str();
    SmallVector<StringRef, 10> commandArgs;
    StringRef(commandString).split(commandArgs, ';', -1, false);
    DriverExecutable = commandArgs[0].str();
    DriverExecutableArgs.assign(std::begin(commandArgs) + 1,
                                std::end(commandArgs));
  }

  if (Args.hasArg(options::OPT_print_target_info)) {
    SmallVector<const char *, 5> commandLine;
    commandLine.push_back("-frontend");
    commandLine.push_back("-print-target-info");
    if (const Arg *targetArg = Args.getLastArg(options::OPT_target)) {
      commandLine.push_back("-target");
      commandLine.push_back(targetArg->getValue());
    }
    if (const Arg *targetVariantArg =
            Args.getLastArg(options::OPT_target_variant)) {
      commandLine.push_back("-target-variant");
      commandLine.push_back(targetVariantArg->getValue());
    }
    if (const Arg *sdkArg = Args.getLastArg(options::OPT_sdk)) {
      commandLine.push_back("-sdk");
      commandLine.push_back(sdkArg->getValue());
    }

    if (const Arg *resourceDirArg = Args.getLastArg(options::OPT_resource_dir)) {
      commandLine.push_back("-resource-dir");
      commandLine.push_back(resourceDirArg->getValue());
    }

    if (Args.hasFlag(options::OPT_static_executable,
                     options::OPT_no_static_executable, false) ||
        Args.hasFlag(options::OPT_static_stdlib, options::OPT_no_static_stdlib,
                     false)) {
      commandLine.push_back("-use-static-resource-dir");
    }

    std::string executable = getSwiftProgramPath();

    // FIXME(https://github.com/apple/swift/issues/54554): This bypasses mechanisms like -v and -###.
    sys::TaskQueue queue;
    queue.addTask(executable.c_str(), commandLine);
    queue.execute(nullptr,
                  [](sys::ProcessId PID, int returnCode,
                      StringRef output, StringRef errors,
                      sys::TaskProcessInformation ProcInfo,
                      void *unused) -> sys::TaskFinishedResponse {
      llvm::outs() << output;
      llvm::errs() << errors;
      return sys::TaskFinishedResponse::ContinueExecution;
    });
    return false;
  }

  if (Args.hasArg(options::OPT_print_supported_features)) {
    SmallVector<const char *, 5> commandLine;
    commandLine.push_back("-frontend");
    commandLine.push_back("-print-supported-features");

    std::string executable = getSwiftProgramPath();

    // FIXME(https://github.com/apple/swift/issues/54554): This bypasses
    // mechanisms like -v and -###.
    sys::TaskQueue queue;
    queue.addTask(executable.c_str(), commandLine);
    queue.execute(nullptr,
                  [](sys::ProcessId PID, int returnCode, StringRef output,
                     StringRef errors, sys::TaskProcessInformation ProcInfo,
                     void *unused) -> sys::TaskFinishedResponse {
                    llvm::outs() << output;
                    llvm::errs() << errors;
                    return sys::TaskFinishedResponse::ContinueExecution;
                  });
    return false;
  }

  return true;
}

std::optional<OutputFileMap>
Driver::buildOutputFileMap(const llvm::opt::DerivedArgList &Args,
                           StringRef workingDirectory) const {
  const Arg *A = Args.getLastArg(options::OPT_output_file_map);
  if (!A)
    return std::nullopt;

  // TODO: perform some preflight checks to ensure the file exists.
  llvm::Expected<OutputFileMap> OFM = OutputFileMap::loadFromPath(
      A->getValue(), workingDirectory);
  if (auto Err = OFM.takeError()) {
    Diags.diagnose(SourceLoc(), diag::error_unable_to_load_output_file_map,
                   llvm::toString(std::move(Err)), A->getValue());
    return std::nullopt;
  }
  return *OFM;
}

void Driver::buildJobs(ArrayRef<const Action *> TopLevelActions,
                       const OutputInfo &OI, const OutputFileMap *OFM,
                       StringRef workingDirectory, const ToolChain &TC,
                       Compilation &C) const {
  llvm::PrettyStackTraceString CrashInfo("Building compilation jobs");

  const DerivedArgList &Args = C.getArgs();
  JobCacheMap JobCache;

  if (Args.hasArg(options::OPT_o) && !OI.shouldLink() &&
      !OI.ShouldTreatModuleAsTopLevelOutput) {
    bool ShouldComplain;
    if (OI.isMultiThreading()) {
      // Multi-threading compilation has multiple outputs unless there's only
      // one input.
      ShouldComplain = C.getInputFiles().size() > 1;
    } else {
      // Single-threaded compilation is a problem if we're compiling more than
      // one file.
      ShouldComplain = 1 < llvm::count_if(C.getActions(), [](const Action *A) {
        return isa<CompileJobAction>(A);
      });
    }

    if (ShouldComplain) {
      Diags.diagnose(SourceLoc(),
                     diag::error_cannot_specify__o_for_multiple_outputs);
    }
  }

  for (const Action *A : TopLevelActions) {
    if (auto *JA = dyn_cast<JobAction>(A)) {
      (void)buildJobsForAction(C, JA, OFM, workingDirectory, /*TopLevel=*/true,
                               JobCache);
    }
  }
}

/// Form a filename based on \p base in \p result, optionally setting its
/// extension to \p newExt and in \p workingDirectory.
static void formFilenameFromBaseAndExt(StringRef base, StringRef newExt,
                                       StringRef workingDirectory,
                                       SmallVectorImpl<char> &result) {
  if (workingDirectory.empty() || llvm::sys::path::is_absolute(base)) {
    result.assign(base.begin(), base.end());
  } else {
    assert(!base.empty() && base != "-" && "unexpected basename");
    result.assign(workingDirectory.begin(), workingDirectory.end());
    llvm::sys::path::append(result, base);
  }

  if (!newExt.empty()) {
    llvm::sys::path::replace_extension(result, newExt);
  }
}

static std::optional<StringRef> getOutputFilenameFromPathArgOrAsTopLevel(
    const OutputInfo &OI, const llvm::opt::DerivedArgList &Args,
    llvm::opt::OptSpecifier PathArg, file_types::ID ExpectedOutputType,
    bool TreatAsTopLevelOutput, StringRef workingDirectory,
    llvm::SmallString<128> &Buffer) {
  if (const Arg *A = Args.getLastArg(PathArg))
    return StringRef(A->getValue());

  if (TreatAsTopLevelOutput) {
    if (const Arg *A = Args.getLastArg(options::OPT_o)) {
      if (OI.CompilerOutputType == ExpectedOutputType)
        return StringRef(A->getValue());

      // Otherwise, put the file next to the top-level output.
      Buffer = A->getValue();
      llvm::sys::path::remove_filename(Buffer);
      llvm::sys::path::append(Buffer, OI.ModuleName);
      llvm::sys::path::replace_extension(
          Buffer, file_types::getExtension(ExpectedOutputType));
      return Buffer.str();
    }

    // A top-level output wasn't specified, so just output to
    // <ModuleName>.<ext>.
    formFilenameFromBaseAndExt(OI.ModuleName,
                               file_types::getExtension(ExpectedOutputType),
                               workingDirectory,
                               Buffer);
    return Buffer.str();
  }

  return std::nullopt;
}

static StringRef assignOutputName(Compilation &C, const JobAction *JA,
                                  llvm::SmallString<128> &Buffer,
                                  StringRef BaseName,
                                  PreserveOnSignal ShouldPreserveOnSignal) {
  // We should output to a temporary file, since we're not at the top level
  // (or are generating a bridging PCH, which is currently always a temp).
  StringRef Stem = llvm::sys::path::stem(BaseName);
  StringRef Suffix = file_types::getExtension(JA->getType());
  std::error_code EC = llvm::sys::fs::createTemporaryFile(Stem, Suffix, Buffer);
  if (EC) {
    C.getDiags().diagnose(SourceLoc(),
                          diag::error_unable_to_make_temporary_file,
                          EC.message());
    return {};
  }
  C.addTemporaryFile(Buffer.str(), ShouldPreserveOnSignal);

  return Buffer.str();
}

static StringRef baseNameForImage(const JobAction *JA, const OutputInfo &OI,
                                  const llvm::Triple &Triple,
                                  llvm::SmallString<128> &Buffer,
                                  StringRef BaseInput, StringRef BaseName) {
  if (JA->size() == 1 && OI.ModuleNameIsFallback && BaseInput != "-")
    return llvm::sys::path::stem(BaseInput);
  
  if (isa<StaticLinkJobAction>(JA)) {
    Buffer = "lib";
    Buffer.append(BaseName);
    Buffer.append(Triple.isOSWindows() ? ".lib" : ".a");
    return Buffer.str();
  }
  
  auto link = dyn_cast<DynamicLinkJobAction>(JA);
  if (!link)
    return BaseName;
  if (link->getKind() != LinkKind::DynamicLibrary)
    return BaseName;

  Buffer = Triple.isOSWindows() ? "" : "lib";
  Buffer.append(BaseName);

  if (Triple.isOSDarwin())
    Buffer.append(".dylib");
  else if (Triple.isOSWindows())
    Buffer.append(".dll");
  else
    Buffer.append(".so");
  return Buffer.str();
}

static StringRef getIndexUnitOutputFilename(Compilation &C,
                                            const JobAction *JA,
                                            const TypeToPathMap *OutputMap,
                                            bool AtTopLevel) {
  if (JA->getType() == file_types::TY_Nothing)
    return {};

  if (OutputMap) {
    auto iter = OutputMap->find(file_types::TY_IndexUnitOutputPath);
    if (iter != OutputMap->end())
      return iter->second;
  }

  if (AtTopLevel) {
    const llvm::opt::DerivedArgList &Args = C.getArgs();
    if (Arg *FinalOutput = Args.getLastArg(options::OPT_index_unit_output_path))
      return FinalOutput->getValue();
  }

  return {};
}

static StringRef getOutputFilename(Compilation &C,
                                   const JobAction *JA,
                                   const TypeToPathMap *OutputMap,
                                   StringRef workingDirectory,
                                   bool AtTopLevel,
                                   StringRef BaseInput,
                                   StringRef PrimaryInput,
                                   llvm::SmallString<128> &Buffer) {
  if (JA->getType() == file_types::TY_Nothing)
    return {};

  // If available, check the OutputMap first.
  if (OutputMap) {
    auto iter = OutputMap->find(JA->getType());
    if (iter != OutputMap->end())
      return iter->second;
  }

  // Process Action-specific output-specifying options next,
  // since we didn't find anything applicable in the OutputMap.
  const OutputInfo &OI = C.getOutputInfo();
  const llvm::opt::DerivedArgList &Args = C.getArgs();

  if (isa<MergeModuleJobAction>(JA)) {
    auto optFilename = getOutputFilenameFromPathArgOrAsTopLevel(
        OI, Args, options::OPT_emit_module_path, file_types::TY_SwiftModuleFile,
        OI.ShouldTreatModuleAsTopLevelOutput, workingDirectory, Buffer);
    if (optFilename)
      return *optFilename;
  }

  // dSYM actions are never treated as top-level.
  if (isa<GenerateDSYMJobAction>(JA)) {
    Buffer = PrimaryInput;
    Buffer.push_back('.');
    Buffer.append(file_types::getExtension(JA->getType()));
    return Buffer.str();
  }

  auto ShouldPreserveOnSignal = PreserveOnSignal::No;

  if (auto *PCHAct = dyn_cast<GeneratePCHJobAction>(JA)) {
    // For a persistent PCH we don't use an output, the frontend determines
    // the filename to use for the PCH.
    assert(!PCHAct->isPersistentPCH());
    ShouldPreserveOnSignal = PreserveOnSignal::Yes;
  }

  // We don't have an output from an Action-specific command line option,
  // so figure one out using the defaults.
  if (AtTopLevel) {
    if (Arg *FinalOutput = Args.getLastArg(options::OPT_o))
      return FinalOutput->getValue();
    if (file_types::isTextual(JA->getType()))
      return "-";
  }

  assert(!BaseInput.empty() &&
         "A Job which produces output must have a BaseInput!");
  StringRef BaseName(BaseInput);
  if (isa<MergeModuleJobAction>(JA) ||
      (OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
       !OI.isMultiThreading()) ||
      JA->getType() == file_types::TY_Image)
    BaseName = OI.ModuleName;

  // We don't yet have a name, assign one.
  if (!AtTopLevel)
    return assignOutputName(C, JA, Buffer, BaseName, ShouldPreserveOnSignal);

  if (JA->getType() == file_types::TY_Image) {
    const llvm::Triple &Triple = C.getToolChain().getTriple();
    SmallString<16> Base =
        baseNameForImage(JA, OI, Triple, Buffer, BaseInput, BaseName);
    formFilenameFromBaseAndExt(Base, /*newExt=*/"", workingDirectory, Buffer);
    return Buffer.str();
  }

  StringRef Suffix = file_types::getExtension(JA->getType());
  assert(Suffix.data() &&
         "All types used for output should have a suffix.");

  formFilenameFromBaseAndExt(llvm::sys::path::filename(BaseName), Suffix,
                             workingDirectory, Buffer);
  return Buffer.str();
}

static bool hasExistingAdditionalOutput(CommandOutput &output,
                                        file_types::ID outputType,
                                        StringRef outputPath = StringRef()) {

  auto existing = output.getAdditionalOutputForType(outputType);
  if (!existing.empty()) {
    assert(outputPath.empty() || outputPath == existing);
    return true;
  }
  return false;
}

static llvm::SmallString<128> computeAuxiliaryOutputPath(
    Compilation &C, CommandOutput &output, file_types::ID outputType,
    const TypeToPathMap *outputMap, StringRef workingDirectory,
    StringRef outputPath = StringRef(),
    llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) {

  if (hasExistingAdditionalOutput(output, outputType, outputPath))
    return {};

  if (outputMap) {
    auto iter = outputMap->find(outputType);
    if (iter != outputMap->end()) {
      StringRef outputMapPath = iter->second;
      // Prefer a path from the OutputMap.
      if (!outputMapPath.empty())
        return outputMapPath;
    }
  }
  if (!outputPath.empty())
    return outputPath;

  if (requireArg.isValid() && !C.getArgs().getLastArg(requireArg)) {
    // This auxiliary output only exists if requireArg is passed, but it
    // wasn't this time.
    return {};
  }

  // Put the auxiliary output file next to "the" primary output file.
  //
  // FIXME: when we're in WMO and have multiple primary outputs, we derive the
  // additional filename here from the _first_ primary output name, which
  // means that in the derived OFM (in Job.cpp) the additional output will
  // have a possibly-surprising name. But that's only half the problem: it
  // also get associated with the first primary _input_, even when there are
  // multiple primary inputs; really it should be associated with the build as
  // a whole -- derived OFM input "" -- but that's a more general thing to
  // fix.
  llvm::SmallString<128> path;
  if (output.getPrimaryOutputType() != file_types::TY_Nothing)
    path = output.getPrimaryOutputFilenames()[0];
  else if (!output.getBaseInput(0).empty())
    path = llvm::sys::path::filename(output.getBaseInput(0));
  else {
    formFilenameFromBaseAndExt(C.getOutputInfo().ModuleName, /*newExt=*/"",
                               workingDirectory, path);
  }
  assert(!path.empty());

  const bool isTempFile = C.isTemporaryFile(path);
  llvm::sys::path::replace_extension(path,
                                     file_types::getExtension(outputType));
  if (isTempFile)
    C.addTemporaryFile(path);
  return path;
}

static void addAuxiliaryOutput(
    Compilation &C, CommandOutput &output, file_types::ID outputType,
    const TypeToPathMap *outputMap, StringRef workingDirectory,
    StringRef outputPath = StringRef(),
    llvm::opt::OptSpecifier requireArg = llvm::opt::OptSpecifier()) {

  const auto path =
      computeAuxiliaryOutputPath(C, output, outputType, outputMap,
                                 workingDirectory, outputPath, requireArg);
  if (path.empty())
    return;
  output.setAdditionalOutputForType(outputType, path);
}

static void addDiagFileOutputForPersistentPCHAction(
    Compilation &C, const GeneratePCHJobAction *JA, CommandOutput &output,
    const TypeToPathMap *outputMap, StringRef workingDirectory) {
  assert(JA->isPersistentPCH());

  // For a persistent PCH we don't use an output, the frontend determines
  // the filename to use for the PCH. For the diagnostics file, try to
  // determine an invocation-specific path inside the directory where the
  // PCH is going to be written, and fallback to a temporary file if we
  // cannot determine such a path.

  StringRef pchOutDir = JA->getPersistentPCHDir();
  StringRef headerPath = output.getBaseInput(JA->getInputIndex());
  StringRef stem = llvm::sys::path::stem(headerPath);
  StringRef suffix =
      file_types::getExtension(file_types::TY_SerializedDiagnostics);
  SmallString<256> outPathBuf;

  if (const Arg *A = C.getArgs().getLastArg(options::OPT_emit_module_path)) {
    // The module file path is unique for a specific module and architecture
    // (it won't be concurrently written to) so we can use the path as hash
    // for determining the filename to use for the diagnostic file.
    StringRef ModuleOutPath = A->getValue();
    outPathBuf = pchOutDir;
    llvm::sys::path::append(outPathBuf, stem);
    outPathBuf += '-';
    auto code = llvm::hash_value(ModuleOutPath);
    llvm::APInt(64, code).toString(outPathBuf, 36, /*Signed=*/false);
    llvm::sys::path::replace_extension(outPathBuf, suffix);
  }

  if (outPathBuf.empty()) {
    // Fallback to creating a temporary file.
    std::error_code EC =
    llvm::sys::fs::createTemporaryFile(stem, suffix, outPathBuf);
    if (EC) {
      C.getDiags().diagnose(SourceLoc(),
                            diag::error_unable_to_make_temporary_file,
                            EC.message());
      return;
    }
    C.addTemporaryFile(outPathBuf.str());
  }

  if (!outPathBuf.empty()) {
    addAuxiliaryOutput(C, output, file_types::TY_SerializedDiagnostics,
                       outputMap, workingDirectory, outPathBuf.str());
  }
}

Job *Driver::buildJobsForAction(Compilation &C, const JobAction *JA,
                                const OutputFileMap *OFM,
                                StringRef workingDirectory,
                                bool AtTopLevel, JobCacheMap &JobCache) const {

  PrettyStackTraceDriverAction CrashInfo("building jobs", JA);

  // 1. See if we've already got this cached.
  const ToolChain &TC = C.getToolChain();
  std::pair<const Action *, const ToolChain *> Key(JA, &TC);
  {
    auto CacheIter = JobCache.find(Key);
    if (CacheIter != JobCache.end()) {
      return CacheIter->second;
    }
  }

  // 2. Build up the list of input jobs.
  SmallVector<const Action *, 4> InputActions;
  SmallVector<const Job *, 4> InputJobs;
  for (const Action *Input : *JA) {
    if (auto *InputJobAction = dyn_cast<JobAction>(Input)) {
      InputJobs.push_back(buildJobsForAction(
          C, InputJobAction, OFM, workingDirectory, false, JobCache));
    } else {
      InputActions.push_back(Input);
    }
  }

  // 3. Determine the CommandOutput for the job.
  StringRef BaseInput;
  StringRef PrimaryInput;
  if (!InputActions.empty()) {
    // Use the first InputAction as our BaseInput and PrimaryInput.
    const InputAction *IA = cast<InputAction>(InputActions[0]);
    BaseInput = IA->getInputArg().getValue();
    PrimaryInput = BaseInput;
  } else if (!InputJobs.empty()) {
    const CommandOutput &Out = InputJobs.front()->getOutput();
    size_t i = JA->getInputIndex();
    // Use the first Job's BaseInput as our BaseInput.
    BaseInput = Out.getBaseInput(i);
    // Use the first Job's Primary Output as our PrimaryInput.
    PrimaryInput = Out.getPrimaryOutputFilenames()[i];
  }

  // With -index-file option, the primary input is the one passed with
  // -index-file-path.
  // FIXME: Figure out how this better fits within the driver infrastructure.
  if (JA->getType() == file_types::TY_IndexData) {
    if (Arg *A = C.getArgs().getLastArg(options::OPT_index_file_path)) {
      BaseInput = A->getValue();
      PrimaryInput = A->getValue();
    }
  }

  const OutputInfo &OI = C.getOutputInfo();
  const TypeToPathMap *OutputMap = nullptr;
  if (OFM) {
    if (isa<CompileJobAction>(JA)) {
      if (OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
        OutputMap = OFM->getOutputMapForSingleOutput();
      } else {
        OutputMap = OFM->getOutputMapForInput(BaseInput);
      }
    } else if (isa<BackendJobAction>(JA)) {
      OutputMap = OFM->getOutputMapForInput(BaseInput);
    }
  }

  std::unique_ptr<CommandOutput> Output(
      new CommandOutput(JA->getType(), C.getDerivedOutputFileMap()));

  PrettyStackTraceDriverCommandOutput CrashInfo2("determining output",
                                                 Output.get());
  llvm::SmallString<128> Buf;
  computeMainOutput(C, JA, OFM, AtTopLevel, InputActions, InputJobs, OutputMap,
                    workingDirectory, BaseInput, PrimaryInput, Buf,
                    Output.get());

  if (OI.ShouldGenerateModule && isa<CompileJobAction>(JA))
    chooseSwiftModuleOutputPath(C, OutputMap, workingDirectory, Output.get());

  if (OI.ShouldGenerateModule &&
      (isa<CompileJobAction>(JA) || isa<MergeModuleJobAction>(JA))) {
    chooseSwiftModuleDocOutputPath(C, OutputMap, workingDirectory, Output.get());
    if (!C.getArgs().hasArg(options::OPT_avoid_emit_module_source_info)) {
      chooseSwiftSourceInfoOutputPath(C, OutputMap, workingDirectory,
                                      Output.get());
    }
  }

  if (C.getArgs().hasArg(options::OPT_emit_module_interface,
                         options::OPT_emit_module_interface_path))
    chooseModuleInterfacePath(C, JA, workingDirectory, Buf,
      file_types::TY_SwiftModuleInterfaceFile, Output.get());

  if (C.getArgs().hasArg(options::OPT_emit_private_module_interface_path))
    chooseModuleInterfacePath(C, JA, workingDirectory, Buf,
      file_types::TY_PrivateSwiftModuleInterfaceFile, Output.get());

  if (C.getArgs().hasArg(options::OPT_package_name) &&
      C.getArgs().hasArg(options::OPT_emit_package_module_interface_path))
    chooseModuleInterfacePath(C, JA, workingDirectory, Buf,
      file_types::TY_PackageSwiftModuleInterfaceFile, Output.get());

  if (C.getArgs().hasArg(options::OPT_update_code) && isa<CompileJobAction>(JA))
    chooseRemappingOutputPath(C, OutputMap, Output.get());

  if (isa<CompileJobAction>(JA) || isa<GeneratePCHJobAction>(JA)) {
    // Choose the serialized diagnostics output path.
    if (C.getArgs().hasArg(options::OPT_serialize_diagnostics))
      chooseSerializedDiagnosticsPath(C, JA, OutputMap, workingDirectory,
                                      Output.get());
  }

  if (isa<CompileJobAction>(JA)) {
    chooseModuleSummaryPath(C, OutputMap, workingDirectory, Buf, Output.get());
  }

  if (isa<MergeModuleJobAction>(JA) ||
      (isa<CompileJobAction>(JA) &&
       OI.CompilerMode == OutputInfo::Mode::SingleCompile)) {
    // An emit-tbd argument gets passed down to a job that sees the whole
    // module, either the -merge-modules job or a -wmo compiler invocation.
    chooseTBDPath(C, OutputMap, workingDirectory, Buf, Output.get());
  }

  if (isa<CompileJobAction>(JA))
    chooseDependenciesOutputPaths(C, OutputMap, workingDirectory, Buf,
                                  Output.get());

  if (C.getArgs().hasArg(options::OPT_save_optimization_record,
                         options::OPT_save_optimization_record_EQ,
                         options::OPT_save_optimization_record_path))
    chooseOptimizationRecordPath(C, workingDirectory, Buf, Output.get());

  if ((isa<MergeModuleJobAction>(JA) ||
       (isa<CompileJobAction>(JA) &&
        OI.CompilerMode == OutputInfo::Mode::SingleCompile)) &&
      C.getArgs().hasArg(options::OPT_emit_objc_header,
                         options::OPT_emit_objc_header_path))
    chooseObjectiveCHeaderOutputPath(C, OutputMap, workingDirectory,
                                     Output.get());

  // 4. Construct a Job which produces the right CommandOutput.
  std::unique_ptr<Job> ownedJob = TC.constructJob(*JA, C, std::move(InputJobs),
                                                  InputActions, 
                                                  std::move(Output), OI);
  Job *J = C.addJob(std::move(ownedJob));

  // 5. Add it to the JobCache, so we don't construct the same Job multiple
  // times.
  JobCache[Key] = J;

  if (DriverPrintBindings) {
    llvm::outs() << "# \"" << TC.getTriple().str()
      << "\" - \"" << llvm::sys::path::filename(J->getExecutable())
      << "\", inputs: [";

    llvm::interleave(
        InputActions.begin(), InputActions.end(),
        [](const Action *A) {
          auto Input = cast<InputAction>(A);
          llvm::outs() << '"' << Input->getInputArg().getValue() << '"';
        },
        [] { llvm::outs() << ", "; });
    if (!InputActions.empty() && !J->getInputs().empty())
      llvm::outs() << ", ";
    llvm::interleave(
        J->getInputs().begin(), J->getInputs().end(),
        [](const Job *Input) {
          auto FileNames = Input->getOutput().getPrimaryOutputFilenames();
          interleave(
              FileNames.begin(), FileNames.end(),
              [](StringRef FileName) {
                llvm::outs() << '"' << FileName << '"';
              },
              [] { llvm::outs() << ", "; });
        },
        [] { llvm::outs() << ", "; });

    llvm::outs() << "], output: {";
    auto OutputFileNames = J->getOutput().getPrimaryOutputFilenames();
    StringRef TypeName =
        file_types::getTypeName(J->getOutput().getPrimaryOutputType());
    interleave(
        OutputFileNames.begin(), OutputFileNames.end(),
        [TypeName](StringRef FileName) {
          llvm::outs() << TypeName << ": \"" << FileName << '"';
        },
        [] { llvm::outs() << ", "; });

    file_types::forAllTypes([&J](file_types::ID Ty) {
      StringRef AdditionalOutput =
        J->getOutput().getAdditionalOutputForType(Ty);
      if (!AdditionalOutput.empty()) {
        llvm::outs() << ", " << file_types::getTypeName(Ty) << ": \""
                     << AdditionalOutput << '"';
      }
    });
    llvm::outs() << '}';

    switch (J->getCondition()) {
    case Job::Condition::Always:
      break;
    case Job::Condition::RunWithoutCascading:
      llvm::outs() << ", condition: run-without-cascading";
      break;
    case Job::Condition::CheckDependencies:
      llvm::outs() << ", condition: check-dependencies";
      break;
    case Job::Condition::NewlyAdded:
      llvm::outs() << ", condition: newly-added";
      break;
    }

    llvm::outs() << '\n';
  }

  return J;
}

void Driver::computeMainOutput(
    Compilation &C, const JobAction *JA, const OutputFileMap *OFM,
    bool AtTopLevel, SmallVectorImpl<const Action *> &InputActions,
    SmallVectorImpl<const Job *> &InputJobs, const TypeToPathMap *OutputMap,
    StringRef workingDirectory, StringRef BaseInput, StringRef PrimaryInput,
    llvm::SmallString<128> &Buf, CommandOutput *Output) const {
  StringRef OutputFile, IndexUnitOutputFile;
  if (C.getOutputInfo().isMultiThreading() && isa<CompileJobAction>(JA) &&
      file_types::isAfterLLVM(JA->getType())) {
    // Multi-threaded compilation: A single frontend command produces multiple
    // output file: one for each input files.
    auto OutputFunc = [&](StringRef Base, StringRef Primary) {
      const TypeToPathMap *OMForInput = nullptr;
      if (OFM)
        OMForInput = OFM->getOutputMapForInput(Base);

      OutputFile = getOutputFilename(C, JA, OMForInput, workingDirectory,
                                     AtTopLevel, Base, Primary, Buf);
      IndexUnitOutputFile = getIndexUnitOutputFilename(C, JA, OMForInput,
                                                       AtTopLevel);
      Output->addPrimaryOutput(CommandInputPair(Base, Primary),
                               OutputFile, IndexUnitOutputFile);
    };
    // Add an output file for each input action.
    for (const Action *A : InputActions) {
      const InputAction *IA = cast<InputAction>(A);
      StringRef IV = IA->getInputArg().getValue();
      OutputFunc(IV, IV);
    }
    // Add an output file for each primary output of each input job.
    for (const Job *IJ : InputJobs) {
      size_t i = 0;
      CommandOutput const &Out = IJ->getOutput();
      for (auto OutPrimary : Out.getPrimaryOutputFilenames()) {
        OutputFunc(Out.getBaseInput(i++), OutPrimary);
      }
    }
  } else {
    // The common case: there is a single output file.
    OutputFile = getOutputFilename(C, JA, OutputMap, workingDirectory,
                                   AtTopLevel, BaseInput, PrimaryInput, Buf);
    IndexUnitOutputFile = getIndexUnitOutputFilename(C, JA, OutputMap,
                                                     AtTopLevel);
    Output->addPrimaryOutput(CommandInputPair(BaseInput, PrimaryInput),
                             OutputFile, IndexUnitOutputFile);
  }
}

void Driver::chooseSwiftModuleOutputPath(Compilation &C,
                                         const TypeToPathMap *OutputMap,
                                         StringRef workingDirectory,
                                         CommandOutput *Output) const {

  if (hasExistingAdditionalOutput(*Output, file_types::TY_SwiftModuleFile))
    return;

  StringRef OFMModuleOutputPath;
  if (OutputMap) {
    auto iter = OutputMap->find(file_types::TY_SwiftModuleFile);
    if (iter != OutputMap->end())
      OFMModuleOutputPath = iter->second;
  }

  const OutputInfo &OI = C.getOutputInfo();
  const Arg *A = C.getArgs().getLastArg(options::OPT_emit_module_path);
  using file_types::TY_SwiftModuleFile;

  if (!OFMModuleOutputPath.empty()) {
    // Prefer a path from the OutputMap.
    Output->setAdditionalOutputForType(TY_SwiftModuleFile, OFMModuleOutputPath);
  } else if (A && OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
    // We're performing a single compilation (and thus no merge module step),
    // so prefer to use -emit-module-path, if present.
    Output->setAdditionalOutputForType(TY_SwiftModuleFile, A->getValue());
  } else if (Output->getPrimaryOutputType() == TY_SwiftModuleFile) {
    // If the primary type is already a module type, we're out of
    // options for overriding the primary name choice: stop now.
    assert(!Output->getPrimaryOutputFilename().empty());
    return;
  } else if (OI.CompilerMode == OutputInfo::Mode::SingleCompile &&
             OI.ShouldTreatModuleAsTopLevelOutput) {
    // We're performing a single compile and don't have -emit-module-path,
    // but have been told to treat the module as a top-level output.
    // Determine an appropriate path.
    llvm::SmallString<128> Path;
    if (const Arg *A = C.getArgs().getLastArg(options::OPT_o)) {
      // Put the module next to the top-level output.
      Path = A->getValue();
      llvm::sys::path::remove_filename(Path);
    } else {
      // A top-level output wasn't specified, so just output to
      // <ModuleName>.swiftmodule in the current directory.
    }
    llvm::sys::path::append(Path, OI.ModuleName);
    llvm::sys::path::replace_extension(
        Path, file_types::getExtension(TY_SwiftModuleFile));
    Output->setAdditionalOutputForType(TY_SwiftModuleFile, Path);
  } else if (Output->getPrimaryOutputType() != file_types::TY_Nothing) {
    // We're only generating the module as an intermediate, so put it next
    // to the primary output of the compile command.
    llvm::SmallString<128> Path(Output->getPrimaryOutputFilenames()[0]);
    assert(!Path.empty());
    bool isTempFile = C.isTemporaryFile(Path);
    llvm::sys::path::replace_extension(
        Path, file_types::getExtension(TY_SwiftModuleFile));
    Output->setAdditionalOutputForType(TY_SwiftModuleFile, Path);
    if (isTempFile)
      C.addTemporaryFile(Path);
  }
}

static void chooseModuleAuxiliaryOutputFilePath(
    Compilation &C, const TypeToPathMap *OutputMap, StringRef workingDirectory,
    CommandOutput *Output, file_types::ID fileID,
    bool shouldUseProjectFolder = false,
    std::optional<options::ID> optId = std::nullopt) {
  if (hasExistingAdditionalOutput(*Output, fileID))
    return;
  // Honor driver option for this path if it's given
  if (optId.has_value()) {
    if (const Arg *A = C.getArgs().getLastArg(*optId)) {
      Output->setAdditionalOutputForType(fileID, StringRef(A->getValue()));
      return;
    }
  }

  StringRef OFMOutputPath;
  if (OutputMap) {
    auto iter = OutputMap->find(fileID);
    if (iter != OutputMap->end())
      OFMOutputPath = iter->second;
  }
  if (!OFMOutputPath.empty()) {
    // Prefer a path from the OutputMap.
    Output->setAdditionalOutputForType(fileID, OFMOutputPath);
  } else if (Output->getPrimaryOutputType() != file_types::TY_Nothing) {
    auto ModulePath = Output->getAnyOutputForType(file_types::TY_SwiftModuleFile);
    bool isTempFile = C.isTemporaryFile(ModulePath);
    auto ModuleName = llvm::sys::path::filename(ModulePath);
    llvm::SmallString<128> Path(llvm::sys::path::parent_path(ModulePath));
    if (shouldUseProjectFolder) {
      llvm::sys::path::append(Path, "Project");
      // If the build system has created a Project dir for us to include the file, use it.
      if (!llvm::sys::fs::exists(Path)) {
        llvm::sys::path::remove_filename(Path);
      }
    }
    llvm::sys::path::append(Path, ModuleName);
    llvm::sys::path::replace_extension(Path, file_types::getExtension(fileID));
    Output->setAdditionalOutputForType(fileID, Path);
    if (isTempFile)
      C.addTemporaryFile(Path);
  }
}

void Driver::chooseSwiftSourceInfoOutputPath(Compilation &C,
                                             const TypeToPathMap *OutputMap,
                                             StringRef workingDirectory,
                                             CommandOutput *Output) const {
  chooseModuleAuxiliaryOutputFilePath(C, OutputMap, workingDirectory, Output,
                                      file_types::TY_SwiftSourceInfoFile,
                                      /*shouldUseProjectFolder*/true,
                                      options::OPT_emit_module_source_info_path);
}

void Driver::chooseSwiftModuleDocOutputPath(Compilation &C,
                                            const TypeToPathMap *OutputMap,
                                            StringRef workingDirectory,
                                            CommandOutput *Output) const {
  chooseModuleAuxiliaryOutputFilePath(C, OutputMap, workingDirectory, Output,
                                      file_types::TY_SwiftModuleDocFile);
}

void Driver::chooseRemappingOutputPath(Compilation &C,
                                       const TypeToPathMap *OutputMap,
                                       CommandOutput *Output) const {

  if (hasExistingAdditionalOutput(*Output, file_types::TY_Remapping))
    return;

  StringRef OFMFixitsOutputPath;
  if (OutputMap) {
    auto iter = OutputMap->find(file_types::TY_Remapping);
    if (iter != OutputMap->end())
      OFMFixitsOutputPath = iter->second;
  }
  if (!OFMFixitsOutputPath.empty()) {
    Output->setAdditionalOutputForType(file_types::ID::TY_Remapping,
                                       OFMFixitsOutputPath);
  } else {
    llvm::SmallString<128> Path(Output->getPrimaryOutputFilenames()[0]);
    bool isTempFile = C.isTemporaryFile(Path);
    llvm::sys::path::replace_extension(Path,
        file_types::getExtension(file_types::ID::TY_Remapping));
    Output->setAdditionalOutputForType(file_types::ID::TY_Remapping, Path);
    if (isTempFile)
      C.addTemporaryFile(Path);
  }
}

void Driver::chooseModuleInterfacePath(Compilation &C, const JobAction *JA,
                                       StringRef workingDirectory,
                                       llvm::SmallString<128> &buffer,
                                       file_types::ID fileType,
                                       CommandOutput *output) const {
  switch (C.getOutputInfo().CompilerMode) {
  case OutputInfo::Mode::StandardCompile:
    if (!isa<MergeModuleJobAction>(JA))
      return;
    break;
  case OutputInfo::Mode::SingleCompile:
    if (!isa<CompileJobAction>(JA))
      return;
    break;
  case OutputInfo::Mode::Immediate:
  case OutputInfo::Mode::REPL:
    llvm_unreachable("these modes aren't usable with 'swiftc'");
  }

  auto pathOpt = options::OPT_emit_module_interface_path;
  if (fileType == file_types::TY_PrivateSwiftModuleInterfaceFile)
    pathOpt = options::OPT_emit_private_module_interface_path;
  else if (fileType == file_types::TY_PackageSwiftModuleInterfaceFile)
    pathOpt = options::OPT_emit_package_module_interface_path;

  StringRef outputPath = *getOutputFilenameFromPathArgOrAsTopLevel(
      C.getOutputInfo(), C.getArgs(), pathOpt, fileType,
      /*TreatAsTopLevelOutput*/true, workingDirectory, buffer);
  output->setAdditionalOutputForType(fileType, outputPath);
}

void Driver::chooseModuleSummaryPath(Compilation &C,
                                     const TypeToPathMap *OutputMap,
                                     StringRef workingDirectory,
                                     llvm::SmallString<128> &Buf,
                                     CommandOutput *Output) const {
  StringRef pathFromArgs;
  if (const Arg *A =
          C.getArgs().getLastArg(options::OPT_emit_module_summary_path)) {
    pathFromArgs = A->getValue();
  }

  addAuxiliaryOutput(C, *Output, file_types::TY_SwiftModuleSummaryFile,
                     OutputMap, workingDirectory, pathFromArgs,
                     /*requireArg=*/options::OPT_emit_module_summary);
}

void Driver::chooseSerializedDiagnosticsPath(Compilation &C,
                                             const JobAction *JA,
                                             const TypeToPathMap *OutputMap,
                                             StringRef workingDirectory,
                                             CommandOutput *Output) const {
  if (C.getArgs().hasArg(options::OPT_serialize_diagnostics)) {
    auto pchJA = dyn_cast<GeneratePCHJobAction>(JA);
    if (pchJA && pchJA->isPersistentPCH()) {
      addDiagFileOutputForPersistentPCHAction(C, pchJA, *Output, OutputMap,
                                              workingDirectory);
    } else {
      addAuxiliaryOutput(C, *Output, file_types::TY_SerializedDiagnostics,
                         OutputMap, workingDirectory);
    }

    // Remove any existing diagnostics files so that clients can detect their
    // presence to determine if a command was run.
    StringRef OutputPath =
        Output->getAnyOutputForType(file_types::TY_SerializedDiagnostics);
    if (llvm::sys::fs::is_regular_file(OutputPath))
      llvm::sys::fs::remove(OutputPath);
  }
}

void Driver::chooseDependenciesOutputPaths(Compilation &C,
                                           const TypeToPathMap *OutputMap,
                                           StringRef workingDirectory,
                                           llvm::SmallString<128> &Buf,
                                           CommandOutput *Output) const {
  if (C.getArgs().hasArg(options::OPT_emit_dependencies)) {
    auto depPath = computeAuxiliaryOutputPath(
        C, *Output, file_types::TY_Dependencies, OutputMap, workingDirectory);
    C.addDependencyPathOrCreateDummy(depPath, [&] {
      addAuxiliaryOutput(C, *Output, file_types::TY_Dependencies, OutputMap,
                         workingDirectory);
    });
  }
  chooseLoadedModuleTracePath(C, workingDirectory, Buf, Output);
}

void Driver::chooseLoadedModuleTracePath(Compilation &C,
                                         StringRef workingDirectory,
                                         llvm::SmallString<128> &Buf,
                                         CommandOutput *Output) const {
  // The loaded-module-trace is the same for all compile jobs: all `import`
  // statements are processed, even ones from non-primary files. Thus, only
  // one of those jobs needs to emit the file, and we can get it to write
  // straight to the desired final location.
  auto tracePathEnvVar = getenv("SWIFT_LOADED_MODULE_TRACE_FILE");
  auto shouldEmitTrace =
      tracePathEnvVar ||
      C.getArgs().hasArg(options::OPT_emit_loaded_module_trace,
                         options::OPT_emit_loaded_module_trace_path);

  if (shouldEmitTrace &&
      C.requestPermissionForFrontendToEmitLoadedModuleTrace()) {
    StringRef filename;
    // Prefer the environment variable.
    if (tracePathEnvVar)
      filename = StringRef(tracePathEnvVar);
    else {
      // By treating this as a top-level output, the return value always
      // exists.
      filename = *getOutputFilenameFromPathArgOrAsTopLevel(
          C.getOutputInfo(), C.getArgs(),
          options::OPT_emit_loaded_module_trace_path,
          file_types::TY_ModuleTrace,
          /*TreatAsTopLevelOutput=*/true, workingDirectory, Buf);
    }

    Output->setAdditionalOutputForType(file_types::TY_ModuleTrace, filename);
  }
}

void Driver::chooseTBDPath(Compilation &C, const TypeToPathMap *OutputMap,
                           StringRef workingDirectory,
                           llvm::SmallString<128> &Buf,
                           CommandOutput *Output) const {
  StringRef pathFromArgs;
  if (const Arg *A = C.getArgs().getLastArg(options::OPT_emit_tbd_path)) {
    pathFromArgs = A->getValue();
  }

  addAuxiliaryOutput(C, *Output, file_types::TY_TBD, OutputMap,
                     workingDirectory, pathFromArgs,
                     /*requireArg=*/options::OPT_emit_tbd);
}

void Driver::chooseOptimizationRecordPath(Compilation &C,
                                          StringRef workingDirectory,
                                          llvm::SmallString<128> &Buf,
                                          CommandOutput *Output) const {
  const OutputInfo &OI = C.getOutputInfo();
  if (OI.CompilerMode == OutputInfo::Mode::SingleCompile) {
    llvm::Expected<file_types::ID> FileType =
        C.getToolChain().remarkFileTypeFromArgs(C.getArgs());
    if (!FileType) {
      Diags.diagnose({}, diag::error_creating_remark_serializer,
                     llvm::toString(FileType.takeError()));
      return;
    }
    auto filename = *getOutputFilenameFromPathArgOrAsTopLevel(
        OI, C.getArgs(), options::OPT_save_optimization_record_path, *FileType,
        /*TreatAsTopLevelOutput=*/true, workingDirectory, Buf);

    Output->setAdditionalOutputForType(*FileType, filename);
  } else
    // FIXME: We should use the OutputMap in this case.
    Diags.diagnose({}, diag::warn_opt_remark_disabled);
}

void Driver::chooseObjectiveCHeaderOutputPath(Compilation &C,
                                              const TypeToPathMap *OutputMap,
                                              StringRef workingDirectory,
                                              CommandOutput *Output) const {

  if (hasExistingAdditionalOutput(*Output, file_types::TY_ClangHeader))
    return;

  StringRef ObjCHeaderPath;
  if (OutputMap) {
    auto iter = OutputMap->find(file_types::TY_ClangHeader);
    if (iter != OutputMap->end())
      ObjCHeaderPath = iter->second;
  }

  if (ObjCHeaderPath.empty())
    if (auto A = C.getArgs().getLastArg(options::OPT_emit_objc_header_path))
      ObjCHeaderPath = A->getValue();

  if (!ObjCHeaderPath.empty()) {
    Output->setAdditionalOutputForType(file_types::TY_ClangHeader,
                                       ObjCHeaderPath);
  } else {
    // Put the header next to the primary output file.
    // FIXME: That's not correct if the user /just/ passed -emit-header
    // and not -emit-module.
    addAuxiliaryOutput(C, *Output, file_types::TY_ClangHeader,
                       /*output file map*/ nullptr, workingDirectory);
  }
}

static unsigned printActions(const Action *A,
                             llvm::DenseMap<const Action *, unsigned> &Ids) {
  if (Ids.count(A))
    return Ids[A];

  std::string str;
  llvm::raw_string_ostream os(str);

  os << Action::getClassName(A->getKind()) << ", ";
  if (const auto *IA = dyn_cast<InputAction>(A)) {
    os << "\"" << IA->getInputArg().getValue() << "\"";
  } else {
    os << "{";
    llvm::interleave(
        *cast<JobAction>(A),
        [&](const Action *Input) { os << printActions(Input, Ids); },
        [&] { os << ", "; });
    os << "}";
  }

  unsigned Id = Ids.size();
  Ids[A] = Id;
  llvm::errs() << Id << ": " << os.str() << ", "
               << file_types::getTypeName(A->getType()) << "\n";

  return Id;
}

void Driver::printActions(const Compilation &C) const {
  llvm::DenseMap<const Action *, unsigned> Ids;
  for (const Action *A : C.getActions()) {
    ::printActions(A, Ids);
  }
}

void Driver::printVersion(const ToolChain &TC, raw_ostream &OS) const {
  OS << version::getSwiftFullVersion(
    version::Version::getCurrentLanguageVersion()) << '\n';
  OS << "Target: " << TC.getTriple().str() << '\n';
}

void Driver::printHelp(bool ShowHidden) const {
  unsigned IncludedFlagsBitmask = 0;
  unsigned ExcludedFlagsBitmask = options::NoDriverOption;

  switch (driverKind) {
  case DriverKind::Interactive:
    ExcludedFlagsBitmask |= options::NoInteractiveOption;
    break;
  case DriverKind::Standard:
  case DriverKind::SILOpt:
  case DriverKind::SILFuncExtractor:
  case DriverKind::SILNM:
  case DriverKind::SILLLVMGen:
  case DriverKind::SILPassPipelineDumper:
  case DriverKind::SwiftDependencyTool:
  case DriverKind::SwiftLLVMOpt:
  case DriverKind::AutolinkExtract:
  case DriverKind::SymbolGraph:
  case DriverKind::APIDigester:
  case DriverKind::CacheTool:
  case DriverKind::ParseTest:
  case DriverKind::SynthesizeInterface:
    ExcludedFlagsBitmask |= options::NoBatchOption;
    break;
  }

  if (!ShowHidden)
    ExcludedFlagsBitmask |= HelpHidden;

  getOpts().printHelp(llvm::outs(), Name.c_str(), "Swift compiler",
                      IncludedFlagsBitmask, ExcludedFlagsBitmask,
                      /*ShowAllAliases*/false);

  // These strings match the descriptions found in the corresponding swiftpm 
  // help pages
  if (driverKind == DriverKind::Interactive) {
    llvm::outs() << "\nSEE ALSO - PACKAGE MANAGER COMMANDS: \n"
        "\t\"swift build\" Build sources into binary products \n"
        "\t\"swift package\" Perform operations on Swift packages \n"
        "\t\"swift run\" Build and run an executable product \n"
        "\t\"swift test\" Build and run tests \n";
  } else {
    llvm::outs() << "\nSEE ALSO: swift build, swift run, swift package, " 
                    "swift test \n";
  }

}

bool OutputInfo::mightHaveExplicitPrimaryInputs(
    const CommandOutput &Output) const {
  switch (CompilerMode) {
  case Mode::StandardCompile:
    return true;
  case Mode::SingleCompile:
    return false;
  case Mode::Immediate:
  case Mode::REPL:
    llvm_unreachable("REPL and immediate modes handled elsewhere");
  }
  llvm_unreachable("unhandled mode");
}
