blob: def117f2783f44890d2701b09d6a702b434c700a [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.h"
#include "base/bind.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "tools/gn/config_values_extractors.h"
#include "tools/gn/deps_iterator.h"
#include "tools/gn/filesystem_utils.h"
#include "tools/gn/scheduler.h"
#include "tools/gn/substitution_writer.h"
namespace {
typedef std::set<const Config*> ConfigSet;
// Merges the public configs from the given target to the given config list.
void MergePublicConfigsFrom(const Target* from_target,
UniqueVector<LabelConfigPair>* dest) {
const UniqueVector<LabelConfigPair>& pub = from_target->public_configs();
dest->Append(pub.begin(), pub.end());
}
// Like MergePublicConfigsFrom above except does the "all dependent" ones. This
// additionally adds all configs to the all_dependent_configs_ of the dest
// target given in *all_dest.
void MergeAllDependentConfigsFrom(const Target* from_target,
UniqueVector<LabelConfigPair>* dest,
UniqueVector<LabelConfigPair>* all_dest) {
for (const auto& pair : from_target->all_dependent_configs()) {
all_dest->push_back(pair);
dest->push_back(pair);
}
}
Err MakeTestOnlyError(const Target* from, const Target* to) {
return Err(from->defined_from(), "Test-only dependency not allowed.",
from->label().GetUserVisibleName(false) + "\n"
"which is NOT marked testonly can't depend on\n" +
to->label().GetUserVisibleName(false) + "\n"
"which is marked testonly. Only targets with \"testonly = true\"\n"
"can depend on other test-only targets.\n"
"\n"
"Either mark it test-only or don't do this dependency.");
}
Err MakeStaticLibDepsError(const Target* from, const Target* to) {
return Err(from->defined_from(),
"Complete static libraries can't depend on static libraries.",
from->label().GetUserVisibleName(false) +
"\n"
"which is a complete static library can't depend on\n" +
to->label().GetUserVisibleName(false) +
"\n"
"which is a static library.\n"
"\n"
"Use source sets for intermediate targets instead.");
}
} // namespace
Target::Target(const Settings* settings, const Label& label)
: Item(settings, label),
output_type_(UNKNOWN),
all_headers_public_(true),
check_includes_(true),
complete_static_lib_(false),
testonly_(false),
hard_dep_(false),
toolchain_(NULL) {
}
Target::~Target() {
}
// static
const char* Target::GetStringForOutputType(OutputType type) {
switch (type) {
case UNKNOWN:
return "Unknown";
case GROUP:
return "Group";
case EXECUTABLE:
return "Executable";
case SHARED_LIBRARY:
return "Shared library";
case STATIC_LIBRARY:
return "Static library";
case SOURCE_SET:
return "Source set";
case COPY_FILES:
return "Copy";
case ACTION:
return "Action";
case ACTION_FOREACH:
return "ActionForEach";
default:
return "";
}
}
Target* Target::AsTarget() {
return this;
}
const Target* Target::AsTarget() const {
return this;
}
bool Target::OnResolved(Err* err) {
DCHECK(output_type_ != UNKNOWN);
DCHECK(toolchain_) << "Toolchain should have been set before resolving.";
// Copy our own dependent configs to the list of configs applying to us.
configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
MergePublicConfigsFrom(this, &configs_);
// Copy our own libs and lib_dirs to the final set. This will be from our
// target and all of our configs. We do this specially since these must be
// inherited through the dependency tree (other flags don't work this way).
for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
const ConfigValues& cur = iter.cur();
all_lib_dirs_.append(cur.lib_dirs().begin(), cur.lib_dirs().end());
all_libs_.append(cur.libs().begin(), cur.libs().end());
}
PullDependentTargetInfo();
PullForwardedDependentConfigs();
PullRecursiveHardDeps();
FillOutputFiles();
if (!CheckVisibility(err))
return false;
if (!CheckTestonly(err))
return false;
if (!CheckNoNestedStaticLibs(err))
return false;
return true;
}
bool Target::IsLinkable() const {
return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY;
}
bool Target::IsFinal() const {
return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
(output_type_ == STATIC_LIBRARY && complete_static_lib_);
}
DepsIteratorRange Target::GetDeps(DepsIterationType type) const {
if (type == DEPS_LINKED) {
return DepsIteratorRange(DepsIterator(
&public_deps_, &private_deps_, nullptr));
}
// All deps.
return DepsIteratorRange(DepsIterator(
&public_deps_, &private_deps_, &data_deps_));
}
std::string Target::GetComputedOutputName(bool include_prefix) const {
DCHECK(toolchain_)
<< "Toolchain must be specified before getting the computed output name.";
const std::string& name = output_name_.empty() ? label().name()
: output_name_;
std::string result;
if (include_prefix) {
const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
const std::string& prefix = tool->output_prefix();
// Only add the prefix if the name doesn't already have it.
if (!StartsWithASCII(name, prefix, true))
result = prefix;
}
result.append(name);
return result;
}
bool Target::SetToolchain(const Toolchain* toolchain, Err* err) {
DCHECK(!toolchain_);
DCHECK_NE(UNKNOWN, output_type_);
toolchain_ = toolchain;
const Tool* tool = toolchain->GetToolForTargetFinalOutput(this);
if (tool)
return true;
// Tool not specified for this target type.
if (err) {
*err = Err(defined_from(), "This target uses an undefined tool.",
base::StringPrintf(
"The target %s\n"
"of type \"%s\"\n"
"uses toolchain %s\n"
"which doesn't have the tool \"%s\" defined.\n\n"
"Alas, I can not continue.",
label().GetUserVisibleName(false).c_str(),
GetStringForOutputType(output_type_),
label().GetToolchainLabel().GetUserVisibleName(false).c_str(),
Toolchain::ToolTypeToName(
toolchain->GetToolTypeForTargetFinalOutput(this)).c_str()));
}
return false;
}
void Target::PullDependentTargetInfo() {
// Gather info from our dependents we need.
for (const auto& pair : GetDeps(DEPS_LINKED)) {
const Target* dep = pair.ptr;
MergeAllDependentConfigsFrom(dep, &configs_, &all_dependent_configs_);
MergePublicConfigsFrom(dep, &configs_);
// Direct dependent libraries.
if (dep->output_type() == STATIC_LIBRARY ||
dep->output_type() == SHARED_LIBRARY ||
dep->output_type() == SOURCE_SET)
inherited_libraries_.push_back(dep);
// Inherited libraries and flags are inherited across static library
// boundaries.
if (!dep->IsFinal()) {
inherited_libraries_.Append(dep->inherited_libraries().begin(),
dep->inherited_libraries().end());
// Inherited library settings.
all_lib_dirs_.append(dep->all_lib_dirs());
all_libs_.append(dep->all_libs());
}
}
}
void Target::PullForwardedDependentConfigs() {
// Pull public configs from each of our dependency's public deps.
for (const auto& dep : public_deps_)
PullForwardedDependentConfigsFrom(dep.ptr);
// Forward public configs if explicitly requested.
for (const auto& dep : forward_dependent_configs_) {
const Target* from_target = dep.ptr;
// The forward_dependent_configs_ must be in the deps (public or private)
// already, so we don't need to bother copying to our configs, only
// forwarding.
DCHECK(std::find_if(private_deps_.begin(), private_deps_.end(),
LabelPtrPtrEquals<Target>(from_target)) !=
private_deps_.end() ||
std::find_if(public_deps_.begin(), public_deps_.end(),
LabelPtrPtrEquals<Target>(from_target)) !=
public_deps_.end());
PullForwardedDependentConfigsFrom(from_target);
}
}
void Target::PullForwardedDependentConfigsFrom(const Target* from) {
public_configs_.Append(from->public_configs().begin(),
from->public_configs().end());
}
void Target::PullRecursiveHardDeps() {
for (const auto& pair : GetDeps(DEPS_LINKED)) {
if (pair.ptr->hard_dep())
recursive_hard_deps_.insert(pair.ptr);
// Android STL doesn't like insert(begin, end) so do it manually.
// TODO(brettw) this can be changed to
// insert(iter.target()->begin(), iter.target()->end())
// when Android uses a better STL.
for (std::set<const Target*>::const_iterator cur =
pair.ptr->recursive_hard_deps().begin();
cur != pair.ptr->recursive_hard_deps().end(); ++cur)
recursive_hard_deps_.insert(*cur);
}
}
void Target::FillOutputFiles() {
const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
switch (output_type_) {
case GROUP:
case SOURCE_SET:
case COPY_FILES:
case ACTION:
case ACTION_FOREACH: {
// These don't get linked to and use stamps which should be the first
// entry in the outputs. These stamps are named
// "<target_out_dir>/<targetname>.stamp".
dependency_output_file_ = GetTargetOutputDirAsOutputFile(this);
dependency_output_file_.value().append(GetComputedOutputName(true));
dependency_output_file_.value().append(".stamp");
break;
}
case EXECUTABLE:
// Executables don't get linked to, but the first output is used for
// dependency management.
CHECK_GE(tool->outputs().list().size(), 1u);
dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
break;
case STATIC_LIBRARY:
// Static libraries both have dependencies and linking going off of the
// first output.
CHECK(tool->outputs().list().size() >= 1);
link_output_file_ = dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
break;
case SHARED_LIBRARY:
CHECK(tool->outputs().list().size() >= 1);
if (tool->link_output().empty() && tool->depend_output().empty()) {
// Default behavior, use the first output file for both.
link_output_file_ = dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
} else {
// Use the tool-specified ones.
if (!tool->link_output().empty()) {
link_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->link_output());
}
if (!tool->depend_output().empty()) {
dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->depend_output());
}
}
break;
case UNKNOWN:
default:
NOTREACHED();
}
}
bool Target::CheckVisibility(Err* err) const {
for (const auto& pair : GetDeps(DEPS_ALL)) {
if (!Visibility::CheckItemVisibility(this, pair.ptr, err))
return false;
}
return true;
}
bool Target::CheckTestonly(Err* err) const {
// If the current target is marked testonly, it can include both testonly
// and non-testonly targets, so there's nothing to check.
if (testonly())
return true;
// Verify no deps have "testonly" set.
for (const auto& pair : GetDeps(DEPS_ALL)) {
if (pair.ptr->testonly()) {
*err = MakeTestOnlyError(this, pair.ptr);
return false;
}
}
return true;
}
bool Target::CheckNoNestedStaticLibs(Err* err) const {
// If the current target is not a complete static library, it can depend on
// static library targets with no problem.
if (!(output_type() == Target::STATIC_LIBRARY && complete_static_lib()))
return true;
// Verify no deps are static libraries.
for (const auto& pair : GetDeps(DEPS_ALL)) {
if (pair.ptr->output_type() == Target::STATIC_LIBRARY) {
*err = MakeStaticLibDepsError(this, pair.ptr);
return false;
}
}
// Verify no inherited libraries are static libraries.
for (const auto& lib : inherited_libraries()) {
if (lib->output_type() == Target::STATIC_LIBRARY) {
*err = MakeStaticLibDepsError(this, lib);
return false;
}
}
return true;
}