Added logging, refactored versioning; STB is now included

- Added logging
- Rewrote versioning code
- Refactored dependency management
  - STB (stb_image.h) is now included
  - All other dependencies now use find_package
- Cross-compilation from Linux to Windows is now possible
This commit is contained in:
2022-10-31 21:12:48 +03:00
parent da10f7c5cd
commit a110c9de03
21 changed files with 3217 additions and 198 deletions

5
main/config.h.in Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#define _VERSION "@VERSION@"
#define _BUILD_ID "@BUILD_ID@"
#cmakedefine VULKAN_ERROR_CHECKING

View File

@@ -12,6 +12,9 @@
#include "rendering.h"
#include "logging.h"
using namespace progressia::main::logging;
namespace progressia {
namespace main {
@@ -56,13 +59,13 @@ void addBox(glm::vec3 origin, glm::vec3 length, glm::vec3 height,
void initialize(GraphicsInterface &gintp) {
std::cout << "game init begin" << std::endl;
debug("game init begin");
gint = &gintp;
texture1.reset(
gint->newTexture(progressia::main::loadImage(u"../assets/texture.png")));
texture2.reset(
gint->newTexture(progressia::main::loadImage(u"../assets/texture2.png")));
texture1.reset(gint->newTexture(
progressia::main::loadImage(u"../assets/texture.png")));
texture2.reset(gint->newTexture(
progressia::main::loadImage(u"../assets/texture2.png")));
// Cube 1
{
@@ -121,7 +124,7 @@ void initialize(GraphicsInterface &gintp) {
perspective.reset(gint->newView());
light.reset(gint->newLight());
std::cout << "game init complete" << std::endl;
debug("game init complete");
}
void renderTick() {
@@ -161,7 +164,7 @@ void renderTick() {
}
void shutdown() {
std::cout << "game shutdown begin" << std::endl;
debug("game shutdown begin");
cube1.reset();
cube2.reset();
@@ -171,7 +174,7 @@ void shutdown() {
light.reset();
perspective.reset();
std::cout << "game shutdown complete" << std::endl;
debug("game shutdown complete");
}
} // namespace main

169
main/logging.cpp Normal file
View File

@@ -0,0 +1,169 @@
#include "logging.h"
#include <chrono>
#include <filesystem>
#include <fstream>
#include <iomanip>
#include <iostream>
#include <mutex>
#include <sstream>
namespace progressia {
namespace main {
namespace detail {
class LogSinkBackend {
private:
bool writing = false;
std::stringstream buffer;
void flush();
public:
LogSinkBackend() {}
std::ostream &getOutput() { return buffer; }
void setWriting(bool writing) {
if (this->writing == writing) {
std::cerr << "Attempting to write two log messages at once"
<< std::endl;
// REPORT_ERROR
exit(1);
}
this->writing = writing;
if (!writing) {
flush();
}
}
};
} // namespace detail
namespace {
std::ofstream openLogFile() {
// FIXME this is relative to bin, not root dir
std::filesystem::create_directories("../run");
std::filesystem::create_directories("../run/logs");
return std::ofstream("../run/logs/latest.log");
}
} // namespace
std::mutex logFileMutex;
std::ofstream logFile = openLogFile();
thread_local detail::LogSinkBackend theBackend;
std::ostream &detail::LogSink::getStream() const {
if (!isCurrentSink) {
std::cerr << "LogSink::getStream() while !isCurrentSink" << std::endl;
// REPORT_ERROR
exit(1);
}
return theBackend.getOutput();
}
detail::LogSink::LogSink(bool isCurrentSink) : isCurrentSink(isCurrentSink) {
if (isCurrentSink) {
theBackend.setWriting(true);
}
}
detail::LogSink::~LogSink() {
if (isCurrentSink) {
theBackend.setWriting(false);
}
}
detail::LogSink::LogSink(LogSink &&moveFrom)
: isCurrentSink(moveFrom.isCurrentSink) {
moveFrom.isCurrentSink = false;
}
void detail::LogSinkBackend::flush() {
auto message = buffer.str();
buffer.str("");
buffer.clear();
{
std::lock_guard<std::mutex> lock(logFileMutex);
// TODO flush less often?
logFile << message << std::endl;
std::cout << message << std::endl;
}
}
namespace {
// FIXME This approach is horribly inefficient. It is also unsafe if any
// other piece of code wants access to std::localtime.
std::mutex getLocalTimeMutex;
std::tm getLocalTimeAndDontExplodePlease() {
std::lock_guard<std::mutex> lock(getLocalTimeMutex);
std::time_t t = std::time(nullptr);
return *std::localtime(&t);
}
} // namespace
detail::LogSink log(LogLevel level, const char *start) {
#ifdef NDEBUG
if (level == LogLevel::DEBUG) {
return detail::LogSink(false);
}
#endif
detail::LogSink sink(true);
auto tm = getLocalTimeAndDontExplodePlease();
sink << std::put_time(&tm, "%T") << " ";
switch (level) {
case LogLevel::DEBUG:
sink << "DEBUG";
break;
case LogLevel::INFO:
sink << "INFO ";
break;
case LogLevel::WARN:
sink << "WARN ";
break;
case LogLevel::ERROR:
sink << "ERROR";
break;
default:
sink << "FATAL";
break;
}
sink << " ";
if (start != nullptr) {
sink << start;
}
return sink;
}
namespace logging {
#ifdef NDEBUG
detail::LogSink debug(const char *) { return detail::LogSink(false); }
#else
detail::LogSink debug(const char *start) { return log(LogLevel::DEBUG, start); }
#endif
detail::LogSink info(const char *start) { return log(LogLevel::INFO, start); }
detail::LogSink warn(const char *start) { return log(LogLevel::WARN, start); }
detail::LogSink error(const char *start) { return log(LogLevel::ERROR, start); }
detail::LogSink fatal(const char *start) { return log(LogLevel::FATAL, start); }
} // namespace logging
} // namespace main
} // namespace progressia

64
main/logging.h Normal file
View File

@@ -0,0 +1,64 @@
#pragma once
#include "boost/core/noncopyable.hpp"
#include <ostream>
namespace progressia {
namespace main {
namespace detail {
class LogSink : private boost::noncopyable {
private:
bool isCurrentSink;
std::ostream &getStream() const;
template <typename T> void put(const T &x) const {
if (isCurrentSink) {
getStream() << x;
}
}
public:
LogSink(bool isCurrentSink);
~LogSink();
LogSink(LogSink &&);
template <typename T>
friend const LogSink &operator<<(const LogSink &sink, const T &x) {
sink.put(x);
return sink;
}
};
} // namespace detail
enum class LogLevel {
// Only interesting when investigating a problem.
DEBUG,
// A regular user may want to see this.
INFO,
// All users should read this; if message persists, something is wrong.
WARN,
// Something is definitely wrong, but we're not crashing (yet).
ERROR,
// This is why the game is about to crash.
FATAL
};
detail::LogSink log(LogLevel, const char *start = nullptr);
namespace logging {
detail::LogSink debug(const char *start = nullptr);
detail::LogSink info(const char *start = nullptr);
detail::LogSink warn(const char *start = nullptr);
detail::LogSink error(const char *start = nullptr);
detail::LogSink fatal(const char *start = nullptr);
} // namespace logging
void initializeLogging();
void shutdownLogging();
} // namespace main
} // namespace progressia

View File

@@ -1,43 +1,40 @@
#pragma once
#include "../config.h"
#include <cstdlib>
namespace progressia {
namespace main {
namespace meta {
namespace detail {
constexpr static uint32_t getVersionNumber(const char *versionStr) {
uint32_t parts[] = {0, 0, 0};
std::size_t partCount = sizeof(parts) / sizeof(parts[0]);
std::size_t currentPart = 0;
for (const char *cp = versionStr; *cp != '\0'; cp++) {
char c = *cp;
if (c == '.') {
currentPart++;
} else if (currentPart < partCount && c >= '0' && c <= '9') {
parts[currentPart] =
parts[currentPart] * 10 + static_cast<uint32_t>(c - '0');
}
}
return (parts[0] << 16) | (parts[1] << 8) | (parts[2] << 0);
}
} // namespace detail
constexpr const char *NAME = "Progressia";
constexpr const char *VERSION = _VERSION;
constexpr const char *BUILD_ID = _BUILD_ID;
#ifndef _MAJOR
#warning Version number (_MAJOR _MINOR _PATCH _BUILD) not set, using 0.0.0+1
#define _MAJOR 0
#define _MINOR 0
#define _PATCH 0
#define _BUILD 1
#endif
using VersionUnit = uint8_t;
using VersionInt = uint32_t;
constexpr struct {
VersionUnit major, minor, patch, build;
VersionInt number;
bool isRelease;
} VERSION{_MAJOR,
_MINOR,
_PATCH,
_BUILD,
(static_cast<VersionInt>(_MAJOR) << 24) |
(static_cast<VersionInt>(_MINOR) << 16) |
(static_cast<VersionInt>(_PATCH) << 8) |
(static_cast<VersionInt>(_BUILD) << 0),
_BUILD == 0};
constexpr uint32_t VERSION_NUMBER = detail::getVersionNumber(_VERSION);
constexpr uint32_t VERSION_MAJOR = (VERSION_NUMBER & 0xFF0000) >> 16;
constexpr uint32_t VERSION_MINOR = (VERSION_NUMBER & 0x00FF00) >> 8;
constexpr uint32_t VERSION_PATCH = (VERSION_NUMBER & 0x0000FF) >> 0;
} // namespace meta
} // namespace main

View File

@@ -8,6 +8,9 @@
#include "stb/stb_image.h"
#include "../logging.h"
using namespace progressia::main::logging;
namespace progressia {
namespace main {
@@ -22,8 +25,7 @@ Image loadImage(const std::filesystem::path &path) {
std::ifstream file(path, std::ios::ate | std::ios::binary);
if (!file.is_open()) {
std::cout << "Could not read a PNG image from file " << path
<< std::endl;
fatal() << "Could not access a PNG image in file " << path;
// REPORT_ERROR
exit(1);
}
@@ -45,8 +47,7 @@ Image loadImage(const std::filesystem::path &path) {
&channelsInFile, STBI_rgb_alpha);
if (stbAllocatedData == NULL) {
std::cout << "Could not load a PNG image from file " << path
<< std::endl;
fatal() << "Could not decode a PNG image from file " << path;
// REPORT_ERROR
exit(1);
}