| // 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. | |
| #ifndef TOOLS_GN_BUILDER_RECORD_H_ | |
| #define TOOLS_GN_BUILDER_RECORD_H_ | |
| #include <memory> | |
| #include <utility> | |
| #include "base/containers/flat_map.h" | |
| #include "gn/item.h" | |
| #include "gn/location.h" | |
| #include "gn/pointer_set.h" | |
| class ParseNode; | |
| // Set this to 1 to enable builder record logging during development. | |
| #define DEBUG_BUILDER_RECORD 0 | |
| #if DEBUG_BUILDER_RECORD | |
| #define DEBUG_BUILDER_RECORD_LOG(fmt, ...) fprintf(stderr, fmt, ##__VA_ARGS__) | |
| #else // !DEBUG_BUILDER_RECORD | |
| #define DEBUG_BUILDER_RECORD_LOG(fmt, ...) \ | |
| do { \ | |
| } while (0) | |
| #endif // !DEBUG_BUILDER_RECORD | |
| // This class models what GN knows about labelled items when resolving | |
| // all dependencies in the configured graph. | |
| // | |
| // Each BuilderRecord provides information about a given graph item | |
| // label, that is augmented by the Builder class. It can be in one of | |
| // the following states, which are taken sequentially, always in the | |
| // same order. | |
| // | |
| // Init | |
| // The record only includes a label, an ItemType and the | |
| // location where that label was first found in the parse tree | |
| // (for error reporting). All other fields are empty or should be | |
| // ignored. | |
| // | |
| // Defined | |
| // An Item instance is also associated with the record, however its | |
| // content is not final, meaning that its Item::OnResolved() method | |
| // has not been called yet. | |
| // | |
| // Resolved | |
| // The record's item's OnResolved() method has been called, and the | |
| // item's state is now final and immutable. | |
| // | |
| // Finalized | |
| // This indicates that the item can be written to the Ninja file. | |
| // | |
| // Records can only change state under certain conditions: | |
| // | |
| // Init -> Defined: | |
| // This requires a new Item instance coming from the BUILD.gn | |
| // evaluator matching the same label. Note that the Item returned | |
| // by the evaluator is incomplete. In particular its ConfigLabelVector | |
| // and TargetLabelVector values only contain null pointers, since | |
| // their labels haven't been resolved yet. | |
| // | |
| // Defined -> Resolved: | |
| // This requires that all standard dependencies are themselves | |
| // in the Resolved state, and that all validations are in the Defined | |
| // state. | |
| // | |
| // Once this condition is reached, the builder adjusts all label | |
| // vectors in the Item to point to the right dependency, then | |
| // the item's OnResolved() method is called. | |
| // | |
| // For example Target::OnResolved() performs config propagation, | |
| // visibility checks, and other computations, then returns the Item | |
| // in its final state, which cannot include null pointers. | |
| // | |
| // Resolved -> Finalized: | |
| // This requires all standard dependencies to be Finalized, and all | |
| // validation dependencies to be Resolved. Ensuring that the | |
| // dependency graph from the root to this item is fully immutable. | |
| // | |
| // Every time a record's state is changed, checks are performed to see | |
| // if the state of other dependents can now be changed accordingly. | |
| // For example, a Defined target needs to wait for all its dependencies | |
| // to be Resolved, before changing its own state to Resolved. This is | |
| // tracked by various counters and sets in the records. | |
| // | |
| // The record's should_generate() flag is set to true to indicate that | |
| // a corresponding Ninja build statement should be generated for the item. | |
| // By default, it is set to true for all targets defined in BUILD.gn files | |
| // evaluated in the default toolchain, and only for targets reachable from | |
| // the root in other toolchains. | |
| // | |
| // The Ninja build statement can only be written if the target's record | |
| // is in the Finalized state. | |
| // | |
| // A note on validations: | |
| // | |
| // Validation dependencies are a bit special, because they can introduce | |
| // cycles in the dependency graph. For example, this is valid: | |
| // | |
| // foo ------validation------> bar | |
| // ^ | | |
| // | | | |
| // +--------deps------------+ | |
| // | |
| // In this case, foo can be Resolved as soon as bar is Defined, but | |
| // won't be Finalized until bar is Resolved itself. This ensures that | |
| // bar's output path is known when foo is written to the Ninja file, | |
| // as it is required by the build statement which will look like: | |
| // | |
| // build foo.output: some_rule |@ bar.output | |
| // | |
| // Ninja doesn't enforce an order for the build statements of foo and | |
| // bar, but computing `bar.output` can only be done when `bar` is | |
| // Resolved. | |
| // | |
| // Another issue is that metadata walks, performed when writing | |
| // the build statement for generated_file() targets, will walk over | |
| // validation dependencies as well. This requires these to be resolved. | |
| // | |
| class BuilderRecord { | |
| public: | |
| using BuilderRecordSet = PointerSet<BuilderRecord>; | |
| enum ItemType : unsigned char { | |
| ITEM_UNKNOWN, | |
| ITEM_TARGET, | |
| ITEM_CONFIG, | |
| ITEM_TOOLCHAIN, | |
| ITEM_POOL | |
| }; | |
| enum RecordState : unsigned char { | |
| STATE_INIT, | |
| STATE_DEFINED, | |
| STATE_RESOLVED, | |
| STATE_FINALIZED, | |
| }; | |
| #if DEBUG_BUILDER_RECORD | |
| static const char* ToDebugCString(RecordState); | |
| #endif // DEBUG_BUILDER_RECORD | |
| BuilderRecord(ItemType type, | |
| const Label& label, | |
| const ParseNode* originally_referenced_from); | |
| ItemType type() const { return type_; } | |
| const Label& label() const { return label_; } | |
| // Returns a user-ready name for the given type. e.g. "target". | |
| static const char* GetNameForType(ItemType type); | |
| // Returns true if the given item is of the given type. | |
| static bool IsItemOfType(const Item* item, ItemType type); | |
| // Returns the type enum for the given item. | |
| static ItemType TypeOfItem(const Item* item); | |
| Item* item() { return item_.get(); } | |
| const Item* item() const { return item_.get(); } | |
| // Indicates from where this item was originally referenced from that caused | |
| // it to be loaded. For targets for which we encountered the declaration | |
| // before a reference, this will be the empty range. | |
| const ParseNode* originally_referenced_from() const { | |
| return originally_referenced_from_; | |
| } | |
| bool should_generate() const { return should_generate_; } | |
| void set_should_generate(bool sg) { should_generate_ = sg; } | |
| // Return true if this record is in the Defined state or a later one. | |
| bool is_defined() const { return state_ >= STATE_DEFINED; } | |
| // Change the record's state to Defined. This only requires | |
| // a new Item instance to be provided. | |
| void SetDefined(std::unique_ptr<Item> item); | |
| // After a call to SetDefined()call these methods for each dependency | |
| // based on its type. This is later used to control state changes. | |
| void AddDep(BuilderRecord* dep_record); | |
| void AddGenDep(BuilderRecord* dep_record); | |
| void AddValidationDep(BuilderRecord* dep_record); | |
| // Return current record state. | |
| RecordState state() const { return state_; } | |
| // Returns true if the items has been resolved. | |
| bool is_resolved() const { return state_ >= STATE_RESOLVED; } | |
| // Returns true if the item can be resolved. This requires all its | |
| // standard dependencies to be resolved, and all its validations to | |
| // be defined. | |
| bool can_resolve() const { | |
| return state_ == STATE_DEFINED && unresolved_count_ == 0; | |
| } | |
| // Change the record's state to Resolved. This requires can_resolve() | |
| // to be true. | |
| void SetResolved(); | |
| // Notify all dependents that the current record's item state | |
| // has changed. |update_dependent()| will be called for any | |
| // dependent whose state may change itself. |from_state| is | |
| // the initial state the record had before its current one. | |
| // Return true on success, or false in case of error. | |
| template <typename UpdateDependent> | |
| bool NotifyDependentsOfStateChange(RecordState from_state, | |
| UpdateDependent update_dependent) { | |
| if (from_state != state_) { | |
| for (auto& [dependent, wait_info] : waiting_map_) { | |
| if (dependent->OnDepStateChange(this, from_state, state_, wait_info)) | |
| if (!update_dependent(dependent)) | |
| return false; | |
| } | |
| } | |
| return true; | |
| } | |
| // Return true if this record has been finalized. | |
| bool is_finalized() const { return state_ >= STATE_FINALIZED; } | |
| // Return true if this record can be finalized. | |
| bool can_finalize() const { | |
| return state_ == STATE_RESOLVED && unfinalized_count_ == 0 && | |
| unresolved_count_ == 0; | |
| } | |
| // Change the state to Finalized. This requires can_finalize() to be true. | |
| void SetFinalized(); | |
| // All records this one is depending on. Note that this includes gen_deps for | |
| // targets, which can have cycles. | |
| BuilderRecordSet& all_deps() { return all_deps_; } | |
| const BuilderRecordSet& all_deps() const { return all_deps_; } | |
| // Get the set of unresolved records this one depends on, | |
| // as a list sorted by label. | |
| std::vector<const BuilderRecord*> GetSortedUnresolvedDeps() const; | |
| // Used by unit-tests. | |
| // Records that are waiting on this one to be defined. This is used for | |
| // "validations" dependencies which don't require the target to be fully | |
| // resolved, only defined. | |
| BuilderRecordSet waiting_on_validation_definition() const { | |
| BuilderRecordSet result; | |
| for (const auto& pair : waiting_map_) { | |
| if (pair.second.wait_validation_defined) | |
| result.add(pair.first); | |
| } | |
| return result; | |
| } | |
| // Records that are waiting on this one to be resolved. This is the other | |
| // end of the "unresolved deps" arrow for standard dependencies. | |
| BuilderRecordSet waiting_on_resolution() const { | |
| BuilderRecordSet result; | |
| for (const auto& pair : waiting_map_) { | |
| if (pair.second.wait_resolved) | |
| result.add(pair.first); | |
| } | |
| return result; | |
| } | |
| // Records that are waiting on this one to be resolved before they can be | |
| // written to the Ninja file. This is used for "validations" dependencies. | |
| BuilderRecordSet waiting_on_validation_resolution() const { | |
| BuilderRecordSet result; | |
| for (const auto& pair : waiting_map_) { | |
| if (pair.second.wait_validation_resolved) | |
| result.add(pair.first); | |
| } | |
| return result; | |
| } | |
| // Comparator function used to sort records from their label. | |
| static bool LabelCompare(const BuilderRecord* a, const BuilderRecord* b) { | |
| return a->label_ < b->label_; | |
| } | |
| // Create debug string describing this record. | |
| std::string ToDebugString() const; | |
| private: | |
| RecordState state_ = STATE_INIT; | |
| ItemType type_; | |
| bool should_generate_ = false; | |
| Label label_; | |
| std::unique_ptr<Item> item_; | |
| const ParseNode* originally_referenced_from_ = nullptr; | |
| // The number of dependencies preventing this record from being resolved. | |
| // This is the sum of two numbers: | |
| // | |
| // - The number of unresolved standard dependencies this record is | |
| // waiting for. For each such dep, dep[record].waiting_on_resolution_ | |
| // contains 'this'. | |
| // | |
| // - The number of undefined validation dependencies this record is | |
| // waiting for. For each such dep, dep[record].waiting_on_definition_ | |
| // contains 'this'. | |
| // | |
| size_t unresolved_count_ = 0; | |
| // The number of dependencies preventing this record from being finalized. | |
| // This is the sum of two numbers: | |
| // | |
| // - The numnber of unfinalized standard dependencies this record is | |
| // waiting for. For each such dep, dep[record].waiting_on_finalization_ | |
| // contains 'this'. | |
| // | |
| // - The number of unresolved validation dependencies this record is | |
| // waiting for. For each such dep, | |
| // dep[record].waiting_on_validation_resolution_ contains |this|. | |
| size_t unfinalized_count_ = 0; | |
| BuilderRecordSet all_deps_; | |
| // A specialized { dependent -> wait_info } map used to track | |
| // which state changes dependents of this record are waiting for. | |
| struct WaitInfo { | |
| bool wait_resolved = false; | |
| bool wait_finalized = false; | |
| bool wait_validation_defined = false; | |
| bool wait_validation_resolved = false; | |
| }; | |
| base::flat_map<BuilderRecord*, WaitInfo> waiting_map_; | |
| // Called by NotifyDependentsOfStateChange() when a dependency | |
| // of the current record has changed its state. Must return | |
| // true if the dependent (current record) can now change state | |
| // too, or false if it is still waiting for other dependency | |
| // changes. | |
| bool OnDepStateChange(BuilderRecord* dep, | |
| RecordState from_state, | |
| RecordState to_state, | |
| WaitInfo& info); | |
| BuilderRecord(const BuilderRecord&) = delete; | |
| BuilderRecord& operator=(const BuilderRecord&) = delete; | |
| }; | |
| #endif // TOOLS_GN_BUILDER_RECORD_H_ |