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