• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "base/strings/stringprintf.h"
11 #include "gn/filesystem_utils.h"
12 #include "gn/label_pattern.h"
13 #include "gn/parse_tree.h"
14 #include "gn/scope.h"
15 #include "gn/substitution_type.h"
16 #include "gn/target.h"
17 #include "gn/value.h"
18 #include "gn/value_extractors.h"
19 #include "gn/variables.h"
20 
21 namespace {
22 
23 // Retrieves value from `scope` named `name` or `old_name`. If the value comes
24 // from the `old_name` a warning is emitted to inform the name is obsolete.
GetValueFromScope(Scope * scope,std::string_view name,std::string_view old_name)25 const Value* GetValueFromScope(Scope* scope,
26                                std::string_view name,
27                                std::string_view old_name) {
28   const Value* value = scope->GetValue(name, true);
29   if (value)
30     return value;
31 
32   value = scope->GetValue(old_name, true);
33   if (value) {
34     // If there is a value found with the old name, print a warning to the
35     // console and use that value. This is to avoid breaking the existing
36     // build rules in the wild.
37     Err err(*value, "Deprecated variable name",
38             base::StringPrintf(
39                 "The name \"%s\" is deprecated, use \"%s\" instead.",
40                 std::string(old_name).c_str(), std::string(name).c_str()));
41     err.PrintNonfatalToStdout();
42   }
43 
44   return value;
45 }
46 
47 }  // namespace
48 
CreateBundleTargetGenerator(Target * target,Scope * scope,const FunctionCallNode * function_call,Err * err)49 CreateBundleTargetGenerator::CreateBundleTargetGenerator(
50     Target* target,
51     Scope* scope,
52     const FunctionCallNode* function_call,
53     Err* err)
54     : TargetGenerator(target, scope, function_call, err) {}
55 
56 CreateBundleTargetGenerator::~CreateBundleTargetGenerator() = default;
57 
DoRun()58 void CreateBundleTargetGenerator::DoRun() {
59   target_->set_output_type(Target::CREATE_BUNDLE);
60 
61   BundleData& bundle_data = target_->bundle_data();
62   if (!FillBundleDir(SourceDir(), variables::kBundleRootDir,
63                      &bundle_data.root_dir()))
64     return;
65   if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleContentsDir,
66                      &bundle_data.contents_dir()))
67     return;
68   if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleResourcesDir,
69                      &bundle_data.resources_dir()))
70     return;
71   if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleExecutableDir,
72                      &bundle_data.executable_dir()))
73     return;
74 
75   if (!FillXcodeExtraAttributes())
76     return;
77 
78   if (!FillProductType())
79     return;
80 
81   if (!FillPartialInfoPlist())
82     return;
83 
84   if (!FillXcodeTestApplicationName())
85     return;
86 
87   if (!FillPostProcessingScript())
88     return;
89 
90   if (!FillPostProcessingSources())
91     return;
92 
93   if (!FillPostProcessingOutputs())
94     return;
95 
96   if (!FillPostProcessingArgs())
97     return;
98 
99   if (!FillBundleDepsFilter())
100     return;
101 
102   if (!FillXcassetCompilerFlags())
103     return;
104 
105   if (!FillTransparent())
106     return;
107 }
108 
FillBundleDir(const SourceDir & bundle_root_dir,std::string_view name,SourceDir * bundle_dir)109 bool CreateBundleTargetGenerator::FillBundleDir(
110     const SourceDir& bundle_root_dir,
111     std::string_view name,
112     SourceDir* bundle_dir) {
113   // All bundle_foo_dir properties are optional. They are only required if they
114   // are used in an expansion. The check is performed there.
115   const Value* value = scope_->GetValue(name, true);
116   if (!value)
117     return true;
118   if (!value->VerifyTypeIs(Value::STRING, err_))
119     return false;
120   std::string str = value->string_value();
121   if (!str.empty() && str[str.size() - 1] != '/')
122     str.push_back('/');
123   if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), str,
124                                  value->origin(), err_))
125     return false;
126   if (str != bundle_root_dir.value() &&
127       !IsStringInOutputDir(bundle_root_dir, str)) {
128     *err_ =
129         Err(value->origin(), "Path is not in bundle root dir.",
130             base::StringPrintf("The given file should be in the bundle root "
131                                "directory or below.Normally you would do "
132                                "\"$bundle_root_dir/foo\". I interpreted this "
133                                "as \"%s\".",
134                                str.c_str()));
135     return false;
136   }
137   *bundle_dir = SourceDir(std::move(str));
138   return true;
139 }
140 
FillXcodeExtraAttributes()141 bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() {
142   // Need to get a mutable value to mark all values in the scope as used. This
143   // cannot be done on a const Scope.
144   Value* value = scope_->GetMutableValue(variables::kXcodeExtraAttributes,
145                                          Scope::SEARCH_CURRENT, true);
146   if (!value)
147     return true;
148 
149   if (!value->VerifyTypeIs(Value::SCOPE, err_))
150     return false;
151 
152   Scope* scope_value = value->scope_value();
153 
154   Scope::KeyValueMap value_map;
155   scope_value->GetCurrentScopeValues(&value_map);
156   scope_value->MarkAllUsed();
157 
158   std::map<std::string, std::string> xcode_extra_attributes;
159   for (const auto& iter : value_map) {
160     if (!iter.second.VerifyTypeIs(Value::STRING, err_))
161       return false;
162 
163     xcode_extra_attributes.insert(
164         std::make_pair(std::string(iter.first), iter.second.string_value()));
165   }
166 
167   target_->bundle_data().xcode_extra_attributes() =
168       std::move(xcode_extra_attributes);
169   return true;
170 }
171 
FillProductType()172 bool CreateBundleTargetGenerator::FillProductType() {
173   const Value* value = scope_->GetValue(variables::kProductType, true);
174   if (!value)
175     return true;
176 
177   if (!value->VerifyTypeIs(Value::STRING, err_))
178     return false;
179 
180   target_->bundle_data().product_type().assign(value->string_value());
181   return true;
182 }
183 
FillPartialInfoPlist()184 bool CreateBundleTargetGenerator::FillPartialInfoPlist() {
185   const Value* value = scope_->GetValue(variables::kPartialInfoPlist, true);
186   if (!value)
187     return true;
188 
189   if (!value->VerifyTypeIs(Value::STRING, err_))
190     return false;
191 
192   const BuildSettings* build_settings = scope_->settings()->build_settings();
193   SourceFile path = scope_->GetSourceDir().ResolveRelativeFile(
194       *value, err_, build_settings->root_path_utf8());
195 
196   if (err_->has_error())
197     return false;
198 
199   if (!EnsureStringIsInOutputDir(build_settings->build_dir(), path.value(),
200                                  value->origin(), err_))
201     return false;
202 
203   target_->bundle_data().set_partial_info_plist(path);
204   return true;
205 }
206 
FillXcodeTestApplicationName()207 bool CreateBundleTargetGenerator::FillXcodeTestApplicationName() {
208   const Value* value =
209       scope_->GetValue(variables::kXcodeTestApplicationName, true);
210   if (!value)
211     return true;
212 
213   if (!value->VerifyTypeIs(Value::STRING, err_))
214     return false;
215 
216   target_->bundle_data().xcode_test_application_name().assign(
217       value->string_value());
218   return true;
219 }
220 
FillPostProcessingScript()221 bool CreateBundleTargetGenerator::FillPostProcessingScript() {
222   const Value* value = GetValueFromScope(
223       scope_, variables::kPostProcessingScript, variables::kCodeSigningScript);
224   if (!value)
225     return true;
226 
227   if (!value->VerifyTypeIs(Value::STRING, err_))
228     return false;
229 
230   SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile(
231       *value, err_, scope_->settings()->build_settings()->root_path_utf8());
232   if (err_->has_error())
233     return false;
234 
235   target_->bundle_data().set_post_processing_script(script_file);
236   return true;
237 }
238 
FillPostProcessingSources()239 bool CreateBundleTargetGenerator::FillPostProcessingSources() {
240   const Value* value =
241       GetValueFromScope(scope_, variables::kPostProcessingSources,
242                         variables::kCodeSigningSources);
243   if (!value)
244     return true;
245 
246   if (target_->bundle_data().post_processing_script().is_null()) {
247     *err_ = Err(function_call_, "No post-processing script.",
248                 "You must define post_processing_script if you use "
249                 "post_processing_sources.");
250     return false;
251   }
252 
253   Target::FileList script_sources;
254   if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
255                                   scope_->GetSourceDir(), &script_sources,
256                                   err_))
257     return false;
258 
259   target_->bundle_data().post_processing_sources() = std::move(script_sources);
260   return true;
261 }
262 
FillPostProcessingOutputs()263 bool CreateBundleTargetGenerator::FillPostProcessingOutputs() {
264   const Value* value =
265       GetValueFromScope(scope_, variables::kPostProcessingOutputs,
266                         variables::kCodeSigningOutputs);
267   if (!value)
268     return true;
269 
270   if (target_->bundle_data().post_processing_script().is_null()) {
271     *err_ = Err(function_call_, "No post-processing script.",
272                 "You must define post_processing_script if you use "
273                 "post_processing_outputs.");
274     return false;
275   }
276 
277   if (!value->VerifyTypeIs(Value::LIST, err_))
278     return false;
279 
280   SubstitutionList& outputs = target_->bundle_data().post_processing_outputs();
281   if (!outputs.Parse(*value, err_))
282     return false;
283 
284   if (outputs.list().empty()) {
285     *err_ = Err(function_call_, "Post-processing script has no output.",
286                 "If you have no outputs, the build system can not tell when "
287                 "your post-processing script needs to be run.");
288     return false;
289   }
290 
291   // Validate that outputs are in the output dir.
292   CHECK_EQ(value->list_value().size(), outputs.list().size());
293   for (size_t i = 0; i < value->list_value().size(); ++i) {
294     if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i],
295                                          value->list_value()[i]))
296       return false;
297   }
298 
299   return true;
300 }
301 
FillPostProcessingArgs()302 bool CreateBundleTargetGenerator::FillPostProcessingArgs() {
303   const Value* value = GetValueFromScope(scope_, variables::kPostProcessingArgs,
304                                          variables::kCodeSigningArgs);
305   if (!value)
306     return true;
307 
308   if (target_->bundle_data().post_processing_script().is_null()) {
309     *err_ = Err(function_call_, "No post-processing script.",
310                 "You must define post_processing_script if you use "
311                 "post_processing_args.");
312     return false;
313   }
314 
315   if (!value->VerifyTypeIs(Value::LIST, err_))
316     return false;
317 
318   return target_->bundle_data().post_processing_args().Parse(*value, err_);
319 }
320 
FillBundleDepsFilter()321 bool CreateBundleTargetGenerator::FillBundleDepsFilter() {
322   const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true);
323   if (!value)
324     return true;
325 
326   if (!value->VerifyTypeIs(Value::LIST, err_))
327     return false;
328 
329   const SourceDir& current_dir = scope_->GetSourceDir();
330   std::vector<LabelPattern>& bundle_deps_filter =
331       target_->bundle_data().bundle_deps_filter();
332   for (const auto& item : value->list_value()) {
333     bundle_deps_filter.push_back(LabelPattern::GetPattern(
334         current_dir, scope_->settings()->build_settings()->root_path_utf8(),
335         item, err_));
336     if (err_->has_error())
337       return false;
338   }
339 
340   return true;
341 }
342 
FillXcassetCompilerFlags()343 bool CreateBundleTargetGenerator::FillXcassetCompilerFlags() {
344   const Value* value = scope_->GetValue(variables::kXcassetCompilerFlags, true);
345   if (!value)
346     return true;
347 
348   if (!value->VerifyTypeIs(Value::LIST, err_))
349     return false;
350 
351   return target_->bundle_data().xcasset_compiler_flags().Parse(*value, err_);
352 }
353 
FillTransparent()354 bool CreateBundleTargetGenerator::FillTransparent() {
355   const Value* value = scope_->GetValue(variables::kTransparent, true);
356   if (!value)
357     return true;
358 
359   if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
360     return false;
361 
362   target_->bundle_data().set_transparent(value->boolean_value());
363   return true;
364 }
365