1 // Copyright 2016 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/create_bundle_target_generator.h"
6
7 #include <map>
8
9 #include "base/logging.h"
10 #include "gn/filesystem_utils.h"
11 #include "gn/label_pattern.h"
12 #include "gn/parse_tree.h"
13 #include "gn/scope.h"
14 #include "gn/substitution_type.h"
15 #include "gn/target.h"
16 #include "gn/value.h"
17 #include "gn/value_extractors.h"
18 #include "gn/variables.h"
19
CreateBundleTargetGenerator(Target * target,Scope * scope,const FunctionCallNode * function_call,Err * err)20 CreateBundleTargetGenerator::CreateBundleTargetGenerator(
21 Target* target,
22 Scope* scope,
23 const FunctionCallNode* function_call,
24 Err* err)
25 : TargetGenerator(target, scope, function_call, err) {}
26
27 CreateBundleTargetGenerator::~CreateBundleTargetGenerator() = default;
28
DoRun()29 void CreateBundleTargetGenerator::DoRun() {
30 target_->set_output_type(Target::CREATE_BUNDLE);
31
32 BundleData& bundle_data = target_->bundle_data();
33 if (!FillBundleDir(SourceDir(), variables::kBundleRootDir,
34 &bundle_data.root_dir()))
35 return;
36 if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleContentsDir,
37 &bundle_data.contents_dir()))
38 return;
39 if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleResourcesDir,
40 &bundle_data.resources_dir()))
41 return;
42 if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleExecutableDir,
43 &bundle_data.executable_dir()))
44 return;
45
46 if (!FillXcodeExtraAttributes())
47 return;
48
49 if (!FillProductType())
50 return;
51
52 if (!FillPartialInfoPlist())
53 return;
54
55 if (!FillXcodeTestApplicationName())
56 return;
57
58 if (!FillCodeSigningScript())
59 return;
60
61 if (!FillCodeSigningSources())
62 return;
63
64 if (!FillCodeSigningOutputs())
65 return;
66
67 if (!FillCodeSigningArgs())
68 return;
69
70 if (!FillBundleDepsFilter())
71 return;
72 }
73
FillBundleDir(const SourceDir & bundle_root_dir,const std::string_view & name,SourceDir * bundle_dir)74 bool CreateBundleTargetGenerator::FillBundleDir(
75 const SourceDir& bundle_root_dir,
76 const std::string_view& name,
77 SourceDir* bundle_dir) {
78 // All bundle_foo_dir properties are optional. They are only required if they
79 // are used in an expansion. The check is performed there.
80 const Value* value = scope_->GetValue(name, true);
81 if (!value)
82 return true;
83 if (!value->VerifyTypeIs(Value::STRING, err_))
84 return false;
85 std::string str = value->string_value();
86 if (!str.empty() && str[str.size() - 1] != '/')
87 str.push_back('/');
88 if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), str,
89 value->origin(), err_))
90 return false;
91 if (str != bundle_root_dir.value() &&
92 !IsStringInOutputDir(bundle_root_dir, str)) {
93 *err_ = Err(
94 value->origin(), "Path is not in bundle root dir.",
95 "The given file should be in the bundle root directory or below.\n"
96 "Normally you would do \"$bundle_root_dir/foo\". I interpreted this\n"
97 "as \"" +
98 str + "\".");
99 return false;
100 }
101 *bundle_dir = SourceDir(std::move(str));
102 return true;
103 }
104
FillXcodeExtraAttributes()105 bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() {
106 // Need to get a mutable value to mark all values in the scope as used. This
107 // cannot be done on a const Scope.
108 Value* value = scope_->GetMutableValue(variables::kXcodeExtraAttributes,
109 Scope::SEARCH_CURRENT, true);
110 if (!value)
111 return true;
112
113 if (!value->VerifyTypeIs(Value::SCOPE, err_))
114 return false;
115
116 Scope* scope_value = value->scope_value();
117
118 Scope::KeyValueMap value_map;
119 scope_value->GetCurrentScopeValues(&value_map);
120 scope_value->MarkAllUsed();
121
122 std::map<std::string, std::string> xcode_extra_attributes;
123 for (const auto& iter : value_map) {
124 if (!iter.second.VerifyTypeIs(Value::STRING, err_))
125 return false;
126
127 xcode_extra_attributes.insert(
128 std::make_pair(std::string(iter.first), iter.second.string_value()));
129 }
130
131 target_->bundle_data().xcode_extra_attributes() =
132 std::move(xcode_extra_attributes);
133 return true;
134 }
135
FillProductType()136 bool CreateBundleTargetGenerator::FillProductType() {
137 const Value* value = scope_->GetValue(variables::kProductType, true);
138 if (!value)
139 return true;
140
141 if (!value->VerifyTypeIs(Value::STRING, err_))
142 return false;
143
144 target_->bundle_data().product_type().assign(value->string_value());
145 return true;
146 }
147
FillPartialInfoPlist()148 bool CreateBundleTargetGenerator::FillPartialInfoPlist() {
149 const Value* value = scope_->GetValue(variables::kPartialInfoPlist, true);
150 if (!value)
151 return true;
152
153 if (!value->VerifyTypeIs(Value::STRING, err_))
154 return false;
155
156 const BuildSettings* build_settings = scope_->settings()->build_settings();
157 SourceFile path = scope_->GetSourceDir().ResolveRelativeFile(
158 *value, err_, build_settings->root_path_utf8());
159
160 if (err_->has_error())
161 return false;
162
163 if (!EnsureStringIsInOutputDir(build_settings->build_dir(), path.value(),
164 value->origin(), err_))
165 return false;
166
167 target_->bundle_data().set_partial_info_plist(path);
168 return true;
169 }
170
FillXcodeTestApplicationName()171 bool CreateBundleTargetGenerator::FillXcodeTestApplicationName() {
172 const Value* value =
173 scope_->GetValue(variables::kXcodeTestApplicationName, true);
174 if (!value)
175 return true;
176
177 if (!value->VerifyTypeIs(Value::STRING, err_))
178 return false;
179
180 target_->bundle_data().xcode_test_application_name().assign(
181 value->string_value());
182 return true;
183 }
184
FillCodeSigningScript()185 bool CreateBundleTargetGenerator::FillCodeSigningScript() {
186 const Value* value = scope_->GetValue(variables::kCodeSigningScript, true);
187 if (!value)
188 return true;
189
190 if (!value->VerifyTypeIs(Value::STRING, err_))
191 return false;
192
193 SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
194 *value, err_, scope_->settings()->build_settings()->root_path_utf8());
195 if (err_->has_error())
196 return false;
197
198 target_->bundle_data().set_code_signing_script(script_file);
199 return true;
200 }
201
FillCodeSigningSources()202 bool CreateBundleTargetGenerator::FillCodeSigningSources() {
203 const Value* value = scope_->GetValue(variables::kCodeSigningSources, true);
204 if (!value)
205 return true;
206
207 if (target_->bundle_data().code_signing_script().is_null()) {
208 *err_ = Err(
209 function_call_,
210 "No code signing script."
211 "You must define code_signing_script if you use code_signing_sources.");
212 return false;
213 }
214
215 Target::FileList script_sources;
216 if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
217 scope_->GetSourceDir(), &script_sources,
218 err_))
219 return false;
220
221 target_->bundle_data().code_signing_sources() = std::move(script_sources);
222 return true;
223 }
224
FillCodeSigningOutputs()225 bool CreateBundleTargetGenerator::FillCodeSigningOutputs() {
226 const Value* value = scope_->GetValue(variables::kCodeSigningOutputs, true);
227 if (!value)
228 return true;
229
230 if (target_->bundle_data().code_signing_script().is_null()) {
231 *err_ = Err(
232 function_call_,
233 "No code signing script."
234 "You must define code_signing_script if you use code_signing_outputs.");
235 return false;
236 }
237
238 if (!value->VerifyTypeIs(Value::LIST, err_))
239 return false;
240
241 SubstitutionList& outputs = target_->bundle_data().code_signing_outputs();
242 if (!outputs.Parse(*value, err_))
243 return false;
244
245 if (outputs.list().empty()) {
246 *err_ =
247 Err(function_call_,
248 "Code signing script has no output."
249 "If you have no outputs, the build system can not tell when your\n"
250 "code signing script needs to be run.");
251 return false;
252 }
253
254 // Validate that outputs are in the output dir.
255 CHECK_EQ(value->list_value().size(), outputs.list().size());
256 for (size_t i = 0; i < value->list_value().size(); ++i) {
257 if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
258 value->list_value()[i]))
259 return false;
260 }
261
262 return true;
263 }
264
FillCodeSigningArgs()265 bool CreateBundleTargetGenerator::FillCodeSigningArgs() {
266 const Value* value = scope_->GetValue(variables::kCodeSigningArgs, true);
267 if (!value)
268 return true;
269
270 if (target_->bundle_data().code_signing_script().is_null()) {
271 *err_ = Err(
272 function_call_,
273 "No code signing script."
274 "You must define code_signing_script if you use code_signing_args.");
275 return false;
276 }
277
278 if (!value->VerifyTypeIs(Value::LIST, err_))
279 return false;
280
281 return target_->bundle_data().code_signing_args().Parse(*value, err_);
282 }
283
FillBundleDepsFilter()284 bool CreateBundleTargetGenerator::FillBundleDepsFilter() {
285 const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true);
286 if (!value)
287 return true;
288
289 if (!value->VerifyTypeIs(Value::LIST, err_))
290 return false;
291
292 const SourceDir& current_dir = scope_->GetSourceDir();
293 std::vector<LabelPattern>& bundle_deps_filter =
294 target_->bundle_data().bundle_deps_filter();
295 for (const auto& item : value->list_value()) {
296 bundle_deps_filter.push_back(LabelPattern::GetPattern(
297 current_dir, scope_->settings()->build_settings()->root_path_utf8(),
298 item, err_));
299 if (err_->has_error())
300 return false;
301 }
302
303 return true;
304 }
305