blob: 52d4816123bb037b9bd53d2618e494d8fd5a29ab [file]
// 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_