blob: be60f500580fc1d76181a7b475f3a57f198c21ec [file] [log] [blame]
// 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 std::string_view& 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(std::string(iter.first), 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;
}