|
Ninja
|
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 }
1.7.5.1