| // 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/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; | 
 | } | 
 |  | 
 | 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; | 
 |   } | 
 |   const bool command_is_required = name_ != GeneralTool::kGeneralToolAction; | 
 |   if (command_.empty() == command_is_required) { | 
 |     *err = Err(defined_from(), "This tool's command is bad.", | 
 |                command_is_required | 
 |                    ? "This tool requires \"command\" to be defined." | 
 |                    : "This tool doesn't support \"command\"."); | 
 |     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; | 
 |   } | 
 |   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::kCToolCxxModule) | 
 |     return std::make_unique<CTool>(CTool::kCToolCxxModule); | 
 |   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); | 
 |  | 
 |   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_MODULEMAP: | 
 |       return CTool::kCToolCxxModule; | 
 |     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_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::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; | 
 |   } | 
 | } |