1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "gn/c_tool.h"
6 #include "gn/c_substitution_type.h"
7 #include "gn/target.h"
8
9 const char* CTool::kCToolCc = "cc";
10 const char* CTool::kCToolCxx = "cxx";
11 const char* CTool::kCToolObjC = "objc";
12 const char* CTool::kCToolObjCxx = "objcxx";
13 const char* CTool::kCToolRc = "rc";
14 const char* CTool::kCToolAsm = "asm";
15 const char* CTool::kCToolAlink = "alink";
16 const char* CTool::kCToolSolink = "solink";
17 const char* CTool::kCToolSolinkModule = "solink_module";
18 const char* CTool::kCToolLink = "link";
19
CTool(const char * n)20 CTool::CTool(const char* n)
21 : Tool(n), depsformat_(DEPS_GCC), precompiled_header_type_(PCH_NONE) {
22 CHECK(ValidateName(n));
23 set_framework_switch("-framework ");
24 set_framework_dir_switch("-F");
25 set_lib_dir_switch("-L");
26 set_lib_switch("-l");
27 set_linker_arg("");
28 }
29
30 CTool::~CTool() = default;
31
AsC()32 CTool* CTool::AsC() {
33 return this;
34 }
AsC() const35 const CTool* CTool::AsC() const {
36 return this;
37 }
38
ValidateName(const char * name) const39 bool CTool::ValidateName(const char* name) const {
40 return name == kCToolCc || name == kCToolCxx || name == kCToolObjC ||
41 name == kCToolObjCxx || name == kCToolRc || name == kCToolAsm ||
42 name == kCToolAlink || name == kCToolSolink ||
43 name == kCToolSolinkModule || name == kCToolLink;
44 }
45
SetComplete()46 void CTool::SetComplete() {
47 SetToolComplete();
48 link_output_.FillRequiredTypes(&substitution_bits_);
49 depend_output_.FillRequiredTypes(&substitution_bits_);
50 }
51
ValidateRuntimeOutputs(Err * err)52 bool CTool::ValidateRuntimeOutputs(Err* err) {
53 if (runtime_outputs().list().empty())
54 return true; // Empty is always OK.
55
56 if (name_ != kCToolSolink && name_ != kCToolSolinkModule &&
57 name_ != kCToolLink) {
58 *err = Err(defined_from(), "This tool specifies runtime_outputs.",
59 "This is only valid for linker tools (alink doesn't count).");
60 return false;
61 }
62
63 for (const SubstitutionPattern& pattern : runtime_outputs().list()) {
64 if (!IsPatternInOutputList(outputs(), pattern)) {
65 *err = Err(defined_from(), "This tool's runtime_outputs is bad.",
66 "It must be a subset of the outputs. The bad one is:\n " +
67 pattern.AsString());
68 return false;
69 }
70 }
71 return true;
72 }
73
74 // Validates either link_output or depend_output. To generalize to either, pass
75 // the associated pattern, and the variable name that should appear in error
76 // messages.
ValidateLinkAndDependOutput(const SubstitutionPattern & pattern,const char * variable_name,Err * err)77 bool CTool::ValidateLinkAndDependOutput(const SubstitutionPattern& pattern,
78 const char* variable_name,
79 Err* err) {
80 if (pattern.empty())
81 return true; // Empty is always OK.
82
83 // It should only be specified for certain tool types.
84 if (name_ != kCToolSolink && name_ != kCToolSolinkModule) {
85 *err = Err(defined_from(),
86 "This tool specifies a " + std::string(variable_name) + ".",
87 "This is only valid for solink and solink_module tools.");
88 return false;
89 }
90
91 if (!IsPatternInOutputList(outputs(), pattern)) {
92 *err = Err(defined_from(), "This tool's link_output is bad.",
93 "It must match one of the outputs.");
94 return false;
95 }
96
97 return true;
98 }
99
ReadPrecompiledHeaderType(Scope * scope,Err * err)100 bool CTool::ReadPrecompiledHeaderType(Scope* scope, Err* err) {
101 const Value* value = scope->GetValue("precompiled_header_type", true);
102 if (!value)
103 return true; // Not present is fine.
104 if (!value->VerifyTypeIs(Value::STRING, err))
105 return false;
106
107 if (value->string_value().empty())
108 return true; // Accept empty string, do nothing (default is "no PCH").
109
110 if (value->string_value() == "gcc") {
111 set_precompiled_header_type(PCH_GCC);
112 return true;
113 } else if (value->string_value() == "msvc") {
114 set_precompiled_header_type(PCH_MSVC);
115 return true;
116 }
117 *err = Err(*value, "Invalid precompiled_header_type",
118 "Must either be empty, \"gcc\", or \"msvc\".");
119 return false;
120 }
121
ReadDepsFormat(Scope * scope,Err * err)122 bool CTool::ReadDepsFormat(Scope* scope, Err* err) {
123 const Value* value = scope->GetValue("depsformat", true);
124 if (!value)
125 return true; // Not present is fine.
126 if (!value->VerifyTypeIs(Value::STRING, err))
127 return false;
128
129 if (value->string_value() == "gcc") {
130 set_depsformat(DEPS_GCC);
131 } else if (value->string_value() == "msvc") {
132 set_depsformat(DEPS_MSVC);
133 } else {
134 *err = Err(*value, "Deps format must be \"gcc\" or \"msvc\".");
135 return false;
136 }
137 return true;
138 }
139
ReadOutputsPatternList(Scope * scope,const char * var,SubstitutionList * field,Err * err)140 bool CTool::ReadOutputsPatternList(Scope* scope,
141 const char* var,
142 SubstitutionList* field,
143 Err* err) {
144 DCHECK(!complete_);
145 const Value* value = scope->GetValue(var, true);
146 if (!value)
147 return true; // Not present is fine.
148 if (!value->VerifyTypeIs(Value::LIST, err))
149 return false;
150
151 SubstitutionList list;
152 if (!list.Parse(*value, err))
153 return false;
154
155 // Validate the right kinds of patterns are used.
156 if (list.list().empty()) {
157 *err = Err(defined_from(), "\"outputs\" must be specified for this tool.");
158 return false;
159 }
160
161 for (const auto& cur_type : list.required_types()) {
162 if (!ValidateOutputSubstitution(cur_type)) {
163 *err = Err(*value, "Pattern not valid here.",
164 "You used the pattern " + std::string(cur_type->name) +
165 " which is not valid\nfor this variable.");
166 return false;
167 }
168 }
169
170 *field = std::move(list);
171 return true;
172 }
173
InitTool(Scope * scope,Toolchain * toolchain,Err * err)174 bool CTool::InitTool(Scope* scope, Toolchain* toolchain, Err* err) {
175 // Initialize default vars.
176 if (!Tool::InitTool(scope, toolchain, err)) {
177 return false;
178 }
179
180 // All C tools should have outputs.
181 if (!ReadOutputsPatternList(scope, "outputs", &outputs_, err)) {
182 return false;
183 }
184
185 if (!ReadDepsFormat(scope, err) || !ReadPrecompiledHeaderType(scope, err) ||
186 !ReadString(scope, "framework_switch", &framework_switch_, err) ||
187 !ReadString(scope, "framework_dir_switch", &framework_dir_switch_, err) ||
188 !ReadString(scope, "lib_switch", &lib_switch_, err) ||
189 !ReadString(scope, "lib_dir_switch", &lib_dir_switch_, err) ||
190 !ReadPattern(scope, "link_output", &link_output_, err) ||
191 !ReadPattern(scope, "depend_output", &depend_output_, err)) {
192 return false;
193 }
194
195 // Validate link_output and depend_output.
196 if (!ValidateLinkAndDependOutput(link_output(), "link_output", err)) {
197 return false;
198 }
199 if (!ValidateLinkAndDependOutput(depend_output(), "depend_output", err)) {
200 return false;
201 }
202 if ((!link_output().empty() && depend_output().empty()) ||
203 (link_output().empty() && !depend_output().empty())) {
204 *err = Err(defined_from(),
205 "Both link_output and depend_output should either "
206 "be specified or they should both be empty.");
207 return false;
208 }
209
210 if (!ValidateRuntimeOutputs(err)) {
211 return false;
212 }
213 return true;
214 }
215
ValidateSubstitution(const Substitution * sub_type) const216 bool CTool::ValidateSubstitution(const Substitution* sub_type) const {
217 if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
218 name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
219 return IsValidCompilerSubstitution(sub_type);
220 else if (name_ == kCToolAlink)
221 return IsValidALinkSubstitution(sub_type);
222 else if (name_ == kCToolSolink || name_ == kCToolSolinkModule ||
223 name_ == kCToolLink)
224 return IsValidLinkerSubstitution(sub_type);
225 NOTREACHED();
226 return false;
227 }
228
ValidateOutputSubstitution(const Substitution * sub_type) const229 bool CTool::ValidateOutputSubstitution(const Substitution* sub_type) const {
230 if (name_ == kCToolCc || name_ == kCToolCxx || name_ == kCToolObjC ||
231 name_ == kCToolObjCxx || name_ == kCToolRc || name_ == kCToolAsm)
232 return IsValidCompilerOutputsSubstitution(sub_type);
233 // ALink uses the standard output file patterns as other linker tools.
234 else if (name_ == kCToolAlink || name_ == kCToolSolink ||
235 name_ == kCToolSolinkModule || name_ == kCToolLink)
236 return IsValidLinkerOutputsSubstitution(sub_type);
237 NOTREACHED();
238 return false;
239 }
240