| // 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 "base/strings/stringprintf.h" | |
| #include "gn/builtin_tool.h" | |
| #include "gn/c_tool.h" | |
| #include "gn/general_tool.h" | |
| #include "gn/output_file.h" | |
| #include "gn/rust_tool.h" | |
| #include "gn/settings.h" | |
| #include "gn/target.h" | |
| #include "gn/value_extractors.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_); | |
| partial_outputs_.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; | |
| } | |
| BuiltinTool* Tool::AsBuiltin() { | |
| return nullptr; | |
| } | |
| const BuiltinTool* Tool::AsBuiltin() 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::ReadInputs(Scope* scope, Err* err) { | |
| DCHECK(!complete_); | |
| const Value* value = scope->GetValue("inputs", true); | |
| if (!value) | |
| return true; // Not present is fine. | |
| std::vector<SourceFile> inputs; | |
| if (!ExtractListOfRelativeFiles(scope->settings()->build_settings(), *value, | |
| scope->GetSourceDir(), &inputs, err)) { | |
| return false; | |
| } | |
| set_inputs(std::move(inputs)); | |
| return true; | |
| } | |
| std::optional<OutputFile> Tool::inputs_phony_or_file( | |
| std::string_view rule_prefix, | |
| const BuildSettings& build_settings) const { | |
| if (inputs_.size() == 1) { | |
| return OutputFile(&build_settings, inputs_[0]); | |
| } else if (inputs_.size() > 1) { | |
| return OutputFile(base::StringPrintf( | |
| "phony/%s%s_inputs", std::string(rule_prefix).c_str(), name_)); | |
| } | |
| return std::nullopt; | |
| } | |
| 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) || | |
| !ReadInputs(scope, 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::kCToolSwift) | |
| return std::make_unique<CTool>(CTool::kCToolSwift); | |
| 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_SWIFT: | |
| return CTool::kCToolSwift; | |
| 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 surprising (i.e. phony 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().InferredCrateType(target)) { | |
| 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; | |
| case RustValues::CRATE_AUTO: | |
| break; | |
| default: | |
| NOTREACHED(); | |
| } | |
| } | |
| switch (target->output_type()) { | |
| 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::ACTION: | |
| case Target::ACTION_FOREACH: | |
| case Target::BUNDLE_DATA: | |
| case Target::COPY_FILES: | |
| case Target::CREATE_BUNDLE: | |
| case Target::GENERATED_FILE: | |
| case Target::GROUP: | |
| case Target::SOURCE_SET: | |
| if (target->settings()->build_settings()->no_stamp_files()) { | |
| return BuiltinTool::kBuiltinToolPhony; | |
| } else { | |
| return GeneralTool::kGeneralToolStamp; | |
| } | |
| default: | |
| NOTREACHED(); | |
| return kToolNone; | |
| } | |
| } |