// Part of the Carbon Language project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception

#include "toolchain/driver/llvm_subcommand.h"

#include "common/command_line.h"
#include "llvm/TargetParser/Host.h"
#include "llvm/TargetParser/Triple.h"
#include "toolchain/base/llvm_tools.h"
#include "toolchain/driver/llvm_runner.h"

namespace Carbon {

static constexpr CommandLine::CommandInfo SubcommandInfo = {
    .name = "llvm",
    .help = R"""(
Runs LLVM's command line tools with the provided arguments.

This subcommand provides access to a collection of LLVM's command line tools via
further subcommands. For each of these tools, their command line can be provided
using positional arguments.
)""",
};

auto LLVMOptions::Build(CommandLine::CommandBuilder& b,
                        DriverSubcommand* subcommand,
                        DriverSubcommand** selected_subcommand) -> void {
  // Add further subcommands for each LLVM tool.
  for (LLVMTool tool : LLVMTool::Tools) {
    // TODO: The subcommand info for each tool is weirdly stored in the
    // `LLVMTool` class instead of here where it makes more logical sense.
    // Either we should figure out how to move it to here or we should more
    // fully document the oddity of having it in the `LLVMTool` class.
    //
    // TODO: Currently, the command line subsystem's help isn't as user friendly
    // for the generated subcommands below this as it could be. Because each of
    // these has a completely generic and stamped out info, the `help` output is
    // very repetitive and doesn't actually contribute much information.
    b.AddSubcommand(tool.subcommand_info(), [&](auto& sub_b) {
      sub_b.AddStringPositionalArg(
          {
              .name = "ARG",
              .help = R"""(
Arguments passed to the LLVM tool.
)""",
          },
          [&](auto& arg_b) { arg_b.Append(&args); });
      sub_b.Do([=, this] {
        subcommand_tool = tool;
        *selected_subcommand = subcommand;
      });
    });
  }

  // The `llvm` subcommand can't be used directly.
  b.RequiresSubcommand();
}

LLVMSubcommand::LLVMSubcommand() : DriverSubcommand(SubcommandInfo) {}

auto LLVMSubcommand::Run(DriverEnv& driver_env) -> DriverResult {
  LLVMRunner runner(driver_env.installation, driver_env.vlog_stream);

  // Don't run arbitrary LLVM tools and libraries when fuzzing.
  if (TestAndDiagnoseIfFuzzingExternalLibraries(driver_env, "llvm")) {
    return {.success = false};
  }

  return {.success = runner.Run(options_.subcommand_tool, options_.args)};
}

}  // namespace Carbon
