blob: 3bb51eb9863c6c9855ef8ff0a9241d5c46cb44de [file] [log] [blame]
// Copyright (c) 2013 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/target_generator.h"
#include "tools/gn/binary_target_generator.h"
#include "tools/gn/build_settings.h"
#include "tools/gn/config.h"
#include "tools/gn/copy_target_generator.h"
#include "tools/gn/err.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/functions.h"
#include "tools/gn/group_target_generator.h"
#include "tools/gn/item_node.h"
#include "tools/gn/parse_tree.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/scope.h"
#include "tools/gn/script_target_generator.h"
#include "tools/gn/target_manager.h"
#include "tools/gn/token.h"
#include "tools/gn/value.h"
#include "tools/gn/value_extractors.h"
#include "tools/gn/variables.h"
TargetGenerator::TargetGenerator(Target* target,
Scope* scope,
const Token& function_token,
Err* err)
: target_(target),
scope_(scope),
function_token_(function_token),
err_(err) {
}
TargetGenerator::~TargetGenerator() {
}
void TargetGenerator::Run() {
// All target types use these.
FillDependentConfigs();
FillData();
FillDependencies();
FillGypFile();
// To type-specific generation.
DoRun();
// Mark the target as complete.
if (!err_->has_error()) {
target_->SetGenerated(&function_token_);
GetBuildSettings()->target_manager().TargetGenerationComplete(
target_->label(), err_);
}
}
// static
void TargetGenerator::GenerateTarget(Scope* scope,
const Token& function_token,
const std::vector<Value>& args,
const std::string& output_type,
Err* err) {
// Name is the argument to the function.
if (args.size() != 1u || args[0].type() != Value::STRING) {
*err = Err(function_token,
"Target generator requires one string argument.",
"Otherwise I'm not sure what to call this target.");
return;
}
// The location of the target is the directory name with no slash at the end.
// FIXME(brettw) validate name.
const Label& toolchain_label = ToolchainLabelForScope(scope);
Label label(scope->GetSourceDir(), args[0].string_value(),
toolchain_label.dir(), toolchain_label.name());
if (g_scheduler->verbose_logging())
g_scheduler->Log("Generating target", label.GetUserVisibleName(true));
Target* target =
scope->settings()->build_settings()->target_manager().GetTarget(
label, function_token.range(), NULL, err);
if (err->has_error())
return;
// Create and call out to the proper generator.
if (output_type == functions::kCopy) {
CopyTargetGenerator generator(target, scope, function_token, err);
generator.Run();
} else if (output_type == functions::kCustom) {
ScriptTargetGenerator generator(target, scope, function_token, err);
generator.Run();
} else if (output_type == functions::kExecutable) {
BinaryTargetGenerator generator(target, scope, function_token,
Target::EXECUTABLE, err);
generator.Run();
} else if (output_type == functions::kGroup) {
GroupTargetGenerator generator(target, scope, function_token, err);
generator.Run();
} else if (output_type == functions::kSharedLibrary) {
BinaryTargetGenerator generator(target, scope, function_token,
Target::SHARED_LIBRARY, err);
generator.Run();
} else if (output_type == functions::kSourceSet) {
BinaryTargetGenerator generator(target, scope, function_token,
Target::SOURCE_SET, err);
generator.Run();
} else if (output_type == functions::kStaticLibrary) {
BinaryTargetGenerator generator(target, scope, function_token,
Target::STATIC_LIBRARY, err);
generator.Run();
} else {
*err = Err(function_token, "Not a known output type",
"I am very confused.");
}
}
const BuildSettings* TargetGenerator::GetBuildSettings() const {
return scope_->settings()->build_settings();
}
void TargetGenerator::FillSources() {
const Value* value = scope_->GetValue(variables::kSources, true);
if (!value)
return;
Target::FileList dest_sources;
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
scope_->GetSourceDir(), &dest_sources, err_))
return;
target_->sources().swap(dest_sources);
}
void TargetGenerator::FillSourcePrereqs() {
const Value* value = scope_->GetValue(variables::kSourcePrereqs, true);
if (!value)
return;
Target::FileList dest_reqs;
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
scope_->GetSourceDir(), &dest_reqs, err_))
return;
target_->source_prereqs().swap(dest_reqs);
}
void TargetGenerator::FillConfigs() {
FillGenericConfigs(variables::kConfigs, &target_->configs());
}
void TargetGenerator::FillDependentConfigs() {
FillGenericConfigs(variables::kAllDependentConfigs,
&target_->all_dependent_configs());
FillGenericConfigs(variables::kDirectDependentConfigs,
&target_->direct_dependent_configs());
}
void TargetGenerator::FillData() {
// TODO(brettW) hook this up to the constant when we have cleaned up
// how data files are used.
const Value* value = scope_->GetValue(variables::kData, true);
if (!value)
return;
Target::FileList dest_data;
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
scope_->GetSourceDir(), &dest_data, err_))
return;
target_->data().swap(dest_data);
}
void TargetGenerator::FillDependencies() {
FillGenericDeps(variables::kDeps, &target_->deps());
FillGenericDeps(variables::kDatadeps, &target_->datadeps());
// This is a list of dependent targets to have their configs fowarded, so
// it goes here rather than in FillConfigs.
FillForwardDependentConfigs();
FillHardDep();
}
void TargetGenerator::FillGypFile() {
const Value* gyp_file_value = scope_->GetValue(variables::kGypFile, true);
if (!gyp_file_value)
return;
if (!gyp_file_value->VerifyTypeIs(Value::STRING, err_))
return;
target_->set_gyp_file(scope_->GetSourceDir().ResolveRelativeFile(
gyp_file_value->string_value()));
}
void TargetGenerator::FillHardDep() {
const Value* hard_dep_value = scope_->GetValue(variables::kHardDep, true);
if (!hard_dep_value)
return;
if (!hard_dep_value->VerifyTypeIs(Value::BOOLEAN, err_))
return;
target_->set_hard_dep(hard_dep_value->boolean_value());
}
void TargetGenerator::FillExternal() {
const Value* value = scope_->GetValue(variables::kExternal, true);
if (!value)
return;
if (!value->VerifyTypeIs(Value::BOOLEAN, err_))
return;
target_->set_external(value->boolean_value());
}
void TargetGenerator::FillOutputs() {
const Value* value = scope_->GetValue(variables::kOutputs, true);
if (!value)
return;
Target::FileList outputs;
if (!ExtractListOfRelativeFiles(scope_->settings()->build_settings(), *value,
scope_->GetSourceDir(), &outputs, err_))
return;
// Validate that outputs are in the output dir.
CHECK(outputs.size() == value->list_value().size());
for (size_t i = 0; i < outputs.size(); i++) {
if (!EnsureStringIsInOutputDir(
GetBuildSettings()->build_dir(),
outputs[i].value(), value->list_value()[i], err_))
return;
}
target_->script_values().outputs().swap(outputs);
}
void TargetGenerator::SetToolchainDependency() {
// TODO(brettw) currently we lock separately for each config, dep, and
// toolchain we add which is bad! Do this in one lock.
ItemTree* tree = &GetBuildSettings()->item_tree();
base::AutoLock lock(tree->lock());
ItemNode* tc_node =
tree->GetExistingNodeLocked(ToolchainLabelForScope(scope_));
target_->item_node()->AddDependency(
GetBuildSettings(), function_token_.range(), tc_node, err_);
}
void TargetGenerator::FillGenericConfigs(const char* var_name,
LabelConfigVector* dest) {
const Value* value = scope_->GetValue(var_name, true);
if (!value)
return;
if (!ExtractListOfLabels(*value, scope_->GetSourceDir(),
ToolchainLabelForScope(scope_), dest, err_))
return;
for (size_t i = 0; i < dest->size(); i++) {
LabelConfigPair& cur = (*dest)[i];
cur.ptr = Config::GetConfig(scope_->settings(),
value->list_value()[i].origin()->GetRange(),
cur.label, target_, err_);
if (err_->has_error())
return;
}
}
void TargetGenerator::FillGenericDeps(const char* var_name,
LabelTargetVector* dest) {
const Value* value = scope_->GetValue(var_name, true);
if (!value)
return;
if (!ExtractListOfLabels(*value, scope_->GetSourceDir(),
ToolchainLabelForScope(scope_), dest, err_))
return;
for (size_t i = 0; i < dest->size(); i++) {
LabelTargetPair& cur = (*dest)[i];
cur.ptr = GetBuildSettings()->target_manager().GetTarget(
cur.label, value->list_value()[i].origin()->GetRange(), target_, err_);
if (err_->has_error())
return;
}
}
void TargetGenerator::FillForwardDependentConfigs() {
const Value* value = scope_->GetValue(
variables::kForwardDependentConfigsFrom, true);
if (!value)
return;
LabelTargetVector& dest = target_->forward_dependent_configs();
if (!ExtractListOfLabels(*value, scope_->GetSourceDir(),
ToolchainLabelForScope(scope_), &dest, err_))
return;
// We currently assume that the list is very small and do a brute-force
// search in the deps for the labeled target. This could be optimized.
const LabelTargetVector& deps = target_->deps();
std::vector<const Target*> forward_from_list;
for (size_t dest_index = 0; dest_index < dest.size(); dest_index++) {
LabelTargetPair& cur_dest = dest[dest_index];
for (size_t dep_index = 0; dep_index < deps.size(); dep_index++) {
if (deps[dep_index].label == cur_dest.label) {
cur_dest.ptr = deps[dep_index].ptr;
break;
}
}
if (!cur_dest.ptr) {
*err_ = Err(cur_dest.origin,
"Can't forward from this target.",
"forward_dependent_configs_from must contain a list of labels that\n"
"must all appear in the deps of the same target.");
return;
}
}
}