• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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