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