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:
+13
-14
@@ -820,9 +820,21 @@ ${CMAKE_SOURCE_DIR}/res/fonts/NotoEmoji-Subset.ttf;\
|
|||||||
${CMAKE_SOURCE_DIR}/res/fonts/TwemojiMozilla-Color.ttf"
|
${CMAKE_SOURCE_DIR}/res/fonts/TwemojiMozilla-Color.ttf"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Embed ui.toml + default_banlist via INCBIN (same pattern as fonts) - zero configure cost, no
|
||||||
|
# generated header, so editing them re-assembles one tiny TU instead of recompiling every includer.
|
||||||
|
configure_file(
|
||||||
|
${CMAKE_SOURCE_DIR}/src/embedded/embedded_text.cpp.in
|
||||||
|
${CMAKE_BINARY_DIR}/generated/embedded_text.cpp
|
||||||
|
@ONLY)
|
||||||
|
set_source_files_properties(
|
||||||
|
${CMAKE_BINARY_DIR}/generated/embedded_text.cpp
|
||||||
|
PROPERTIES OBJECT_DEPENDS
|
||||||
|
"${CMAKE_SOURCE_DIR}/res/themes/ui.toml;${CMAKE_SOURCE_DIR}/res/default_banlist.txt")
|
||||||
|
|
||||||
add_executable(ObsidianDragon
|
add_executable(ObsidianDragon
|
||||||
${APP_SOURCES}
|
${APP_SOURCES}
|
||||||
${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
|
${CMAKE_BINARY_DIR}/generated/embedded_fonts.cpp
|
||||||
|
${CMAKE_BINARY_DIR}/generated/embedded_text.cpp
|
||||||
${IMGUI_SOURCES}
|
${IMGUI_SOURCES}
|
||||||
${QRCODE_SOURCES}
|
${QRCODE_SOURCES}
|
||||||
${GLAD_SOURCES}
|
${GLAD_SOURCES}
|
||||||
@@ -1045,20 +1057,6 @@ if(LANG_FILES)
|
|||||||
message(STATUS " Language files: ${LANG_FILES}")
|
message(STATUS " Language files: ${LANG_FILES}")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# Embed ui.toml into the binary so it's always available at runtime
|
|
||||||
include(${CMAKE_SOURCE_DIR}/cmake/EmbedResources.cmake)
|
|
||||||
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/generated)
|
|
||||||
embed_resource(
|
|
||||||
${CMAKE_SOURCE_DIR}/res/themes/ui.toml
|
|
||||||
${CMAKE_BINARY_DIR}/generated/ui_toml_embedded.h
|
|
||||||
ui_toml
|
|
||||||
)
|
|
||||||
embed_resource(
|
|
||||||
${CMAKE_SOURCE_DIR}/res/default_banlist.txt
|
|
||||||
${CMAKE_BINARY_DIR}/generated/default_banlist_embedded.h
|
|
||||||
default_banlist
|
|
||||||
)
|
|
||||||
|
|
||||||
# Note: xmrig is embedded via build.sh (embedded_data.h) for Windows builds,
|
# Note: xmrig is embedded via build.sh (embedded_data.h) for Windows builds,
|
||||||
# following the same pattern as daemon embedding.
|
# following the same pattern as daemon embedding.
|
||||||
|
|
||||||
@@ -1276,6 +1274,7 @@ if(BUILD_TESTING)
|
|||||||
src/resources/embedded_resources.cpp
|
src/resources/embedded_resources.cpp
|
||||||
src/util/secure_vault.cpp
|
src/util/secure_vault.cpp
|
||||||
src/util/platform.cpp
|
src/util/platform.cpp
|
||||||
|
${CMAKE_BINARY_DIR}/generated/embedded_text.cpp
|
||||||
src/util/coalescing_writer.cpp
|
src/util/coalescing_writer.cpp
|
||||||
src/util/logger.cpp
|
src/util/logger.cpp
|
||||||
src/util/lite_server_probe.cpp
|
src/util/lite_server_probe.cpp
|
||||||
|
|||||||
@@ -1,42 +0,0 @@
|
|||||||
# CMake script to embed binary files as C arrays
|
|
||||||
|
|
||||||
function(embed_resource RESOURCE_FILE OUTPUT_FILE VAR_NAME)
|
|
||||||
file(READ "${RESOURCE_FILE}" HEX_CONTENT HEX)
|
|
||||||
|
|
||||||
# Convert hex to C array format
|
|
||||||
string(LENGTH "${HEX_CONTENT}" HEX_LENGTH)
|
|
||||||
math(EXPR BYTE_COUNT "${HEX_LENGTH} / 2")
|
|
||||||
|
|
||||||
set(ARRAY_CONTENT "")
|
|
||||||
set(INDEX 0)
|
|
||||||
while(INDEX LESS HEX_LENGTH)
|
|
||||||
string(SUBSTRING "${HEX_CONTENT}" ${INDEX} 2 BYTE)
|
|
||||||
string(APPEND ARRAY_CONTENT "0x${BYTE},")
|
|
||||||
math(EXPR INDEX "${INDEX} + 2")
|
|
||||||
math(EXPR BYTE_NUM "${INDEX} / 2")
|
|
||||||
math(EXPR MOD "${BYTE_NUM} % 16")
|
|
||||||
if(MOD EQUAL 0)
|
|
||||||
string(APPEND ARRAY_CONTENT "\n ")
|
|
||||||
endif()
|
|
||||||
endwhile()
|
|
||||||
|
|
||||||
# Generate header file
|
|
||||||
set(HEADER_CONTENT "// Auto-generated embedded resource - DO NOT EDIT
|
|
||||||
#pragma once
|
|
||||||
#include <cstddef>
|
|
||||||
#include <cstdint>
|
|
||||||
|
|
||||||
namespace embedded {
|
|
||||||
|
|
||||||
inline constexpr unsigned char ${VAR_NAME}_data[] = {
|
|
||||||
${ARRAY_CONTENT}
|
|
||||||
};
|
|
||||||
|
|
||||||
inline constexpr size_t ${VAR_NAME}_size = ${BYTE_COUNT};
|
|
||||||
|
|
||||||
} // namespace embedded
|
|
||||||
")
|
|
||||||
|
|
||||||
file(WRITE "${OUTPUT_FILE}" "${HEADER_CONTENT}")
|
|
||||||
message(STATUS "Embedded ${RESOURCE_FILE} -> ${OUTPUT_FILE} (${BYTE_COUNT} bytes)")
|
|
||||||
endfunction()
|
|
||||||
+8
-19
@@ -89,13 +89,8 @@
|
|||||||
#include "ui/schema/skin_manager.h"
|
#include "ui/schema/skin_manager.h"
|
||||||
#include "util/perf_log.h"
|
#include "util/perf_log.h"
|
||||||
|
|
||||||
// Embedded ui.toml (generated at build time) — fallback when file not on disk
|
// Embedded ui.toml (INCBIN) — fallback when file not on disk
|
||||||
#if __has_include("ui_toml_embedded.h")
|
#include "embedded/embedded_text.h"
|
||||||
#include "ui_toml_embedded.h"
|
|
||||||
#define HAS_EMBEDDED_UI_TOML 1
|
|
||||||
#else
|
|
||||||
#define HAS_EMBEDDED_UI_TOML 0
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
@@ -367,25 +362,23 @@ bool App::loadBaseUiSchema()
|
|||||||
std::string schemaPath = util::Platform::getExecutableDirectory() + "/res/themes/ui.toml";
|
std::string schemaPath = util::Platform::getExecutableDirectory() + "/res/themes/ui.toml";
|
||||||
bool loaded = false;
|
bool loaded = false;
|
||||||
|
|
||||||
#if HAS_EMBEDDED_UI_TOML
|
|
||||||
// If on-disk ui.toml exists but differs in size from the embedded
|
// If on-disk ui.toml exists but differs in size from the embedded
|
||||||
// version, a newer wallet binary is running against stale theme
|
// version, a newer wallet binary is running against stale theme
|
||||||
// files. Overwrite the on-disk copy so layout matches the binary.
|
// files. Overwrite the on-disk copy so layout matches the binary.
|
||||||
if (std::filesystem::exists(schemaPath)) {
|
if (std::filesystem::exists(schemaPath)) {
|
||||||
std::error_code ec;
|
std::error_code ec;
|
||||||
auto diskSize = std::filesystem::file_size(schemaPath, 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",
|
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);
|
std::ofstream ofs(schemaPath, std::ios::binary | std::ios::trunc);
|
||||||
if (ofs.is_open()) {
|
if (ofs.is_open()) {
|
||||||
ofs.write(reinterpret_cast<const char*>(embedded::ui_toml_data),
|
ofs.write(reinterpret_cast<const char*>(g_ui_toml_data),
|
||||||
embedded::ui_toml_size);
|
g_ui_toml_size);
|
||||||
ofs.close();
|
ofs.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
if (std::filesystem::exists(schemaPath)) {
|
if (std::filesystem::exists(schemaPath)) {
|
||||||
loaded = ui::schema::UISchema::instance().loadFromFile(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
|
// Fallback: load from build-time embedded data when file not on disk
|
||||||
// (e.g., single-file Windows distribution without res/ directory)
|
// (e.g., single-file Windows distribution without res/ directory)
|
||||||
if (!loaded) {
|
if (!loaded) {
|
||||||
#if HAS_EMBEDDED_UI_TOML
|
std::string embedded(reinterpret_cast<const char*>(g_ui_toml_data),
|
||||||
std::string embedded(reinterpret_cast<const char*>(embedded::ui_toml_data),
|
g_ui_toml_size);
|
||||||
embedded::ui_toml_size);
|
|
||||||
ui::schema::UISchema::instance().loadFromString(embedded, "embedded");
|
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();
|
return ui::schema::UISchema::instance().isLoaded();
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -54,7 +54,7 @@
|
|||||||
#include "util/pool_registry.h"
|
#include "util/pool_registry.h"
|
||||||
#include "ui/notifications.h"
|
#include "ui/notifications.h"
|
||||||
#include "ui/schema/skin_manager.h"
|
#include "ui/schema/skin_manager.h"
|
||||||
#include "default_banlist_embedded.h"
|
#include "embedded/embedded_text.h"
|
||||||
#include "util/amount_format.h"
|
#include "util/amount_format.h"
|
||||||
#include "util/http_download.h"
|
#include "util/http_download.h"
|
||||||
#include "data/exchange_info.h"
|
#include "data/exchange_info.h"
|
||||||
@@ -3742,8 +3742,8 @@ void App::applyDefaultBanlist()
|
|||||||
if (!state_.connected || !rpc_ || !worker_) return;
|
if (!state_.connected || !rpc_ || !worker_) return;
|
||||||
|
|
||||||
// Parse the embedded default_banlist.txt (compiled from res/default_banlist.txt)
|
// Parse the embedded default_banlist.txt (compiled from res/default_banlist.txt)
|
||||||
std::string data(reinterpret_cast<const char*>(embedded::default_banlist_data),
|
std::string data(reinterpret_cast<const char*>(g_default_banlist_data),
|
||||||
embedded::default_banlist_size);
|
g_default_banlist_size);
|
||||||
|
|
||||||
std::vector<std::string> ips;
|
std::vector<std::string> ips;
|
||||||
size_t pos = 0;
|
size_t pos = 0;
|
||||||
|
|||||||
@@ -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");
|
||||||
@@ -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
@@ -15,13 +15,8 @@
|
|||||||
#include <system_error>
|
#include <system_error>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
// Embedded ui.toml (generated at build time)
|
// Embedded ui.toml (INCBIN)
|
||||||
#if __has_include("ui_toml_embedded.h")
|
#include "embedded/embedded_text.h"
|
||||||
#include "ui_toml_embedded.h"
|
|
||||||
#define HAS_EMBEDDED_UI_TOML 1
|
|
||||||
#else
|
|
||||||
#define HAS_EMBEDDED_UI_TOML 0
|
|
||||||
#endif
|
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
@@ -562,20 +557,7 @@ void Platform::ensureObsidianDragonSetup()
|
|||||||
// Use the build-time embedded copy first; fall back to the
|
// Use the build-time embedded copy first; fall back to the
|
||||||
// bundled file on disk.
|
// bundled file on disk.
|
||||||
std::string content;
|
std::string content;
|
||||||
#if HAS_EMBEDDED_UI_TOML
|
content.assign(reinterpret_cast<const char*>(g_ui_toml_data), g_ui_toml_size);
|
||||||
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
|
|
||||||
|
|
||||||
if (!content.empty()) {
|
if (!content.empty()) {
|
||||||
// Patch the theme metadata so the example is clearly
|
// Patch the theme metadata so the example is clearly
|
||||||
|
|||||||
Reference in New Issue
Block a user