From 5a949bd7c6ad816b82db64ff87ee5ed69fa377ad Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 13:57:47 +0300 Subject: [PATCH 01/25] Update CMakeLists.txt --- CMakeLists.txt | 54 ++++++++++++++++++++++++++++++-------------------- 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c057b31..9cdd839 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -184,7 +184,6 @@ set(EMBED_HEADER_FILE [=[ // DO NOT EDIT THIS FILE!!! #ifndef BATTERY_EMBED_HPP #define BATTERY_EMBED_HPP - #include #include #include @@ -193,19 +192,14 @@ set(EMBED_HEADER_FILE [=[ #include #include #include - #ifndef __cpp_constexpr_dynamic_alloc # error "battery::embed requires C++20 (Your compiler does not provide __cpp_constexpr_dynamic_alloc, which is needed for battery::embed)" #endif - namespace b { - struct EmbedInternal { - class EmbeddedFile { public: constexpr EmbeddedFile() = default; - constexpr EmbeddedFile( const std::string_view& data, const std::string_view& filename @@ -219,37 +213,28 @@ namespace b { , m_fullFilepath(fullFilepath) #endif // !B_PRODUCTION_MODE {} - [[nodiscard]] std::string str() const { return m_data.data(); } - [[nodiscard]] const char* data() const { return m_data.data(); } - [[nodiscard]] std::vector vec() const { return { m_data.begin(), m_data.end() }; } - [[nodiscard]] size_t length() const { return m_data.size(); } - [[nodiscard]] size_t size() const { return m_data.size(); } - operator std::string() const { return str(); } - operator std::vector() const { return vec(); } - void get(const std::function& callback); - private: std::string_view m_data; std::string_view m_filename; @@ -257,7 +242,6 @@ namespace b { std::string_view m_fullFilepath; #endif }; // class EmbeddedFile - ${EMBEDDED_FILES_DECLARATIONS} }; // struct embedded_files @@ -290,6 +274,13 @@ namespace b { } } + // === НОВАЯ ФУНКЦИЯ: Получить список всех встроенных файлов === + [[nodiscard]] inline std::vector embed_list() { + return { + ${EMBEDDED_FILES_LIST} + }; + } + } // namespace b inline std::ostream& operator<<(std::ostream& stream, const b::EmbedInternal::EmbeddedFile& file) { @@ -333,29 +324,50 @@ endfunction(_embed_generate_all_hpps) function(_embed_generate_hpp TARGET) string(TOLOWER "${TARGET}" TARGET) string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" TARGET "${TARGET}") - if (NOT EMBED_IDENTIFIERS) return() endif() + + # Генерация объявлений статических файлов set(EMBEDDED_FILES_DECLARATIONS "") - list(LENGTH EMBED_IDENTIFIERS num_identifiers) foreach (IDENTIFIER IN LISTS EMBED_IDENTIFIERS) if (IDENTIFIER MATCHES "^${TARGET}_") - set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n ") + set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") endif() endforeach() + + # Генерация тела embed() set(EMBEDDED_FILES_RETURNS "") - math(EXPR num_identifiers "${num_identifiers} - 1") + list(LENGTH EMBED_IDENTIFIERS num_identifiers) foreach (INDEX RANGE ${num_identifiers}) list(GET EMBED_IDENTIFIERS ${INDEX} IDENTIFIER) list(GET EMBED_FILENAMES ${INDEX} FILENAME) if (IDENTIFIER MATCHES "^${TARGET}_") - set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if constexpr (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\n else ") + set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if constexpr (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") + endif() + endforeach() + + # Генерация списка имён файлов для embed_list() + set(EMBEDDED_FILES_LIST "") + foreach (IDENTIFIER IN LISTS EMBED_IDENTIFIERS) + list(GET EMBED_FILENAMES ${INDEX} FILENAME) + if (IDENTIFIER MATCHES "^${TARGET}_") + set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") endif() + math(EXPR INDEX "${INDEX} + 1") endforeach() + + # Удаляем последнюю запятую и перенос + string(REGEX REPLACE ",\n$" "" EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST}") + + # Подставляем всё в шаблон file(READ ${EMBED_BINARY_DIR}/embed_header_file_template.hpp EMBED_HEADER_FILE) string(CONFIGURE "${EMBED_HEADER_FILE}" EMBED_HEADER_FILE_GENERATED) + + # Записываем результат set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET}/include/battery/embed.hpp") + get_filename_component(EMBED_HPP_DIR "${EMBED_HPP}" DIRECTORY) + file(MAKE_DIRECTORY "${EMBED_HPP_DIR}") file(WRITE "${EMBED_HPP}" "${EMBED_HEADER_FILE_GENERATED}") endfunction(_embed_generate_hpp) From d524009d3f272197243a4f833227eee82ab33052 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 14:54:27 +0300 Subject: [PATCH 02/25] Back to C++17 --- CMakeLists.txt | 89 +++++++++++++++++++++++++------------------------- 1 file changed, 44 insertions(+), 45 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cdd839..debeee0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,95 +186,94 @@ set(EMBED_HEADER_FILE [=[ #define BATTERY_EMBED_HPP #include #include -#include #include #include #include #include #include -#ifndef __cpp_constexpr_dynamic_alloc -# error "battery::embed requires C++20 (Your compiler does not provide __cpp_constexpr_dynamic_alloc, which is needed for battery::embed)" + +// Отключаем проверку C++20 для совместимости +#if 0 +# ifndef __cpp_constexpr_dynamic_alloc +# error "battery::embed requires C++20 ..." +# endif #endif + namespace b { struct EmbedInternal { class EmbeddedFile { public: - constexpr EmbeddedFile() = default; - constexpr EmbeddedFile( - const std::string_view& data, - const std::string_view& filename + EmbeddedFile() = default; + + EmbeddedFile( + const char* data, size_t size, + const char* filename #ifndef B_PRODUCTION_MODE - , const std::string_view& fullFilepath -#endif // !B_PRODUCTION_MODE + , const char* fullFilepath +#endif ) : m_data(data) + , m_size(size) , m_filename(filename) #ifndef B_PRODUCTION_MODE , m_fullFilepath(fullFilepath) -#endif // !B_PRODUCTION_MODE +#endif {} + [[nodiscard]] std::string str() const { - return m_data.data(); - } - [[nodiscard]] const char* data() const { - return m_data.data(); + return std::string(m_data, m_size); } + [[nodiscard]] const char* data() const { return m_data; } [[nodiscard]] std::vector vec() const { - return { m_data.begin(), m_data.end() }; - } - [[nodiscard]] size_t length() const { - return m_data.size(); - } - [[nodiscard]] size_t size() const { - return m_data.size(); - } - operator std::string() const { - return str(); - } - operator std::vector() const { - return vec(); + return { reinterpret_cast(m_data), + reinterpret_cast(m_data) + m_size }; } + [[nodiscard]] size_t size() const { return m_size; } + [[nodiscard]] size_t length() const { return m_size; } + + operator std::string() const { return str(); } + operator std::vector() const { return vec(); } + void get(const std::function& callback); + private: - std::string_view m_data; - std::string_view m_filename; + const char* m_data = nullptr; + size_t m_size = 0; + const char* m_filename = nullptr; #ifndef B_PRODUCTION_MODE - std::string_view m_fullFilepath; + const char* m_fullFilepath = nullptr; #endif - }; // class EmbeddedFile + }; + ${EMBEDDED_FILES_DECLARATIONS} - }; // struct embedded_files + }; template struct embed_string_literal { - constexpr embed_string_literal(const char (&str)[N]) { + embed_string_literal(const char (&str)[N]) { std::copy_n(str, N, value); } - constexpr bool operator!=(const embed_string_literal& other) const { - return std::equal(value, value + N, other.value); + bool operator!=(const embed_string_literal& other) const { + return !std::equal(value, value + N, other.value); } - [[nodiscard]] std::string str() const { - return std::string(value, N); - } - [[nodiscard]] constexpr bool _false() const { + [[nodiscard]] bool _false() const { return false; } char value[N]; }; template - constexpr bool operator==(const embed_string_literal& left, const char (&right)[M]) { + bool operator==(const embed_string_literal& left, const char (&right)[M]) { return std::equal(left.value, left.value + N, right); } template - constexpr EmbedInternal::EmbeddedFile embed() { - ${EMBEDDED_FILES_RETURNS}{ - static_assert(identifier._false(), "[b::embed<>] No such file or directory"); - } + EmbedInternal::EmbeddedFile embed() { + ${EMBEDDED_FILES_RETURNS} + throw std::runtime_error("[b::embed<>] No such file or directory"); } - // === НОВАЯ ФУНКЦИЯ: Получить список всех встроенных файлов === + // Функция для получения списка всех встроенных файлов [[nodiscard]] inline std::vector embed_list() { return { ${EMBEDDED_FILES_LIST} From a8ab06e4e9f842e68540808a8479293658c7da04 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 15:08:20 +0300 Subject: [PATCH 03/25] Update CMakeLists.txt buld fixing --- CMakeLists.txt | 82 ++++++++++++++++++++------------------------------ 1 file changed, 33 insertions(+), 49 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index debeee0..9d14651 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,13 +192,6 @@ set(EMBED_HEADER_FILE [=[ #include #include -// Отключаем проверку C++20 для совместимости -#if 0 -# ifndef __cpp_constexpr_dynamic_alloc -# error "battery::embed requires C++20 ..." -# endif -#endif - namespace b { struct EmbedInternal { class EmbeddedFile { @@ -327,48 +320,40 @@ function(_embed_generate_hpp TARGET) return() endif() - # Генерация объявлений статических файлов set(EMBEDDED_FILES_DECLARATIONS "") - foreach (IDENTIFIER IN LISTS EMBED_IDENTIFIERS) - if (IDENTIFIER MATCHES "^${TARGET}_") - set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") - endif() - endforeach() - - # Генерация тела embed() set(EMBEDDED_FILES_RETURNS "") + set(EMBEDDED_FILES_LIST "") + + # Проходим по индексам list(LENGTH EMBED_IDENTIFIERS num_identifiers) - foreach (INDEX RANGE ${num_identifiers}) + foreach(INDEX RANGE ${num_identifiers}) list(GET EMBED_IDENTIFIERS ${INDEX} IDENTIFIER) list(GET EMBED_FILENAMES ${INDEX} FILENAME) if (IDENTIFIER MATCHES "^${TARGET}_") + # Объявление + set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") + + # Возврат в embed<> set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if constexpr (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") - endif() - endforeach() - # Генерация списка имён файлов для embed_list() - set(EMBEDDED_FILES_LIST "") - foreach (IDENTIFIER IN LISTS EMBED_IDENTIFIERS) - list(GET EMBED_FILENAMES ${INDEX} FILENAME) - if (IDENTIFIER MATCHES "^${TARGET}_") + # Добавляем в список set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") endif() - math(EXPR INDEX "${INDEX} + 1") endforeach() - # Удаляем последнюю запятую и перенос + # Убираем последнюю запятую string(REGEX REPLACE ",\n$" "" EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST}") - # Подставляем всё в шаблон + # Генерируем заголовок file(READ ${EMBED_BINARY_DIR}/embed_header_file_template.hpp EMBED_HEADER_FILE) string(CONFIGURE "${EMBED_HEADER_FILE}" EMBED_HEADER_FILE_GENERATED) - # Записываем результат + # Путь к embed.hpp set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET}/include/battery/embed.hpp") get_filename_component(EMBED_HPP_DIR "${EMBED_HPP}" DIRECTORY) file(MAKE_DIRECTORY "${EMBED_HPP_DIR}") file(WRITE "${EMBED_HPP}" "${EMBED_HEADER_FILE_GENERATED}") -endfunction(_embed_generate_hpp) +endfunction() # Internal function for validating an identifier function(embed_validate_identifier IDENTIFIER) # Validate the identifier against C variable naming rules @@ -381,51 +366,51 @@ endfunction() function(b_embed TARGET FILENAME) string(TOLOWER "${TARGET}" TARGET_ID) string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" TARGET_ID "${TARGET_ID}") - if (IS_ABSOLUTE "${FILENAME}") message(FATAL_ERROR "embed: File name must be relative to the current source directory: '${FILENAME}'") endif() - # Make the identifier string(TOLOWER "${TARGET_ID}_${FILENAME}" IDENTIFIER) string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" IDENTIFIER "${IDENTIFIER}") # Replace all invalid characters with underscores embed_validate_identifier("${IDENTIFIER}") # Validate the identifier against C variable naming rules - # Set up paths get_filename_component(FULL_PATH "${FILENAME}" ABSOLUTE) # Make the file path absolute set(CPP_FILE "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/src/${IDENTIFIER}.cpp") - # If identifier already in use list(FIND EMBED_IDENTIFIERS ${IDENTIFIER} EMBED_USED_IDENTIFIERS_INDEX) if (NOT EMBED_USED_IDENTIFIERS_INDEX EQUAL -1) message(FATAL_ERROR "embed: Identifier already in use: '${IDENTIFIER}'") endif() - set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "list of all identifiers used by the embed library") - set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library") - set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library") + # === Добавляем идентификатор и имя файла === + set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "list of all identifiers used by the embed library" FORCE) + set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library" FORCE) + set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) - # This action generates both files and is called on-demand whenever the resource file changes + # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === + _embed_generate_hpp(${TARGET}) + + # === Генерация .cpp файла для встраивания === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") set(EMBED_CPP_TEMPLATE "${EMBED_BINARY_DIR}/embed_source_file_template.cpp") add_custom_command( - COMMAND ${CMAKE_COMMAND} - -DINFILE=${EMBED_CPP_TEMPLATE} - -DOUTFILE=${CPP_FILE} - -DFULL_PATH=${FULL_PATH} - -DIDENTIFIER=${IDENTIFIER} - -DFILENAME=${FILENAME} - -P "${EMBED_BINARY_DIR}/generate.cmake" - DEPENDS "${FULL_PATH}" "${EMBED_HPP}" "${EMBED_BINARY_DIR}/generate.cmake" "${EMBED_BINARY_DIR}/embed_source_file_template.cpp" - OUTPUT ${CPP_FILE} - VERBATIM + COMMAND ${CMAKE_COMMAND} + -DINFILE=${EMBED_CPP_TEMPLATE} + -DOUTFILE=${CPP_FILE} + -DFULL_PATH=${FULL_PATH} + -DIDENTIFIER=${IDENTIFIER} + -DFILENAME=${FILENAME} + -P "${EMBED_BINARY_DIR}/generate.cmake" + DEPENDS "${FULL_PATH}" "${EMBED_HPP}" "${EMBED_BINARY_DIR}/generate.cmake" "${EMBED_BINARY_DIR}/embed_source_file_template.cpp" + OUTPUT ${CPP_FILE} + VERBATIM ) - # Add the generated files to the target + # === Добавляем файлы в таргет (теперь embed.hpp уже существует!) === target_include_directories(${TARGET} PUBLIC $ $ ) - target_sources(${TARGET} PRIVATE ${CPP_FILE} ${EMBED_BINARY_DIR}/embed_impl.cpp ${EMBED_HPP}) + target_sources(${TARGET} PRIVATE ${CPP_FILE} ${EMBED_BINARY_DIR}/embed_impl.cpp) target_compile_definitions(${TARGET} PRIVATE _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING) if (NOT B_PRODUCTION_MODE) @@ -442,7 +427,6 @@ function(b_embed TARGET FILENAME) source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery PREFIX "embed/autogen" FILES ${EMBED_HPP}) source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "/embed" FILES ${FULL_PATH}) endif() - endfunction() function(b_embed_proxy_target MAIN_TARGET PROXY_TARGET) From bc0aa852e827bb3114a17a775c78f6ad04b0c244 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 15:17:24 +0300 Subject: [PATCH 04/25] Update CMakeLists.txt --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d14651..22c47d2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -387,7 +387,7 @@ function(b_embed TARGET FILENAME) set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === - _embed_generate_hpp(${TARGET}) + #_embed_generate_hpp(${TARGET}) # === Генерация .cpp файла для встраивания === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") From 0c939238ab9717efa2d078ee225152c102011903 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 15:20:53 +0300 Subject: [PATCH 05/25] Update CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 22c47d2..e0b88a2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,7 +304,7 @@ set(EMBED_FILENAMES "" CACHE INTERNAL "list of all filenames used by battery::em set(EMBED_TARGETS "" CACHE INTERNAL "list of all targets used by battery::embed") # Defer the function call until the end of the configure step -cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _embed_generate_all_hpps()) +# cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _embed_generate_all_hpps()) function(_embed_generate_all_hpps) message(STATUS "Generating b::embed() HPP files ...") foreach (TARGET ${EMBED_TARGETS}) @@ -387,7 +387,7 @@ function(b_embed TARGET FILENAME) set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === - #_embed_generate_hpp(${TARGET}) + # _embed_generate_hpp(${TARGET}) # === Генерация .cpp файла для встраивания === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") From a5804b1e1327f9a44bde1c776067128b6115299e Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 15:25:57 +0300 Subject: [PATCH 06/25] Update CMakeLists.txt --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e0b88a2..9d14651 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -304,7 +304,7 @@ set(EMBED_FILENAMES "" CACHE INTERNAL "list of all filenames used by battery::em set(EMBED_TARGETS "" CACHE INTERNAL "list of all targets used by battery::embed") # Defer the function call until the end of the configure step -# cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _embed_generate_all_hpps()) +cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _embed_generate_all_hpps()) function(_embed_generate_all_hpps) message(STATUS "Generating b::embed() HPP files ...") foreach (TARGET ${EMBED_TARGETS}) @@ -387,7 +387,7 @@ function(b_embed TARGET FILENAME) set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === - # _embed_generate_hpp(${TARGET}) + _embed_generate_hpp(${TARGET}) # === Генерация .cpp файла для встраивания === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") From 39952daa6a11589d349b72821b0d94db1ae37406 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 15:32:21 +0300 Subject: [PATCH 07/25] Manual list call --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d14651..c376018 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -385,10 +385,7 @@ function(b_embed TARGET FILENAME) set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "list of all identifiers used by the embed library" FORCE) set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library" FORCE) set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) - - # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === - _embed_generate_hpp(${TARGET}) - + # === Генерация .cpp файла для встраивания === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") set(EMBED_CPP_TEMPLATE "${EMBED_BINARY_DIR}/embed_source_file_template.cpp") From d09bc8a8579d71fb998007f1d1710abb4a9b07e9 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 15:38:41 +0300 Subject: [PATCH 08/25] Fixed index bug --- CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c376018..0c9f13a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -326,7 +326,8 @@ function(_embed_generate_hpp TARGET) # Проходим по индексам list(LENGTH EMBED_IDENTIFIERS num_identifiers) - foreach(INDEX RANGE ${num_identifiers}) + math(EXPR last_index "${num_identifiers} - 1") + foreach(INDEX RANGE 0 ${last_index}) list(GET EMBED_IDENTIFIERS ${INDEX} IDENTIFIER) list(GET EMBED_FILENAMES ${INDEX} FILENAME) if (IDENTIFIER MATCHES "^${TARGET}_") @@ -386,6 +387,9 @@ function(b_embed TARGET FILENAME) set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library" FORCE) set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) + # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === + _embed_generate_hpp(${TARGET}) + # === Генерация .cpp файла для встраивания === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") set(EMBED_CPP_TEMPLATE "${EMBED_BINARY_DIR}/embed_source_file_template.cpp") From 4d8b3a26a3b537e6778b5ef1e3269e738ca0034d Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 16:03:21 +0300 Subject: [PATCH 09/25] fixed compile errors --- CMakeLists.txt | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 0c9f13a..ba910aa 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -22,10 +22,8 @@ set(EMBED_SOURCE_FILE_TEMPLATE [==[ namespace b { EmbedInternal::EmbeddedFile EmbedInternal::${IDENTIFIER} = { - std::string_view( ${GENERATED_BYTE_ARRAY}, ${FILESIZE} - ) , "${FILENAME}" #ifndef B_PRODUCTION_MODE , "${FULL_PATH}" @@ -260,8 +258,8 @@ namespace b { return std::equal(left.value, left.value + N, right); } - template - EmbedInternal::EmbeddedFile embed() { + template + EmbedInternal::EmbeddedFile embed(const embed_string_literal& identifier) { ${EMBEDDED_FILES_RETURNS} throw std::runtime_error("[b::embed<>] No such file or directory"); } From bb1342efd43ad44c18488a5a30c0dbf2b00304fd Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 16:15:03 +0300 Subject: [PATCH 10/25] Fixed for arbitrary string type --- CMakeLists.txt | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index ba910aa..9cf03bd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -238,28 +238,9 @@ namespace b { ${EMBEDDED_FILES_DECLARATIONS} }; - - template - struct embed_string_literal { - embed_string_literal(const char (&str)[N]) { - std::copy_n(str, N, value); - } - bool operator!=(const embed_string_literal& other) const { - return !std::equal(value, value + N, other.value); - } - [[nodiscard]] bool _false() const { - return false; - } - char value[N]; - }; - - template - bool operator==(const embed_string_literal& left, const char (&right)[M]) { - return std::equal(left.value, left.value + N, right); - } - - template - EmbedInternal::EmbeddedFile embed(const embed_string_literal& identifier) { + + template + EmbedInternal::EmbeddedFile embed(const S& identifier) { ${EMBEDDED_FILES_RETURNS} throw std::runtime_error("[b::embed<>] No such file or directory"); } From aa062ac10e98ae6d35615ad4c42ed80b97e93b8e Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 16:19:18 +0300 Subject: [PATCH 11/25] Fixed demand constexpr fetish --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9cf03bd..55f6b22 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -314,7 +314,7 @@ function(_embed_generate_hpp TARGET) set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") # Возврат в embed<> - set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if constexpr (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") + set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") # Добавляем в список set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") From 6489a85253ef35718079186799a0e7afb06ac8fe Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 16:28:37 +0300 Subject: [PATCH 12/25] Update README.md --- README.md | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 25875e2..5489563 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Embedding files into executables made easy. Single CMake file, Modern C++, a min ## What is this? -This is a CMake-based C++20 library that allows you to very easily embed resource files +This is a CMake-based C++17 library that allows you to very easily embed resource files into your executable. It is meant to be used for files such as icons, images, shader code, configuration files, etc.. You don't have to worry about distributing them with your application as the files are part of the application once compiled. It might also be an advantage that an end @@ -199,7 +199,9 @@ src/main.cpp #include "battery/embed.hpp" int main() { - std::cout << b::embed<"resources/message.txt">() << std::endl; + for (auto filename : b::embed_list()) { + std::cout << "Embedded file: " << filename << "[" << b::embed(filename).size() << "]" << std::endl; + } return 0; } ``` @@ -222,11 +224,11 @@ That's it, now run it and see what happens! The returned object is a class that provides a number of functions and conversion operators for convenience. ```cpp -std::string file = b::embed<"resources/message.txt">().str(); -const char* pointer = b::embed<"resources/message.txt">().data(); -size_t length = b::embed<"resources/message.txt">().length(); -size_t size = b::embed<"resources/message.txt">().size(); -std::vector vec = b::embed<"resources/message.txt">().vec(); +std::string file = b::embed("resources/message.txt").str(); +const char* pointer = b::embed("resources/message.txt").data(); +size_t length = b::embed("resources/message.txt").length(); +size_t size = b::embed("resources/message.txt").size(); +std::vector vec = b::embed("resources/message.txt").vec(); ``` And all conversions also exist as operators, allowing for this: @@ -235,8 +237,8 @@ And all conversions also exist as operators, allowing for this: void foo_str(const std::string& test); void foo_vec(const std::vector& test); -foo_str(b::embed<"resources/message.txt">()); -foo_vec(b::embed<"resources/message.txt">()); +foo_str(b::embed("resources/message.txt")); +foo_vec(b::embed("resources/message.txt")); ``` ### Hot-reloading files @@ -257,7 +259,7 @@ int main() { // This lambda is called from another thread every time the file changes on-disk, // and in production mode, it is called immediately from the same thread, once. - b::embed<"assets/source.txt">().get([&file] (const auto& content) { + b::embed("assets/source.txt").get([&file] (const auto& content) { file = content.str(); std::cout << "File changed: " << file << std::endl; }); @@ -285,7 +287,7 @@ void test() { } // Or pass it along - auto icon = b::embed<"resources/message.txt">(); + auto icon = b::embed("resources/message.txt); old_c_style_function(icon.data(), icon.size()); } ``` From e3a4e1a068a950d7e0300baa5c07fedab7c0bc3e Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 16:31:37 +0300 Subject: [PATCH 13/25] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5489563..77e4624 100644 --- a/README.md +++ b/README.md @@ -65,7 +65,7 @@ Currently, every file would need to be retrieved and written to disk manually. ## Requirements - CMake >=3.21 - - A C++20 compiler + - A C++17 compiler This project requires C++20. If you are stuck with an older version, you can use [Release v1.0.0](https://github.com/batterycenter/embed/releases/tag/v1.0.0). It was made for C++14. From c759e0310aa99ce0f510aff5b8653ec891265274 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 16:35:23 +0300 Subject: [PATCH 14/25] Update README.md --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 77e4624..b42015f 100644 --- a/README.md +++ b/README.md @@ -67,7 +67,7 @@ Currently, every file would need to be retrieved and written to disk manually. - CMake >=3.21 - A C++17 compiler -This project requires C++20. If you are stuck with an older version, you can use [Release v1.0.0](https://github.com/batterycenter/embed/releases/tag/v1.0.0). It was made for C++14. +This project requires C++17. If you are stuck with an older version, you can use [Release v1.0.0](https://github.com/batterycenter/embed/releases/tag/v1.0.0). It was made for C++14. ## How it works @@ -330,7 +330,7 @@ b_embed(MyTarget-resources resource2.txt) CMakeLists.txt: ```cmake add_executable(MyTarget) -target_compile_features(MyTarget PUBLIC cxx_std_20) +target_compile_features(MyTarget PUBLIC cxx_std_17) add_subdirectory(resources) ``` From 3185ea60b08b6c43114646ea67405f19ae1c456b Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 18:24:52 +0300 Subject: [PATCH 15/25] Fixed bugs in B_PRODUCTION_MODE --- CMakeLists.txt | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 55f6b22..4829979 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,6 +49,7 @@ set(EMBED_MASTER_SOURCE_FILE [==[ #include #include #include +#include #endif // !B_PRODUCTION_MODE and !B_OS_WEB namespace b { @@ -140,7 +141,7 @@ namespace b { continue; } filedata.newContent = *fileresult; - EmbedInternal::EmbeddedFile newFile(filedata.newContent, filedata.filename, filedata.filepath); + EmbedInternal::EmbeddedFile newFile(filedata.newContent.c_str(), filedata.newContent.size(), filedata.filename.c_str(), filedata.filepath.c_str()); filedata.callback(newFile); } } @@ -156,7 +157,8 @@ namespace b { #else // B_PRODUCTION_MODE or B_OS_WEB auto fileresult = embed_read_file(m_fullFilepath); if (fileresult) { - m_data = *fileresult; + m_storage = std::move(*fileresult); + m_data = m_storage.c_str(); } callback(*this); @@ -232,6 +234,7 @@ namespace b { size_t m_size = 0; const char* m_filename = nullptr; #ifndef B_PRODUCTION_MODE + std::string m_storage; const char* m_fullFilepath = nullptr; #endif }; @@ -303,32 +306,29 @@ function(_embed_generate_hpp TARGET) set(EMBEDDED_FILES_RETURNS "") set(EMBEDDED_FILES_LIST "") - # Проходим по индексам + # Iterate over indices list(LENGTH EMBED_IDENTIFIERS num_identifiers) math(EXPR last_index "${num_identifiers} - 1") foreach(INDEX RANGE 0 ${last_index}) list(GET EMBED_IDENTIFIERS ${INDEX} IDENTIFIER) list(GET EMBED_FILENAMES ${INDEX} FILENAME) if (IDENTIFIER MATCHES "^${TARGET}_") - # Объявление set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") - # Возврат в embed<> set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") - # Добавляем в список set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") endif() endforeach() - # Убираем последнюю запятую + # Remove the last comma string(REGEX REPLACE ",\n$" "" EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST}") - # Генерируем заголовок + # Generate the header file file(READ ${EMBED_BINARY_DIR}/embed_header_file_template.hpp EMBED_HEADER_FILE) string(CONFIGURE "${EMBED_HEADER_FILE}" EMBED_HEADER_FILE_GENERATED) - # Путь к embed.hpp + # Path to embed.hpp set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET}/include/battery/embed.hpp") get_filename_component(EMBED_HPP_DIR "${EMBED_HPP}" DIRECTORY) file(MAKE_DIRECTORY "${EMBED_HPP_DIR}") @@ -361,15 +361,15 @@ function(b_embed TARGET FILENAME) if (NOT EMBED_USED_IDENTIFIERS_INDEX EQUAL -1) message(FATAL_ERROR "embed: Identifier already in use: '${IDENTIFIER}'") endif() - # === Добавляем идентификатор и имя файла === + # === Add identifier and file name === set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "list of all identifiers used by the embed library" FORCE) set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library" FORCE) set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) - # === ГЕНЕРИРУЕМ embed.hpp СРАЗУ ДЛЯ ЭТОГО ТАРГЕТА === + # === Generate embed.hpp lazy === _embed_generate_hpp(${TARGET}) - # === Генерация .cpp файла для встраивания === + # === Generate .cpp file for embed === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") set(EMBED_CPP_TEMPLATE "${EMBED_BINARY_DIR}/embed_source_file_template.cpp") add_custom_command( @@ -385,7 +385,7 @@ function(b_embed TARGET FILENAME) VERBATIM ) - # === Добавляем файлы в таргет (теперь embed.hpp уже существует!) === + # === Adding files to the target (embed.hpp now already exists!) === target_include_directories(${TARGET} PUBLIC $ $ From 10b97748019f6c49271c7a7bf3404cd1eef0ab44 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 1 Aug 2025 18:28:03 +0300 Subject: [PATCH 16/25] Typo fixed --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4829979..e934811 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -49,7 +49,7 @@ set(EMBED_MASTER_SOURCE_FILE [==[ #include #include #include -#include +#include #endif // !B_PRODUCTION_MODE and !B_OS_WEB namespace b { From 194ecc57287cab29415700b16b51cd94f51707c1 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Sat, 2 Aug 2025 12:12:13 +0300 Subject: [PATCH 17/25] Changed on unsigned char storage to avoid C1091, fixed bug in get() with uninitialized m_size. --- CMakeLists.txt | 52 +++++++++++++++++++++++++++++++++----------------- 1 file changed, 34 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e934811..bc3305d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,9 +20,11 @@ set(EMBED_SOURCE_FILE_TEMPLATE [==[ #include "battery/embed.hpp" namespace b { - + const unsigned char ${IDENTIFIER}_data[] = { + ${GENERATED_BYTE_ARRAY} + }; EmbedInternal::EmbeddedFile EmbedInternal::${IDENTIFIER} = { - ${GENERATED_BYTE_ARRAY}, + ${IDENTIFIER}_data, ${FILESIZE} , "${FILENAME}" #ifndef B_PRODUCTION_MODE @@ -158,7 +160,8 @@ namespace b { auto fileresult = embed_read_file(m_fullFilepath); if (fileresult) { m_storage = std::move(*fileresult); - m_data = m_storage.c_str(); + m_data = reinterpret_cast(m_storage.c_str()); + m_size = m_storage.size(); } callback(*this); @@ -199,7 +202,7 @@ namespace b { EmbeddedFile() = default; EmbeddedFile( - const char* data, size_t size, + const unsigned char* data, size_t size, const char* filename #ifndef B_PRODUCTION_MODE , const char* fullFilepath @@ -214,12 +217,11 @@ namespace b { {} [[nodiscard]] std::string str() const { - return std::string(m_data, m_size); + return std::string(reinterpret_cast(m_data), m_size); } - [[nodiscard]] const char* data() const { return m_data; } + [[nodiscard]] const unsigned char* data() const { return m_data; } [[nodiscard]] std::vector vec() const { - return { reinterpret_cast(m_data), - reinterpret_cast(m_data) + m_size }; + return { m_data, m_data + m_size }; } [[nodiscard]] size_t size() const { return m_size; } [[nodiscard]] size_t length() const { return m_size; } @@ -230,7 +232,7 @@ namespace b { void get(const std::function& callback); private: - const char* m_data = nullptr; + const unsigned char* m_data = nullptr; size_t m_size = 0; const char* m_filename = nullptr; #ifndef B_PRODUCTION_MODE @@ -268,17 +270,31 @@ file(WRITE ${EMBED_BINARY_DIR}/embed_header_file_template.hpp "${EMBED_HEADER_FI # The cmake script to embed the files. This is called later on-demand, in a separate process set(EMBED_GENERATE_SCRIPT [=[ -file(READ "${FULL_PATH}" GENERATED_BYTE_ARRAY HEX) -string(LENGTH "${GENERATED_BYTE_ARRAY}" FILESIZE) -math(EXPR FILESIZE "${FILESIZE} / 2") - -string(REPEAT "[0-9a-f]" 32 PATTERN) -set(GENERATED_BYTE_ARRAY "\"${GENERATED_BYTE_ARRAY}") -string(REGEX REPLACE "${PATTERN}" "\\0\"\n \"" GENERATED_BYTE_ARRAY ${GENERATED_BYTE_ARRAY}) -string(REGEX REPLACE "([0-9a-f][0-9a-f])" "\\\\x\\1" GENERATED_BYTE_ARRAY ${GENERATED_BYTE_ARRAY}) -set(GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}\"") +file(READ "${FULL_PATH}" HEX_DATA HEX) +string(LENGTH "${HEX_DATA}" HEX_LENGTH) +math(EXPR BYTE_COUNT "${HEX_LENGTH} / 2") + +# Формируем массив 0x__, 0x__, ... +set(GENERATED_BYTE_ARRAY "") +foreach(BYTE_INDEX RANGE 0 ${BYTE_COUNT}) + math(EXPR START_OFFSET "${BYTE_INDEX} * 2") + if(START_OFFSET LESS HEX_LENGTH) + string(SUBSTRING "${HEX_DATA}" ${START_OFFSET} 2 BYTE) + if(GENERATED_BYTE_ARRAY) + set(GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}, 0x${BYTE}") + else() + set(GENERATED_BYTE_ARRAY "0x${BYTE}") + endif() + endif() +endforeach() + +# Размер файла в байтах +math(EXPR FILESIZE "${BYTE_COUNT}") + +# Теперь configure_file подставит GENERATED_BYTE_ARRAY как массив configure_file(${INFILE} ${OUTFILE}) ]=]) + file(WRITE ${EMBED_BINARY_DIR}/generate.cmake ${EMBED_GENERATE_SCRIPT}) set(EMBED_IDENTIFIERS "" CACHE INTERNAL "list of all identifiers used by battery::embed") From 01d049a1360b2657015cf8b1a51f0975502ff7a9 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Sat, 2 Aug 2025 12:24:07 +0300 Subject: [PATCH 18/25] Update CMakeLists.txt --- CMakeLists.txt | 127 +++++++++++++++++-------------------------------- 1 file changed, 44 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3305d..9d6b0b2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,14 +11,13 @@ option(B_EMBED_SILENCE_DEVMODE_WARNING "Disable the battery::embed devmode warni set(EMBED_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/embed CACHE INTERNAL "binary directory of the battery::embed library" FORCE) # The template for how to generate the .cpp file +# Теперь: массив 0x48, 0x65, ... вместо строки "\x48\x65..." set(EMBED_SOURCE_FILE_TEMPLATE [==[ // File generated using battery::embed (https://github.com/batterycenter/embed) // Embedded file: '${FILENAME}' as '${IDENTIFIER}' // Filesize: ${FILESIZE} bytes // DO NOT EDIT THIS FILE!!! - #include "battery/embed.hpp" - namespace b { const unsigned char ${IDENTIFIER}_data[] = { ${GENERATED_BYTE_ARRAY} @@ -31,7 +30,6 @@ namespace b { , "${FULL_PATH}" #endif // !B_PRODUCTION_MODE }; - } // namespace b ]==]) file(WRITE ${EMBED_BINARY_DIR}/embed_source_file_template.cpp "${EMBED_SOURCE_FILE_TEMPLATE}") @@ -40,9 +38,7 @@ file(WRITE ${EMBED_BINARY_DIR}/embed_source_file_template.cpp "${EMBED_SOURCE_FI set(EMBED_MASTER_SOURCE_FILE [==[ // File generated using battery::embed (https://github.com/batterycenter/embed) // DO NOT EDIT THIS FILE!!! - #include "battery/embed.hpp" - #if !defined(B_PRODUCTION_MODE) && !defined(B_OS_WEB) #include #include @@ -55,13 +51,7 @@ set(EMBED_MASTER_SOURCE_FILE [==[ #endif // !B_PRODUCTION_MODE and !B_OS_WEB namespace b { - #if !defined(B_PRODUCTION_MODE) && !defined(B_OS_WEB) - - // All this crazyness is necessary because we need to use std::string_view because of constexpr, - // and the string_view only points to a char array, so we need a string that outlives - // the string_view it is assigned to. - struct FileData { std::string filename; std::string newContent; @@ -69,64 +59,54 @@ namespace b { std::filesystem::file_time_type lastWriteTime; std::function callback; }; - static std::mutex embeddedFilesMapMutex; - static std::unordered_map embeddedFiles; - + static std::unordered_map embeddedFiles; + #ifdef _WIN32 #ifdef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif - static std::wstring embed_widen(const std::string& str) { std::wstring_convert> converter; return converter.from_bytes(str); } - #ifdef __clang__ #pragma GCC diagnostic pop #endif - #endif // _WIN32 static std::optional embed_read_file(const std::string_view& filename) { #ifdef _WIN32 std::ifstream file(embed_widen(std::string(filename)).c_str(), std::ios::binary); -#else // _WIN32 +#else std::ifstream file(std::string(filename), std::ios::binary); -#endif // _WIN32 +#endif if (file.fail()) { return {}; } return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); } - // Apple-Clang does not support std::jthread yet, hence we make our own version of jthread here: class AutoThread { public: template AutoThread(Tfunc&& function) : m_thread([&]() { function(m_shouldStop); }) {} - ~AutoThread() { m_shouldStop = true; m_thread.join(); } - AutoThread(const AutoThread& other) = delete; AutoThread(AutoThread&& other) = delete; AutoThread& operator=(const AutoThread& other) = delete; AutoThread& operator=(AutoThread&& other) = delete; - private: std::thread m_thread; std::atomic m_shouldStop = false; }; - // This is a global file watcher thread that is created at program start and automatically destroyed at program end - // All embedded files are hot-reloaded by this thread. AutoThread embeddedFileWatcherThread = AutoThread([](std::atomic& shouldStop) { while (!shouldStop) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); @@ -135,53 +115,51 @@ namespace b { std::filesystem::path filepath(key); std::error_code ec; auto newWriteTime = std::filesystem::last_write_time(filepath, ec); - if (!ec) { - if (newWriteTime != filedata.lastWriteTime) { - filedata.lastWriteTime = newWriteTime; - auto fileresult = embed_read_file(key); - if (!fileresult) { - continue; - } + if (!ec && newWriteTime != filedata.lastWriteTime) { + filedata.lastWriteTime = newWriteTime; + auto fileresult = embed_read_file(key); + if (fileresult) { filedata.newContent = *fileresult; - EmbedInternal::EmbeddedFile newFile(filedata.newContent.c_str(), filedata.newContent.size(), filedata.filename.c_str(), filedata.filepath.c_str()); + EmbedInternal::EmbeddedFile newFile( + reinterpret_cast(filedata.newContent.c_str()), + filedata.newContent.size(), + filedata.filename.c_str(), + filedata.filepath.c_str() + ); filedata.callback(newFile); } } } } }); - #endif // !B_PRODUCTION_MODE and !B_OS_WEB void EmbedInternal::EmbeddedFile::get(const std::function& callback) { #if defined(B_PRODUCTION_MODE) || defined(B_OS_WEB) callback(*this); -#else // B_PRODUCTION_MODE or B_OS_WEB +#else auto fileresult = embed_read_file(m_fullFilepath); if (fileresult) { m_storage = std::move(*fileresult); m_data = reinterpret_cast(m_storage.c_str()); - m_size = m_storage.size(); + m_size = m_storage.size(); // ✅ Обновляем размер! } callback(*this); - FileData fileData; fileData.filename = m_filename; - fileData.newContent = m_data; + fileData.newContent = m_storage; // ✅ Используем m_storage fileData.filepath = m_fullFilepath; fileData.callback = callback; fileData.lastWriteTime = std::filesystem::last_write_time(m_fullFilepath); - std::lock_guard lock(embeddedFilesMapMutex); embeddedFiles[std::string(m_fullFilepath)] = fileData; -#endif // B_PRODUCTION_MODE or B_OS_WEB +#endif } - } // namespace b ]==]) file(WRITE ${EMBED_BINARY_DIR}/embed_impl.cpp "${EMBED_MASTER_SOURCE_FILE}") -# The common battery::embed header and source files +# The common battery::embed header set(EMBED_HEADER_FILE [=[ // File generated by battery::embed // DO NOT EDIT THIS FILE!!! @@ -200,7 +178,6 @@ namespace b { class EmbeddedFile { public: EmbeddedFile() = default; - EmbeddedFile( const unsigned char* data, size_t size, const char* filename @@ -219,10 +196,13 @@ namespace b { [[nodiscard]] std::string str() const { return std::string(reinterpret_cast(m_data), m_size); } + [[nodiscard]] const unsigned char* data() const { return m_data; } + [[nodiscard]] std::vector vec() const { return { m_data, m_data + m_size }; } + [[nodiscard]] size_t size() const { return m_size; } [[nodiscard]] size_t length() const { return m_size; } @@ -243,20 +223,18 @@ namespace b { ${EMBEDDED_FILES_DECLARATIONS} }; - + template EmbedInternal::EmbeddedFile embed(const S& identifier) { ${EMBEDDED_FILES_RETURNS} throw std::runtime_error("[b::embed<>] No such file or directory"); } - // Функция для получения списка всех встроенных файлов [[nodiscard]] inline std::vector embed_list() { return { ${EMBEDDED_FILES_LIST} }; } - } // namespace b inline std::ostream& operator<<(std::ostream& stream, const b::EmbedInternal::EmbeddedFile& file) { @@ -268,13 +246,12 @@ inline std::ostream& operator<<(std::ostream& stream, const b::EmbedInternal::Em ]=]) file(WRITE ${EMBED_BINARY_DIR}/embed_header_file_template.hpp "${EMBED_HEADER_FILE}") -# The cmake script to embed the files. This is called later on-demand, in a separate process +# The script to generate the byte array as unsigned char[] set(EMBED_GENERATE_SCRIPT [=[ file(READ "${FULL_PATH}" HEX_DATA HEX) string(LENGTH "${HEX_DATA}" HEX_LENGTH) math(EXPR BYTE_COUNT "${HEX_LENGTH} / 2") -# Формируем массив 0x__, 0x__, ... set(GENERATED_BYTE_ARRAY "") foreach(BYTE_INDEX RANGE 0 ${BYTE_COUNT}) math(EXPR START_OFFSET "${BYTE_INDEX} * 2") @@ -288,20 +265,18 @@ foreach(BYTE_INDEX RANGE 0 ${BYTE_COUNT}) endif() endforeach() -# Размер файла в байтах math(EXPR FILESIZE "${BYTE_COUNT}") -# Теперь configure_file подставит GENERATED_BYTE_ARRAY как массив configure_file(${INFILE} ${OUTFILE}) ]=]) +file(WRITE ${EMBED_BINARY_DIR}/generate.cmake "${EMBED_GENERATE_SCRIPT}") -file(WRITE ${EMBED_BINARY_DIR}/generate.cmake ${EMBED_GENERATE_SCRIPT}) - +# Cache variables set(EMBED_IDENTIFIERS "" CACHE INTERNAL "list of all identifiers used by battery::embed") set(EMBED_FILENAMES "" CACHE INTERNAL "list of all filenames used by battery::embed") set(EMBED_TARGETS "" CACHE INTERNAL "list of all targets used by battery::embed") -# Defer the function call until the end of the configure step +# Defer header generation cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _embed_generate_all_hpps()) function(_embed_generate_all_hpps) message(STATUS "Generating b::embed() HPP files ...") @@ -309,7 +284,7 @@ function(_embed_generate_all_hpps) _embed_generate_hpp(${TARGET}) endforeach() message(STATUS "Generating b::embed() HPP files ... Done") -endfunction(_embed_generate_all_hpps) +endfunction() function(_embed_generate_hpp TARGET) string(TOLOWER "${TARGET}" TARGET) @@ -322,7 +297,6 @@ function(_embed_generate_hpp TARGET) set(EMBEDDED_FILES_RETURNS "") set(EMBEDDED_FILES_LIST "") - # Iterate over indices list(LENGTH EMBED_IDENTIFIERS num_identifiers) math(EXPR last_index "${num_identifiers} - 1") foreach(INDEX RANGE 0 ${last_index}) @@ -330,62 +304,52 @@ function(_embed_generate_hpp TARGET) list(GET EMBED_FILENAMES ${INDEX} FILENAME) if (IDENTIFIER MATCHES "^${TARGET}_") set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") - set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") - set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") endif() endforeach() - # Remove the last comma string(REGEX REPLACE ",\n$" "" EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST}") - # Generate the header file file(READ ${EMBED_BINARY_DIR}/embed_header_file_template.hpp EMBED_HEADER_FILE) string(CONFIGURE "${EMBED_HEADER_FILE}" EMBED_HEADER_FILE_GENERATED) - - # Path to embed.hpp set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET}/include/battery/embed.hpp") get_filename_component(EMBED_HPP_DIR "${EMBED_HPP}" DIRECTORY) file(MAKE_DIRECTORY "${EMBED_HPP_DIR}") file(WRITE "${EMBED_HPP}" "${EMBED_HEADER_FILE_GENERATED}") endfunction() -# Internal function for validating an identifier -function(embed_validate_identifier IDENTIFIER) # Validate the identifier against C variable naming rules +function(embed_validate_identifier IDENTIFIER) if (NOT IDENTIFIER MATCHES "^[a-zA-Z_][a-zA-Z0-9_]*$") message(FATAL_ERROR "embed: Identifier contains invalid characters: '${IDENTIFIER}'") endif() endfunction() -# The main function for embedding files function(b_embed TARGET FILENAME) string(TOLOWER "${TARGET}" TARGET_ID) string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" TARGET_ID "${TARGET_ID}") if (IS_ABSOLUTE "${FILENAME}") message(FATAL_ERROR "embed: File name must be relative to the current source directory: '${FILENAME}'") endif() - # Make the identifier + string(TOLOWER "${TARGET_ID}_${FILENAME}" IDENTIFIER) - string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" IDENTIFIER "${IDENTIFIER}") # Replace all invalid characters with underscores - embed_validate_identifier("${IDENTIFIER}") # Validate the identifier against C variable naming rules - # Set up paths - get_filename_component(FULL_PATH "${FILENAME}" ABSOLUTE) # Make the file path absolute + string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" IDENTIFIER "${IDENTIFIER}") + embed_validate_identifier("${IDENTIFIER}") + + get_filename_component(FULL_PATH "${FILENAME}" ABSOLUTE) set(CPP_FILE "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/src/${IDENTIFIER}.cpp") - # If identifier already in use + list(FIND EMBED_IDENTIFIERS ${IDENTIFIER} EMBED_USED_IDENTIFIERS_INDEX) if (NOT EMBED_USED_IDENTIFIERS_INDEX EQUAL -1) message(FATAL_ERROR "embed: Identifier already in use: '${IDENTIFIER}'") endif() - # === Add identifier and file name === - set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "list of all identifiers used by the embed library" FORCE) - set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library" FORCE) - set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) - - # === Generate embed.hpp lazy === + + set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "" FORCE) + set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "" FORCE) + set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "" FORCE) + _embed_generate_hpp(${TARGET}) - - # === Generate .cpp file for embed === + set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") set(EMBED_CPP_TEMPLATE "${EMBED_BINARY_DIR}/embed_source_file_template.cpp") add_custom_command( @@ -401,7 +365,6 @@ function(b_embed TARGET FILENAME) VERBATIM ) - # === Adding files to the target (embed.hpp now already exists!) === target_include_directories(${TARGET} PUBLIC $ $ @@ -432,17 +395,15 @@ function(b_embed_proxy_target MAIN_TARGET PROXY_TARGET) set_target_properties(${PROXY_TARGET} PROPERTIES FOLDER "embed-proxy-targets") endfunction() -# Build all examples if (B_EMBED_BUILD_EXAMPLES) - add_subdirectory(examples) # Set Example 'simple' as the default project in Visual Studio + add_subdirectory(examples) set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT simple) endif() -# Set the predefined targets folder for Visual Studio if (PROJECT_IS_TOP_LEVEL) set(PREDEFINED_TARGETS_FOLDER "CMakePredefinedTargets") endif() if (NOT B_EMBED_SILENCE_DEVMODE_WARNING AND NOT B_PRODUCTION_MODE) message(WARNING "Battery::Embed is in development mode, enabling hotreload. Before deploying your application, use -DB_PRODUCTION_MODE=ON to strip all development features such as hotreload and absolute filepaths.") -endif () +endif() From c7e93e5d8160fb5f026ce000006f630be71f2bb7 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Sat, 2 Aug 2025 12:32:46 +0300 Subject: [PATCH 19/25] revert --- CMakeLists.txt | 127 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 44 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9d6b0b2..bc3305d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,13 +11,14 @@ option(B_EMBED_SILENCE_DEVMODE_WARNING "Disable the battery::embed devmode warni set(EMBED_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/embed CACHE INTERNAL "binary directory of the battery::embed library" FORCE) # The template for how to generate the .cpp file -# Теперь: массив 0x48, 0x65, ... вместо строки "\x48\x65..." set(EMBED_SOURCE_FILE_TEMPLATE [==[ // File generated using battery::embed (https://github.com/batterycenter/embed) // Embedded file: '${FILENAME}' as '${IDENTIFIER}' // Filesize: ${FILESIZE} bytes // DO NOT EDIT THIS FILE!!! + #include "battery/embed.hpp" + namespace b { const unsigned char ${IDENTIFIER}_data[] = { ${GENERATED_BYTE_ARRAY} @@ -30,6 +31,7 @@ namespace b { , "${FULL_PATH}" #endif // !B_PRODUCTION_MODE }; + } // namespace b ]==]) file(WRITE ${EMBED_BINARY_DIR}/embed_source_file_template.cpp "${EMBED_SOURCE_FILE_TEMPLATE}") @@ -38,7 +40,9 @@ file(WRITE ${EMBED_BINARY_DIR}/embed_source_file_template.cpp "${EMBED_SOURCE_FI set(EMBED_MASTER_SOURCE_FILE [==[ // File generated using battery::embed (https://github.com/batterycenter/embed) // DO NOT EDIT THIS FILE!!! + #include "battery/embed.hpp" + #if !defined(B_PRODUCTION_MODE) && !defined(B_OS_WEB) #include #include @@ -51,7 +55,13 @@ set(EMBED_MASTER_SOURCE_FILE [==[ #endif // !B_PRODUCTION_MODE and !B_OS_WEB namespace b { + #if !defined(B_PRODUCTION_MODE) && !defined(B_OS_WEB) + + // All this crazyness is necessary because we need to use std::string_view because of constexpr, + // and the string_view only points to a char array, so we need a string that outlives + // the string_view it is assigned to. + struct FileData { std::string filename; std::string newContent; @@ -59,54 +69,64 @@ namespace b { std::filesystem::file_time_type lastWriteTime; std::function callback; }; - static std::mutex embeddedFilesMapMutex; - static std::unordered_map embeddedFiles; + static std::mutex embeddedFilesMapMutex; + static std::unordered_map embeddedFiles; + #ifdef _WIN32 #ifdef __clang__ #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdeprecated-declarations" #endif + static std::wstring embed_widen(const std::string& str) { std::wstring_convert> converter; return converter.from_bytes(str); } + #ifdef __clang__ #pragma GCC diagnostic pop #endif + #endif // _WIN32 static std::optional embed_read_file(const std::string_view& filename) { #ifdef _WIN32 std::ifstream file(embed_widen(std::string(filename)).c_str(), std::ios::binary); -#else +#else // _WIN32 std::ifstream file(std::string(filename), std::ios::binary); -#endif +#endif // _WIN32 if (file.fail()) { return {}; } return std::string((std::istreambuf_iterator(file)), std::istreambuf_iterator()); } + // Apple-Clang does not support std::jthread yet, hence we make our own version of jthread here: class AutoThread { public: template AutoThread(Tfunc&& function) : m_thread([&]() { function(m_shouldStop); }) {} + ~AutoThread() { m_shouldStop = true; m_thread.join(); } + AutoThread(const AutoThread& other) = delete; AutoThread(AutoThread&& other) = delete; AutoThread& operator=(const AutoThread& other) = delete; AutoThread& operator=(AutoThread&& other) = delete; + private: std::thread m_thread; std::atomic m_shouldStop = false; }; + // This is a global file watcher thread that is created at program start and automatically destroyed at program end + // All embedded files are hot-reloaded by this thread. AutoThread embeddedFileWatcherThread = AutoThread([](std::atomic& shouldStop) { while (!shouldStop) { std::this_thread::sleep_for(std::chrono::milliseconds(100)); @@ -115,51 +135,53 @@ namespace b { std::filesystem::path filepath(key); std::error_code ec; auto newWriteTime = std::filesystem::last_write_time(filepath, ec); - if (!ec && newWriteTime != filedata.lastWriteTime) { - filedata.lastWriteTime = newWriteTime; - auto fileresult = embed_read_file(key); - if (fileresult) { + if (!ec) { + if (newWriteTime != filedata.lastWriteTime) { + filedata.lastWriteTime = newWriteTime; + auto fileresult = embed_read_file(key); + if (!fileresult) { + continue; + } filedata.newContent = *fileresult; - EmbedInternal::EmbeddedFile newFile( - reinterpret_cast(filedata.newContent.c_str()), - filedata.newContent.size(), - filedata.filename.c_str(), - filedata.filepath.c_str() - ); + EmbedInternal::EmbeddedFile newFile(filedata.newContent.c_str(), filedata.newContent.size(), filedata.filename.c_str(), filedata.filepath.c_str()); filedata.callback(newFile); } } } } }); + #endif // !B_PRODUCTION_MODE and !B_OS_WEB void EmbedInternal::EmbeddedFile::get(const std::function& callback) { #if defined(B_PRODUCTION_MODE) || defined(B_OS_WEB) callback(*this); -#else +#else // B_PRODUCTION_MODE or B_OS_WEB auto fileresult = embed_read_file(m_fullFilepath); if (fileresult) { m_storage = std::move(*fileresult); m_data = reinterpret_cast(m_storage.c_str()); - m_size = m_storage.size(); // ✅ Обновляем размер! + m_size = m_storage.size(); } callback(*this); + FileData fileData; fileData.filename = m_filename; - fileData.newContent = m_storage; // ✅ Используем m_storage + fileData.newContent = m_data; fileData.filepath = m_fullFilepath; fileData.callback = callback; fileData.lastWriteTime = std::filesystem::last_write_time(m_fullFilepath); + std::lock_guard lock(embeddedFilesMapMutex); embeddedFiles[std::string(m_fullFilepath)] = fileData; -#endif +#endif // B_PRODUCTION_MODE or B_OS_WEB } + } // namespace b ]==]) file(WRITE ${EMBED_BINARY_DIR}/embed_impl.cpp "${EMBED_MASTER_SOURCE_FILE}") -# The common battery::embed header +# The common battery::embed header and source files set(EMBED_HEADER_FILE [=[ // File generated by battery::embed // DO NOT EDIT THIS FILE!!! @@ -178,6 +200,7 @@ namespace b { class EmbeddedFile { public: EmbeddedFile() = default; + EmbeddedFile( const unsigned char* data, size_t size, const char* filename @@ -196,13 +219,10 @@ namespace b { [[nodiscard]] std::string str() const { return std::string(reinterpret_cast(m_data), m_size); } - [[nodiscard]] const unsigned char* data() const { return m_data; } - [[nodiscard]] std::vector vec() const { return { m_data, m_data + m_size }; } - [[nodiscard]] size_t size() const { return m_size; } [[nodiscard]] size_t length() const { return m_size; } @@ -223,18 +243,20 @@ namespace b { ${EMBEDDED_FILES_DECLARATIONS} }; - + template EmbedInternal::EmbeddedFile embed(const S& identifier) { ${EMBEDDED_FILES_RETURNS} throw std::runtime_error("[b::embed<>] No such file or directory"); } + // Функция для получения списка всех встроенных файлов [[nodiscard]] inline std::vector embed_list() { return { ${EMBEDDED_FILES_LIST} }; } + } // namespace b inline std::ostream& operator<<(std::ostream& stream, const b::EmbedInternal::EmbeddedFile& file) { @@ -246,12 +268,13 @@ inline std::ostream& operator<<(std::ostream& stream, const b::EmbedInternal::Em ]=]) file(WRITE ${EMBED_BINARY_DIR}/embed_header_file_template.hpp "${EMBED_HEADER_FILE}") -# The script to generate the byte array as unsigned char[] +# The cmake script to embed the files. This is called later on-demand, in a separate process set(EMBED_GENERATE_SCRIPT [=[ file(READ "${FULL_PATH}" HEX_DATA HEX) string(LENGTH "${HEX_DATA}" HEX_LENGTH) math(EXPR BYTE_COUNT "${HEX_LENGTH} / 2") +# Формируем массив 0x__, 0x__, ... set(GENERATED_BYTE_ARRAY "") foreach(BYTE_INDEX RANGE 0 ${BYTE_COUNT}) math(EXPR START_OFFSET "${BYTE_INDEX} * 2") @@ -265,18 +288,20 @@ foreach(BYTE_INDEX RANGE 0 ${BYTE_COUNT}) endif() endforeach() +# Размер файла в байтах math(EXPR FILESIZE "${BYTE_COUNT}") +# Теперь configure_file подставит GENERATED_BYTE_ARRAY как массив configure_file(${INFILE} ${OUTFILE}) ]=]) -file(WRITE ${EMBED_BINARY_DIR}/generate.cmake "${EMBED_GENERATE_SCRIPT}") -# Cache variables +file(WRITE ${EMBED_BINARY_DIR}/generate.cmake ${EMBED_GENERATE_SCRIPT}) + set(EMBED_IDENTIFIERS "" CACHE INTERNAL "list of all identifiers used by battery::embed") set(EMBED_FILENAMES "" CACHE INTERNAL "list of all filenames used by battery::embed") set(EMBED_TARGETS "" CACHE INTERNAL "list of all targets used by battery::embed") -# Defer header generation +# Defer the function call until the end of the configure step cmake_language(DEFER DIRECTORY ${CMAKE_SOURCE_DIR} CALL _embed_generate_all_hpps()) function(_embed_generate_all_hpps) message(STATUS "Generating b::embed() HPP files ...") @@ -284,7 +309,7 @@ function(_embed_generate_all_hpps) _embed_generate_hpp(${TARGET}) endforeach() message(STATUS "Generating b::embed() HPP files ... Done") -endfunction() +endfunction(_embed_generate_all_hpps) function(_embed_generate_hpp TARGET) string(TOLOWER "${TARGET}" TARGET) @@ -297,6 +322,7 @@ function(_embed_generate_hpp TARGET) set(EMBEDDED_FILES_RETURNS "") set(EMBEDDED_FILES_LIST "") + # Iterate over indices list(LENGTH EMBED_IDENTIFIERS num_identifiers) math(EXPR last_index "${num_identifiers} - 1") foreach(INDEX RANGE 0 ${last_index}) @@ -304,52 +330,62 @@ function(_embed_generate_hpp TARGET) list(GET EMBED_FILENAMES ${INDEX} FILENAME) if (IDENTIFIER MATCHES "^${TARGET}_") set(EMBEDDED_FILES_DECLARATIONS "${EMBEDDED_FILES_DECLARATIONS}static EmbeddedFile ${IDENTIFIER};\n") + set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") + set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") endif() endforeach() + # Remove the last comma string(REGEX REPLACE ",\n$" "" EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST}") + # Generate the header file file(READ ${EMBED_BINARY_DIR}/embed_header_file_template.hpp EMBED_HEADER_FILE) string(CONFIGURE "${EMBED_HEADER_FILE}" EMBED_HEADER_FILE_GENERATED) + + # Path to embed.hpp set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET}/include/battery/embed.hpp") get_filename_component(EMBED_HPP_DIR "${EMBED_HPP}" DIRECTORY) file(MAKE_DIRECTORY "${EMBED_HPP_DIR}") file(WRITE "${EMBED_HPP}" "${EMBED_HEADER_FILE_GENERATED}") endfunction() -function(embed_validate_identifier IDENTIFIER) +# Internal function for validating an identifier +function(embed_validate_identifier IDENTIFIER) # Validate the identifier against C variable naming rules if (NOT IDENTIFIER MATCHES "^[a-zA-Z_][a-zA-Z0-9_]*$") message(FATAL_ERROR "embed: Identifier contains invalid characters: '${IDENTIFIER}'") endif() endfunction() +# The main function for embedding files function(b_embed TARGET FILENAME) string(TOLOWER "${TARGET}" TARGET_ID) string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" TARGET_ID "${TARGET_ID}") if (IS_ABSOLUTE "${FILENAME}") message(FATAL_ERROR "embed: File name must be relative to the current source directory: '${FILENAME}'") endif() - + # Make the identifier string(TOLOWER "${TARGET_ID}_${FILENAME}" IDENTIFIER) - string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" IDENTIFIER "${IDENTIFIER}") - embed_validate_identifier("${IDENTIFIER}") - - get_filename_component(FULL_PATH "${FILENAME}" ABSOLUTE) + string(REGEX REPLACE "[^a-zA-Z0-9_]" "_" IDENTIFIER "${IDENTIFIER}") # Replace all invalid characters with underscores + embed_validate_identifier("${IDENTIFIER}") # Validate the identifier against C variable naming rules + # Set up paths + get_filename_component(FULL_PATH "${FILENAME}" ABSOLUTE) # Make the file path absolute set(CPP_FILE "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/src/${IDENTIFIER}.cpp") - + # If identifier already in use list(FIND EMBED_IDENTIFIERS ${IDENTIFIER} EMBED_USED_IDENTIFIERS_INDEX) if (NOT EMBED_USED_IDENTIFIERS_INDEX EQUAL -1) message(FATAL_ERROR "embed: Identifier already in use: '${IDENTIFIER}'") endif() - - set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "" FORCE) - set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "" FORCE) - set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "" FORCE) - + # === Add identifier and file name === + set(EMBED_IDENTIFIERS ${EMBED_IDENTIFIERS} ${IDENTIFIER} CACHE INTERNAL "list of all identifiers used by the embed library" FORCE) + set(EMBED_FILENAMES ${EMBED_FILENAMES} ${FILENAME} CACHE INTERNAL "list of all filenames used by the embed library" FORCE) + set(EMBED_TARGETS ${EMBED_TARGETS} ${TARGET} CACHE INTERNAL "list of all targets used by the embed library" FORCE) + + # === Generate embed.hpp lazy === _embed_generate_hpp(${TARGET}) - + + # === Generate .cpp file for embed === set(EMBED_HPP "${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery/embed.hpp") set(EMBED_CPP_TEMPLATE "${EMBED_BINARY_DIR}/embed_source_file_template.cpp") add_custom_command( @@ -365,6 +401,7 @@ function(b_embed TARGET FILENAME) VERBATIM ) + # === Adding files to the target (embed.hpp now already exists!) === target_include_directories(${TARGET} PUBLIC $ $ @@ -395,15 +432,17 @@ function(b_embed_proxy_target MAIN_TARGET PROXY_TARGET) set_target_properties(${PROXY_TARGET} PROPERTIES FOLDER "embed-proxy-targets") endfunction() +# Build all examples if (B_EMBED_BUILD_EXAMPLES) - add_subdirectory(examples) + add_subdirectory(examples) # Set Example 'simple' as the default project in Visual Studio set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT simple) endif() +# Set the predefined targets folder for Visual Studio if (PROJECT_IS_TOP_LEVEL) set(PREDEFINED_TARGETS_FOLDER "CMakePredefinedTargets") endif() if (NOT B_EMBED_SILENCE_DEVMODE_WARNING AND NOT B_PRODUCTION_MODE) message(WARNING "Battery::Embed is in development mode, enabling hotreload. Before deploying your application, use -DB_PRODUCTION_MODE=ON to strip all development features such as hotreload and absolute filepaths.") -endif() +endif () From 9c003a5c2cf4129dc3fbde3306855aa5574a6db8 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Sat, 2 Aug 2025 12:42:36 +0300 Subject: [PATCH 20/25] Fixed generate script --- CMakeLists.txt | 23 ++++++++--------------- 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bc3305d..d240ef2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,24 +274,17 @@ file(READ "${FULL_PATH}" HEX_DATA HEX) string(LENGTH "${HEX_DATA}" HEX_LENGTH) math(EXPR BYTE_COUNT "${HEX_LENGTH} / 2") -# Формируем массив 0x__, 0x__, ... -set(GENERATED_BYTE_ARRAY "") -foreach(BYTE_INDEX RANGE 0 ${BYTE_COUNT}) - math(EXPR START_OFFSET "${BYTE_INDEX} * 2") - if(START_OFFSET LESS HEX_LENGTH) - string(SUBSTRING "${HEX_DATA}" ${START_OFFSET} 2 BYTE) - if(GENERATED_BYTE_ARRAY) - set(GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}, 0x${BYTE}") - else() - set(GENERATED_BYTE_ARRAY "0x${BYTE}") - endif() - endif() -endforeach() +# Преобразуем hex-строку в массив 0x__, 0x__, ... +set(GENERATED_BYTE_ARRAY "${HEX_DATA}") +string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1" GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}") +string(REGEX REPLACE "(0x[0-9a-f][0-9a-f])(?=0x)" "\\1, " GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}") + +# Убедимся, что нет лишних пробелов +string(STRIP "${GENERATED_BYTE_ARRAY}" GENERATED_BYTE_ARRAY) -# Размер файла в байтах +# Размер math(EXPR FILESIZE "${BYTE_COUNT}") -# Теперь configure_file подставит GENERATED_BYTE_ARRAY как массив configure_file(${INFILE} ${OUTFILE}) ]=]) From fcb57c6fce62f3a2e852cfdebd16453b4891a697 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Sat, 2 Aug 2025 12:46:43 +0300 Subject: [PATCH 21/25] Fixed regexp in generate script --- CMakeLists.txt | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d240ef2..5d73ede 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -274,15 +274,19 @@ file(READ "${FULL_PATH}" HEX_DATA HEX) string(LENGTH "${HEX_DATA}" HEX_LENGTH) math(EXPR BYTE_COUNT "${HEX_LENGTH} / 2") -# Преобразуем hex-строку в массив 0x__, 0x__, ... +# Преобразуем hex-строку: 48656c6c6f → 0x48, 0x65, 0x6c, 0x6c, 0x6f set(GENERATED_BYTE_ARRAY "${HEX_DATA}") + +# Шаг 1: добавляем 0x перед каждой парой string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1" GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}") -string(REGEX REPLACE "(0x[0-9a-f][0-9a-f])(?=0x)" "\\1, " GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}") -# Убедимся, что нет лишних пробелов -string(STRIP "${GENERATED_BYTE_ARRAY}" GENERATED_BYTE_ARRAY) +# Шаг 2: добавляем запятую и пробел после каждого байта +string(REGEX REPLACE "(0x[0-9a-f][0-9a-f])" "\\1, " GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}") + +# Шаг 3: удаляем лишнюю запятую и пробел в конце +string(REGEX REPLACE ", $" "" GENERATED_BYTE_ARRAY "${GENERATED_BYTE_ARRAY}") -# Размер +# Устанавливаем размер math(EXPR FILESIZE "${BYTE_COUNT}") configure_file(${INFILE} ${OUTFILE}) From 484c161588ddde2ebfcb1b419e885161582adebc Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Thu, 28 Aug 2025 14:21:56 +0300 Subject: [PATCH 22/25] Update CMakeLists.txt --- CMakeLists.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d73ede..94512be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -414,11 +414,26 @@ function(b_embed TARGET FILENAME) endif () if (MSVC) + # target_sources(${TARGET} PRIVATE ${FULL_PATH}) + # source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/src PREFIX "embed/autogen" FILES ${CPP_FILE}) + # source_group(TREE ${EMBED_BINARY_DIR} PREFIX "embed/autogen" FILES ${EMBED_BINARY_DIR}/embed_impl.cpp) + # source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery PREFIX "embed/autogen" FILES ${EMBED_HPP}) + # source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "/embed" FILES ${FULL_PATH}) + target_sources(${TARGET} PRIVATE ${FULL_PATH}) + set_source_files_properties(${FULL_PATH} PROPERTIES HEADER_FILE_ONLY TRUE) + source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/src PREFIX "embed/autogen" FILES ${CPP_FILE}) source_group(TREE ${EMBED_BINARY_DIR} PREFIX "embed/autogen" FILES ${EMBED_BINARY_DIR}/embed_impl.cpp) source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery PREFIX "embed/autogen" FILES ${EMBED_HPP}) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "/embed" FILES ${FULL_PATH}) + + cmake_path(IS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}" "${FULL_PATH}" NORMALIZE IS_SUBPATH) + if (IS_SUBPATH) + source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "embed" FILES ${FULL_PATH}) + else() + source_group("embed" FILES ${FULL_PATH}) + endif() + endif() endfunction() From df436613cc89b95355c17c205f7b75e68a97ff51 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Thu, 28 Aug 2025 14:31:58 +0300 Subject: [PATCH 23/25] Update CMakeLists.txt --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 94512be..9a4a5e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -426,10 +426,11 @@ function(b_embed TARGET FILENAME) source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/src PREFIX "embed/autogen" FILES ${CPP_FILE}) source_group(TREE ${EMBED_BINARY_DIR} PREFIX "embed/autogen" FILES ${EMBED_BINARY_DIR}/embed_impl.cpp) source_group(TREE ${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include/battery PREFIX "embed/autogen" FILES ${EMBED_HPP}) - - cmake_path(IS_PREFIX "${CMAKE_CURRENT_SOURCE_DIR}" "${FULL_PATH}" NORMALIZE IS_SUBPATH) - if (IS_SUBPATH) - source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" PREFIX "embed" FILES ${FULL_PATH}) + + set(_embed_src_root "${CMAKE_CURRENT_SOURCE_DIR}") + cmake_path(IS_PREFIX _embed_src_root "${FULL_PATH}" NORMALIZE _is_subpath) + if (_is_subpath) + source_group(TREE "${_embed_src_root}" PREFIX "embed" FILES ${FULL_PATH}) else() source_group("embed" FILES ${FULL_PATH}) endif() From 81a33fbf6a2732b045490bb8f1787086c58eabbd Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Tue, 9 Dec 2025 03:04:00 +0300 Subject: [PATCH 24/25] Update CMakeLists.txt Fixed case for const strings. --- CMakeLists.txt | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9a4a5e2..adafc69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,6 +250,11 @@ namespace b { throw std::runtime_error("[b::embed<>] No such file or directory"); } + EmbedInternal::EmbeddedFile embed(const char* identifier) { + ${EMBEDDED_FILES_RET_CHARP} + throw std::runtime_error("[b::embed<>] No such file or directory"); + } + // Функция для получения списка всех встроенных файлов [[nodiscard]] inline std::vector embed_list() { return { @@ -317,6 +322,7 @@ function(_embed_generate_hpp TARGET) set(EMBEDDED_FILES_DECLARATIONS "") set(EMBEDDED_FILES_RETURNS "") + set(EMBEDDED_FILES_RET_CHARP "") set(EMBEDDED_FILES_LIST "") # Iterate over indices @@ -330,6 +336,8 @@ function(_embed_generate_hpp TARGET) set(EMBEDDED_FILES_RETURNS "${EMBEDDED_FILES_RETURNS}if (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\nelse ") + set(EMBEDDED_FILES_RET_CHARP "${EMBEDDED_FILES_RET_CHARP}if (identifier == std::string(\"${FILENAME}\")) { return EmbedInternal::${IDENTIFIER}; }\nelse ") + set(EMBEDDED_FILES_LIST "${EMBEDDED_FILES_LIST} \"${FILENAME}\",\n") endif() endforeach() From 1a7b9c39ece21441f559258cad7760c07f0d8901 Mon Sep 17 00:00:00 2001 From: "Maxim L. Grishin" Date: Fri, 12 Dec 2025 01:41:43 +0300 Subject: [PATCH 25/25] Update CMakeLists.txt, fixed compilation of hpp --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index adafc69..1989e3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -250,7 +250,7 @@ namespace b { throw std::runtime_error("[b::embed<>] No such file or directory"); } - EmbedInternal::EmbeddedFile embed(const char* identifier) { + inline EmbedInternal::EmbeddedFile embed(const char* identifier) { ${EMBEDDED_FILES_RET_CHARP} throw std::runtime_error("[b::embed<>] No such file or directory"); }