• 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 #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