|  | // Copyright 2019 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 "tools/gn/c_tool.h" | 
|  | #include "tools/gn/c_substitution_type.h" | 
|  | #include "tools/gn/target.h" | 
|  |  | 
|  | const char* CTool::kCToolCc = "cc"; | 
|  | const char* CTool::kCToolCxx = "cxx"; | 
|  | const char* CTool::kCToolObjC = "objc"; | 
|  | const char* CTool::kCToolObjCxx = "objcxx"; | 
|  | const char* CTool::kCToolRc = "rc"; | 
|  | const char* CTool::kCToolAsm = "asm"; | 
|  | const char* CTool::kCToolAlink = "alink"; | 
|  | const char* CTool::kCToolSolink = "solink"; | 
|  | const char* CTool::kCToolSolinkModule = "solink_module"; | 
|  | const char* CTool::kCToolLink = "link"; | 
|  |  | 
|  | CTool::CTool(const char* n) | 
|  | : Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) { | 
|  | CHECK(ValidateName(n)); | 
|  | } | 
|  |  | 
|  | CTool::~CTool() = default; | 
|  |  | 
|  | CTool* CTool::AsC() { | 
|  | return this; | 
|  | } | 
|  | const CTool* CTool::AsC() const { | 
|  | return this; | 
|  | } | 
|  |  | 
|  | bool CTool::ValidateName(const char* name) const { | 
|  | return name == kCToolCc || name == kCToolCxx || name == kCToolObjC || | 
|  | name == kCToolObjCxx || name == kCToolRc || name == kCToolAsm || | 
|  | name == kCToolAlink || name == kCToolSolink || | 
|  | name == kCToolSolinkModule || name == kCToolLink; | 
|  | } | 
|  |  | 
|  | void CTool::SetComplete() { | 
|  | SetToolComplete(); | 
|  | link_output_.FillRequiredTypes(&substitution_bits_); | 
|  | depend_output_.FillRequiredTypes(&substitution_bits_); | 
|  | } | 
|  |  | 
|  | bool CTool::ValidateRuntimeOutputs(Err* err) { | 
|  | if (runtime_outputs().list().empty()) | 
|  | return true;  // Empty is always OK. | 
|  |  | 
|  | if (name_ != kCToolSolink && name_ != kCToolSolinkModule && | 
|  | name_ != kCToolLink) { | 
|  | *err = Err(defined_from(), "This tool specifies runtime_outputs.", | 
|  | "This is only valid for linker tools (alink doesn't count)."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (const SubstitutionPattern& pattern : runtime_outputs().list()) { | 
|  | if (!IsPatternInOutputList(outputs(), pattern)) { | 
|  | *err = Err(defined_from(), "This tool's runtime_outputs is bad.", | 
|  | "It must be a subset of the outputs. The bad one is:\n  " + | 
|  | pattern.AsString()); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | // Validates either link_output or depend_output. To generalize to either, pass | 
|  | // the associated pattern, and the variable name that should appear in error | 
|  | // messages. | 
|  | bool CTool::ValidateLinkAndDependOutput(const SubstitutionPattern& pattern, | 
|  | const char* variable_name, | 
|  | Err* err) { | 
|  | if (pattern.empty()) | 
|  | return true;  // Empty is always OK. | 
|  |  | 
|  | // It should only be specified for certain tool types. | 
|  | if (name_ != kCToolSolink && name_ != kCToolSolinkModule) { | 
|  | *err = Err(defined_from(), | 
|  | "This tool specifies a " + std::string(variable_name) + ".", | 
|  | "This is only valid for solink and solink_module tools."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!IsPatternInOutputList(outputs(), pattern)) { | 
|  | *err = Err(defined_from(), "This tool's link_output is bad.", | 
|  | "It must match one of the outputs."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CTool::ReadPrecompiledHeaderType(Scope* scope, Err* err) { | 
|  | const Value* value = scope->GetValue("precompiled_header_type", 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, do nothing (default is "no PCH"). | 
|  |  | 
|  | if (value->string_value() == "gcc") { | 
|  | set_precompiled_header_type(PCH_GCC); | 
|  | return true; | 
|  | } else if (value->string_value() == "msvc") { | 
|  | set_precompiled_header_type(PCH_MSVC); | 
|  | return true; | 
|  | } | 
|  | *err = Err(*value, "Invalid precompiled_header_type", | 
|  | "Must either be empty, \"gcc\", or \"msvc\"."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CTool::ReadDepsFormat(Scope* scope, Err* err) { | 
|  | const Value* value = scope->GetValue("depsformat", true); | 
|  | if (!value) | 
|  | return true;  // Not present is fine. | 
|  | if (!value->VerifyTypeIs(Value::STRING, err)) | 
|  | return false; | 
|  |  | 
|  | if (value->string_value() == "gcc") { | 
|  | set_depsformat(DEPS_GCC); | 
|  | } else if (value->string_value() == "msvc") { | 
|  | set_depsformat(DEPS_MSVC); | 
|  | } else { | 
|  | *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\"."); | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CTool::ReadOutputsPatternList(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 (list.list().empty()) { | 
|  | *err = Err(defined_from(), "\"outputs\" must be specified for this tool."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for (const auto& cur_type : list.required_types()) { | 
|  | if (!ValidateOutputSubstitution(cur_type)) { | 
|  | *err = Err(*value, "Pattern not valid here.", | 
|  | "You used the pattern " + std::string(cur_type->name) + | 
|  | " which is not valid\nfor this variable."); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | *field = std::move(list); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) { | 
|  | // Initialize default vars. | 
|  | if (!Tool::InitTool(scope, toolchain, err)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // All C tools should have outputs. | 
|  | if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) || | 
|  | !ReadString(scope, "lib_switch", &lib_switch_, err) || | 
|  | !ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) || | 
|  | !ReadPattern(scope, "link_output", &link_output_, err) || | 
|  | !ReadPattern(scope, "depend_output", &depend_output_, err)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Validate link_output and depend_output. | 
|  | if (!ValidateLinkAndDependOutput(link_output(), "link_output", err)) { | 
|  | return false; | 
|  | } | 
|  | if (!ValidateLinkAndDependOutput(depend_output(), "depend_output", err)) { | 
|  | return false; | 
|  | } | 
|  | if ((!link_output().empty() && depend_output().empty()) || | 
|  | (link_output().empty() && !depend_output().empty())) { | 
|  | *err = Err(defined_from(), | 
|  | "Both link_output and depend_output should either " | 
|  | "be specified or they should both be empty."); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if (!ValidateRuntimeOutputs(err)) { | 
|  | return false; | 
|  | } | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool CTool::ValidateSubstitution(const Substitution* sub_type) const { | 
|  | if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC || | 
|  | name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm) | 
|  | return IsValidCompilerSubstitution(sub_type); | 
|  | else if (name_ == kCToolAlink) | 
|  | return IsValidALinkSubstitution(sub_type); | 
|  | else if (name_ == kCToolSolink || name_ == kCToolSolinkModule || | 
|  | name_ == kCToolLink) | 
|  | return IsValidLinkerSubstitution(sub_type); | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool CTool::ValidateOutputSubstitution(const Substitution* sub_type) const { | 
|  | if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC || | 
|  | name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm) | 
|  | return IsValidCompilerOutputsSubstitution(sub_type); | 
|  | // ALink uses the standard output file patterns as other linker tools. | 
|  | else if (name_ == kCToolAlink || name_ == kCToolSolink || | 
|  | name_ == kCToolSolinkModule || name_ == kCToolLink) | 
|  | return IsValidLinkerOutputsSubstitution(sub_type); | 
|  | NOTREACHED(); | 
|  | return false; | 
|  | } |