blob: e0bea41104b638c1cd5f6032b70d3d9ea7470829 [file] [log] [blame]
// 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 "gn/c_tool.h"
#include "base/strings/stringprintf.h"
#include "gn/c_substitution_type.h"
#include "gn/target.h"
const char* CTool::kCToolCc = "cc";
const char* CTool::kCToolCxx = "cxx";
const char* CTool::kCToolCxxModule = "cxx_module";
const char* CTool::kCToolObjC = "objc";
const char* CTool::kCToolObjCxx = "objcxx";
const char* CTool::kCToolRc = "rc";
const char* CTool::kCToolAsm = "asm";
const char* CTool::kCToolSwift = "swift";
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));
set_framework_switch("-framework ");
set_weak_framework_switch("-weak_framework ");
set_framework_dir_switch("-F");
set_lib_dir_switch("-L");
set_lib_switch("-l");
set_linker_arg("");
}
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 == kCToolCxxModule ||
name == kCToolObjC || name == kCToolObjCxx || name == kCToolRc ||
name == kCToolSwift || 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,
bool required,
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() && required) {
*err =
Err(defined_from(),
base::StringPrintf("\"%s\" must be specified for this tool.", var));
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", /*required=*/true, &outputs_,
err)) {
return false;
}
if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) ||
!ReadString(scope, "framework_switch", &framework_switch_, err) ||
!ReadString(scope, "weak_framework_switch", &weak_framework_switch_,
err) ||
!ReadString(scope, "framework_dir_switch", &framework_dir_switch_, err) ||
!ReadString(scope, "lib_switch", &lib_switch_, err) ||
!ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) ||
!ReadPattern(scope, "link_output", &link_output_, err) ||
!ReadString(scope, "swiftmodule_switch", &swiftmodule_switch_, err) ||
!ReadPattern(scope, "depend_output", &depend_output_, err)) {
return false;
}
// Swift tool can optionally specify partial_outputs.
if (name_ == kCToolSwift) {
if (!ReadOutputsPatternList(scope, "partial_outputs", /*required=*/false,
&partial_outputs_, 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()) {
*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_ == kCToolCxxModule ||
name_ == kCToolObjC || name_ == kCToolObjCxx || name_ == kCToolRc ||
name_ == kCToolAsm)
return IsValidCompilerSubstitution(sub_type);
if (name_ == kCToolSwift)
return IsValidSwiftCompilerSubstitution(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_ == kCToolCxxModule ||
name_ == kCToolObjC || name_ == kCToolObjCxx || name_ == kCToolRc ||
name_ == kCToolAsm)
return IsValidCompilerOutputsSubstitution(sub_type);
if (name_ == kCToolSwift)
return IsValidSwiftCompilerOutputsSubstitution(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;
}