| // 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 "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*>; | |
| // Used to optimize the search for a target generating a given output file. | |
| // Keeps track of the last target and index where the file was found, so that | |
| // the next search can start from there. | |
| struct CheckSourceGeneratedCursor { | |
| const Target* target = nullptr; | |
| size_t index = 0; | |
| }; | |
| // 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 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."); | |
| } | |
| // Return true if |file| is a direct output of |target|. Uses |cursor| to speed | |
| // up consecutive calls to this function when files are checked in the same | |
| // order as a target's real outputs, which happens extremely often. | |
| 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; | |
| } | |
| // Set check_private_deps to true for the first invocation since a target | |
| // can see all of its dependencies. For recursive invocations this will be set | |
| // to false to follow only public dependency paths. | |
| // | |
| // Pass a pointer to an empty set for the first invocation. This will be used | |
| // to avoid duplicate checking. | |
| // | |
| // Checking of object files is optional because it is much slower. This allows | |
| // us to check targets for normal outputs, and then as a second pass check | |
| // object files (since we know it will be an error otherwise). This allows | |
| // us to avoid computing all object file names in the common case. | |
| 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; // Already checked this one and it's not found. | |
| if (HasDirectOutput(target, file, cursor)) { | |
| return true; | |
| } | |
| if (file == target->write_runtime_deps_output()) | |
| return true; | |
| // Check binary target intermediate files if requested. | |
| 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; | |
| } | |
| } | |
| // Only check private deps if requested. | |
| 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; // Found a path. | |
| } | |
| 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; // Found a path. | |
| } | |
| } | |
| } | |
| if (check_data_deps) { | |
| check_data_deps = false; // Consider only direct data_deps. | |
| for (const auto& pair : target->data_deps()) { | |
| if (EnsureFileIsGeneratedByDependency( | |
| pair.ptr, file, false, consider_object_files, check_data_deps, | |
| seen_targets, cursor)) | |
| return true; // Found a path. | |
| } | |
| } | |
| // Check all public dependencies (don't do data ones since those are | |
| // runtime-only). | |
| for (const auto& pair : target->public_deps()) { | |
| if (EnsureFileIsGeneratedByDependency( | |
| pair.ptr, file, false, consider_object_files, check_data_deps, | |
| seen_targets, cursor)) | |
| return true; // Found a path. | |
| } | |
| 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; // Not in output dir, this is OK. | |
| // Tell the scheduler about unknown files. This will be noted for later so | |
| // the list of files written by the GN build itself (often response files) | |
| // can be filtered out of this list. | |
| OutputFile out_file(build_settings, source); | |
| TargetSet seen_targets; | |
| bool check_data_deps = false; | |
| bool consider_object_files = false; | |
| // If this is not the first file, start by looking where the last one was | |
| // found. | |
| 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(); | |
| // Allow dependency to be through data_deps for files generated by gn. | |
| check_data_deps = | |
| g_scheduler->IsFileGeneratedByWriteRuntimeDeps(out_file) || | |
| g_scheduler->IsFileGeneratedByTarget(source); | |
| // Check object files (much slower and very rare) only if the "normal" | |
| // output check failed. | |
| 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); | |
| } | |
| } | |
| // check_this indicates if the given target should be matched against the | |
| // patterns. It should be set to false for the first call since assert_no_deps | |
| // shouldn't match the target itself. | |
| // | |
| // visited should point to an empty set, this will be used to prevent | |
| // multiple visits. | |
| // | |
| // *failure_path_str will be filled with a string describing the path of the | |
| // dependency failure, and failure_pattern will indicate the pattern in | |
| // assert_no that matched the target. | |
| // | |
| // Returns true if everything is OK. failure_path_str and failure_pattern_index | |
| // will be unchanged in this case. | |
| 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; // Already checked this target. | |
| if (check_this) { | |
| // Check this target against the given list of patterns. | |
| for (const LabelPattern& pattern : assert_no) { | |
| if (pattern.Matches(target->label())) { | |
| // Found a match. | |
| *failure_pattern = &pattern; | |
| *failure_path_str = | |
| kIndentPath + target->label().GetUserVisibleName(false); | |
| return false; | |
| } | |
| } | |
| } | |
| // Recursively check dependencies. | |
| 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)) { | |
| // To reconstruct the path, prepend the current target to the error. | |
| std::string prepend_path = | |
| kIndentPath + target->label().GetUserVisibleName(false) + " ->\n"; | |
| failure_path_str->insert(0, prepend_path); | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| } // namespace | |
| 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; | |
| // A technical note on accessors defined below: Using a static global | |
| // constant is much faster at runtime than using a static local one. | |
| // | |
| // In other words: | |
| // | |
| // static const Foo kEmptyFoo; | |
| // | |
| // const Foo& Target::foo() const { | |
| // return foo_ ? *foo_ : kEmptyFoo; | |
| // } | |
| // | |
| // Is considerably faster than: | |
| // | |
| // const Foo& Target::foo() const { | |
| // if (foo_) { | |
| // return *foo_; | |
| // } else { | |
| // static const Foo kEmptyFoo; | |
| // return kEmptyFoo; | |
| // } | |
| // } | |
| // | |
| // Because the latter requires relatively expensive atomic operations | |
| // in the second branch. | |
| // | |
| 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_; | |
| } | |
| // static | |
| 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()); | |
| // Copy this target's own dependent and public configs to the list of configs | |
| // applying to it. | |
| configs_.Append(all_dependent_configs_.begin(), all_dependent_configs_.end()); | |
| MergePublicConfigsFrom(this, &configs_); | |
| // Check visibility for just this target's own configs, before dependents are | |
| // added, but after public_configs and all_dependent_configs are merged. | |
| if (!CheckConfigVisibility(err)) | |
| return false; | |
| // Copy public configs from all dependencies into the list of configs | |
| // applying to this target (configs_). | |
| PullDependentTargetConfigs(); | |
| // Copies public dependencies' public configs to this target's public | |
| // configs. These configs have already been applied to this target by | |
| // PullDependentTargetConfigs above, along with the public configs from | |
| // private deps. This step re-exports them as public configs for targets that | |
| // depend on this one. | |
| 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 { | |
| 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 { | |
| // BUNDLE_DATA exists only to declare inputs to subsequent CREATE_BUNDLE | |
| // targets. Changing only contents of the bundle data target should not cause | |
| // a binary to be re-linked. It should affect only the CREATE_BUNDLE steps | |
| // instead. As a result, normal targets should treat this as a data | |
| // dependency. | |
| return output_type_ == BUNDLE_DATA; | |
| } | |
| bool Target::ShouldGenerate() const { | |
| const auto& root_patterns = settings()->build_settings()->root_patterns(); | |
| if (root_patterns.empty()) { | |
| // By default, generate all targets that belong to the default toolchain. | |
| 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)); | |
| } | |
| // All deps. | |
| 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) { | |
| // Only add the prefix if the name doesn't already have it and it's not | |
| // being overridden. | |
| if (!output_prefix_override_ && !name.starts_with(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; | |
| // 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(), | |
| 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) { | |
| // Binary target with normal outputs (source sets have phony targets). | |
| DCHECK(IsBinary()) << static_cast<int>(output_type()); | |
| if (!build_complete) { | |
| // Can't access the toolchain for a target before the build is complete. | |
| // Otherwise it will race with loading and setting the toolchain | |
| // definition. | |
| *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 { | |
| // Everything else (like a group or bundle_data) has a phony output. The | |
| // dependency output phony should have computed what this is. This won't be | |
| // valid unless the build is complete. | |
| if (!build_complete) { | |
| *err = Err(loc_for_error, kBuildIncompleteMsg); | |
| return false; | |
| } | |
| // The dependency output might be empty if there is no output file or a | |
| // phony alias for a set of inputs. | |
| if (has_dependency_output()) { | |
| outputs->push_back( | |
| dependency_output().AsSourceFile(settings()->build_settings())); | |
| } | |
| } | |
| return true; | |
| } | |
| bool Target::GetOutputFilesForSource(const SourceFile& source, | |
| const char** computed_tool_type, | |
| std::vector<OutputFile>* outputs) const { | |
| DCHECK(toolchain()); // Should be resolved before calling. | |
| outputs->clear(); | |
| *computed_tool_type = Tool::kToolNone; | |
| if (output_type() == Target::COPY_FILES || | |
| output_type() == Target::ACTION_FOREACH) { | |
| // These target types apply the output pattern to the input. | |
| std::vector<SourceFile> output_files; | |
| SubstitutionWriter::ApplyListToSourceAsOutputFile( | |
| this, settings(), action_values().outputs(), source, outputs); | |
| } else if (!IsBinary()) { | |
| // All other non-binary target types just return the target outputs. We | |
| // don't know if the build is complete and it doesn't matter for non-binary | |
| // targets, so just assume it's not and pass "false". | |
| std::vector<SourceFile> outputs_as_source_files; | |
| Err err; // We can ignore the error and return empty for failure. | |
| GetOutputsAsSourceFiles(LocationRange(), false, &outputs_as_source_files, | |
| &err); | |
| // Convert to output files. | |
| for (const auto& cur : outputs_as_source_files) | |
| outputs->emplace_back(OutputFile(settings()->build_settings(), cur)); | |
| } else { | |
| // All binary targets do a tool lookup. | |
| DCHECK(IsBinary()); | |
| const SourceFile::Type file_type = source.GetType(); | |
| if (file_type == SourceFile::SOURCE_UNKNOWN) | |
| return false; | |
| if (file_type == SourceFile::SOURCE_O) { | |
| // Object files just get passed to the output and not compiled. | |
| outputs->emplace_back(OutputFile(settings()->build_settings(), source)); | |
| return true; | |
| } | |
| // Rust generates on a module level, not source. | |
| if (file_type == SourceFile::SOURCE_RS) | |
| return false; | |
| *computed_tool_type = Tool::GetToolTypeForSourceType(file_type); | |
| if (*computed_tool_type == Tool::kToolNone) | |
| return false; // No tool for this file (it's a header file or something). | |
| const Tool* tool = toolchain_->GetTool(*computed_tool_type); | |
| if (!tool) | |
| return false; // Tool does not apply for this toolchain.file. | |
| // Swift may generate on a module or source level. | |
| 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(); | |
| // Figure out what output(s) this compiler produces. | |
| 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::PullRecursiveBundleData() { | |
| const bool is_create_bundle = output_type_ == CREATE_BUNDLE; | |
| for (const auto& pair : GetDeps(DEPS_LINKED)) { | |
| // Don't propagate across toolchain. | |
| if (pair.ptr->toolchain() != toolchain()) | |
| continue; | |
| // Don't propagete through create_bundle, unless it is transparent. | |
| if (pair.ptr->output_type() == CREATE_BUNDLE && | |
| !pair.ptr->bundle_data().transparent()) { | |
| continue; | |
| } | |
| // Direct dependency on a bundle_data target. | |
| if (pair.ptr->output_type() == BUNDLE_DATA) { | |
| bundle_data().AddBundleData(pair.ptr, is_create_bundle); | |
| } | |
| // Recursive bundle_data information from all dependencies. | |
| 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 { | |
| // This check is only necessary if this target will result in a phony target. | |
| // Phony targets with no real inputs are treated as always dirty. | |
| // Actions and generated_file always have at least one input file: the script | |
| // used to execute the action or generated file itself. As such, they will | |
| // never have an input-less phony target. We check this first to elide the | |
| // common checks. | |
| if (output_type() == ACTION || output_type() == ACTION_FOREACH || | |
| output_type() == GENERATED_FILE) { | |
| return true; | |
| } | |
| // If any of this target's dependencies is non-phony target or a phony target | |
| // with real inputs, then this target should be considered to have inputs. | |
| for (const auto& pair : GetDeps(DEPS_ALL)) { | |
| if (pair.ptr->has_dependency_output()) { | |
| return true; | |
| } | |
| } | |
| if (output_type() == BUNDLE_DATA) { | |
| return !sources().empty(); | |
| } | |
| if (output_type() == CREATE_BUNDLE) { | |
| // CREATE_BUNDLE targets pick up most of their inputs in the form of | |
| // dependencies on bundle_data targets, which were checked above when | |
| // looping through GetDeps. This code handles the remaining possible | |
| // CREATE_BUNDLE inputs. | |
| return !bundle_data().assets_catalog_sources().empty() || | |
| !bundle_data().partial_info_plist().is_null() || | |
| !bundle_data().post_processing_script().is_null(); | |
| } | |
| // If any of this target's sources will result in output files, then this | |
| // target should be considered to have real inputs. | |
| std::vector<OutputFile> tool_outputs; | |
| return std::any_of( | |
| sources().begin(), sources().end(), [&, this](const auto& source) { | |
| // Swift files always results in output files, but the name cannot | |
| // be derived from the source file via GetOutputFilesForSource(...). | |
| 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()) { | |
| dependency_output_alias_ = | |
| GetBuildDirForTargetAsOutputFile(this, BuildDirType::PHONY); | |
| dependency_output_alias_.value().append(label().name()); | |
| } | |
| } else { | |
| // 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". Setting "output_name" does not | |
| // affect the stamp file name: it is always based on the original target | |
| // name. | |
| 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: | |
| // Executables and loadable modules don't get linked to, but the first | |
| // output is used for dependency management. | |
| 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()) { | |
| // Default to the first output for the runtime output. | |
| runtime_outputs_.push_back(dependency_output_file_); | |
| } else { | |
| SubstitutionWriter::ApplyListToLinkerAsOutputFile( | |
| this, tool, tool->runtime_outputs(), &runtime_outputs_); | |
| } | |
| break; | |
| case RUST_LIBRARY: | |
| case STATIC_LIBRARY: | |
| // Static libraries both have dependencies and linking going off of the | |
| // first output. | |
| 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) { | |
| // Default behavior, use the first output file for both. | |
| 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()) { | |
| // Default to the link output for the runtime output. | |
| runtime_outputs_.push_back(link_output_file_); | |
| } else { | |
| SubstitutionWriter::ApplyListToLinkerAsOutputFile( | |
| this, tool, *runtime_outputs_ptr, &runtime_outputs_); | |
| } | |
| break; | |
| } | |
| case UNKNOWN: | |
| default: | |
| NOTREACHED(); | |
| } | |
| // Count anything generated from bundle_data dependencies. | |
| if (output_type_ == CREATE_BUNDLE) { | |
| if (!bundle_data().GetOutputFiles(settings(), this, &computed_outputs_, | |
| err)) | |
| return false; | |
| } | |
| // Count all outputs from this tool as something generated by this target. | |
| if (check_tool_outputs) { | |
| SubstitutionWriter::ApplyListToLinkerAsOutputFile( | |
| this, tool, tool->outputs(), &computed_outputs_); | |
| // Output names aren't canonicalized in the same way that source files | |
| // are. For example, the tool outputs often use | |
| // {{some_var}}/{{output_name}} which expands to "./foo", but this won't | |
| // match "foo" which is what we'll compute when converting a SourceFile to | |
| // an OutputFile. | |
| for (auto& out : computed_outputs_) | |
| NormalizePath(&out.value()); | |
| } | |
| // Also count anything the target has declared to be an output. | |
| 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)); | |
| } | |
| return true; | |
| } | |
| bool Target::ResolvePrecompiledHeaders(Err* err) { | |
| // Precompiled headers are stored on a ConfigValues struct. This way, the | |
| // build can set all the precompiled header settings in a config and apply | |
| // it to many targets. Likewise, the precompiled header values may be | |
| // specified directly on a target. | |
| // | |
| // Unlike other values on configs which are lists that just get concatenated, | |
| // the precompiled header settings are unique values. We allow them to be | |
| // specified anywhere, but if they are specified in more than one place all | |
| // places must match. | |
| // Track where the current settings came from for issuing errors. | |
| 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; // Skip the one on the target itself. | |
| const Config* config = iter.GetCurrentConfig(); | |
| const ConfigValues& cur = config->resolved_values(); | |
| if (!cur.has_precompiled_headers()) | |
| continue; // This one has no precompiled header info, skip. | |
| if (has_precompiled_headers) { | |
| // Already have a precompiled header values, the settings must match. | |
| 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 { | |
| // Have settings from a config, apply them to ourselves. | |
| 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, 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, 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 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; | |
| } | |
| } | |
| // Verify no configs have "testonly" set. | |
| 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 { | |
| // Checks that any inputs or sources to this target that are in the build | |
| // directory are generated by a target that this one transitively depends on | |
| // in some way. We already guarantee that all generated files are written | |
| // to the build dir. | |
| // | |
| // See Scheduler::AddUnknownGeneratedInput's declaration for more. | |
| 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); | |
| } | |
| // TODO(agrieve): Check all_libs_ here as well (those that are source files). | |
| // http://crbug.com/571731 | |
| } | |
| 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, this is the top-level target and thus we don't want to | |
| // collect its metadata, only that of its deps and data_deps. | |
| if (deps_only) { | |
| // Empty string will be converted below to mean all deps and data_deps. | |
| // Origin is null because this isn't declared anywhere, and should never | |
| // trigger any errors. | |
| next_walk_keys.push_back(Value(nullptr, "")); | |
| } else { | |
| // Otherwise, we walk this target and collect the appropriate data. | |
| // NOTE: Always call WalkStep() even when have_metadata() is false, | |
| // because WalkStep() will append to 'next_walk_keys' in this case. | |
| // See https://crbug.com/1273069. | |
| if (!metadata().WalkStep(settings()->build_settings(), keys_to_extract, | |
| keys_to_walk, rebase_dir, &next_walk_keys, | |
| ¤t_result, err)) | |
| return false; | |
| } | |
| // Gather walk keys and find the appropriate target. Targets identified in | |
| // the walk key set must be deps or data_deps of the declaring target. | |
| 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 we hit an empty string in this list, add all deps and data_deps. The | |
| // ordering in the resulting list of values as a result will be the data | |
| // from each explicitly listed dep prior to this, followed by all data in | |
| // walk order of the remaining deps. | |
| if (next.string_value().empty()) { | |
| for (const auto& dep : all_deps) { | |
| // If we haven't walked this dep yet, go down into it. | |
| 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; | |
| } | |
| } | |
| // Any other walk keys are superfluous, as they can only be a subset of | |
| // all deps. | |
| break; | |
| } | |
| // Otherwise, look through the target's deps for the specified one. | |
| // Canonicalize the label if possible. | |
| 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) { | |
| // Match against the label with the toolchain. | |
| if (dep.label.GetUserVisibleName(true) == canonicalize_next_label) { | |
| // If we haven't walked this dep yet, go down into it. | |
| 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; | |
| } | |
| // We found it, so we can exit this search now. | |
| found_next = true; | |
| break; | |
| } | |
| } | |
| // If we didn't find the specified dep in the target, that's an error. | |
| // Propagate it back to the user. | |
| 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; | |
| } |