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