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