• 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 "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