Merge "Add usesninjalogasweightlist option"
diff --git a/src/build.cc b/src/build.cc
index 6c66eb2..4ccfaf3 100644
--- a/src/build.cc
+++ b/src/build.cc
@@ -105,19 +105,6 @@
   std::unordered_set<HashedStr> key_data_;
   std::unordered_map<HashedStrView, int64_t> map_data_;
 };
-
-// Get weight from data source, it isn't related to Edge.weight because
-// Edge.weight is used for task distribution across pools which we don't
-// want to do that in this context.
-int64_t GetWeight(const WeightDataSource& data_source, Edge* edge) {
-  if (!edge || edge->outputs_ready()) {
-    return 0;
-  }
-  if (edge->is_phony()) {
-    return 1;
-  }
-  return data_source.Get(edge->outputs_[0]->globalPath().h).value_or(1);
-}
 }  // namespace
 
 Plan::Plan(Builder* builder)
@@ -646,8 +633,6 @@
         }
       }
     }
-  } else {
-    Fatal("data_source should exist here.");
   }
   std::vector<std::pair<Edge*, int64_t>> todos;
   std::unique_ptr<ThreadPool> thread_pool = CreateThreadPool();
@@ -656,10 +641,35 @@
       todos.emplace_back(std::make_pair(node->in_edge(), 0));
     }
   }
+
+  // Get weight from data source or ninja log, it isn't related to Edge.weight,
+  // because Edge.weight is used for task distribution across pools which we don't
+  // want to do that in this context.
+  auto weight_getter = [&data_source, this](Edge* edge) -> int64_t {
+    if (!edge || edge->outputs_ready()) {
+      return 0;
+    }
+    if (edge->is_phony()) {
+      return 1;
+    }
+    if (config_.weight_list_path) {
+      return data_source.Get(edge->outputs_[0]->globalPath().h).value_or(1);
+    } else if (config_.ninja_log_as_weight_list) {
+      if (scan_.build_log()) {
+        auto* entry = scan_.build_log()->LookupByOutput(edge->outputs_[0]->globalPath());
+        if (entry) {
+          return entry->end_time - entry->start_time + 1;
+        }
+      }
+      return 1;
+    } else {
+      Fatal("either weight_list_path or ninja_log_as_weight_list should be set.");
+    }
+  };
   while (!todos.empty()) {
     // Traverse edges in BFS manner, and update each edge's critical path based priority from
     // accumulated weight.
-    const auto& result = ParallelMap(thread_pool.get(), todos, [&data_source] (auto& p) {
+    const auto& result = ParallelMap(thread_pool.get(), todos, [&weight_getter] (auto& p) {
       // the pair is composed of a visiting edge and accumulated critical path based priority.
       auto* e = p.first;
       auto acc = p.second;
@@ -667,7 +677,7 @@
       if (!e) {
         return std::unordered_map<Edge*, int64_t>();
       }
-      auto run = GetWeight(data_source, e);
+      auto run = weight_getter(e);
       auto new_priority = run + acc;
       // Skip if priority isn't updated
       if (new_priority <= e->priority()) {
@@ -689,7 +699,7 @@
 
       std::unordered_map<Edge*, int64_t> next_todo_map;
       for (auto* ne : next_edges) {
-        auto next_run = GetWeight(data_source, ne);
+        auto next_run = weight_getter(ne);
         if (next_run + e->priority() > ne->priority()) {
           next_todo_map.try_emplace(ne, e->priority());
         }
@@ -718,7 +728,7 @@
   if (!scan_.RecomputeNodesDirty(nodes, &validation_nodes, err))
     return false;
 
-  if (config_.weight_list_path) {
+  if (config_.weight_list_path || config_.ninja_log_as_weight_list) {
     RefreshPriority(nodes);
   }
 
diff --git a/src/build.h b/src/build.h
index 1283ee8..e58ac33 100644
--- a/src/build.h
+++ b/src/build.h
@@ -233,6 +233,8 @@
   ///   out/bin/bar,5
   /// Note that the default weight is 1 for a module which isn't included in the list.
   std::optional<std::string> weight_list_path;
+
+  bool ninja_log_as_weight_list;
 };
 
 /// Builder wraps the build process: starting commands, updating status.
diff --git a/src/ninja.cc b/src/ninja.cc
index 19fc605..75da99c 100644
--- a/src/ninja.cc
+++ b/src/ninja.cc
@@ -1308,6 +1308,7 @@
 "                             that these warnings work:\n"
 "                                undeclaredsymlinkoutputs\n"
 "  preremoveoutputs={yes,no}  whether to remove outputs before running rule\n"
+"  usesninjalogasweightlist={yes,no}  whether to use ninja log as source of weight list\n"
 "  usesweightlist={<file path>,no}  whether to prioritize some rules based on weight list from file\n");
     return false;
   } else if (name == "usesphonyoutputs=yes") {
@@ -1334,6 +1335,12 @@
   } else if (auto n = name.find("usesweightlist=") != std::string::npos) {
     config->weight_list_path = name.substr(n + strlen("usesweightlist=" ) - 1);
     return true;
+  } else if (name == "usesninjalogasweightlist=yes") {
+    config->ninja_log_as_weight_list = true;
+    return true;
+  } else if (name == "usesninjalogasweightlist=no") {
+    config->ninja_log_as_weight_list = false;
+    return true;
   } else {
     const char* suggestion =
         SpellcheckString(name.c_str(),
@@ -1627,6 +1634,10 @@
     Fatal("preremoveoutputs=yes requires usesphonyoutputs=yes.");
   }
 
+  if (config->weight_list_path && config->ninja_log_as_weight_list) {
+    Fatal("only one of --usesninjalogasweightlist=yes or --usesweightlist=<path> may be specified");
+  }
+
   return -1;
 }