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