text: // Copyright 2014 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "gn/tool.h"
  
  #include "gn/c_tool.h"
  #include "gn/general_tool.h"
  #include "gn/rust_tool.h"
  #include "gn/csharp_tool.h"
  #include "gn/settings.h"
  #include "gn/target.h"
  
  const char* Tool::kToolNone = "";
  
  Tool::Tool(const char* n) : name_(n) {}
  
  Tool::~Tool() = default;
  
  void Tool::SetToolComplete() {
    DCHECK(!complete_);
    complete_ = true;
  
    command_.FillRequiredTypes(&substitution_bits_);
    depfile_.FillRequiredTypes(&substitution_bits_);
    description_.FillRequiredTypes(&substitution_bits_);
    outputs_.FillRequiredTypes(&substitution_bits_);
    rspfile_.FillRequiredTypes(&substitution_bits_);
    rspfile_content_.FillRequiredTypes(&substitution_bits_);
  }
  
  GeneralTool* Tool::AsGeneral() {
    return nullptr;
  }
  
  const GeneralTool* Tool::AsGeneral() const {
    return nullptr;
  }
  
  CTool* Tool::AsC() {
    return nullptr;
  }
  
  const CTool* Tool::AsC() const {
    return nullptr;
  }
  
  RustTool* Tool::AsRust() {
    return nullptr;
  }
  const RustTool* Tool::AsRust() const {
    return nullptr;
  }
  
  CSharpTool* Tool::AsCSharp() {
    return nullptr;
  }
  const CSharpTool* Tool::AsCSharp() const {
    return nullptr;
  }
  
  bool Tool::IsPatternInOutputList(const SubstitutionList& output_list,
                                   const SubstitutionPattern& pattern) const {
    for (const auto& cur : output_list.list()) {
      if (pattern.ranges().size() == cur.ranges().size() &&
          std::equal(pattern.ranges().begin(), pattern.ranges().end(),
                     cur.ranges().begin()))
        return true;
    }
    return false;
  }
  
  bool Tool::ValidateSubstitutionList(
      const std::vector<const Substitution*>& list,
      const Value* origin,
      Err* err) const {
    for (const auto& cur_type : list) {
      if (!ValidateSubstitution(cur_type)) {
        *err = Err(*origin, "Pattern not valid here.",
                   "You used the pattern " + std::string(cur_type->name) +
                       " which is not valid\nfor this variable.");
        return false;
      }
    }
    return true;
  }
  
  bool Tool::ReadBool(Scope* scope, const char* var, bool* field, Err* err) {
    DCHECK(!complete_);
    const Value* v = scope->GetValue(var, true);
    if (!v)
      return true;  // Not present is fine.
    if (!v->VerifyTypeIs(Value::BOOLEAN, err))
      return false;
    *field = v->boolean_value();
    return true;
  }
  
  bool Tool::ReadString(Scope* scope,
                        const char* var,
                        std::string* field,
                        Err* err) {
    DCHECK(!complete_);
    const Value* v = scope->GetValue(var, true);
    if (!v)
      return true;  // Not present is fine.
    if (!v->VerifyTypeIs(Value::STRING, err))
      return false;
    *field = v->string_value();
    return true;
  }
  
  bool Tool::ReadPattern(Scope* scope,
                         const char* var,
                         SubstitutionPattern* field,
                         Err* err) {
    DCHECK(!complete_);
    const Value* value = scope->GetValue(var, true);
    if (!value)
      return true;  // Not present is fine.
    if (!value->VerifyTypeIs(Value::STRING, err))
      return false;
  
    SubstitutionPattern pattern;
    if (!pattern.Parse(*value, err))
      return false;
    if (!ValidateSubstitutionList(pattern.required_types(), value, err))
      return false;
  
    *field = std::move(pattern);
    return true;
  }
  
  bool Tool::ReadPatternList(Scope* scope,
                             const char* var,
                             SubstitutionList* field,
                             Err* err) {
    DCHECK(!complete_);
    const Value* value = scope->GetValue(var, true);
    if (!value)
      return true;  // Not present is fine.
    if (!value->VerifyTypeIs(Value::LIST, err))
      return false;
  
    SubstitutionList list;
    if (!list.Parse(*value, err))
      return false;
  
    // Validate the right kinds of patterns are used.
    if (!ValidateSubstitutionList(list.required_types(), value, err))
      return false;
  
    *field = std::move(list);
    return true;
  }
  
  bool Tool::ReadLabel(Scope* scope,
                       const char* var,
                       const Label& current_toolchain,
                       LabelPtrPair<Pool>* field,
                       Err* err) {
    DCHECK(!complete_);
    const Value* v = scope->GetValue(var, true);
    if (!v)
      return true;  // Not present is fine.
  
    Label label =
        Label::Resolve(scope->GetSourceDir(),
                       scope->settings()->build_settings()->root_path_utf8(),
                       current_toolchain, *v, err);
    if (err->has_error())
      return false;
  
    LabelPtrPair<Pool> pair(label);
    pair.origin = defined_from();
  
    *field = std::move(pair);
    return true;
  }
  
  bool Tool::ReadOutputExtension(Scope* scope, Err* err) {
    DCHECK(!complete_);
    const Value* value = scope->GetValue("default_output_extension", true);
    if (!value)
      return true;  // Not present is fine.
    if (!value->VerifyTypeIs(Value::STRING, err))
      return false;
  
    if (value->string_value().empty())
      return true;  // Accept empty string.
  
    if (value->string_value()[0] != '.') {
      *err = Err(*value, "default_output_extension must begin with a '.'");
      return false;
    }
  
    set_default_output_extension(value->string_value());
    return true;
  }
  
  bool Tool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
    if (!ReadPattern(scope, "command", &command_, err) ||
        !ReadString(scope, "command_launcher", &command_launcher_, err) ||
        !ReadOutputExtension(scope, err) ||
        !ReadPattern(scope, "depfile", &depfile_, err) ||
        !ReadPattern(scope, "description", &description_, err) ||
        !ReadPatternList(scope, "runtime_outputs", &runtime_outputs_, err) ||
        !ReadString(scope, "output_prefix", &output_prefix_, err) ||
        !ReadPattern(scope, "default_output_dir", &default_output_dir_, err) ||
        !ReadBool(scope, "restat", &restat_, err) ||
        !ReadPattern(scope, "rspfile", &rspfile_, err) ||
        !ReadPattern(scope, "rspfile_content", &rspfile_content_, err) ||
        !ReadLabel(scope, "pool", toolchain->label(), &pool_, err)) {
      return false;
    }
    return true;
  }
  
  std::unique_ptr<Tool> Tool::CreateTool(const ParseNode* function,
                                         const std::string& name,
                                         Scope* scope,
                                         Toolchain* toolchain,
                                         Err* err) {
    std::unique_ptr<Tool> tool = CreateTool(name);
    if (!tool) {
      *err = Err(function, "Unknown tool type.");
      return nullptr;
    }
    if (CTool* c_tool = tool->AsC()) {
      if (c_tool->InitTool(scope, toolchain, err))
        return tool;
      return nullptr;
    }
    if (GeneralTool* general_tool = tool->AsGeneral()) {
      if (general_tool->InitTool(scope, toolchain, err))
        return tool;
      return nullptr;
    }
    if (RustTool* rust_tool = tool->AsRust()) {
      if (rust_tool->InitTool(scope, toolchain, err))
        return tool;
      return nullptr;
    }
    if (CSharpTool* csharp_tool = tool->AsCSharp()) {
      if (csharp_tool->InitTool(scope, toolchain, err))
        return tool;
      return nullptr;
    }
    NOTREACHED();
    *err = Err(function, "Unknown tool type.");
    return nullptr;
  }
  
  // static
  std::unique_ptr<Tool> Tool::CreateTool(const std::string& name) {
    // C tools
    if (name == CTool::kCToolCc)
      return std::make_unique<CTool>(CTool::kCToolCc);
    else if (name == CTool::kCToolCxx)
      return std::make_unique<CTool>(CTool::kCToolCxx);
    else if (name == CTool::kCToolObjC)
      return std::make_unique<CTool>(CTool::kCToolObjC);
    else if (name == CTool::kCToolObjCxx)
      return std::make_unique<CTool>(CTool::kCToolObjCxx);
    else if (name == CTool::kCToolRc)
      return std::make_unique<CTool>(CTool::kCToolRc);
    else if (name == CTool::kCToolAsm)
      return std::make_unique<CTool>(CTool::kCToolAsm);
    else if (name == CTool::kCToolAlink)
      return std::make_unique<CTool>(CTool::kCToolAlink);
    else if (name == CTool::kCToolSolink)
      return std::make_unique<CTool>(CTool::kCToolSolink);
    else if (name == CTool::kCToolSolinkModule)
      return std::make_unique<CTool>(CTool::kCToolSolinkModule);
    else if (name == CTool::kCToolLink)
      return std::make_unique<CTool>(CTool::kCToolLink);
  
    // General tools
    else if (name == GeneralTool::kGeneralToolAction)
      return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolAction);
    else if (name == GeneralTool::kGeneralToolStamp)
      return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolStamp);
    else if (name == GeneralTool::kGeneralToolCopy)
      return std::make_unique<GeneralTool>(GeneralTool::kGeneralToolCopy);
    else if (name == GeneralTool::kGeneralToolCopyBundleData)
      return std::make_unique<GeneralTool>(
          GeneralTool::kGeneralToolCopyBundleData);
    else if (name == GeneralTool::kGeneralToolCompileXCAssets)
      return std::make_unique<GeneralTool>(
          GeneralTool::kGeneralToolCompileXCAssets);
  
    // Rust tool
    else if (name == RustTool::kRsToolBin)
      return std::make_unique<RustTool>(RustTool::kRsToolBin);
    else if (name == RustTool::kRsToolCDylib)
      return std::make_unique<RustTool>(RustTool::kRsToolCDylib);
    else if (name == RustTool::kRsToolDylib)
      return std::make_unique<RustTool>(RustTool::kRsToolDylib);
    else if (name == RustTool::kRsToolMacro)
      return std::make_unique<RustTool>(RustTool::kRsToolMacro);
    else if (name == RustTool::kRsToolRlib)
      return std::make_unique<RustTool>(RustTool::kRsToolRlib);
    else if (name == RustTool::kRsToolStaticlib)
      return std::make_unique<RustTool>(RustTool::kRsToolStaticlib);
  
    // CSharp tool
    else if (name == CSharpTool::kMSBuild)
      return std::make_unique<CSharpTool>(CSharpTool::kMSBuild);
    else if (name == CSharpTool::kGenerator)
      return std::make_unique<CSharpTool>(CSharpTool::kGenerator);
  
    return nullptr;
  }
  
  // static
  const char* Tool::GetToolTypeForSourceType(SourceFile::Type type) {
    switch (type) {
      case SourceFile::SOURCE_C:
        return CTool::kCToolCc;
      case SourceFile::SOURCE_CPP:
        return CTool::kCToolCxx;
      case SourceFile::SOURCE_M:
        return CTool::kCToolObjC;
      case SourceFile::SOURCE_MM:
        return CTool::kCToolObjCxx;
      case SourceFile::SOURCE_ASM:
      case SourceFile::SOURCE_S:
        return CTool::kCToolAsm;
      case SourceFile::SOURCE_RC:
        return CTool::kCToolRc;
      case SourceFile::SOURCE_RS:
        return RustTool::kRsToolBin;
      case SourceFile::SOURCE_CS:
      case SourceFile::SOURCE_XAML:
        return CSharpTool::kMSBuild;
      case SourceFile::SOURCE_UNKNOWN:
      case SourceFile::SOURCE_H:
      case SourceFile::SOURCE_O:
      case SourceFile::SOURCE_DEF:
      case SourceFile::SOURCE_GO:
        return kToolNone;
      default:
        NOTREACHED();
        return kToolNone;
    }
  }
  
  // static
  const char* Tool::GetToolTypeForTargetFinalOutput(const Target* target) {
    // The contents of this list might be suprising (i.e. stamp tool for copy
    // rules). See the header for why.
    // TODO(crbug.com/gn/39): Don't emit stamp files for single-output targets.
    if (target->source_types_used().RustSourceUsed()) {
      switch (target->rust_values().crate_type()) {
        case RustValues::CRATE_AUTO: {
          switch (target->output_type()) {
            case Target::EXECUTABLE:
              return RustTool::kRsToolBin;
            case Target::SHARED_LIBRARY:
              return RustTool::kRsToolDylib;
            case Target::STATIC_LIBRARY:
              return RustTool::kRsToolStaticlib;
            case Target::RUST_LIBRARY:
              return RustTool::kRsToolRlib;
            case Target::RUST_PROC_MACRO:
              return RustTool::kRsToolMacro;
            default:
              break;
          }
          break;
        }
        case RustValues::CRATE_BIN:
          return RustTool::kRsToolBin;
        case RustValues::CRATE_CDYLIB:
          return RustTool::kRsToolCDylib;
        case RustValues::CRATE_DYLIB:
          return RustTool::kRsToolDylib;
        case RustValues::CRATE_PROC_MACRO:
          return RustTool::kRsToolMacro;
        case RustValues::CRATE_RLIB:
          return RustTool::kRsToolRlib;
        case RustValues::CRATE_STATICLIB:
          return RustTool::kRsToolStaticlib;
        default:
          NOTREACHED();
      }
    }
    switch (target->output_type()) {
      case Target::GROUP:
        return GeneralTool::kGeneralToolStamp;
      case Target::EXECUTABLE:
        return CTool::kCToolLink;
      case Target::SHARED_LIBRARY:
        return CTool::kCToolSolink;
      case Target::LOADABLE_MODULE:
        return CTool::kCToolSolinkModule;
      case Target::STATIC_LIBRARY:
        return CTool::kCToolAlink;
      case Target::SOURCE_SET:
        return GeneralTool::kGeneralToolStamp;
      case Target::CSHARP_ASSEMBLY:
        return CSharpTool::kMSBuild;
      case Target::ACTION:
      case Target::ACTION_FOREACH:
      case Target::BUNDLE_DATA:
      case Target::CREATE_BUNDLE:
      case Target::COPY_FILES:
      case Target::GENERATED_FILE:
        return GeneralTool::kGeneralToolStamp;
      default:
        NOTREACHED();
        return kToolNone;
    }
  }
  