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/action_target_generator.h"
6
7 #include "tools/gn/build_settings.h"
8 #include "tools/gn/err.h"
9 #include "tools/gn/filesystem_utils.h"
10 #include "tools/gn/parse_tree.h"
11 #include "tools/gn/scope.h"
12 #include "tools/gn/value.h"
13 #include "tools/gn/value_extractors.h"
14 #include "tools/gn/variables.h"
15
16 namespace {
17
18 // Returns true if the list of files looks like it might have a {{ }} pattern
19 // in it. Used for error checking.
FileListHasPattern(const Target::FileList & files)20 bool FileListHasPattern(const Target::FileList& files) {
21 for (size_t i = 0; i < files.size(); i++) {
22 if (files[i].value().find("{{") != std::string::npos &&
23 files[i].value().find("}}") != std::string::npos)
24 return true;
25 }
26 return false;
27 }
28
29 } // namespace
30
ActionTargetGenerator(Target * target,Scope * scope,const FunctionCallNode * function_call,Target::OutputType type,Err * err)31 ActionTargetGenerator::ActionTargetGenerator(
32 Target* target,
33 Scope* scope,
34 const FunctionCallNode* function_call,
35 Target::OutputType type,
36 Err* err)
37 : TargetGenerator(target, scope, function_call, err),
38 output_type_(type) {
39 }
40
~ActionTargetGenerator()41 ActionTargetGenerator::~ActionTargetGenerator() {
42 }
43
DoRun()44 void ActionTargetGenerator::DoRun() {
45 target_->set_output_type(output_type_);
46
47 FillSources();
48 if (err_->has_error())
49 return;
50 if (output_type_ == Target::ACTION_FOREACH && target_->sources().empty()) {
51 // Foreach rules must always have some sources to have an effect.
52 *err_ = Err(function_call_, "action_foreach target has no sources.",
53 "If you don't specify any sources, there is nothing to run your\n"
54 "script over.");
55 return;
56 }
57
58 FillInputs();
59 if (err_->has_error())
60 return;
61
62 FillScript();
63 if (err_->has_error())
64 return;
65
66 FillScriptArgs();
67 if (err_->has_error())
68 return;
69
70 FillOutputs();
71 if (err_->has_error())
72 return;
73
74 FillDepfile();
75 if (err_->has_error())
76 return;
77
78 CheckOutputs();
79 if (err_->has_error())
80 return;
81
82 // Action outputs don't depend on the current toolchain so we can skip adding
83 // that dependency.
84 }
85
FillScript()86 void ActionTargetGenerator::FillScript() {
87 // If this gets called, the target type requires a script, so error out
88 // if it doesn't have one.
89 const Value* value = scope_->GetValue(variables::kScript, true);
90 if (!value) {
91 *err_ = Err(function_call_, "This target type requires a \"script\".");
92 return;
93 }
94 if (!value->VerifyTypeIs(Value::STRING, err_))
95 return;
96
97 SourceFile script_file =
98 scope_->GetSourceDir().ResolveRelativeFile(value->string_value());
99 if (script_file.value().empty()) {
100 *err_ = Err(*value, "script name is empty");
101 return;
102 }
103 target_->action_values().set_script(script_file);
104 }
105
FillScriptArgs()106 void ActionTargetGenerator::FillScriptArgs() {
107 const Value* value = scope_->GetValue(variables::kArgs, true);
108 if (!value)
109 return;
110
111 std::vector<std::string> args;
112 if (!ExtractListOfStringValues(*value, &args, err_))
113 return;
114 target_->action_values().swap_in_args(&args);
115 }
116
FillDepfile()117 void ActionTargetGenerator::FillDepfile() {
118 const Value* value = scope_->GetValue(variables::kDepfile, true);
119 if (!value)
120 return;
121 target_->action_values().set_depfile(
122 scope_->settings()->build_settings()->build_dir().ResolveRelativeFile(
123 value->string_value()));
124 }
125
CheckOutputs()126 void ActionTargetGenerator::CheckOutputs() {
127 const Target::FileList& outputs = target_->action_values().outputs();
128 if (outputs.empty()) {
129 *err_ = Err(function_call_, "Action has no outputs.",
130 "If you have no outputs, the build system can not tell when your\n"
131 "script needs to be run.");
132 return;
133 }
134
135 if (output_type_ == Target::ACTION) {
136 // Make sure the outputs for an action have no patterns in them.
137 if (FileListHasPattern(outputs)) {
138 *err_ = Err(function_call_, "Action has patterns in the output.",
139 "An action target should have the outputs completely specified. If\n"
140 "you want to provide a mapping from source to output, use an\n"
141 "\"action_foreach\" target.");
142 return;
143 }
144 } else if (output_type_ == Target::ACTION_FOREACH) {
145 // A foreach target should always have a pattern in the outputs.
146 if (!FileListHasPattern(outputs)) {
147 *err_ = Err(function_call_,
148 "action_foreach should have a pattern in the output.",
149 "An action_foreach target should have a source expansion pattern in\n"
150 "it to map source file to unique output file name. Otherwise, the\n"
151 "build system can't determine when your script needs to be run.");
152 return;
153 }
154 }
155 }
156