Ninja
build_log.cc
Go to the documentation of this file.
00001 // Copyright 2011 Google Inc. All Rights Reserved.
00002 //
00003 // Licensed under the Apache License, Version 2.0 (the "License");
00004 // you may not use this file except in compliance with the License.
00005 // You may obtain a copy of the License at
00006 //
00007 //     http://www.apache.org/licenses/LICENSE-2.0
00008 //
00009 // Unless required by applicable law or agreed to in writing, software
00010 // distributed under the License is distributed on an "AS IS" BASIS,
00011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00012 // See the License for the specific language governing permissions and
00013 // limitations under the License.
00014 
00015 #include "build_log.h"
00016 
00017 #include <errno.h>
00018 #include <stdio.h>
00019 #include <stdlib.h>
00020 #include <string.h>
00021 
00022 #include "build.h"
00023 #include "graph.h"
00024 #include "util.h"
00025 
00026 // Implementation details:
00027 // Each run's log appends to the log file.
00028 // To load, we run through all log entries in series, throwing away
00029 // older runs.
00030 // Once the number of redundant entries exceeds a threshold, we write
00031 // out a new file and replace the existing one with it.
00032 
00033 namespace {
00034 
00035 const char kFileSignature[] = "# ninja log v%d\n";
00036 const int kCurrentVersion = 3;
00037 
00038 }
00039 
00040 BuildLog::BuildLog()
00041   : log_file_(NULL), config_(NULL), needs_recompaction_(false) {}
00042 
00043 bool BuildLog::OpenForWrite(const string& path, string* err) {
00044   if (config_ && config_->dry_run)
00045     return true;  // Do nothing, report success.
00046 
00047   if (needs_recompaction_) {
00048     Close();
00049     if (!Recompact(path, err))
00050       return false;
00051   }
00052 
00053   log_file_ = fopen(path.c_str(), "ab");
00054   if (!log_file_) {
00055     *err = strerror(errno);
00056     return false;
00057   }
00058   setvbuf(log_file_, NULL, _IOLBF, BUFSIZ);
00059   SetCloseOnExec(fileno(log_file_));
00060 
00061   if (ftell(log_file_) == 0) {
00062     if (fprintf(log_file_, kFileSignature, kCurrentVersion) < 0) {
00063       *err = strerror(errno);
00064       return false;
00065     }
00066   }
00067 
00068   return true;
00069 }
00070 
00071 void BuildLog::RecordCommand(Edge* edge, int start_time, int end_time,
00072                              time_t restat_mtime) {
00073   const string command = edge->EvaluateCommand();
00074   for (vector<Node*>::iterator out = edge->outputs_.begin();
00075        out != edge->outputs_.end(); ++out) {
00076     const string& path = (*out)->file_->path_;
00077     Log::iterator i = log_.find(path.c_str());
00078     LogEntry* log_entry;
00079     if (i != log_.end()) {
00080       log_entry = i->second;
00081     } else {
00082       log_entry = new LogEntry;
00083       log_entry->output = path;
00084       log_.insert(make_pair(log_entry->output.c_str(), log_entry));
00085     }
00086     log_entry->command = command;
00087     log_entry->start_time = start_time;
00088     log_entry->end_time = end_time;
00089     log_entry->restat_mtime = restat_mtime;
00090 
00091     if (log_file_)
00092       WriteEntry(log_file_, *log_entry);
00093   }
00094 }
00095 
00096 void BuildLog::Close() {
00097   if (log_file_)
00098     fclose(log_file_);
00099   log_file_ = NULL;
00100 }
00101 
00102 bool BuildLog::Load(const string& path, string* err) {
00103   FILE* file = fopen(path.c_str(), "r");
00104   if (!file) {
00105     if (errno == ENOENT)
00106       return true;
00107     *err = strerror(errno);
00108     return false;
00109   }
00110 
00111   int log_version = 0;
00112   int unique_entry_count = 0;
00113   int total_entry_count = 0;
00114 
00115   char buf[256 << 10];
00116   while (fgets(buf, sizeof(buf), file)) {
00117     if (!log_version) {
00118       log_version = 1;  // Assume by default.
00119       if (sscanf(buf, kFileSignature, &log_version) > 0)
00120         continue;
00121     }
00122     char* start = buf;
00123     char* end = strchr(start, ' ');
00124     if (!end)
00125       continue;
00126     *end = 0;
00127 
00128     int start_time = 0, end_time = 0;
00129     time_t restat_mtime = 0;
00130 
00131     if (log_version == 1) {
00132       // In v1 we logged how long the command took; we don't use this info.
00133       // int time_ms = atoi(start);
00134       start = end + 1;
00135     } else {
00136       // In v2 we log the start time and the end time.
00137       start_time = atoi(start);
00138       start = end + 1;
00139 
00140       char* end = strchr(start, ' ');
00141       if (!end)
00142         continue;
00143       *end = 0;
00144       end_time = atoi(start);
00145       start = end + 1;
00146     }
00147     
00148     if (log_version >= 3) {
00149       // In v3 we log the restat mtime.
00150       char* end = strchr(start, ' ');
00151       if (!end)
00152         continue;
00153       *end = 0;
00154       restat_mtime = atol(start);
00155       start = end + 1;
00156     }
00157 
00158     end = strchr(start, ' ');
00159     if (!end)
00160       continue;
00161     string output = string(start, end - start);
00162 
00163     start = end + 1;
00164     end = strchr(start, '\n');
00165     if (!end)
00166       continue;
00167 
00168     LogEntry* entry;
00169     Log::iterator i = log_.find(output.c_str());
00170     if (i != log_.end()) {
00171       entry = i->second;
00172     } else {
00173       entry = new LogEntry;
00174       entry->output = output;
00175       log_.insert(make_pair(entry->output.c_str(), entry));
00176       ++unique_entry_count;
00177     }
00178     ++total_entry_count;
00179 
00180     entry->start_time = start_time;
00181     entry->end_time = end_time;
00182     entry->restat_mtime = restat_mtime;
00183     entry->command = string(start, end - start);
00184   }
00185 
00186   // Decide whether it's time to rebuild the log:
00187   // - if we're upgrading versions
00188   // - if it's getting large
00189   int kMinCompactionEntryCount = 100;
00190   int kCompactionRatio = 3;
00191   if (log_version < kCurrentVersion) {
00192     needs_recompaction_ = true;
00193   } else if (total_entry_count > kMinCompactionEntryCount &&
00194              total_entry_count > unique_entry_count * kCompactionRatio) {
00195     needs_recompaction_ = true;
00196   }
00197 
00198   fclose(file);
00199 
00200   return true;
00201 }
00202 
00203 BuildLog::LogEntry* BuildLog::LookupByOutput(const string& path) {
00204   Log::iterator i = log_.find(path.c_str());
00205   if (i != log_.end())
00206     return i->second;
00207   return NULL;
00208 }
00209 
00210 void BuildLog::WriteEntry(FILE* f, const LogEntry& entry) {
00211   fprintf(f, "%d %d %ld %s %s\n",
00212           entry.start_time, entry.end_time, (long) entry.restat_mtime,
00213           entry.output.c_str(), entry.command.c_str());
00214 }
00215 
00216 bool BuildLog::Recompact(const string& path, string* err) {
00217   printf("Recompacting log...\n");
00218 
00219   string temp_path = path + ".recompact";
00220   FILE* f = fopen(temp_path.c_str(), "wb");
00221   if (!f) {
00222     *err = strerror(errno);
00223     return false;
00224   }
00225 
00226   if (fprintf(f, kFileSignature, kCurrentVersion) < 0) {
00227     *err = strerror(errno);
00228     return false;
00229   }
00230 
00231   for (Log::iterator i = log_.begin(); i != log_.end(); ++i) {
00232     WriteEntry(f, *i->second);
00233   }
00234 
00235   fclose(f);
00236   if (unlink(path.c_str()) < 0) {
00237     *err = strerror(errno);
00238     return false;
00239   }
00240 
00241   if (rename(temp_path.c_str(), path.c_str()) < 0) {
00242     *err = strerror(errno);
00243     return false;
00244   }
00245 
00246   return true;
00247 }