build: embed ui.toml and banlist with INCBIN instead of a configure loop

ui.toml and the default banlist were embedded by a CMake function that
read each file and emitted a C array header one byte at a time in a
configure-time loop. That is slow to configure, and because the result
is a header, editing either resource recompiled every translation unit
that included it.

- Both files are now embedded with INCBIN from a small generated
  translation unit, the same pattern already used for the fonts. There
  is no generated header, so editing a resource re-assembles one tiny
  object file instead of triggering a wide recompile, and configure does
  no per-byte work.
- Consumers include a hand-written header declaring the symbols and
  always use the embedded data (the __has_include fallback is gone). The
  data is not NUL-terminated, so every consumer passes an explicit size.
- The old EmbedResources.cmake script is removed, and the generated unit
  is added to the app and test builds.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-10-13 22:03:00 -05:00
co-authored by Claude Opus 4.8
parent 3fd52fbdaf
commit e77b960cc5
7 changed files with 41 additions and 99 deletions
+8 -19
View File
@@ -89,13 +89,8 @@
#include "ui/schema/skin_manager.h"
#include "util/perf_log.h"
// Embedded ui.toml (generated at build time) — fallback when file not on disk
#if __has_include("ui_toml_embedded.h")
#include "ui_toml_embedded.h"
#define HAS_EMBEDDED_UI_TOML 1
#else
#define HAS_EMBEDDED_UI_TOML 0
#endif
// Embedded ui.toml (INCBIN) — fallback when file not on disk
#include "embedded/embedded_text.h"
#include "imgui.h"
#include <nlohmann/json.hpp>
@@ -367,25 +362,23 @@ bool App::loadBaseUiSchema()
std::string schemaPath = util::Platform::getExecutableDirectory() + "/res/themes/ui.toml";
bool loaded = false;
#if HAS_EMBEDDED_UI_TOML
// If on-disk ui.toml exists but differs in size from the embedded
// version, a newer wallet binary is running against stale theme
// files. Overwrite the on-disk copy so layout matches the binary.
if (std::filesystem::exists(schemaPath)) {
std::error_code ec;
auto diskSize = std::filesystem::file_size(schemaPath, ec);
if (!ec && diskSize != static_cast<std::uintmax_t>(embedded::ui_toml_size)) {
if (!ec && diskSize != static_cast<std::uintmax_t>(g_ui_toml_size)) {
DEBUG_LOGF("[INFO] ui.toml on disk (%ju bytes) differs from embedded (%zu bytes) — updating\n",
(uintmax_t)diskSize, embedded::ui_toml_size);
(uintmax_t)diskSize, (size_t)g_ui_toml_size);
std::ofstream ofs(schemaPath, std::ios::binary | std::ios::trunc);
if (ofs.is_open()) {
ofs.write(reinterpret_cast<const char*>(embedded::ui_toml_data),
embedded::ui_toml_size);
ofs.write(reinterpret_cast<const char*>(g_ui_toml_data),
g_ui_toml_size);
ofs.close();
}
}
}
#endif
if (std::filesystem::exists(schemaPath)) {
loaded = ui::schema::UISchema::instance().loadFromFile(schemaPath);
@@ -393,13 +386,9 @@ bool App::loadBaseUiSchema()
// Fallback: load from build-time embedded data when file not on disk
// (e.g., single-file Windows distribution without res/ directory)
if (!loaded) {
#if HAS_EMBEDDED_UI_TOML
std::string embedded(reinterpret_cast<const char*>(embedded::ui_toml_data),
embedded::ui_toml_size);
std::string embedded(reinterpret_cast<const char*>(g_ui_toml_data),
g_ui_toml_size);
ui::schema::UISchema::instance().loadFromString(embedded, "embedded");
#else
DEBUG_LOGF("Warning: ui.toml not found at %s and no embedded fallback\n", schemaPath.c_str());
#endif
}
return ui::schema::UISchema::instance().isLoaded();
}
+3 -3
View File
@@ -54,7 +54,7 @@
#include "util/pool_registry.h"
#include "ui/notifications.h"
#include "ui/schema/skin_manager.h"
#include "default_banlist_embedded.h"
#include "embedded/embedded_text.h"
#include "util/amount_format.h"
#include "util/http_download.h"
#include "data/exchange_info.h"
@@ -3742,8 +3742,8 @@ void App::applyDefaultBanlist()
if (!state_.connected || !rpc_ || !worker_) return;
// Parse the embedded default_banlist.txt (compiled from res/default_banlist.txt)
std::string data(reinterpret_cast<const char*>(embedded::default_banlist_data),
embedded::default_banlist_size);
std::string data(reinterpret_cast<const char*>(g_default_banlist_data),
g_default_banlist_size);
std::vector<std::string> ips;
size_t pos = 0;
+4
View File
@@ -0,0 +1,4 @@
// DragonX Wallet - INCBIN text-resource embedding (CMake-generated). Do NOT edit the generated copy.
#include "incbin.h"
INCBIN(ui_toml, "@CMAKE_SOURCE_DIR@/res/themes/ui.toml");
INCBIN(default_banlist, "@CMAKE_SOURCE_DIR@/res/default_banlist.txt");
+10
View File
@@ -0,0 +1,10 @@
#pragma once
#include <cstdint>
// Defined in generated/embedded_text.cpp via INCBIN (.incbin). Data is NOT NUL-terminated;
// every consumer passes an explicit size. Size is `unsigned int`.
extern "C" {
extern const unsigned char g_ui_toml_data[];
extern const unsigned int g_ui_toml_size;
extern const unsigned char g_default_banlist_data[];
extern const unsigned int g_default_banlist_size;
}
+3 -21
View File
@@ -15,13 +15,8 @@
#include <system_error>
#include <thread>
// Embedded ui.toml (generated at build time)
#if __has_include("ui_toml_embedded.h")
#include "ui_toml_embedded.h"
#define HAS_EMBEDDED_UI_TOML 1
#else
#define HAS_EMBEDDED_UI_TOML 0
#endif
// Embedded ui.toml (INCBIN)
#include "embedded/embedded_text.h"
#include <sys/stat.h>
#ifdef _WIN32
@@ -562,20 +557,7 @@ void Platform::ensureObsidianDragonSetup()
// Use the build-time embedded copy first; fall back to the
// bundled file on disk.
std::string content;
#if HAS_EMBEDDED_UI_TOML
content.assign(reinterpret_cast<const char*>(embedded::ui_toml_data),
embedded::ui_toml_size);
#else
{
fs::path bundled = fs::path(getExecutableDirectory()) / "res" / "themes" / "ui.toml";
std::ifstream src(bundled);
if (src.is_open()) {
std::ostringstream ss;
ss << src.rdbuf();
content = ss.str();
}
}
#endif
content.assign(reinterpret_cast<const char*>(g_ui_toml_data), g_ui_toml_size);
if (!content.empty()) {
// Patch the theme metadata so the example is clearly