1 // Copyright 2018 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/generated_file_target_generator.h"
6
7 #include "gn/err.h"
8 #include "gn/filesystem_utils.h"
9 #include "gn/parse_tree.h"
10 #include "gn/scope.h"
11 #include "gn/target.h"
12 #include "gn/variables.h"
13
GeneratedFileTargetGenerator( Target* target, Scope* scope, const FunctionCallNode* function_call, Target::OutputType type, Err* err)14 GeneratedFileTargetGenerator::GeneratedFileTargetGenerator(
15 Target* target,
16 Scope* scope,
17 const FunctionCallNode* function_call,
18 Target::OutputType type,
19 Err* err)
20 : TargetGenerator(target, scope, function_call, err), output_type_(type) {}
21
22 GeneratedFileTargetGenerator::~GeneratedFileTargetGenerator() = default;
23
DoRun()24 void GeneratedFileTargetGenerator::DoRun() {
25 target_->set_output_type(output_type_);
26
27 if (!FillOutputs(false))
28 return;
29 if (target_->action_values().outputs().list().size() != 1) {
30 *err_ = Err(
31 function_call_, "generated_file target must have exactly one output.",
32 "You must specify exactly one value in the \"outputs\" array for the "
33 "destination of the write\n(see \"gn help generated_file\").");
34 return;
35 }
36
37 if (!FillContents())
38 return;
39 if (!FillDataKeys())
40 return;
41
42 // One of data and data_keys should be defined.
43 if (!contents_defined_ && !data_keys_defined_) {
44 *err_ = Err(
45 function_call_, "Either contents or data_keys should be set.",
46 "The generated_file target requires either the \"contents\" variable "
47 "or the \"data_keys\" variable be set. See \"gn help "
48 "generated_file\".");
49 return;
50 }
51
52 if (!FillRebase())
53 return;
54 if (!FillWalkKeys())
55 return;
56
57 if (!FillOutputConversion())
58 return;
59 }
60
FillContents()61 bool GeneratedFileTargetGenerator::FillContents() {
62 const Value* value = scope_->GetValue(variables::kWriteValueContents, true);
63 if (!value)
64 return true;
65 target_->set_contents(*value);
66 contents_defined_ = true;
67 return true;
68 }
69
IsMetadataCollectionTarget( std::string_view variable, const ParseNode* origin)70 bool GeneratedFileTargetGenerator::IsMetadataCollectionTarget(
71 std::string_view variable,
72 const ParseNode* origin) {
73 if (contents_defined_) {
74 *err_ =
75 Err(origin, std::string(variable) + " won't be used.",
76 "\"contents\" is defined on this target, and so setting " +
77 std::string(variable) +
78 " will have no effect as no metadata collection will occur.");
79 return false;
80 }
81 return true;
82 }
83
FillOutputConversion()84 bool GeneratedFileTargetGenerator::FillOutputConversion() {
85 const Value* value =
86 scope_->GetValue(variables::kWriteOutputConversion, true);
87 if (!value) {
88 target_->set_output_conversion(Value(function_call_, ""));
89 return true;
90 }
91 if (!value->VerifyTypeIs(Value::STRING, err_))
92 return false;
93
94 // Otherwise, the value itself will be checked when the conversion is done.
95 target_->set_output_conversion(*value);
96 return true;
97 }
98
FillRebase()99 bool GeneratedFileTargetGenerator::FillRebase() {
100 const Value* value = scope_->GetValue(variables::kRebase, true);
101 if (!value)
102 return true;
103 if (!IsMetadataCollectionTarget(variables::kRebase, value->origin()))
104 return false;
105 if (!value->VerifyTypeIs(Value::STRING, err_))
106 return false;
107
108 if (value->string_value().empty())
109 return true; // Treat empty string as the default and do nothing.
110
111 const BuildSettings* build_settings = scope_->settings()->build_settings();
112 SourceDir dir = scope_->GetSourceDir().ResolveRelativeDir(
113 *value, err_, build_settings->root_path_utf8());
114 if (err_->has_error())
115 return false;
116
117 target_->set_rebase(dir);
118 return true;
119 }
120
FillDataKeys()121 bool GeneratedFileTargetGenerator::FillDataKeys() {
122 const Value* value = scope_->GetValue(variables::kDataKeys, true);
123 if (!value)
124 return true;
125 if (!IsMetadataCollectionTarget(variables::kDataKeys, value->origin()))
126 return false;
127 if (!value->VerifyTypeIs(Value::LIST, err_))
128 return false;
129
130 for (const Value& v : value->list_value()) {
131 // Keys must be strings.
132 if (!v.VerifyTypeIs(Value::STRING, err_))
133 return false;
134 target_->data_keys().push_back(v.string_value());
135 }
136
137 data_keys_defined_ = true;
138 return true;
139 }
140
FillWalkKeys()141 bool GeneratedFileTargetGenerator::FillWalkKeys() {
142 const Value* value = scope_->GetValue(variables::kWalkKeys, true);
143 // If we define this and contents, that's an error.
144 if (value &&
145 !IsMetadataCollectionTarget(variables::kWalkKeys, value->origin()))
146 return false;
147
148 // If we don't define it, we want the default value which is a list
149 // containing the empty string.
150 if (!value) {
151 target_->walk_keys().push_back("");
152 return true;
153 }
154
155 // Otherwise, pull and validate the specified value.
156 if (!value->VerifyTypeIs(Value::LIST, err_))
157 return false;
158 for (const Value& v : value->list_value()) {
159 // Keys must be strings.
160 if (!v.VerifyTypeIs(Value::STRING, err_))
161 return false;
162 target_->walk_keys().push_back(v.string_value());
163 }
164 return true;
165 }
166