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