cmConfigureLog.cxx 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
  2. file Copyright.txt or https://cmake.org/licensing for details. */
  3. #include "cmConfigureLog.h"
  4. #include <cassert>
  5. #include <cstdio>
  6. #include <iterator>
  7. #include <sstream>
  8. #include <utility>
  9. #include <cmext/string_view>
  10. #include <cm3p/json/writer.h>
  11. #include "cm_utf8.h"
  12. #include "cmListFileCache.h"
  13. #include "cmMakefile.h"
  14. #include "cmStringAlgorithms.h"
  15. #include "cmSystemTools.h"
  16. #include "cmake.h"
  17. cmConfigureLog::cmConfigureLog(std::string logDir)
  18. : LogDir(std::move(logDir))
  19. {
  20. Json::StreamWriterBuilder builder;
  21. this->Encoder.reset(builder.newStreamWriter());
  22. }
  23. cmConfigureLog::~cmConfigureLog()
  24. {
  25. if (this->Opened) {
  26. this->EndObject();
  27. this->Stream << "...\n";
  28. }
  29. }
  30. void cmConfigureLog::WriteBacktrace(cmMakefile const& mf)
  31. {
  32. std::vector<std::string> backtrace;
  33. auto root = mf.GetCMakeInstance()->GetHomeDirectory();
  34. for (auto bt = mf.GetBacktrace(); !bt.Empty(); bt = bt.Pop()) {
  35. auto t = bt.Top();
  36. if (!t.Name.empty() || t.Line == cmListFileContext::DeferPlaceholderLine) {
  37. t.FilePath = cmSystemTools::RelativeIfUnder(root, t.FilePath);
  38. std::ostringstream s;
  39. s << t;
  40. backtrace.emplace_back(s.str());
  41. }
  42. }
  43. this->WriteValue("backtrace"_s, backtrace);
  44. }
  45. void cmConfigureLog::EnsureInit()
  46. {
  47. if (this->Opened) {
  48. return;
  49. }
  50. assert(!this->Stream.is_open());
  51. std::string name = cmStrCat(this->LogDir, "/CMakeConfigureLog.yaml");
  52. this->Stream.open(name.c_str(), std::ios::out | std::ios::app);
  53. this->Opened = true;
  54. this->Stream << "\n---\n";
  55. this->BeginObject("version"_s);
  56. this->WriteValue("major"_s, 1);
  57. this->WriteValue("minor"_s, 0);
  58. this->EndObject();
  59. this->BeginObject("events"_s);
  60. }
  61. cmsys::ofstream& cmConfigureLog::BeginLine()
  62. {
  63. for (unsigned i = 0; i < this->Indent; ++i) {
  64. this->Stream << " ";
  65. }
  66. return this->Stream;
  67. }
  68. void cmConfigureLog::EndLine()
  69. {
  70. this->Stream << std::endl;
  71. }
  72. void cmConfigureLog::BeginObject(cm::string_view key)
  73. {
  74. this->BeginLine() << key << ':';
  75. this->EndLine();
  76. ++this->Indent;
  77. }
  78. void cmConfigureLog::EndObject()
  79. {
  80. assert(this->Indent);
  81. --this->Indent;
  82. }
  83. void cmConfigureLog::BeginEvent(std::string const& kind)
  84. {
  85. this->EnsureInit();
  86. this->BeginLine() << '-';
  87. this->EndLine();
  88. ++this->Indent;
  89. this->WriteValue("kind"_s, kind);
  90. }
  91. void cmConfigureLog::EndEvent()
  92. {
  93. assert(this->Indent);
  94. --this->Indent;
  95. }
  96. void cmConfigureLog::WriteValue(cm::string_view key, std::nullptr_t)
  97. {
  98. this->BeginLine() << key << ": null";
  99. this->EndLine();
  100. }
  101. void cmConfigureLog::WriteValue(cm::string_view key, bool value)
  102. {
  103. this->BeginLine() << key << ": " << (value ? "true" : "false");
  104. this->EndLine();
  105. }
  106. void cmConfigureLog::WriteValue(cm::string_view key, int value)
  107. {
  108. this->BeginLine() << key << ": " << value;
  109. this->EndLine();
  110. }
  111. void cmConfigureLog::WriteValue(cm::string_view key, std::string const& value)
  112. {
  113. this->BeginLine() << key << ": ";
  114. this->Encoder->write(value, &this->Stream);
  115. this->EndLine();
  116. }
  117. void cmConfigureLog::WriteValue(cm::string_view key,
  118. std::vector<std::string> const& list)
  119. {
  120. this->BeginObject(key);
  121. for (auto const& value : list) {
  122. this->BeginLine() << "- ";
  123. this->Encoder->write(value, &this->Stream);
  124. this->EndLine();
  125. }
  126. this->EndObject();
  127. }
  128. void cmConfigureLog::WriteLiteralTextBlock(cm::string_view key,
  129. cm::string_view text)
  130. {
  131. this->BeginLine() << key << ": |";
  132. this->EndLine();
  133. auto const l = text.length();
  134. if (l) {
  135. ++this->Indent;
  136. this->BeginLine();
  137. auto i = decltype(l){ 0 };
  138. while (i < l) {
  139. // YAML allows ' ', '\t' and "printable characters", but NOT other
  140. // ASCII whitespace; those must be escaped, as must the upper UNICODE
  141. // control characters (U+0080 - U+009F)
  142. static constexpr unsigned int C1_LAST = 0x9F;
  143. auto const c = static_cast<unsigned char>(text[i]);
  144. switch (c) {
  145. case '\r':
  146. // Print a carriage return only if it is not followed by a line feed.
  147. ++i;
  148. if (i == l || text[i] != '\n') {
  149. this->WriteEscape(c);
  150. }
  151. break;
  152. case '\n':
  153. // Print any line feeds except the very last one
  154. if (i + 1 < l) {
  155. this->EndLine();
  156. this->BeginLine();
  157. }
  158. ++i;
  159. break;
  160. case '\t':
  161. // Print horizontal tab verbatim
  162. this->Stream.put('\t');
  163. ++i;
  164. break;
  165. case '\\':
  166. // Escape backslash for disambiguation
  167. this->Stream << "\\\\";
  168. ++i;
  169. break;
  170. default:
  171. if (c >= 32 && c < 127) {
  172. // Print ascii byte.
  173. this->Stream.put(text[i]);
  174. ++i;
  175. break;
  176. } else if (c > 127) {
  177. // Decode a UTF-8 sequence.
  178. unsigned int c32;
  179. auto const* const s = text.data() + i;
  180. auto const* const e = text.data() + l;
  181. auto const* const n = cm_utf8_decode_character(s, e, &c32);
  182. if (n > s && c32 > C1_LAST) {
  183. auto const k = std::distance(s, n);
  184. this->Stream.write(s, static_cast<std::streamsize>(k));
  185. i += static_cast<unsigned>(k);
  186. break;
  187. }
  188. }
  189. // Escape non-printable byte.
  190. this->WriteEscape(c);
  191. ++i;
  192. break;
  193. }
  194. }
  195. this->EndLine();
  196. --this->Indent;
  197. }
  198. }
  199. void cmConfigureLog::WriteEscape(unsigned char c)
  200. {
  201. char buffer[6];
  202. int n = snprintf(buffer, sizeof(buffer), "\\x%02x", c);
  203. if (n > 0) {
  204. this->Stream.write(buffer, n);
  205. }
  206. }