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