| // Copyright 2016 The Chromium Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "tools/gn/create_bundle_target_generator.h" | |
| #include <map> | |
| #include "base/logging.h" | |
| #include "tools/gn/filesystem_utils.h" | |
| #include "tools/gn/label_pattern.h" | |
| #include "tools/gn/parse_tree.h" | |
| #include "tools/gn/scope.h" | |
| #include "tools/gn/substitution_type.h" | |
| #include "tools/gn/target.h" | |
| #include "tools/gn/value.h" | |
| #include "tools/gn/value_extractors.h" | |
| #include "tools/gn/variables.h" | |
| CreateBundleTargetGenerator::CreateBundleTargetGenerator( | |
| Target* target, | |
| Scope* scope, | |
| const FunctionCallNode* function_call, | |
| Err* err) | |
| : TargetGenerator(target, scope, function_call, err) {} | |
| CreateBundleTargetGenerator::~CreateBundleTargetGenerator() = default; | |
| void CreateBundleTargetGenerator::DoRun() { | |
| target_->set_output_type(Target::CREATE_BUNDLE); | |
| BundleData& bundle_data = target_->bundle_data(); | |
| if (!FillBundleDir(SourceDir(), variables::kBundleRootDir, | |
| &bundle_data.root_dir())) | |
| return; | |
| if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleContentsDir, | |
| &bundle_data.contents_dir())) | |
| return; | |
| if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleResourcesDir, | |
| &bundle_data.resources_dir())) | |
| return; | |
| if (!FillBundleDir(bundle_data.root_dir(), variables::kBundleExecutableDir, | |
| &bundle_data.executable_dir())) | |
| return; | |
| if (!FillXcodeExtraAttributes()) | |
| return; | |
| if (!FillProductType()) | |
| return; | |
| if (!FillPartialInfoPlist()) | |
| return; | |
| if (!FillXcodeTestApplicationName()) | |
| return; | |
| if (!FillCodeSigningScript()) | |
| return; | |
| if (!FillCodeSigningSources()) | |
| return; | |
| if (!FillCodeSigningOutputs()) | |
| return; | |
| if (!FillCodeSigningArgs()) | |
| return; | |
| if (!FillBundleDepsFilter()) | |
| return; | |
| } | |
| bool CreateBundleTargetGenerator::FillBundleDir( | |
| const SourceDir& bundle_root_dir, | |
| const base::StringPiece& name, | |
| SourceDir* bundle_dir) { | |
| // All bundle_foo_dir properties are optional. They are only required if they | |
| // are used in an expansion. The check is performed there. | |
| const Value* value = scope_->GetValue(name, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| std::string str = value->string_value(); | |
| if (!str.empty() && str[str.size() - 1] != '/') | |
| str.push_back('/'); | |
| if (!EnsureStringIsInOutputDir(GetBuildSettings()->build_dir(), str, | |
| value->origin(), err_)) | |
| return false; | |
| if (str != bundle_root_dir.value() && | |
| !IsStringInOutputDir(bundle_root_dir, str)) { | |
| *err_ = Err( | |
| value->origin(), "Path is not in bundle root dir.", | |
| "The given file should be in the bundle root directory or below.\n" | |
| "Normally you would do \"$bundle_root_dir/foo\". I interpreted this\n" | |
| "as \"" + | |
| str + "\"."); | |
| return false; | |
| } | |
| *bundle_dir = SourceDir(std::move(str)); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillXcodeExtraAttributes() { | |
| // Need to get a mutable value to mark all values in the scope as used. This | |
| // cannot be done on a const Scope. | |
| Value* value = scope_->GetMutableValue(variables::kXcodeExtraAttributes, | |
| Scope::SEARCH_CURRENT, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::SCOPE, err_)) | |
| return false; | |
| Scope* scope_value = value->scope_value(); | |
| Scope::KeyValueMap value_map; | |
| scope_value->GetCurrentScopeValues(&value_map); | |
| scope_value->MarkAllUsed(); | |
| std::map<std::string, std::string> xcode_extra_attributes; | |
| for (const auto& iter : value_map) { | |
| if (!iter.second.VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| xcode_extra_attributes.insert( | |
| std::make_pair(iter.first.as_string(), iter.second.string_value())); | |
| } | |
| target_->bundle_data().xcode_extra_attributes() = | |
| std::move(xcode_extra_attributes); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillProductType() { | |
| const Value* value = scope_->GetValue(variables::kProductType, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| target_->bundle_data().product_type().assign(value->string_value()); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillPartialInfoPlist() { | |
| const Value* value = scope_->GetValue(variables::kPartialInfoPlist, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| const BuildSettings* build_settings = scope_->settings()->build_settings(); | |
| SourceFile path = scope_->GetSourceDir().ResolveRelativeFile( | |
| *value, err_, build_settings->root_path_utf8()); | |
| if (err_->has_error()) | |
| return false; | |
| if (!EnsureStringIsInOutputDir(build_settings->build_dir(), path.value(), | |
| value->origin(), err_)) | |
| return false; | |
| target_->bundle_data().set_partial_info_plist(path); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillXcodeTestApplicationName() { | |
| const Value* value = | |
| scope_->GetValue(variables::kXcodeTestApplicationName, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| target_->bundle_data().xcode_test_application_name().assign( | |
| value->string_value()); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillCodeSigningScript() { | |
| const Value* value = scope_->GetValue(variables::kCodeSigningScript, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::STRING, err_)) | |
| return false; | |
| SourceFile script_file = scope_->GetSourceDir().ResolveRelativeFile( | |
| *value, err_, scope_->settings()->build_settings()->root_path_utf8()); | |
| if (err_->has_error()) | |
| return false; | |
| target_->bundle_data().set_code_signing_script(script_file); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillCodeSigningSources() { | |
| const Value* value = scope_->GetValue(variables::kCodeSigningSources, true); | |
| if (!value) | |
| return true; | |
| if (target_->bundle_data().code_signing_script().is_null()) { | |
| *err_ = Err( | |
| function_call_, | |
| "No code signing script." | |
| "You must define code_signing_script if you use code_signing_sources."); | |
| return false; | |
| } | |
| Target::FileList script_sources; | |
| if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value, | |
| scope_->GetSourceDir(), &script_sources, | |
| err_)) | |
| return false; | |
| target_->bundle_data().code_signing_sources() = std::move(script_sources); | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillCodeSigningOutputs() { | |
| const Value* value = scope_->GetValue(variables::kCodeSigningOutputs, true); | |
| if (!value) | |
| return true; | |
| if (target_->bundle_data().code_signing_script().is_null()) { | |
| *err_ = Err( | |
| function_call_, | |
| "No code signing script." | |
| "You must define code_signing_script if you use code_signing_outputs."); | |
| return false; | |
| } | |
| if (!value->VerifyTypeIs(Value::LIST, err_)) | |
| return false; | |
| SubstitutionList& outputs = target_->bundle_data().code_signing_outputs(); | |
| if (!outputs.Parse(*value, err_)) | |
| return false; | |
| if (outputs.list().empty()) { | |
| *err_ = | |
| Err(function_call_, | |
| "Code signing script has no output." | |
| "If you have no outputs, the build system can not tell when your\n" | |
| "code signing script needs to be run."); | |
| return false; | |
| } | |
| // Validate that outputs are in the output dir. | |
| CHECK_EQ(value->list_value().size(), outputs.list().size()); | |
| for (size_t i = 0; i < value->list_value().size(); ++i) { | |
| if (!EnsureSubstitutionIsInOutputDir(outputs.list()[i], | |
| value->list_value()[i])) | |
| return false; | |
| } | |
| return true; | |
| } | |
| bool CreateBundleTargetGenerator::FillCodeSigningArgs() { | |
| const Value* value = scope_->GetValue(variables::kCodeSigningArgs, true); | |
| if (!value) | |
| return true; | |
| if (target_->bundle_data().code_signing_script().is_null()) { | |
| *err_ = Err( | |
| function_call_, | |
| "No code signing script." | |
| "You must define code_signing_script if you use code_signing_args."); | |
| return false; | |
| } | |
| if (!value->VerifyTypeIs(Value::LIST, err_)) | |
| return false; | |
| return target_->bundle_data().code_signing_args().Parse(*value, err_); | |
| } | |
| bool CreateBundleTargetGenerator::FillBundleDepsFilter() { | |
| const Value* value = scope_->GetValue(variables::kBundleDepsFilter, true); | |
| if (!value) | |
| return true; | |
| if (!value->VerifyTypeIs(Value::LIST, err_)) | |
| return false; | |
| const SourceDir& current_dir = scope_->GetSourceDir(); | |
| std::vector<LabelPattern>& bundle_deps_filter = | |
| target_->bundle_data().bundle_deps_filter(); | |
| for (const auto& item : value->list_value()) { | |
| bundle_deps_filter.push_back( | |
| LabelPattern::GetPattern(current_dir, item, err_)); | |
| if (err_->has_error()) | |
| return false; | |
| } | |
| return true; | |
| } |