mirror of
https://github.com/Kitware/CMake.git
synced 2025-10-14 19:08:07 +08:00

The file API code used unsigned long to hold the major version in most places, but not all. Some places used unsigned int, and an important one of those is the cmFileApi::BuildVersion() function. As a result, it has never been safe for a large value not representable by an unsigned int to be used in these variables. Convert all of the file API version number variables and function arguments to use unsigned int consistently. This avoids any size mismatch warnings when passing values around. They also don't need to be unsigned long, as we never expect version numbers to be anything even close to what an unsigned int cannot represent.
324 lines
7.8 KiB
C++
324 lines
7.8 KiB
C++
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
|
file LICENSE.rst 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 "cmRange.h"
|
|
#include "cmStringAlgorithms.h"
|
|
#include "cmSystemTools.h"
|
|
#include "cmake.h"
|
|
|
|
cmConfigureLog::cmConfigureLog(std::string logDir,
|
|
std::vector<unsigned int> logVersions)
|
|
: LogDir(std::move(logDir))
|
|
, LogVersions(std::move(logVersions))
|
|
{
|
|
// Always emit events for the latest log version.
|
|
static unsigned int const 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 int> 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::WriteChecks(cmMakefile const& mf)
|
|
{
|
|
if (!mf.GetCMakeInstance()->HasCheckInProgress()) {
|
|
return;
|
|
}
|
|
this->BeginObject("checks"_s);
|
|
for (auto const& value :
|
|
cmReverseRange(mf.GetCMakeInstance()->GetCheckInProgressMessages())) {
|
|
this->BeginLine() << "- ";
|
|
this->Encoder->write(value, &this->Stream);
|
|
this->EndLine();
|
|
}
|
|
this->EndObject();
|
|
}
|
|
|
|
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::BeginArray()
|
|
{
|
|
++this->Indent;
|
|
}
|
|
|
|
void cmConfigureLog::NextArrayElement()
|
|
{
|
|
assert(this->Indent);
|
|
--this->Indent;
|
|
this->BeginLine() << '-';
|
|
this->EndLine();
|
|
++this->Indent;
|
|
}
|
|
|
|
void cmConfigureLog::EndArray()
|
|
{
|
|
assert(this->Indent);
|
|
--this->Indent;
|
|
}
|
|
|
|
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, cmMakefile const& mf)
|
|
{
|
|
this->EnsureInit();
|
|
|
|
this->BeginLine() << '-';
|
|
this->EndLine();
|
|
|
|
++this->Indent;
|
|
|
|
this->WriteValue("kind"_s, kind);
|
|
this->WriteBacktrace(mf);
|
|
this->WriteChecks(mf);
|
|
}
|
|
|
|
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::WriteValue(cm::string_view key,
|
|
std::map<std::string, std::string> const& map)
|
|
{
|
|
static std::string const rawKeyChars = //
|
|
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" //
|
|
"abcdefghijklmnopqrstuvwxyz" //
|
|
"0123456789" //
|
|
"-_" //
|
|
;
|
|
this->BeginObject(key);
|
|
for (auto const& entry : map) {
|
|
if (entry.first.find_first_not_of(rawKeyChars) == std::string::npos) {
|
|
this->WriteValue(entry.first, entry.second);
|
|
} else {
|
|
this->BeginLine();
|
|
this->Encoder->write(entry.first, &this->Stream);
|
|
this->Stream << ": ";
|
|
this->Encoder->write(entry.second, &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);
|
|
}
|
|
}
|