1 // Copyright (c) 2013 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/action_target_generator.h"
6
7 #include "base/stl_util.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "gn/build_settings.h"
11 #include "gn/config_values_generator.h"
12 #include "gn/err.h"
13 #include "gn/filesystem_utils.h"
14 #include "gn/functions.h"
15 #include "gn/parse_tree.h"
16 #include "gn/scope.h"
17 #include "gn/value.h"
18 #include "gn/value_extractors.h"
19 #include "gn/variables.h"
20
ActionTargetGenerator(Target * target,Scope * scope,const FunctionCallNode * function_call,Target::OutputType type,Err * err)21 ActionTargetGenerator::ActionTargetGenerator(
22 Target* target,
23 Scope* scope,
24 const FunctionCallNode* function_call,
25 Target::OutputType type,
26 Err* err)
27 : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
28
29 ActionTargetGenerator::~ActionTargetGenerator() = default;
30
DoRun()31 void ActionTargetGenerator::DoRun() {
32 target_->set_output_type(output_type_);
33
34 if (!FillSources())
35 return;
36 if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) {
37 // Foreach rules must always have some sources to have an effect.
38 *err_ =
39 Err(function_call_, "action_foreach target has no sources.",
40 "If you don't specify any sources, there is nothing to run your\n"
41 "script over.");
42 return;
43 }
44
45 if (!FillInputs())
46 return;
47
48 if (!FillScript())
49 return;
50
51 if (!FillScriptArgs())
52 return;
53
54 if (!FillResponseFileContents())
55 return;
56
57 if (!FillOutputs(output_type_ == Target::ACTION_FOREACH))
58 return;
59
60 if (!FillDepfile())
61 return;
62
63 if (!FillMnemonic())
64 return;
65
66 if (!FillPool())
67 return;
68
69 if (!FillCheckIncludes())
70 return;
71
72 if (!FillConfigs())
73 return;
74
75 if (!CheckOutputs())
76 return;
77
78 // Config values (compiler flags, etc.) set directly on this target.
79 ConfigValuesGenerator gen(&target_->config_values(), scope_,
80 scope_->GetSourceDir(), err_);
81 gen.Run();
82 if (err_->has_error())
83 return;
84
85 // Action outputs don't depend on the current toolchain so we can skip adding
86 // that dependency.
87
88 // response_file_contents and {{response_file_name}} in the args must go
89 // together.
90 const auto& required_args_substitutions =
91 target_->action_values().args().required_types();
92 bool has_rsp_file_name = base::ContainsValue(required_args_substitutions,
93 &SubstitutionRspFileName);
94 if (target_->action_values().uses_rsp_file() && !has_rsp_file_name) {
95 *err_ = Err(
96 function_call_, "Missing {{response_file_name}} in args.",
97 "This target defines response_file_contents but doesn't use\n"
98 "{{response_file_name}} in the args, which means the response file\n"
99 "will be unused.");
100 return;
101 }
102 if (!target_->action_values().uses_rsp_file() && has_rsp_file_name) {
103 *err_ = Err(
104 function_call_, "Missing response_file_contents definition.",
105 "This target uses {{response_file_name}} in the args, but does not\n"
106 "define response_file_contents which means the response file\n"
107 "will be empty.");
108 return;
109 }
110 }
111
FillScript()112 bool ActionTargetGenerator::FillScript() {
113 // If this gets called, the target type requires a script, so error out
114 // if it doesn't have one.
115 const Value* value = scope_->GetValue(variables::kScript, true);
116 if (!value) {
117 *err_ = Err(function_call_, "This target type requires a \"script\".");
118 return false;
119 }
120 if (!value->VerifyTypeIs(Value::STRING, err_))
121 return false;
122
123 SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
124 *value, err_, scope_->settings()->build_settings()->root_path_utf8());
125 if (err_->has_error())
126 return false;
127 target_->action_values().set_script(script_file);
128 return true;
129 }
130
FillScriptArgs()131 bool ActionTargetGenerator::FillScriptArgs() {
132 const Value* value = scope_->GetValue(variables::kArgs, true);
133 if (!value)
134 return true; // Nothing to do.
135
136 if (!target_->action_values().args().Parse(*value, err_))
137 return false;
138 if (!EnsureValidSubstitutions(
139 target_->action_values().args().required_types(),
140 &IsValidScriptArgsSubstitution, value->origin(), err_))
141 return false;
142
143 return true;
144 }
145
FillResponseFileContents()146 bool ActionTargetGenerator::FillResponseFileContents() {
147 const Value* value = scope_->GetValue(variables::kResponseFileContents, true);
148 if (!value)
149 return true; // Nothing to do.
150
151 if (!target_->action_values().rsp_file_contents().Parse(*value, err_))
152 return false;
153 if (!EnsureValidSubstitutions(
154 target_->action_values().rsp_file_contents().required_types(),
155 &IsValidSourceSubstitution, value->origin(), err_))
156 return false;
157
158 return true;
159 }
160
FillDepfile()161 bool ActionTargetGenerator::FillDepfile() {
162 const Value* value = scope_->GetValue(variables::kDepfile, true);
163 if (!value)
164 return true;
165
166 SubstitutionPattern depfile;
167 if (!depfile.Parse(*value, err_))
168 return false;
169 if (!EnsureSubstitutionIsInOutputDir(depfile, *value))
170 return false;
171
172 target_->action_values().set_depfile(depfile);
173 return true;
174 }
175
FillMnemonic()176 bool ActionTargetGenerator::FillMnemonic() {
177 const Value* value = scope_->GetValue(variables::kMnemonic, true);
178 if (!value)
179 return true;
180
181 if (!value->VerifyTypeIs(Value::STRING, err_))
182 return false;
183
184 auto s = value->string_value();
185 if (!base::IsStringUTF8(s)) {
186 *err_ = Err(value->origin(), "Mnemonics must be valid UTF-8");
187 return false;
188 }
189 auto widestr = base::UTF8ToUTF16(s);
190 if (std::any_of(widestr.begin(), widestr.end(), base::IsUnicodeWhitespace)) {
191 *err_ = Err(value->origin(), "Mnemonics can't contain whitespace");
192 return false;
193 }
194
195 target_->action_values().mnemonic() = std::move(s);
196 return true;
197 }
198
FillPool()199 bool ActionTargetGenerator::FillPool() {
200 const Value* value = scope_->GetValue(variables::kPool, true);
201 if (!value)
202 return true;
203
204 Label label =
205 Label::Resolve(scope_->GetSourceDir(),
206 scope_->settings()->build_settings()->root_path_utf8(),
207 ToolchainLabelForScope(scope_), *value, err_);
208 if (err_->has_error())
209 return false;
210
211 LabelPtrPair<Pool> pair(label);
212 pair.origin = target_->defined_from();
213
214 target_->set_pool(std::move(pair));
215 return true;
216 }
217
CheckOutputs()218 bool ActionTargetGenerator::CheckOutputs() {
219 const SubstitutionList& outputs = target_->action_values().outputs();
220 if (outputs.list().empty()) {
221 *err_ =
222 Err(function_call_, "Action has no outputs.",
223 "If you have no outputs, the build system can not tell when your\n"
224 "script needs to be run.");
225 return false;
226 }
227
228 if (output_type_ == Target::ACTION) {
229 if (!outputs.required_types().empty()) {
230 *err_ = Err(
231 function_call_, "Action has patterns in the output.",
232 "An action target should have the outputs completely specified. If\n"
233 "you want to provide a mapping from source to output, use an\n"
234 "\"action_foreach\" target.");
235 return false;
236 }
237 } else if (output_type_ == Target::ACTION_FOREACH) {
238 // A foreach target should always have a pattern in the outputs.
239 if (outputs.required_types().empty()) {
240 *err_ = Err(
241 function_call_, "action_foreach should have a pattern in the output.",
242 "An action_foreach target should have a source expansion pattern in\n"
243 "it to map source file to unique output file name. Otherwise, the\n"
244 "build system can't determine when your script needs to be run.");
245 return false;
246 }
247 }
248 return true;
249 }
250
FillInputs()251 bool ActionTargetGenerator::FillInputs() {
252 const Value* value = scope_->GetValue(variables::kInputs, true);
253 if (!value)
254 return true;
255
256 Target::FileList dest_inputs;
257 if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
258 scope_->GetSourceDir(), &dest_inputs, err_))
259 return false;
260 target_->config_values().inputs().swap(dest_inputs);
261 return true;
262 }
263