#include "gn/target.h"
#include <stddef.h>
#include <algorithm>
#include "base/stl_util.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "gn/c_tool.h"
#include "gn/config_values_extractors.h"
#include "gn/deps_iterator.h"
#include "gn/filesystem_utils.h"
#include "gn/functions.h"
#include "gn/rust_tool.h"
#include "gn/scheduler.h"
#include "gn/substitution_writer.h"
#include "gn/tool.h"
#include "gn/toolchain.h"
#include "gn/trace.h"
namespace {
using ConfigSet = std::set<const Config*>;
struct CheckSourceGeneratedCursor {
const Target* target = nullptr;
size_t index = 0;
};
void MergePublicConfigsFrom(const Target* from_target,
UniqueVector<LabelConfigPair>* dest) {
const UniqueVector<LabelConfigPair>& pub = from_target->public_configs();
dest->Append(pub.begin(), pub.end());
}
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 Item* from, const Item* to) {
bool with_toolchain = from->settings()->ShouldShowToolchain({
&from->label(),
&to->label(),
});
return Err(
from->defined_from(), "Test-only dependency not allowed.",
from->label().GetUserVisibleName(with_toolchain) +
"\n"
"which is NOT marked testonly can't depend on\n" +
to->label().GetUserVisibleName(with_toolchain) +
"\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.");
}
bool HasDirectOutput(const Target* target,
const OutputFile& file,
CheckSourceGeneratedCursor* cursor) {
const auto& computed_outputs = target->computed_outputs();
size_t start_index = target == cursor->target ? cursor->index + 1 : 0;
size_t count = computed_outputs.size();
for (size_t i = 0; i < count; ++i) {
size_t idx = (start_index + i) % count;
const auto& cur = computed_outputs[idx];
if (file == cur) {
cursor->target = target;
cursor->index = idx;
return true;
}
}
return false;
}
bool EnsureFileIsGeneratedByDependency(const Target* target,
const OutputFile& file,
bool check_private_deps,
bool consider_object_files,
bool check_data_deps,
TargetSet* seen_targets,
CheckSourceGeneratedCursor* cursor) {
if (!seen_targets->add(target))
return false;
if (HasDirectOutput(target, file, cursor)) {
return true;
}
if (file == target->write_runtime_deps_output())
return true;
if (consider_object_files && target->IsBinary()) {
std::vector<OutputFile> source_outputs;
for (const SourceFile& source : target->sources()) {
const char* tool_name;
if (!target->GetOutputFilesForSource(source, &tool_name, &source_outputs))
continue;
if (base::ContainsValue(source_outputs, file))
return true;
}
}
if (check_private_deps) {
for (const auto& pair : target->private_deps()) {
if (EnsureFileIsGeneratedByDependency(
pair.ptr, file, false, consider_object_files, check_data_deps,
seen_targets, cursor))
return true;
}
if (target->output_type() == Target::CREATE_BUNDLE) {
for (const auto* dep : target->bundle_data().bundle_deps()) {
if (EnsureFileIsGeneratedByDependency(
dep, file, false, consider_object_files, check_data_deps,
seen_targets, cursor))
return true;
}
}
}
if (check_data_deps) {
check_data_deps = false;
for (const auto& pair : target->data_deps()) {
if (EnsureFileIsGeneratedByDependency(
pair.ptr, file, false, consider_object_files, check_data_deps,
seen_targets, cursor))
return true;
}
}
for (const auto& pair : target->public_deps()) {
if (EnsureFileIsGeneratedByDependency(
pair.ptr, file, false, consider_object_files, check_data_deps,
seen_targets, cursor))
return true;
}
return false;
}
void CheckSourceGenerated(const Target* source_target,
const SourceFile& source,
CheckSourceGeneratedCursor* cursor) {
const auto& build_settings = source_target->settings()->build_settings();
if (!IsStringInOutputDir(build_settings->build_dir(), source.value()))
return;
OutputFile out_file(build_settings, source);
TargetSet seen_targets;
bool check_data_deps = false;
bool consider_object_files = false;
if (cursor->target) {
bool check_private_deps = cursor->target == source_target;
if (EnsureFileIsGeneratedByDependency(
cursor->target, out_file, check_private_deps, consider_object_files,
check_data_deps, &seen_targets, cursor))
return;
}
bool check_private_deps = true;
if (!EnsureFileIsGeneratedByDependency(
source_target, out_file, check_private_deps, consider_object_files,
check_data_deps, &seen_targets, cursor)) {
seen_targets.clear();
check_data_deps =
g_scheduler->IsFileGeneratedByWriteRuntimeDeps(out_file) ||
g_scheduler->IsFileGeneratedByTarget(source);
consider_object_files = !check_data_deps;
if (!EnsureFileIsGeneratedByDependency(
source_target, out_file, check_private_deps, consider_object_files,
check_data_deps, &seen_targets, cursor))
g_scheduler->AddUnknownGeneratedInput(source_target, source);
}
}
bool RecursiveCheckAssertNoDeps(const Target* target,
bool check_this,
const std::vector<LabelPattern>& assert_no,
TargetSet* visited,
std::string* failure_path_str,
const LabelPattern** failure_pattern) {
static const char kIndentPath[] = " ";
if (!visited->add(target))
return true;
if (check_this) {
for (const LabelPattern& pattern : assert_no) {
if (pattern.Matches(target->label())) {
*failure_pattern = &pattern;
*failure_path_str =
kIndentPath + target->label().GetUserVisibleName(false);
return false;
}
}
}
for (const auto& pair : target->GetDeps(Target::DEPS_ALL)) {
if (pair.ptr->output_type() == Target::EXECUTABLE)
continue;
if (!RecursiveCheckAssertNoDeps(pair.ptr, true, assert_no, visited,
failure_path_str, failure_pattern)) {
std::string prepend_path =
kIndentPath + target->label().GetUserVisibleName(false) + " ->\n";
failure_path_str->insert(0, prepend_path);
return false;
}
}
return true;
}
}
const char kExecution_Help[] =
R"(Build graph and execution overview
Overall build flow
1. Look for ".gn" file (see "gn help dotfile") in the current directory and
walk up the directory tree until one is found. Set this directory to be
the "source root" and interpret this file to find the name of the build
config file.
2. Execute the build config file identified by .gn to set up the global
variables and default toolchain name. Any arguments, variables, defaults,
etc. set up in this file will be visible to all files in the build.
Any values set in the `default_args` scope will be merged into
subsequent `declare_args()` scopes and override the default values.
3. Process the --args command line option or load the arguments from
the args.gn file in the build directory. These values will be merged
into any subsequent declare_args() scope (after the `default_args`
are merged in) to override the default values. See `help buildargs`
for more on how args are handled.
4. Load the BUILDCONFIG.gn file and create a dedicated scope for it.
5. Load the //BUILD.gn (in the source root directory). The BUILD.gn
file is executed in a scope whose parent scope is the BUILDCONFIG.gn
file, i.e., only the definitions in the BUILDCONFIG.gn file exist.
5. If the BUILD.gn file imports other files, each of those other
files is executed in a separate scope whose parent is the BUILDCONFIG.gn
file, i.e., no definitions from the importing BUILD.gn file are
available. When the imported file has been fully processed, its scope
is merged into the BUILD.gn file's scope. If there is a conflict
(both the BUILD.gn file and the imported file define some variable
or rule with the same name but different values), a runtime error
will be thrown. See "gn help import" for more on this.
6. Recursively evaluate rules and load BUILD.gn in other directories as
necessary to resolve dependencies. If a BUILD file isn't found in the
specified location, GN will look in the corresponding location inside
the secondary_source defined in the dotfile (see "gn help dotfile").
Each BUILD.gn file will again be executed in a new scope whose only
parent is BUILDCONFIG.gn's scope.
7. If a target is referenced using an alternate toolchain, then
1. The toolchain file is loaded in a scope whose parent is the
BUILDCONFIG.gn file.
2. The BUILDCONFIG.gn file is re-loaded and re-parsed into a new
scope, with any `toolchain_args` merged into the defaults. See
`help buildargs` for more on how args are handled.
3. The BUILD.gn containing the target is then parsed as in step 5,
only we use the scope from step 7.2 instead of the default
BUILDCONFIG.gn scope.
8. When a target's dependencies are resolved, write out the `.ninja`
file to disk.
9. When all targets are resolved, write out the root build.ninja file.
Note that the BUILD.gn file name may be modulated by .gn arguments such as
build_file_extension.
Executing target definitions and templates
Build files are loaded in parallel. This means it is impossible to
interrogate a target from GN code for any information not derivable from its
label (see "gn help label"). The exception is the get_target_outputs()
function which requires the target being interrogated to have been defined
previously in the same file.
Targets are declared by their type and given a name:
static_library("my_static_library") {
... target parameter definitions ...
}
There is also a generic "target" function for programmatically defined types
(see "gn help target").
You can define new types using templates (see "gn help template"). A template
defines some custom code that expands to one or more other targets. When a
template is invoked, it is executed in the scope of the file that defined the
template (as described above). To access values from the caller's scope, you
must use the `invoker` variable (see "gn help template" for more on the
invoker).
Before executing the code inside the target's { }, the target defaults are
applied (see "gn help set_defaults"). It will inject implicit variable
definitions that can be overridden by the target code as necessary. Typically
this mechanism is used to inject a default set of configs that define the
global compiler and linker flags.
Which targets are built
All targets encountered in the default toolchain (see "gn help toolchain")
will have build rules generated for them, even if no other targets reference
them. Their dependencies must resolve and they will be added to the implicit
"all" rule (see "gn help ninja_rules").
Targets in non-default toolchains will only be generated when they are
required (directly or transitively) to build a target in the default
toolchain.
Some targets might be associated but without a formal build dependency (for
example, related tools or optional variants). A target that is marked as
"generated" can propagate its generated state to an associated target using
"gen_deps". This will make the referenced dependency have Ninja rules
generated in the same cases the source target has but without a build-time
dependency and even in non-default toolchains.
See also "gn help ninja_rules".
Dependencies
The only difference between "public_deps" and "deps" except for pushing
configs around the build tree and allowing includes for the purposes of "gn
check".
A target's "data_deps" are guaranteed to be built whenever the target is
built, but the ordering is not defined. The meaning of this is dependencies
required at runtime. Currently data deps will be complete before the target
is linked, but this is not semantically guaranteed and this is undesirable
from a build performance perspective. Since we hope to change this in the
future, do not rely on this behavior.
)";
Target::Target(const Settings* settings,
const Label& label,
const SourceFileSet& build_dependency_files)
: Item(settings, label, build_dependency_files) {}
Target::~Target() = default;
static const BundleData kEmptyBundleData;
const BundleData& Target::bundle_data() const {
return bundle_data_ ? *bundle_data_ : kEmptyBundleData;
}
BundleData& Target::bundle_data() {
if (!bundle_data_)
bundle_data_ = std::make_unique<BundleData>();
return *bundle_data_;
}
static ConfigValues kEmptyConfigValues;
const ConfigValues& Target::config_values() const {
return config_values_ ? *config_values_ : kEmptyConfigValues;
}
ConfigValues& Target::config_values() {
if (!config_values_)
config_values_ = std::make_unique<ConfigValues>();
return *config_values_;
}
static const ActionValues kEmptyActionValues;
const ActionValues& Target::action_values() const {
return action_values_ ? *action_values_ : kEmptyActionValues;
}
ActionValues& Target::action_values() {
if (!action_values_)
action_values_ = std::make_unique<ActionValues>();
return *action_values_;
}
static const RustValues kEmptyRustValues;
const RustValues& Target::rust_values() const {
return rust_values_ ? *rust_values_ : kEmptyRustValues;
}
RustValues& Target::rust_values() {
if (!rust_values_)
rust_values_ = std::make_unique<RustValues>();
return *rust_values_;
}
static const SwiftValues kEmptySwiftValues;
const SwiftValues& Target::swift_values() const {
return swift_values_ ? *swift_values_ : kEmptySwiftValues;
}
SwiftValues& Target::swift_values() {
if (!swift_values_)
swift_values_ = std::make_unique<SwiftValues>();
return *swift_values_;
}
static const Metadata kEmptyMetadata;
const Metadata& Target::metadata() const {
return metadata_ ? *metadata_ : kEmptyMetadata;
}
Metadata& Target::metadata() {
if (!metadata_)
metadata_ = std::make_unique<Metadata>();
return *metadata_;
}
static const Target::GeneratedFile kEmptyGeneratedFile;
const Target::GeneratedFile& Target::generated_file() const {
return generated_file_ ? *generated_file_ : kEmptyGeneratedFile;
}
Target::GeneratedFile& Target::generated_file() {
if (!generated_file_)
generated_file_ = std::make_unique<Target::GeneratedFile>();
return *generated_file_;
}
const char* Target::GetStringForOutputType(OutputType type) {
switch (type) {
case UNKNOWN:
return "unknown";
case GROUP:
return functions::kGroup;
case EXECUTABLE:
return functions::kExecutable;
case LOADABLE_MODULE:
return functions::kLoadableModule;
case SHARED_LIBRARY:
return functions::kSharedLibrary;
case STATIC_LIBRARY:
return functions::kStaticLibrary;
case SOURCE_SET:
return functions::kSourceSet;
case COPY_FILES:
return functions::kCopy;
case ACTION:
return functions::kAction;
case ACTION_FOREACH:
return functions::kActionForEach;
case BUNDLE_DATA:
return functions::kBundleData;
case CREATE_BUNDLE:
return functions::kCreateBundle;
case GENERATED_FILE:
return functions::kGeneratedFile;
case RUST_LIBRARY:
return functions::kRustLibrary;
case RUST_PROC_MACRO:
return functions::kRustProcMacro;
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.";
ScopedTrace trace(TraceItem::TRACE_ON_RESOLVED, label());
trace.SetToolchain(settings()->toolchain_label());
configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end());
MergePublicConfigsFrom(this, &configs_);
if (!CheckConfigVisibility(err))
return false;
PullDependentTargetConfigs();
for (const auto& dep : public_deps_) {
if (dep.ptr->toolchain() == toolchain() ||
dep.ptr->toolchain()->propagates_configs())
public_configs_.Append(dep.ptr->public_configs().begin(),
dep.ptr->public_configs().end());
}
PullRecursiveBundleData();
if (!ResolvePrecompiledHeaders(err))
return false;
if (!FillOutputFiles(err))
return false;
if (!SwiftValues::OnTargetResolved(this, err))
return false;
if (!CheckSourceSetLanguages(err))
return false;
if (!CheckVisibility(err))
return false;
if (!CheckTestonly(err))
return false;
if (!CheckAssertNoDeps(err))
return false;
CheckSourcesGenerated();
if (!write_runtime_deps_output_.value().empty())
g_scheduler->AddWriteRuntimeDepsTarget(this);
if (output_type_ == GENERATED_FILE) {
DCHECK(!computed_outputs_.empty());
g_scheduler->AddGeneratedFile(
computed_outputs_[0].AsSourceFile(settings()->build_settings()));
}
return true;
}
bool Target::IsBinary() const {
return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
output_type_ == LOADABLE_MODULE || output_type_ == STATIC_LIBRARY ||
output_type_ == SOURCE_SET || output_type_ == RUST_LIBRARY ||
output_type_ == RUST_PROC_MACRO;
}
bool Target::IsLinkable() const {
if (output_type_ == COPY_FILES) {
return copy_linkable_file();
}
return output_type_ == STATIC_LIBRARY || output_type_ == SHARED_LIBRARY ||
output_type_ == RUST_LIBRARY || output_type_ == RUST_PROC_MACRO;
}
bool Target::IsFinal() const {
return output_type_ == EXECUTABLE || output_type_ == SHARED_LIBRARY ||
output_type_ == LOADABLE_MODULE || output_type_ == ACTION ||
output_type_ == ACTION_FOREACH || output_type_ == COPY_FILES ||
output_type_ == CREATE_BUNDLE || output_type_ == RUST_PROC_MACRO ||
(output_type_ == STATIC_LIBRARY && complete_static_lib_);
}
bool Target::IsDataOnly() const {
return output_type_ == BUNDLE_DATA;
}
bool Target::ShouldGenerate() const {
const auto& root_patterns = settings()->build_settings()->root_patterns();
if (root_patterns.empty()) {
return settings()->is_default();
}
return LabelPattern::VectorMatches(root_patterns, label());
}
DepsIteratorRange Target::GetDeps(DepsIterationType type) const {
if (type == DEPS_LINKED) {
return DepsIteratorRange(
DepsIterator(&public_deps_, &private_deps_, nullptr));
}
return DepsIteratorRange(
DepsIterator(&public_deps_, &private_deps_, &data_deps_));
}
std::string Target::GetComputedOutputName() 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;
const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
if (tool) {
if (!output_prefix_override_ && !base::starts_with(name, tool->output_prefix()))
result = tool->output_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;
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(),
Tool::GetToolTypeForTargetFinalOutput(this)));
}
return false;
}
bool Target::GetOutputsAsSourceFiles(const LocationRange& loc_for_error,
bool build_complete,
std::vector<SourceFile>* outputs,
Err* err) const {
const static char kBuildIncompleteMsg[] =
"This target is a binary target which can't be queried for its "
"outputs\nduring the build. It will work for action, action_foreach, "
"generated_file,\nand copy targets.";
outputs->clear();
std::vector<SourceFile> files;
if (output_type() == Target::ACTION || output_type() == Target::COPY_FILES ||
output_type() == Target::ACTION_FOREACH ||
output_type() == Target::GENERATED_FILE) {
action_values().GetOutputsAsSourceFiles(this, outputs);
} else if (output_type() == Target::CREATE_BUNDLE) {
if (!bundle_data().GetOutputsAsSourceFiles(settings(), this, outputs, err))
return false;
} else if (IsBinary() && output_type() != Target::SOURCE_SET) {
DCHECK(IsBinary()) << static_cast<int>(output_type());
if (!build_complete) {
*err = Err(loc_for_error, kBuildIncompleteMsg);
return false;
}
const Tool* tool = toolchain()->GetToolForTargetFinalOutput(this);
std::vector<OutputFile> output_files;
SubstitutionWriter::ApplyListToLinkerAsOutputFile(
this, tool, tool->outputs(), &output_files);
for (const OutputFile& output_file : output_files) {
outputs->push_back(
output_file.AsSourceFile(settings()->build_settings()));
}
} else {
if (!build_complete) {
*err = Err(loc_for_error, kBuildIncompleteMsg);
return false;
}
outputs->push_back(
dependency_output_file().AsSourceFile(settings()->build_settings()));
}
return true;
}
bool Target::GetOutputFilesForSource(const SourceFile& source,
const char** computed_tool_type,
std::vector<OutputFile>* outputs) const {
DCHECK(toolchain());
outputs->clear();
*computed_tool_type = Tool::kToolNone;
if (output_type() == Target::COPY_FILES ||
output_type() == Target::ACTION_FOREACH) {
std::vector<SourceFile> output_files;
SubstitutionWriter::ApplyListToSourceAsOutputFile(
this, settings(), action_values().outputs(), source, outputs);
} else if (!IsBinary()) {
std::vector<SourceFile> outputs_as_source_files;
Err err;
GetOutputsAsSourceFiles(LocationRange(), false, &outputs_as_source_files,
&err);
for (const auto& cur : outputs_as_source_files)
outputs->emplace_back(OutputFile(settings()->build_settings(), cur));
} else {
DCHECK(IsBinary());
const SourceFile::Type file_type = source.GetType();
if (file_type == SourceFile::SOURCE_UNKNOWN)
return false;
if (file_type == SourceFile::SOURCE_O) {
outputs->emplace_back(OutputFile(settings()->build_settings(), source));
return true;
}
if (file_type == SourceFile::SOURCE_RS)
return false;
*computed_tool_type = Tool::GetToolTypeForSourceType(file_type);
if (*computed_tool_type == Tool::kToolNone)
return false;
const Tool* tool = toolchain_->GetTool(*computed_tool_type);
if (!tool)
return false;
if (file_type == SourceFile::SOURCE_SWIFT) {
if (tool->partial_outputs().list().empty())
return false;
}
const SubstitutionList& substitution_list =
file_type == SourceFile::SOURCE_SWIFT ? tool->partial_outputs()
: tool->outputs();
SubstitutionWriter::ApplyListToCompilerAsOutputFile(
this, source, substitution_list, outputs);
}
return !outputs->empty();
}
void Target::PullDependentTargetConfigs() {
for (const auto& pair : GetDeps(DEPS_LINKED)) {
if (pair.ptr->toolchain() == toolchain() ||
pair.ptr->toolchain()->propagates_configs())
MergeAllDependentConfigsFrom(pair.ptr, &configs_,
&all_dependent_configs_);
}
for (const auto& pair : GetDeps(DEPS_LINKED)) {
if (pair.ptr->toolchain() == toolchain() ||
pair.ptr->toolchain()->propagates_configs())
MergePublicConfigsFrom(pair.ptr, &configs_);
}
}
void Target::PullDependentTargetLibsFrom(const Target* dep, bool is_public) {
if (dep->output_type() == STATIC_LIBRARY ||
dep->output_type() == SHARED_LIBRARY ||
dep->output_type() == RUST_LIBRARY ||
dep->output_type() == SOURCE_SET ||
(dep->output_type() == CREATE_BUNDLE &&
dep->bundle_data().is_framework()) ||
(dep->output_type() == COPY_FILES &&
dep->copy_linkable_file())) {
inherited_libraries_.Append(dep, is_public);
}
if (dep->output_type() == STATIC_LIBRARY ||
dep->output_type() == SHARED_LIBRARY ||
dep->output_type() == SOURCE_SET || dep->output_type() == RUST_LIBRARY ||
dep->output_type() == GROUP ||
(dep->output_type() == COPY_FILES &&
dep->copy_linkable_file())) {
rust_transitive_inherited_libs_.Append(dep, true);
rust_transitive_inheritable_libs_.Append(dep, is_public);
rust_transitive_inherited_libs_.AppendInherited(
dep->rust_transitive_inheritable_libs(), true);
rust_transitive_inheritable_libs_.AppendInherited(
dep->rust_transitive_inheritable_libs(), is_public);
} else if (dep->output_type() == RUST_PROC_MACRO) {
rust_transitive_inherited_libs_.Append(dep, true);
rust_transitive_inheritable_libs_.Append(dep, is_public);
}
if ((dep->output_type() == SHARED_LIBRARY) ||
(dep->output_type() == COPY_FILES &&
dep->copy_linkable_file())) {
inherited_libraries_.AppendPublicSharedLibraries(dep->inherited_libraries(),
is_public);
} else {
InheritedLibraries transitive;
if (!dep->IsFinal()) {
for (const auto& [inherited, inherited_is_public] :
dep->inherited_libraries().GetOrderedAndPublicFlag()) {
transitive.Append(inherited, is_public && inherited_is_public);
}
} else if (dep->complete_static_lib()) {
for (const auto& [inherited, inherited_is_public] :
dep->inherited_libraries().GetOrderedAndPublicFlag()) {
if (inherited->IsFinal()) {
transitive.Append(inherited, is_public && inherited_is_public);
}
}
}
for (const auto& [target, pub] : transitive.GetOrderedAndPublicFlag()) {
if (target->output_type() != RUST_PROC_MACRO) {
inherited_libraries_.Append(target, pub);
}
}
}
if (!dep->IsFinal() || dep->output_type() == STATIC_LIBRARY) {
all_lib_dirs_.Append(dep->all_lib_dirs());
all_libs_.Append(dep->all_libs());
all_framework_dirs_.Append(dep->all_framework_dirs());
all_frameworks_.Append(dep->all_frameworks());
all_weak_frameworks_.Append(dep->all_weak_frameworks());
}
}
void Target::PullDependentTargetLibs() {
for (const auto& dep : public_deps_)
PullDependentTargetLibsFrom(dep.ptr, true);
for (const auto& dep : private_deps_)
PullDependentTargetLibsFrom(dep.ptr, false);
}
void Target::PullRecursiveHardDeps() {
for (const auto& pair : GetDeps(DEPS_LINKED)) {
if (hard_dep() || pair.ptr->hard_dep()) {
recursive_hard_deps_.insert(pair.ptr);
continue;
}
if (pair.ptr->IsBinary() && !pair.ptr->all_headers_public() &&
pair.ptr->public_headers().empty() &&
!pair.ptr->builds_swift_module()) {
continue;
}
recursive_hard_deps_.insert(pair.ptr->recursive_hard_deps().begin(),
pair.ptr->recursive_hard_deps().end());
}
}
void Target::PullRecursiveBundleData() {
const bool is_create_bundle = output_type_ == CREATE_BUNDLE;
for (const auto& pair : GetDeps(DEPS_LINKED)) {
if (pair.ptr->toolchain() != toolchain())
continue;
if (pair.ptr->output_type() == CREATE_BUNDLE &&
!pair.ptr->bundle_data().transparent()) {
continue;
}
if (pair.ptr->output_type() == BUNDLE_DATA) {
bundle_data().AddBundleData(pair.ptr, is_create_bundle);
}
if (pair.ptr->has_bundle_data()) {
for (const auto* target : pair.ptr->bundle_data().forwarded_bundle_deps())
bundle_data().AddBundleData(target, is_create_bundle);
}
}
if (has_bundle_data())
bundle_data().OnTargetResolved(this);
}
bool Target::HasRealInputs() const {
if (output_type() == ACTION || output_type() == ACTION_FOREACH ||
output_type() == GENERATED_FILE) {
return true;
}
for (const auto& pair : GetDeps(DEPS_ALL)) {
if (pair.ptr->has_dependency_output()) {
return true;
}
}
if (!validations_.empty()) {
return true;
}
if (output_type() == BUNDLE_DATA) {
return !sources().empty();
}
if (output_type() == CREATE_BUNDLE) {
return !bundle_data().assets_catalog_sources().empty() ||
!bundle_data().partial_info_plist().is_null() ||
!bundle_data().post_processing_script().is_null();
}
std::vector<OutputFile> tool_outputs;
return std::any_of(
sources().begin(), sources().end(), [&, this](const auto& source) {
if (source.GetType() == SourceFile::SOURCE_SWIFT) {
return true;
}
const char* tool_name = Tool::kToolNone;
return GetOutputFilesForSource(source, &tool_name, &tool_outputs);
});
}
bool Target::FillOutputFiles(Err* err) {
const Tool* tool = toolchain_->GetToolForTargetFinalOutput(this);
bool check_tool_outputs = false;
switch (output_type_) {
case ACTION:
case ACTION_FOREACH:
case BUNDLE_DATA:
case COPY_FILES:
case CREATE_BUNDLE:
case GENERATED_FILE:
case GROUP:
case SOURCE_SET: {
if (settings()->build_settings()->no_stamp_files()) {
if (HasRealInputs() || output_type_ == GROUP ||
output_type_ == SOURCE_SET) {
dependency_output_alias_ =
GetBuildDirForTargetAsOutputFile(this, BuildDirType::PHONY);
dependency_output_alias_.value().append(label().name());
}
} else {
dependency_output_file_ =
GetBuildDirForTargetAsOutputFile(this, BuildDirType::OBJ);
dependency_output_file_.value().append(label().name());
dependency_output_file_.value().append(".stamp");
}
break;
}
case EXECUTABLE:
case LOADABLE_MODULE:
CHECK_GE(tool->outputs().list().size(), 1u);
check_tool_outputs = true;
dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
if (tool->runtime_outputs().list().empty()) {
runtime_outputs_.push_back(dependency_output_file_);
} else {
SubstitutionWriter::ApplyListToLinkerAsOutputFile(
this, tool, tool->runtime_outputs(), &runtime_outputs_);
}
break;
case RUST_LIBRARY:
case STATIC_LIBRARY:
CHECK(tool->outputs().list().size() >= 1);
check_tool_outputs = true;
link_output_file_ = dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
break;
case RUST_PROC_MACRO:
case SHARED_LIBRARY: {
CHECK(tool->outputs().list().size() >= 1);
check_tool_outputs = true;
const SubstitutionPattern* link_output_ptr = nullptr;
const SubstitutionPattern* depend_output_ptr = nullptr;
const SubstitutionList* runtime_outputs_ptr = nullptr;
if (const CTool* ctool = tool->AsC()) {
link_output_ptr =
ctool->link_output().empty() ? nullptr : &ctool->link_output();
depend_output_ptr =
ctool->depend_output().empty() ? nullptr : &ctool->depend_output();
runtime_outputs_ptr = &ctool->runtime_outputs();
} else if (const RustTool* rust_tool = tool->AsRust()) {
link_output_ptr = rust_tool->link_output().empty()
? nullptr
: &rust_tool->link_output();
depend_output_ptr = rust_tool->depend_output().empty()
? nullptr
: &rust_tool->depend_output();
runtime_outputs_ptr = &rust_tool->runtime_outputs();
}
if (!link_output_ptr && !depend_output_ptr) {
link_output_file_ = dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, tool->outputs().list()[0]);
} else {
if (link_output_ptr) {
link_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, *link_output_ptr);
}
if (depend_output_ptr) {
dependency_output_file_ =
SubstitutionWriter::ApplyPatternToLinkerAsOutputFile(
this, tool, *depend_output_ptr);
}
}
if (!runtime_outputs_ptr || runtime_outputs_ptr->list().empty()) {
runtime_outputs_.push_back(link_output_file_);
} else {
SubstitutionWriter::ApplyListToLinkerAsOutputFile(
this, tool, *runtime_outputs_ptr, &runtime_outputs_);
}
break;
}
case UNKNOWN:
default:
NOTREACHED();
}
if (output_type_ == CREATE_BUNDLE) {
if (!bundle_data().GetOutputFiles(settings(), this, &computed_outputs_,
err))
return false;
}
if (check_tool_outputs) {
SubstitutionWriter::ApplyListToLinkerAsOutputFile(
this, tool, tool->outputs(), &computed_outputs_);
for (auto& out : computed_outputs_)
NormalizePath(&out.value());
}
if (action_values_.get()) {
std::vector<SourceFile> outputs_as_sources;
action_values_->GetOutputsAsSourceFiles(this, &outputs_as_sources);
for (const SourceFile& out : outputs_as_sources)
computed_outputs_.push_back(
OutputFile(settings()->build_settings(), out));
}
if ((output_type_ == COPY_FILES) && copy_linkable_file()) {
std::vector<OutputFile> tool_outputs;
link_output_file_ = computed_outputs()[0];
}
return true;
}
bool Target::ResolvePrecompiledHeaders(Err* err) {
bool has_precompiled_headers =
config_values_.get() && config_values_->has_precompiled_headers();
const Label* pch_header_settings_from = NULL;
if (has_precompiled_headers)
pch_header_settings_from = &label();
for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
if (!iter.GetCurrentConfig())
continue;
const Config* config = iter.GetCurrentConfig();
const ConfigValues& cur = config->resolved_values();
if (!cur.has_precompiled_headers())
continue;
if (has_precompiled_headers) {
if (config_values_->precompiled_header() != cur.precompiled_header() ||
config_values_->precompiled_source() != cur.precompiled_source()) {
bool with_toolchain = settings()->ShouldShowToolchain({
&label(),
pch_header_settings_from,
&config->label(),
});
*err = Err(
defined_from(), "Precompiled header setting conflict.",
"The target " + label().GetUserVisibleName(with_toolchain) +
"\n"
"has conflicting precompiled header settings.\n"
"\n"
"From " +
pch_header_settings_from->GetUserVisibleName(with_toolchain) +
"\n header: " + config_values_->precompiled_header() +
"\n source: " + config_values_->precompiled_source().value() +
"\n\n"
"From " +
config->label().GetUserVisibleName(with_toolchain) +
"\n header: " + cur.precompiled_header() +
"\n source: " + cur.precompiled_source().value());
return false;
}
} else {
pch_header_settings_from = &config->label();
config_values().set_precompiled_header(cur.precompiled_header());
config_values().set_precompiled_source(cur.precompiled_source());
}
}
return true;
}
bool Target::CheckVisibility(Err* err) const {
for (const auto& pair : GetDeps(DEPS_ALL)) {
if (!Visibility::CheckItemVisibility(this, pair.ptr, pair.is_external_deps, err))
return false;
}
return true;
}
bool Target::CheckConfigVisibility(Err* err) const {
for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
if (const Config* config = iter.GetCurrentConfig())
if (!Visibility::CheckItemVisibility(this, config, false, err))
return false;
}
return true;
}
bool Target::CheckSourceSetLanguages(Err* err) const {
if (output_type() == Target::SOURCE_SET &&
source_types_used().RustSourceUsed()) {
*err = Err(defined_from(), "source_set contained Rust code.",
label().GetUserVisibleName(!settings()->is_default()) +
" has Rust code. Only C/C++ source_sets are supported.");
return false;
}
return true;
}
bool Target::CheckTestonly(Err* err) const {
if (testonly())
return true;
for (const auto& pair : GetDeps(DEPS_ALL)) {
if (pair.ptr->testonly()) {
*err = MakeTestOnlyError(this, pair.ptr);
return false;
}
}
for (const auto& pair : validations_) {
if (pair.ptr->testonly()) {
*err = MakeTestOnlyError(this, pair.ptr);
return false;
}
}
for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
if (const Config* config = iter.GetCurrentConfig()) {
if (config->testonly()) {
*err = MakeTestOnlyError(this, config);
return false;
}
}
}
return true;
}
bool Target::CheckAssertNoDeps(Err* err) const {
if (assert_no_deps_.empty())
return true;
TargetSet visited;
std::string failure_path_str;
const LabelPattern* failure_pattern = nullptr;
if (!RecursiveCheckAssertNoDeps(this, false, assert_no_deps_, &visited,
&failure_path_str, &failure_pattern)) {
*err = Err(
defined_from(), "assert_no_deps failed.",
label().GetUserVisibleName(!settings()->is_default()) +
" has an assert_no_deps entry:\n " + failure_pattern->Describe() +
"\nwhich fails for the dependency path:\n" + failure_path_str);
return false;
}
return true;
}
void Target::CheckSourcesGenerated() const {
CheckSourceGeneratedCursor cursor;
for (const SourceFile& file : sources_)
CheckSourceGenerated(this, file, &cursor);
cursor.target = nullptr;
for (ConfigValuesIterator iter(this); !iter.done(); iter.Next()) {
for (const SourceFile& file : iter.cur().inputs())
CheckSourceGenerated(this, file, &cursor);
}
}
bool Target::GetMetadata(const std::vector<std::string>& keys_to_extract,
const std::vector<std::string>& keys_to_walk,
const SourceDir& rebase_dir,
bool deps_only,
std::vector<Value>* result,
TargetSet* targets_walked,
Err* err) const {
std::vector<Value> next_walk_keys;
std::vector<Value> current_result;
if (deps_only) {
next_walk_keys.push_back(Value(nullptr, ""));
} else {
if (!metadata().WalkStep(settings()->build_settings(), keys_to_extract,
keys_to_walk, rebase_dir, &next_walk_keys,
¤t_result, err))
return false;
}
const DepsIteratorRange& all_deps = GetDeps(Target::DEPS_ALL);
const SourceDir& current_dir = label().dir();
for (const auto& next : next_walk_keys) {
DCHECK(next.type() == Value::STRING);
if (next.string_value().empty()) {
for (const auto& dep : all_deps) {
if (targets_walked->add(dep.ptr)) {
if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
false, result, targets_walked, err))
return false;
}
}
for (const auto& dep : validations_) {
if (targets_walked->add(dep.ptr)) {
if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
false, result, targets_walked, err))
return false;
}
}
break;
}
Label next_label = Label::Resolve(
current_dir, settings()->build_settings()->root_path_utf8(),
settings()->toolchain_label(), next, err);
if (next_label.is_null()) {
*err = Err(next.origin(), std::string("Failed to canonicalize ") +
next.string_value() + std::string("."));
}
std::string canonicalize_next_label = next_label.GetUserVisibleName(true);
bool found_next = false;
for (const auto& dep : all_deps) {
if (dep.label.GetUserVisibleName(true) == canonicalize_next_label) {
if (targets_walked->add(dep.ptr)) {
if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
false, result, targets_walked, err))
return false;
}
found_next = true;
break;
}
}
if (!found_next) {
for (const auto& dep : validations_) {
if (dep.label.GetUserVisibleName(true) == canonicalize_next_label) {
if (targets_walked->add(dep.ptr)) {
if (!dep.ptr->GetMetadata(keys_to_extract, keys_to_walk, rebase_dir,
false, result, targets_walked, err))
return false;
}
found_next = true;
break;
}
}
}
if (!found_next) {
*err = Err(next.origin(),
std::string("I was expecting ") + canonicalize_next_label +
std::string(" to be a dependency of ") +
label().GetUserVisibleName(true) +
". Make sure it's included in the deps or data_deps, and "
"that you've specified the appropriate toolchain.");
return false;
}
}
result->insert(result->end(), std::make_move_iterator(current_result.begin()),
std::make_move_iterator(current_result.end()));
return true;
}