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