| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261 |
- /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
- file Copyright.txt or https://cmake.org/licensing for details. */
- #include "cmConfigureLog.h"
- #include <cassert>
- #include <cstdio>
- #include <iterator>
- #include <sstream>
- #include <utility>
- #include <cmext/algorithm>
- #include <cmext/string_view>
- #include <cm3p/json/writer.h>
- #include "cm_utf8.h"
- #include "cmListFileCache.h"
- #include "cmMakefile.h"
- #include "cmStringAlgorithms.h"
- #include "cmSystemTools.h"
- #include "cmake.h"
- cmConfigureLog::cmConfigureLog(std::string logDir,
- std::vector<unsigned long> logVersions)
- : LogDir(std::move(logDir))
- , LogVersions(std::move(logVersions))
- {
- // Always emit events for the latest log version.
- static const unsigned long LatestLogVersion = 1;
- if (!cm::contains(this->LogVersions, LatestLogVersion)) {
- this->LogVersions.emplace_back(LatestLogVersion);
- }
- Json::StreamWriterBuilder builder;
- this->Encoder.reset(builder.newStreamWriter());
- }
- cmConfigureLog::~cmConfigureLog()
- {
- if (this->Opened) {
- this->EndObject();
- this->Stream << "...\n";
- }
- }
- bool cmConfigureLog::IsAnyLogVersionEnabled(
- std::vector<unsigned long> const& v) const
- {
- // Both input lists are sorted. Look for a matching element.
- auto i1 = v.cbegin();
- auto i2 = this->LogVersions.cbegin();
- while (i1 != v.cend() && i2 != this->LogVersions.cend()) {
- if (*i1 < *i2) {
- ++i1;
- } else if (*i2 < *i1) {
- ++i2;
- } else {
- return true;
- }
- }
- return false;
- }
- void cmConfigureLog::WriteBacktrace(cmMakefile const& mf)
- {
- std::vector<std::string> backtrace;
- auto root = mf.GetCMakeInstance()->GetHomeDirectory();
- for (auto bt = mf.GetBacktrace(); !bt.Empty(); bt = bt.Pop()) {
- auto t = bt.Top();
- if (!t.Name.empty() || t.Line == cmListFileContext::DeferPlaceholderLine) {
- t.FilePath = cmSystemTools::RelativeIfUnder(root, t.FilePath);
- std::ostringstream s;
- s << t;
- backtrace.emplace_back(s.str());
- }
- }
- this->WriteValue("backtrace"_s, backtrace);
- }
- void cmConfigureLog::EnsureInit()
- {
- if (this->Opened) {
- return;
- }
- assert(!this->Stream.is_open());
- std::string name = cmStrCat(this->LogDir, "/CMakeConfigureLog.yaml");
- this->Stream.open(name.c_str(), std::ios::out | std::ios::app);
- this->Opened = true;
- this->Stream << "\n---\n";
- this->BeginObject("events"_s);
- }
- cmsys::ofstream& cmConfigureLog::BeginLine()
- {
- for (unsigned i = 0; i < this->Indent; ++i) {
- this->Stream << " ";
- }
- return this->Stream;
- }
- void cmConfigureLog::EndLine()
- {
- this->Stream << std::endl;
- }
- void cmConfigureLog::BeginObject(cm::string_view key)
- {
- this->BeginLine() << key << ':';
- this->EndLine();
- ++this->Indent;
- }
- void cmConfigureLog::EndObject()
- {
- assert(this->Indent);
- --this->Indent;
- }
- void cmConfigureLog::BeginEvent(std::string const& kind)
- {
- this->EnsureInit();
- this->BeginLine() << '-';
- this->EndLine();
- ++this->Indent;
- this->WriteValue("kind"_s, kind);
- }
- void cmConfigureLog::EndEvent()
- {
- assert(this->Indent);
- --this->Indent;
- }
- void cmConfigureLog::WriteValue(cm::string_view key, std::nullptr_t)
- {
- this->BeginLine() << key << ": null";
- this->EndLine();
- }
- void cmConfigureLog::WriteValue(cm::string_view key, bool value)
- {
- this->BeginLine() << key << ": " << (value ? "true" : "false");
- this->EndLine();
- }
- void cmConfigureLog::WriteValue(cm::string_view key, int value)
- {
- this->BeginLine() << key << ": " << value;
- this->EndLine();
- }
- void cmConfigureLog::WriteValue(cm::string_view key, std::string const& value)
- {
- this->BeginLine() << key << ": ";
- this->Encoder->write(value, &this->Stream);
- this->EndLine();
- }
- void cmConfigureLog::WriteValue(cm::string_view key,
- std::vector<std::string> const& list)
- {
- this->BeginObject(key);
- for (auto const& value : list) {
- this->BeginLine() << "- ";
- this->Encoder->write(value, &this->Stream);
- this->EndLine();
- }
- this->EndObject();
- }
- void cmConfigureLog::WriteLiteralTextBlock(cm::string_view key,
- cm::string_view text)
- {
- this->BeginLine() << key << ": |";
- this->EndLine();
- auto const l = text.length();
- if (l) {
- ++this->Indent;
- this->BeginLine();
- auto i = decltype(l){ 0 };
- while (i < l) {
- // YAML allows ' ', '\t' and "printable characters", but NOT other
- // ASCII whitespace; those must be escaped, as must the upper UNICODE
- // control characters (U+0080 - U+009F)
- static constexpr unsigned int C1_LAST = 0x9F;
- auto const c = static_cast<unsigned char>(text[i]);
- switch (c) {
- case '\r':
- // Print a carriage return only if it is not followed by a line feed.
- ++i;
- if (i == l || text[i] != '\n') {
- this->WriteEscape(c);
- }
- break;
- case '\n':
- // Print any line feeds except the very last one
- if (i + 1 < l) {
- this->EndLine();
- this->BeginLine();
- }
- ++i;
- break;
- case '\t':
- // Print horizontal tab verbatim
- this->Stream.put('\t');
- ++i;
- break;
- case '\\':
- // Escape backslash for disambiguation
- this->Stream << "\\\\";
- ++i;
- break;
- default:
- if (c >= 32 && c < 127) {
- // Print ascii byte.
- this->Stream.put(text[i]);
- ++i;
- break;
- } else if (c > 127) {
- // Decode a UTF-8 sequence.
- unsigned int c32;
- auto const* const s = text.data() + i;
- auto const* const e = text.data() + l;
- auto const* const n = cm_utf8_decode_character(s, e, &c32);
- if (n > s && c32 > C1_LAST) {
- auto const k = std::distance(s, n);
- this->Stream.write(s, static_cast<std::streamsize>(k));
- i += static_cast<unsigned>(k);
- break;
- }
- }
- // Escape non-printable byte.
- this->WriteEscape(c);
- ++i;
- break;
- }
- }
- this->EndLine();
- --this->Indent;
- }
- }
- void cmConfigureLog::WriteEscape(unsigned char c)
- {
- char buffer[6];
- int n = snprintf(buffer, sizeof(buffer), "\\x%02x", c);
- if (n > 0) {
- this->Stream.write(buffer, n);
- }
- }
|