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 if (!FillXcassetCompilerFlags())
74 return;
75 }
76
FillBundleDir(const SourceDir & bundle_root_dir,std::string_view name,SourceDir * bundle_dir)77 bool CreateBundleTargetGenerator::FillBundleDir(
78 const SourceDir& bundle_root_dir,
79 std::string_view name,
80 SourceDir* bundle_dir) {
81 // All bundle_foo_dir properties are optional. They are only required if they
82 // are used in an expansion. The check is performed there.
83 const Value* value = scope_->GetValue(name, true);
84 if (!value)
85 return true;
86 if (!value->VerifyTypeIs(Value::STRING, err_))
87 return false;
88 std::string str = value->string_value();
89 if (!str.empty() && str[str.size() - 1] != '/')
90 str.push_back('/');
91 if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), str,
92 value->origin(), err_))
93 return false;
94 if (str != bundle_root_dir.value() &&
95 !IsStringInOutputDir(bundle_root_dir, str)) {
96 *err_ = Err(
97 value->origin(), "Path is not in bundle root dir.",
98 "The given file should be in the bundle root directory or below.\n"
99 "Normally you would do \"$bundle_root_dir/foo\". I interpreted this\n"
100 "as \"" +
101 str + "\".");
102 return false;
103 }
104 *bundle_dir = SourceDir(std::move(str));
105 return true;
106 }
107
FillXcodeExtraAttributes()108 bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() {
109 // Need to get a mutable value to mark all values in the scope as used. This
110 // cannot be done on a const Scope.
111 Value* value = scope_->GetMutableValue(variables::kXcodeExtraAttributes,
112 Scope::SEARCH_CURRENT, true);
113 if (!value)
114 return true;
115
116 if (!value->VerifyTypeIs(Value::SCOPE, err_))
117 return false;
118
119 Scope* scope_value = value->scope_value();
120
121 Scope::KeyValueMap value_map;
122 scope_value->GetCurrentScopeValues(&value_map);
123 scope_value->MarkAllUsed();
124
125 std::map<std::string, std::string> xcode_extra_attributes;
126 for (const auto& iter : value_map) {
127 if (!iter.second.VerifyTypeIs(Value::STRING, err_))
128 return false;
129
130 xcode_extra_attributes.insert(
131 std::make_pair(std::string(iter.first), iter.second.string_value()));
132 }
133
134 target_->bundle_data().xcode_extra_attributes() =
135 std::move(xcode_extra_attributes);
136 return true;
137 }
138
FillProductType()139 bool CreateBundleTargetGenerator::FillProductType() {
140 const Value* value = scope_->GetValue(variables::kProductType, true);
141 if (!value)
142 return true;
143
144 if (!value->VerifyTypeIs(Value::STRING, err_))
145 return false;
146
147 target_->bundle_data().product_type().assign(value->string_value());
148 return true;
149 }
150
FillPartialInfoPlist()151 bool CreateBundleTargetGenerator::FillPartialInfoPlist() {
152 const Value* value = scope_->GetValue(variables::kPartialInfoPlist, true);
153 if (!value)
154 return true;
155
156 if (!value->VerifyTypeIs(Value::STRING, err_))
157 return false;
158
159 const BuildSettings* build_settings = scope_->settings()->build_settings();
160 SourceFile path = scope_->GetSourceDir().ResolveRelativeFile(
161 *value, err_, build_settings->root_path_utf8());
162
163 if (err_->has_error())
164 return false;
165
166 if (!EnsureStringIsInOutputDir(build_settings->build_dir(), path.value(),
167 value->origin(), err_))
168 return false;
169
170 target_->bundle_data().set_partial_info_plist(path);
171 return true;
172 }
173
FillXcodeTestApplicationName()174 bool CreateBundleTargetGenerator::FillXcodeTestApplicationName() {
175 const Value* value =
176 scope_->GetValue(variables::kXcodeTestApplicationName, true);
177 if (!value)
178 return true;
179
180 if (!value->VerifyTypeIs(Value::STRING, err_))
181 return false;
182
183 target_->bundle_data().xcode_test_application_name().assign(
184 value->string_value());
185 return true;
186 }
187
FillCodeSigningScript()188 bool CreateBundleTargetGenerator::FillCodeSigningScript() {
189 const Value* value = scope_->GetValue(variables::kCodeSigningScript, true);
190 if (!value)
191 return true;
192
193 if (!value->VerifyTypeIs(Value::STRING, err_))
194 return false;
195
196 SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
197 *value, err_, scope_->settings()->build_settings()->root_path_utf8());
198 if (err_->has_error())
199 return false;
200
201 target_->bundle_data().set_code_signing_script(script_file);
202 return true;
203 }
204
FillCodeSigningSources()205 bool CreateBundleTargetGenerator::FillCodeSigningSources() {
206 const Value* value = scope_->GetValue(variables::kCodeSigningSources, true);
207 if (!value)
208 return true;
209
210 if (target_->bundle_data().code_signing_script().is_null()) {
211 *err_ = Err(
212 function_call_,
213 "No code signing script."
214 "You must define code_signing_script if you use code_signing_sources.");
215 return false;
216 }
217
218 Target::FileList script_sources;
219 if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
220 scope_->GetSourceDir(), &script_sources,
221 err_))
222 return false;
223
224 target_->bundle_data().code_signing_sources() = std::move(script_sources);
225 return true;
226 }
227
FillCodeSigningOutputs()228 bool CreateBundleTargetGenerator::FillCodeSigningOutputs() {
229 const Value* value = scope_->GetValue(variables::kCodeSigningOutputs, true);
230 if (!value)
231 return true;
232
233 if (target_->bundle_data().code_signing_script().is_null()) {
234 *err_ = Err(
235 function_call_,
236 "No code signing script."
237 "You must define code_signing_script if you use code_signing_outputs.");
238 return false;
239 }
240
241 if (!value->VerifyTypeIs(Value::LIST, err_))
242 return false;
243
244 SubstitutionList& outputs = target_->bundle_data().code_signing_outputs();
245 if (!outputs.Parse(*value, err_))
246 return false;
247
248 if (outputs.list().empty()) {
249 *err_ =
250 Err(function_call_,
251 "Code signing script has no output."
252 "If you have no outputs, the build system can not tell when your\n"
253 "code signing script needs to be run.");
254 return false;
255 }
256
257 // Validate that outputs are in the output dir.
258 CHECK_EQ(value->list_value().size(), outputs.list().size());
259 for (size_t i = 0; i < value->list_value().size(); ++i) {
260 if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
261 value->list_value()[i]))
262 return false;
263 }
264
265 return true;
266 }
267
FillCodeSigningArgs()268 bool CreateBundleTargetGenerator::FillCodeSigningArgs() {
269 const Value* value = scope_->GetValue(variables::kCodeSigningArgs, true);
270 if (!value)
271 return true;
272
273 if (target_->bundle_data().code_signing_script().is_null()) {
274 *err_ = Err(
275 function_call_,
276 "No code signing script."
277 "You must define code_signing_script if you use code_signing_args.");
278 return false;
279 }
280
281 if (!value->VerifyTypeIs(Value::LIST, err_))
282 return false;
283
284 return target_->bundle_data().code_signing_args().Parse(*value, err_);
285 }
286
FillBundleDepsFilter()287 bool CreateBundleTargetGenerator::FillBundleDepsFilter() {
288 const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true);
289 if (!value)
290 return true;
291
292 if (!value->VerifyTypeIs(Value::LIST, err_))
293 return false;
294
295 const SourceDir& current_dir = scope_->GetSourceDir();
296 std::vector<LabelPattern>& bundle_deps_filter =
297 target_->bundle_data().bundle_deps_filter();
298 for (const auto& item : value->list_value()) {
299 bundle_deps_filter.push_back(LabelPattern::GetPattern(
300 current_dir, scope_->settings()->build_settings()->root_path_utf8(),
301 item, err_));
302 if (err_->has_error())
303 return false;
304 }
305
306 return true;
307 }
308
FillXcassetCompilerFlags()309 bool CreateBundleTargetGenerator::FillXcassetCompilerFlags() {
310 const Value* value = scope_->GetValue(variables::kXcassetCompilerFlags, true);
311 if (!value)
312 return true;
313
314 if (!value->VerifyTypeIs(Value::LIST, err_))
315 return false;
316
317 return target_->bundle_data().xcasset_compiler_flags().Parse(*value, err_);
318 }
319