| // 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/builder_record.h" |
| |
| #include "gn/item.h" |
| |
| BuilderRecord::BuilderRecord(ItemType type, |
| const Label& label, |
| const ParseNode* originally_referenced_from) |
| : type_(type), |
| label_(label), |
| originally_referenced_from_(originally_referenced_from) {} |
| |
| // static |
| const char* BuilderRecord::GetNameForType(ItemType type) { |
| switch (type) { |
| case ITEM_TARGET: |
| return "target"; |
| case ITEM_CONFIG: |
| return "config"; |
| case ITEM_TOOLCHAIN: |
| return "toolchain"; |
| case ITEM_POOL: |
| return "pool"; |
| case ITEM_UNKNOWN: |
| default: |
| return "unknown"; |
| } |
| } |
| |
| // static |
| bool BuilderRecord::IsItemOfType(const Item* item, ItemType type) { |
| switch (type) { |
| case ITEM_TARGET: |
| return !!item->AsTarget(); |
| case ITEM_CONFIG: |
| return !!item->AsConfig(); |
| case ITEM_TOOLCHAIN: |
| return !!item->AsToolchain(); |
| case ITEM_POOL: |
| return !!item->AsPool(); |
| case ITEM_UNKNOWN: |
| default: |
| return false; |
| } |
| } |
| |
| // static |
| BuilderRecord::ItemType BuilderRecord::TypeOfItem(const Item* item) { |
| if (item->AsTarget()) |
| return ITEM_TARGET; |
| if (item->AsConfig()) |
| return ITEM_CONFIG; |
| if (item->AsToolchain()) |
| return ITEM_TOOLCHAIN; |
| if (item->AsPool()) |
| return ITEM_POOL; |
| |
| NOTREACHED(); |
| return ITEM_UNKNOWN; |
| } |
| |
| #if DEBUG_BUILDER_RECORD |
| // static |
| const char* BuilderRecord::ToDebugCString(RecordState state) { |
| switch (state) { |
| case STATE_INIT: |
| return "INIT"; |
| case STATE_DEFINED: |
| return "DEFINED"; |
| case STATE_RESOLVED: |
| return "RESOLVED"; |
| case STATE_FINALIZED: |
| return "FINALIZED"; |
| default: |
| return "<<<<UNKNOWN STATE>>>>>>"; |
| } |
| } |
| #endif // DEBUG_BUILDER_RECORD |
| |
| void BuilderRecord::SetDefined(std::unique_ptr<Item> item) { |
| DCHECK(state_ == STATE_INIT); |
| state_ = STATE_DEFINED; |
| item_ = std::move(item); |
| } |
| |
| void BuilderRecord::AddDep(BuilderRecord* dep) { |
| DCHECK(state_ == STATE_DEFINED); |
| all_deps_.add(dep); |
| WaitInfo* info = nullptr; |
| if (!dep->is_resolved()) { |
| // This cannot be resolved yet if any of its standard dependencies is not |
| // resolved. |
| if (!info) |
| info = &dep->waiting_map_[this]; |
| if (!info->wait_resolved) { |
| info->wait_resolved = true; |
| unresolved_count_++; |
| DEBUG_BUILDER_RECORD_LOG("-- AddDep %s -wait_resolved-> %s\n", |
| this->ToDebugString().c_str(), |
| dep->ToDebugString().c_str()); |
| } |
| } |
| if (!dep->is_finalized()) { |
| // Finalization of the current record is blocked until the |
| // dependency is finalized. |
| if (!info) |
| info = &dep->waiting_map_[this]; |
| if (!info->wait_finalized) { |
| info->wait_finalized = true; |
| unfinalized_count_++; |
| DEBUG_BUILDER_RECORD_LOG("-- AddDep %s -wait_finalized-> %s\n", |
| this->ToDebugString().c_str(), |
| dep->ToDebugString().c_str()); |
| } |
| } |
| } |
| |
| void BuilderRecord::AddGenDep(BuilderRecord* dep) { |
| DCHECK(state_ == STATE_DEFINED); |
| all_deps_.insert(dep); |
| } |
| |
| void BuilderRecord::AddValidationDep(BuilderRecord* dep) { |
| DCHECK(state_ == STATE_DEFINED); |
| // Validation dependencies are treated differently than normal dependencies: |
| // 1. They are added to all_deps_ for graph traversal (should_generate |
| // propagation) and error checking. |
| // 2. They block resolution ONLY if they are undefined (!item). This allows |
| // cycles (A is validated by B, B depends on A) to resolve. |
| // 3. They block writing if they are unresolved. This prevents race conditions |
| // where we might write the ninja file before the validation output path |
| // is computed. |
| all_deps_.add(dep); |
| WaitInfo* info = nullptr; |
| if (!dep->is_defined()) { |
| // The record cannot be resolved yet if any of its validation dependencies |
| // is not defined. |
| if (!info) |
| info = &dep->waiting_map_[this]; |
| if (!info->wait_validation_defined) { |
| info->wait_validation_defined = true; |
| unresolved_count_++; |
| DEBUG_BUILDER_RECORD_LOG( |
| "-- AddValidationDep %s -wait_validation_defined-> %s\n", |
| this->ToDebugString().c_str(), dep->ToDebugString().c_str()); |
| } |
| } |
| if (!dep->is_resolved()) { |
| // This record cannot be finalized if any of its validation deps is not |
| // resolved. |
| if (!info) |
| info = &dep->waiting_map_[this]; |
| if (!info->wait_validation_resolved) { |
| info->wait_validation_resolved = true; |
| unfinalized_count_++; |
| DEBUG_BUILDER_RECORD_LOG( |
| "-- AddValidationDep %s -wait_validation_resolved-> %s\n", |
| this->ToDebugString().c_str(), dep->ToDebugString().c_str()); |
| } |
| } |
| } |
| |
| bool BuilderRecord::OnDepStateChange(BuilderRecord* dep, |
| RecordState from_state, |
| RecordState to_state, |
| BuilderRecord::WaitInfo& info) { |
| bool result = false; |
| |
| DCHECK(all_deps_.contains(dep)); |
| |
| DEBUG_BUILDER_RECORD_LOG( |
| "-- OnDepStateChange %s -> %s (%s->%s)\n", this->ToDebugString().c_str(), |
| dep->ToDebugString().c_str(), ToDebugCString(from_state), |
| ToDebugCString(to_state)); |
| if (to_state >= STATE_DEFINED && info.wait_validation_defined) { |
| info.wait_validation_defined = false; |
| DCHECK(state_ < STATE_RESOLVED); |
| DCHECK(unresolved_count_ > 0); |
| if (--unresolved_count_ == 0) { |
| DEBUG_BUILDER_RECORD_LOG(" CAN RESOLVE (validation)\n"); |
| result = true; |
| } |
| } |
| if (to_state >= STATE_RESOLVED && info.wait_resolved) { |
| info.wait_resolved = false; |
| DCHECK(state_ < STATE_RESOLVED); |
| DCHECK(unresolved_count_ > 0); |
| if (--unresolved_count_ == 0) { |
| DEBUG_BUILDER_RECORD_LOG(" CAN RESOLVE\n"); |
| result = true; |
| } |
| } |
| if (to_state >= STATE_RESOLVED && info.wait_validation_resolved) { |
| info.wait_validation_resolved = false; |
| DCHECK(state_ < STATE_FINALIZED); |
| DCHECK(unfinalized_count_ > 0); |
| if (--unfinalized_count_ == 0) { |
| DEBUG_BUILDER_RECORD_LOG(" CAN FINALIZE (validation)\n"); |
| result = true; |
| } |
| } |
| if (to_state >= STATE_FINALIZED && info.wait_finalized) { |
| info.wait_finalized = false; |
| DCHECK(state_ < STATE_FINALIZED); |
| DCHECK(unfinalized_count_ > 0); |
| if (--unfinalized_count_ == 0) { |
| DEBUG_BUILDER_RECORD_LOG(" CAN FINALIZE\n"); |
| result = true; |
| } |
| } |
| return result; |
| } |
| |
| void BuilderRecord::SetResolved() { |
| DCHECK(can_resolve()); |
| state_ = STATE_RESOLVED; |
| } |
| |
| void BuilderRecord::SetFinalized() { |
| DCHECK(can_finalize()); |
| state_ = STATE_FINALIZED; |
| } |
| |
| std::vector<const BuilderRecord*> BuilderRecord::GetSortedUnresolvedDeps() |
| const { |
| std::vector<const BuilderRecord*> result; |
| for (auto it = all_deps_.begin(); it.valid(); ++it) { |
| const BuilderRecord* dep = *it; |
| auto wait_it = dep->waiting_map_.find(this); |
| if (wait_it == dep->waiting_map_.end()) |
| continue; |
| if (wait_it->second.wait_resolved || |
| wait_it->second.wait_validation_defined) { |
| result.push_back(dep); |
| } |
| } |
| std::sort(result.begin(), result.end(), LabelCompare); |
| return result; |
| } |
| |
| #if DEBUG_BUILDER_RECORD |
| std::string BuilderRecord::ToDebugString() const { |
| std::string result = label_.GetUserVisibleName(false); |
| result += " ("; |
| switch (state_) { |
| case STATE_INIT: |
| result += "INIT"; |
| break; |
| case STATE_DEFINED: |
| result += "DEFINED"; |
| break; |
| case STATE_RESOLVED: |
| result += "RESOLVED"; |
| break; |
| case STATE_FINALIZED: |
| result += "FINALIZED"; |
| break; |
| default: |
| result += "UNKNOWN"; |
| break; |
| } |
| result += " unresolved=" + std::to_string(unresolved_count_); |
| result += " unfinalized=" + std::to_string(unfinalized_count_); |
| if (should_generate_) |
| result += " generated"; |
| result += ")"; |
| return result; |
| } |
| #endif // DEBUG_BUILDER_RECORD |