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