// Copyright 2011 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "util.h" #ifdef _WIN32 #include #include #endif #include #include #include #include #include #include #include #include #ifndef _WIN32 #include #endif #include #if defined(__APPLE__) || defined(__FreeBSD__) #include #elif defined(__SVR4) && defined(__sun) #include #include #elif defined(linux) || defined(__GLIBC__) #include #endif #include "edit_distance.h" #include "metrics.h" void Fatal(const char* msg, ...) { va_list ap; fprintf(stderr, "ninja: fatal: "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); fprintf(stderr, "\n"); #ifdef _WIN32 // On Windows, some tools may inject extra threads. // exit() may block on locks held by those threads, so forcibly exit. fflush(stderr); fflush(stdout); ExitProcess(1); #else exit(1); #endif } void Warning(const char* msg, ...) { va_list ap; fprintf(stderr, "ninja: warning: "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); fprintf(stderr, "\n"); } void Error(const char* msg, ...) { va_list ap; fprintf(stderr, "ninja: error: "); va_start(ap, msg); vfprintf(stderr, msg, ap); va_end(ap); fprintf(stderr, "\n"); } bool CanonicalizePath(string* path, string* err) { METRIC_RECORD("canonicalize str"); size_t len = path->size(); char* str = 0; if (len > 0) str = &(*path)[0]; if (!CanonicalizePath(str, &len, err)) return false; path->resize(len); return true; } bool CanonicalizePath(char* path, size_t* len, string* err) { // WARNING: this function is performance-critical; please benchmark // any changes you make to it. METRIC_RECORD("canonicalize path"); if (*len == 0) { *err = "empty path"; return false; } const int kMaxPathComponents = 30; char* components[kMaxPathComponents]; int component_count = 0; char* start = path; char* dst = start; const char* src = start; const char* end = start + *len; if (*src == '/') { #ifdef _WIN32 // network path starts with // if (*len > 1 && *(src + 1) == '/') { src += 2; dst += 2; } else { ++src; ++dst; } #else ++src; ++dst; #endif } while (src < end) { if (*src == '.') { if (src + 1 == end || src[1] == '/') { // '.' component; eliminate. src += 2; continue; } else if (src[1] == '.' && (src + 2 == end || src[2] == '/')) { // '..' component. Back up if possible. if (component_count > 0) { dst = components[component_count - 1]; src += 3; --component_count; } else { *dst++ = *src++; *dst++ = *src++; *dst++ = *src++; } continue; } } if (*src == '/') { src++; continue; } if (component_count == kMaxPathComponents) Fatal("path has too many components : %s", path); components[component_count] = dst; ++component_count; while (*src != '/' && src != end) *dst++ = *src++; *dst++ = *src++; // Copy '/' or final \0 character as well. } if (dst == start) { *err = "path canonicalizes to the empty path"; return false; } *len = dst - start - 1; return true; } int ReadFile(const string& path, string* contents, string* err) { FILE* f = fopen(path.c_str(), "r"); if (!f) { err->assign(strerror(errno)); return -errno; } char buf[64 << 10]; size_t len; while ((len = fread(buf, 1, sizeof(buf), f)) > 0) { contents->append(buf, len); } if (ferror(f)) { err->assign(strerror(errno)); // XXX errno? contents->clear(); fclose(f); return -errno; } fclose(f); return 0; } void SetCloseOnExec(int fd) { #ifndef _WIN32 int flags = fcntl(fd, F_GETFD); if (flags < 0) { perror("fcntl(F_GETFD)"); } else { if (fcntl(fd, F_SETFD, flags | FD_CLOEXEC) < 0) perror("fcntl(F_SETFD)"); } #else HANDLE hd = (HANDLE) _get_osfhandle(fd); if (! SetHandleInformation(hd, HANDLE_FLAG_INHERIT, 0)) { fprintf(stderr, "SetHandleInformation(): %s", GetLastErrorString().c_str()); } #endif // ! _WIN32 } const char* SpellcheckStringV(const string& text, const vector& words) { const bool kAllowReplacements = true; const int kMaxValidEditDistance = 3; int min_distance = kMaxValidEditDistance + 1; const char* result = NULL; for (vector::const_iterator i = words.begin(); i != words.end(); ++i) { int distance = EditDistance(*i, text, kAllowReplacements, kMaxValidEditDistance); if (distance < min_distance) { min_distance = distance; result = *i; } } return result; } const char* SpellcheckString(const string& text, ...) { va_list ap; va_start(ap, text); vector words; const char* word; while ((word = va_arg(ap, const char*))) words.push_back(word); return SpellcheckStringV(text, words); } #ifdef _WIN32 string GetLastErrorString() { DWORD err = GetLastError(); char* msg_buf; FormatMessageA( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (char*)&msg_buf, 0, NULL); string msg = msg_buf; LocalFree(msg_buf); return msg; } void Win32Fatal(const char* function) { Fatal("%s: %s", function, GetLastErrorString().c_str()); } #endif static bool islatinalpha(int c) { // isalpha() is locale-dependent. return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); } string StripAnsiEscapeCodes(const string& in) { string stripped; stripped.reserve(in.size()); for (size_t i = 0; i < in.size(); ++i) { if (in[i] != '\33') { // Not an escape code. stripped.push_back(in[i]); continue; } // Only strip CSIs for now. if (i + 1 >= in.size()) break; if (in[i + 1] != '[') continue; // Not a CSI. i += 2; // Skip everything up to and including the next [a-zA-Z]. while (i < in.size() && !islatinalpha(in[i])) ++i; } return stripped; } #if defined(linux) || defined(__GLIBC__) int GetProcessorCount() { return get_nprocs(); } #elif defined(__APPLE__) || defined(__FreeBSD__) int GetProcessorCount() { int processors; size_t processors_size = sizeof(processors); int name[] = {CTL_HW, HW_NCPU}; if (sysctl(name, sizeof(name) / sizeof(int), &processors, &processors_size, NULL, 0) < 0) { return 0; } return processors; } #elif defined(_WIN32) int GetProcessorCount() { SYSTEM_INFO info; GetSystemInfo(&info); return info.dwNumberOfProcessors; } #else // This is what get_nprocs() should be doing in the Linux implementation // above, but in a more standard way. int GetProcessorCount() { return sysconf(_SC_NPROCESSORS_ONLN); } #endif #if defined(_WIN32) || defined(__CYGWIN__) double GetLoadAverage() { // TODO(nicolas.despres@gmail.com): Find a way to implement it on Windows. // Remember to also update Usage() when this is fixed. return -0.0f; } #else double GetLoadAverage() { double loadavg[3] = { 0.0f, 0.0f, 0.0f }; if (getloadavg(loadavg, 3) < 0) { // Maybe we should return an error here or the availability of // getloadavg(3) should be checked when ninja is configured. return -0.0f; } return loadavg[0]; } #endif // _WIN32 string ElideMiddle(const string& str, size_t width) { const int kMargin = 3; // Space for "...". string result = str; if (result.size() + kMargin > width) { size_t elide_size = (width - kMargin) / 2; result = result.substr(0, elide_size) + "..." + result.substr(result.size() - elide_size, elide_size); } return result; }