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 "tools/gn/target_generator.h"
6
7#include "base/files/file_path.h"
8#include "base/logging.h"
9#include "tools/gn/build_settings.h"
10#include "tools/gn/config.h"
11#include "tools/gn/config_values_generator.h"
12#include "tools/gn/err.h"
13#include "tools/gn/filesystem_utils.h"
14#include "tools/gn/functions.h"
15#include "tools/gn/input_file.h"
16#include "tools/gn/item_node.h"
17#include "tools/gn/ninja_target_writer.h"
18#include "tools/gn/parse_tree.h"
19#include "tools/gn/scheduler.h"
20#include "tools/gn/scope.h"
21#include "tools/gn/target_manager.h"
22#include "tools/gn/token.h"
23#include "tools/gn/value.h"
24#include "tools/gn/value_extractors.h"
25#include "tools/gn/variables.h"
26
27namespace {
28
29bool TypeHasConfigs(Target::OutputType type) {
30  return type == Target::EXECUTABLE ||
31         type == Target::SHARED_LIBRARY ||
32         type == Target::STATIC_LIBRARY ||
33         type == Target::LOADABLE_MODULE;
34}
35
36bool TypeHasConfigValues(Target::OutputType type) {
37  return type == Target::EXECUTABLE ||
38         type == Target::SHARED_LIBRARY ||
39         type == Target::STATIC_LIBRARY ||
40         type == Target::LOADABLE_MODULE;
41}
42
43bool TypeHasSources(Target::OutputType type) {
44  return type != Target::NONE;
45}
46
47bool TypeHasData(Target::OutputType type) {
48  return type != Target::NONE;
49}
50
51bool TypeHasDestDir(Target::OutputType type) {
52  return type == Target::COPY_FILES;
53}
54
55bool TypeHasOutputs(Target::OutputType type) {
56  return type == Target::CUSTOM;
57}
58
59}  // namespace
60
61TargetGenerator::TargetGenerator(Target* target,
62                                 Scope* scope,
63                                 const Token& function_token,
64                                 const std::vector<Value>& args,
65                                 const std::string& output_type,
66                                 Err* err)
67    : target_(target),
68      scope_(scope),
69      function_token_(function_token),
70      args_(args),
71      output_type_(output_type),
72      err_(err),
73      input_directory_(function_token.location().file()->dir()) {
74}
75
76TargetGenerator::~TargetGenerator() {
77}
78
79void TargetGenerator::Run() {
80  // Output type.
81  Target::OutputType output_type = GetOutputType();
82  target_->set_output_type(output_type);
83  if (err_->has_error())
84    return;
85
86  if (TypeHasConfigs(output_type)) {
87    FillConfigs();
88    FillAllDependentConfigs();
89    FillDirectDependentConfigs();
90  }
91  if (TypeHasSources(output_type))
92    FillSources();
93  if (TypeHasData(output_type))
94    FillData();
95  if (output_type == Target::CUSTOM) {
96    FillScript();
97    FillScriptArgs();
98  }
99  if (TypeHasOutputs(output_type))
100    FillOutputs();
101  FillDependencies();  // All types have dependencies.
102  FillDataDependencies();  // All types have dependencies.
103
104  if (TypeHasConfigValues(output_type)) {
105    ConfigValuesGenerator gen(&target_->config_values(), scope_,
106                              function_token_, input_directory_, err_);
107    gen.Run();
108    if (err_->has_error())
109      return;
110  }
111
112  if (TypeHasDestDir(output_type))
113    FillDestDir();
114
115  // Set the toolchain as a dependency of the target.
116  // TODO(brettw) currently we lock separately for each config, dep, and
117  // toolchain we add which is bad! Do this in one lock.
118  {
119    ItemTree* tree = &GetBuildSettings()->item_tree();
120    base::AutoLock lock(tree->lock());
121    ItemNode* tc_node =
122        tree->GetExistingNodeLocked(ToolchainLabelForScope(scope_));
123    if (!tree->GetExistingNodeLocked(target_->label())->AddDependency(
124            GetBuildSettings(), function_token_.range(), tc_node, err_))
125      return;
126  }
127
128  target_->SetGenerated(&function_token_);
129  GetBuildSettings()->target_manager().TargetGenerationComplete(
130      target_->label(), err_);
131}
132
133// static
134void TargetGenerator::GenerateTarget(Scope* scope,
135                                     const Token& function_token,
136                                     const std::vector<Value>& args,
137                                     const std::string& output_type,
138                                     Err* err) {
139  // Name is the argument to the function.
140  if (args.size() != 1u || args[0].type() != Value::STRING) {
141    *err = Err(function_token,
142        "Target generator requires one string argument.",
143        "Otherwise I'm not sure what to call this target.");
144    return;
145  }
146
147  // The location of the target is the directory name with no slash at the end.
148  // FIXME(brettw) validate name.
149  const Label& toolchain_label = ToolchainLabelForScope(scope);
150  Label label(function_token.location().file()->dir(),
151              args[0].string_value(),
152              toolchain_label.dir(), toolchain_label.name());
153
154  if (g_scheduler->verbose_logging())
155    g_scheduler->Log("Generating target", label.GetUserVisibleName(true));
156
157  Target* t = scope->settings()->build_settings()->target_manager().GetTarget(
158      label, function_token.range(), NULL, err);
159  if (err->has_error())
160    return;
161
162  TargetGenerator gen(t, scope, function_token, args, output_type, err);
163  gen.Run();
164}
165
166Target::OutputType TargetGenerator::GetOutputType() const {
167  if (output_type_ == functions::kGroup)
168    return Target::NONE;
169  if (output_type_ == functions::kExecutable)
170    return Target::EXECUTABLE;
171  if (output_type_ == functions::kSharedLibrary)
172    return Target::SHARED_LIBRARY;
173  if (output_type_ == functions::kStaticLibrary)
174    return Target::STATIC_LIBRARY;
175  // TODO(brettw) what does loadable module mean?
176  //if (output_type_ == ???)
177  //  return Target::LOADABLE_MODULE;
178  if (output_type_ == functions::kCopy)
179    return Target::COPY_FILES;
180  if (output_type_ == functions::kCustom)
181    return Target::CUSTOM;
182
183  *err_ = Err(function_token_, "Not a known output type",
184              "I am very confused.");
185  return Target::NONE;
186}
187
188void TargetGenerator::FillGenericConfigs(
189    const char* var_name,
190    void (Target::*setter)(std::vector<const Config*>*)) {
191  const Value* value = scope_->GetValue(var_name, true);
192  if (!value)
193    return;
194
195  std::vector<Label> labels;
196  if (!ExtractListOfLabels(*value, input_directory_,
197                           ToolchainLabelForScope(scope_), &labels, err_))
198    return;
199
200  std::vector<const Config*> dest_configs;
201  dest_configs.resize(labels.size());
202  for (size_t i = 0; i < labels.size(); i++) {
203    dest_configs[i] = Config::GetConfig(
204        scope_->settings(),
205        value->list_value()[i].origin()->GetRange(),
206        labels[i], target_, err_);
207    if (err_->has_error())
208      return;
209  }
210  (target_->*setter)(&dest_configs);
211}
212
213void TargetGenerator::FillGenericDeps(
214    const char* var_name,
215    void (Target::*setter)(std::vector<const Target*>*)) {
216  const Value* value = scope_->GetValue(var_name, true);
217  if (!value)
218    return;
219
220  std::vector<Label> labels;
221  if (!ExtractListOfLabels(*value, input_directory_,
222                           ToolchainLabelForScope(scope_), &labels, err_))
223    return;
224
225  std::vector<const Target*> dest_deps;
226  dest_deps.resize(labels.size());
227  for (size_t i = 0; i < labels.size(); i++) {
228    dest_deps[i] = GetBuildSettings()->target_manager().GetTarget(
229        labels[i], value->list_value()[i].origin()->GetRange(), target_, err_);
230    if (err_->has_error())
231      return;
232  }
233
234  (target_->*setter)(&dest_deps);
235}
236
237void TargetGenerator::FillConfigs() {
238  FillGenericConfigs(variables::kConfigs, &Target::swap_in_configs);
239}
240
241void TargetGenerator::FillAllDependentConfigs() {
242  FillGenericConfigs(variables::kAllDependentConfigs,
243                     &Target::swap_in_all_dependent_configs);
244}
245
246void TargetGenerator::FillDirectDependentConfigs() {
247  FillGenericConfigs(variables::kDirectDependentConfigs,
248                     &Target::swap_in_direct_dependent_configs);
249}
250
251void TargetGenerator::FillSources() {
252  const Value* value = scope_->GetValue(variables::kSources, true);
253  if (!value)
254    return;
255
256  Target::FileList dest_sources;
257  if (!ExtractListOfRelativeFiles(*value, input_directory_, &dest_sources,
258                                  err_))
259    return;
260  target_->swap_in_sources(&dest_sources);
261}
262
263void TargetGenerator::FillData() {
264  // TODO(brettW) hook this up to the constant when we have cleaned up
265  // how data files are used.
266  const Value* value = scope_->GetValue("data", true);
267  if (!value)
268    return;
269
270  Target::FileList dest_data;
271  if (!ExtractListOfRelativeFiles(*value, input_directory_, &dest_data,
272                                  err_))
273    return;
274  target_->swap_in_data(&dest_data);
275}
276
277void TargetGenerator::FillDependencies() {
278  FillGenericDeps(variables::kDeps, &Target::swap_in_deps);
279}
280
281void TargetGenerator::FillDataDependencies() {
282  FillGenericDeps(variables::kDatadeps, &Target::swap_in_datadeps);
283}
284
285void TargetGenerator::FillDestDir() {
286  // Destdir is required for all targets that use it.
287  const Value* value = scope_->GetValue("destdir", true);
288  if (!value) {
289    *err_ = Err(function_token_, "This target type requires a \"destdir\".");
290    return;
291  }
292  if (!value->VerifyTypeIs(Value::STRING, err_))
293    return;
294
295  if (!EnsureStringIsInOutputDir(
296          GetBuildSettings()->build_dir(),
297          value->string_value(), *value, err_))
298    return;
299  target_->set_destdir(SourceDir(value->string_value()));
300}
301
302void TargetGenerator::FillScript() {
303  // If this gets called, the target type requires a script, so error out
304  // if it doesn't have one.
305  const Value* value = scope_->GetValue("script", true);
306  if (!value) {
307    *err_ = Err(function_token_, "This target type requires a \"script\".");
308    return;
309  }
310  if (!value->VerifyTypeIs(Value::STRING, err_))
311    return;
312
313  target_->set_script(
314      input_directory_.ResolveRelativeFile(value->string_value()));
315}
316
317void TargetGenerator::FillScriptArgs() {
318  const Value* value = scope_->GetValue("args", true);
319  if (!value)
320    return;
321
322  std::vector<std::string> args;
323  if (!ExtractListOfStringValues(*value, &args, err_))
324    return;
325  target_->swap_in_script_args(&args);
326}
327
328void TargetGenerator::FillOutputs() {
329  const Value* value = scope_->GetValue("outputs", true);
330  if (!value)
331    return;
332
333  Target::FileList outputs;
334  if (!ExtractListOfRelativeFiles(*value, input_directory_, &outputs, err_))
335    return;
336
337  // Validate that outputs are in the output dir.
338  CHECK(outputs.size() == value->list_value().size());
339  for (size_t i = 0; i < outputs.size(); i++) {
340    if (!EnsureStringIsInOutputDir(
341            GetBuildSettings()->build_dir(),
342            outputs[i].value(), value->list_value()[i], err_))
343      return;
344  }
345  target_->swap_in_outputs(&outputs);
346}
347
348const BuildSettings* TargetGenerator::GetBuildSettings() const {
349  return scope_->settings()->build_settings();
350}
351