Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5a949bd
Update CMakeLists.txt
MaxAkaAltmer Aug 1, 2025
d524009
Back to C++17
MaxAkaAltmer Aug 1, 2025
a8ab06e
Update CMakeLists.txt buld fixing
MaxAkaAltmer Aug 1, 2025
bc0aa85
Update CMakeLists.txt
MaxAkaAltmer Aug 1, 2025
0c93923
Update CMakeLists.txt
MaxAkaAltmer Aug 1, 2025
a5804b1
Update CMakeLists.txt
MaxAkaAltmer Aug 1, 2025
39952da
Manual list call
MaxAkaAltmer Aug 1, 2025
d09bc8a
Fixed index bug
MaxAkaAltmer Aug 1, 2025
4d8b3a2
fixed compile errors
MaxAkaAltmer Aug 1, 2025
bb1342e
Fixed for arbitrary string type
MaxAkaAltmer Aug 1, 2025
aa062ac
Fixed demand constexpr fetish
MaxAkaAltmer Aug 1, 2025
6489a85
Update README.md
MaxAkaAltmer Aug 1, 2025
e3a4e1a
Update README.md
MaxAkaAltmer Aug 1, 2025
c759e03
Update README.md
MaxAkaAltmer Aug 1, 2025
3185ea6
Fixed bugs in B_PRODUCTION_MODE
MaxAkaAltmer Aug 1, 2025
10b9774
Typo fixed
MaxAkaAltmer Aug 1, 2025
194ecc5
Changed on unsigned char storage to avoid C1091, fixed bug in get() w…
MaxAkaAltmer Aug 2, 2025
01d049a
Update CMakeLists.txt
MaxAkaAltmer Aug 2, 2025
c7e93e5
revert
MaxAkaAltmer Aug 2, 2025
9c003a5
Fixed generate script
MaxAkaAltmer Aug 2, 2025
fcb57c6
Fixed regexp in generate script
MaxAkaAltmer Aug 2, 2025
484c161
Update CMakeLists.txt
MaxAkaAltmer Aug 28, 2025
df43661
Update CMakeLists.txt
MaxAkaAltmer Aug 28, 2025
81a33fb
Update CMakeLists.txt
MaxAkaAltmer Dec 9, 2025
1a7b9c3
Update CMakeLists.txt, fixed compilation of hpp
MaxAkaAltmer Dec 11, 2025
5e91dbd
Update CMakeLists.txt - added temination zero to support direct using…
MaxAkaAltmer Apr 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
244 changes: 128 additions & 116 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,20 +12,20 @@ set(EMBED_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/embed CACHE INTERNAL "binary di

# The template for how to generate the .cpp file
set(EMBED_SOURCE_FILE_TEMPLATE [==[
// File generated using battery::embed (https://github.com/batterycenter/embed)
// File generated using fork of battery::embed (https://github.com/MaxAkaAltmer/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}
, 0 };
EmbedInternal::EmbeddedFile EmbedInternal::${IDENTIFIER} = {
std::string_view(
${GENERATED_BYTE_ARRAY},
${IDENTIFIER}_data,
${FILESIZE}
)
, "${FILENAME}"
#ifndef B_PRODUCTION_MODE
, "${FULL_PATH}"
Expand All @@ -51,6 +51,7 @@ set(EMBED_MASTER_SOURCE_FILE [==[
#include <mutex>
#include <unordered_map>
#include <optional>
#include <atomic>
#endif // !B_PRODUCTION_MODE and !B_OS_WEB

namespace b {
Expand Down Expand Up @@ -142,7 +143,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);
}
}
Expand All @@ -158,7 +159,9 @@ 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 = reinterpret_cast<const unsigned char*>(m_storage.c_str());
m_size = m_storage.size();
}
callback(*this);

Expand All @@ -184,110 +187,79 @@ set(EMBED_HEADER_FILE [=[
// DO NOT EDIT THIS FILE!!!
#ifndef BATTERY_EMBED_HPP
#define BATTERY_EMBED_HPP

#include <vector>
#include <string>
#include <string_view>
#include <stdexcept>
#include <sstream>
#include <functional>
#include <cstdint>
#include <algorithm>

#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;
EmbeddedFile() = default;

constexpr EmbeddedFile(
const std::string_view& data,
const std::string_view& filename
EmbeddedFile(
const unsigned 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(reinterpret_cast<const char*>(m_data), m_size);
}

[[nodiscard]] const unsigned char* data() const { return m_data; }
[[nodiscard]] std::vector<uint8_t> vec() const {
return { m_data.begin(), m_data.end() };
}

[[nodiscard]] size_t length() const {
return m_data.size();
return { m_data, m_data + m_size };
}
[[nodiscard]] size_t size() const { return m_size; }
[[nodiscard]] size_t length() const { return m_size; }

[[nodiscard]] size_t size() const {
return m_data.size();
}

operator std::string() const {
return str();
}

operator std::vector<uint8_t>() const {
return vec();
}
operator std::string() const { return str(); }
operator std::vector<uint8_t>() const { return vec(); }

void get(const std::function<void(const b::EmbedInternal::EmbeddedFile&)>& callback);

private:
std::string_view m_data;
std::string_view m_filename;
const unsigned char* m_data = nullptr;
size_t m_size = 0;
const char* m_filename = nullptr;
#ifndef B_PRODUCTION_MODE
std::string_view m_fullFilepath;
std::string m_storage;
const char* m_fullFilepath = nullptr;
#endif
}; // class EmbeddedFile
};

${EMBEDDED_FILES_DECLARATIONS}
}; // struct embedded_files

template<size_t N>
struct embed_string_literal {
constexpr 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);
}
[[nodiscard]] std::string str() const {
return std::string(value, N);
}
[[nodiscard]] constexpr bool _false() const {
return false;
}
char value[N];
};

template<class S>
EmbedInternal::EmbeddedFile embed(const S& identifier) {
${EMBEDDED_FILES_RETURNS}
throw std::runtime_error("[b::embed<>] No such file or directory");
}

template<size_t N, size_t M>
constexpr bool operator==(const embed_string_literal<N>& left, const char (&right)[M]) {
return std::equal(left.value, left.value + N, right);
inline EmbedInternal::EmbeddedFile embed(const char* identifier) {
${EMBEDDED_FILES_RET_CHARP}
throw std::runtime_error("[b::embed<>] No such file or directory");
}

template<embed_string_literal identifier>
constexpr EmbedInternal::EmbeddedFile embed() {
${EMBEDDED_FILES_RETURNS}{
static_assert(identifier._false(), "[b::embed<>] No such file or directory");
}
// Функция для получения списка всех встроенных файлов
[[nodiscard]] inline std::vector<std::string> embed_list() {
return {
${EMBEDDED_FILES_LIST}
};
}

} // namespace b
Expand All @@ -303,17 +275,28 @@ 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")

# Преобразуем 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}")

# Шаг 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})
]=])

file(WRITE ${EMBED_BINARY_DIR}/generate.cmake ${EMBED_GENERATE_SCRIPT})

set(EMBED_IDENTIFIERS "" CACHE INTERNAL "list of all identifiers used by battery::embed")
Expand All @@ -333,31 +316,45 @@ 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 ")
endif()
endforeach()
set(EMBEDDED_FILES_RETURNS "")
math(EXPR num_identifiers "${num_identifiers} - 1")
foreach (INDEX RANGE ${num_identifiers})
set(EMBEDDED_FILES_RET_CHARP "")
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_RETURNS "${EMBEDDED_FILES_RETURNS}if constexpr (identifier == \"${FILENAME}\") { return EmbedInternal::${IDENTIFIER}; }\n else ")
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_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()

# 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(_embed_generate_hpp)
endfunction()

# Internal function for validating an identifier
function(embed_validate_identifier IDENTIFIER) # Validate the identifier against C variable naming rules
Expand All @@ -370,51 +367,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")

# This action generates both files and is called on-demand whenever the resource file changes
# === 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(
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
# === Adding files to the target (embed.hpp now already exists!) ===
target_include_directories(${TARGET} PUBLIC
$<BUILD_INTERFACE:${EMBED_BINARY_DIR}/autogen/${TARGET_ID}/include>
$<INSTALL_INTERFACE:>
)
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)
Expand All @@ -425,13 +422,28 @@ 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})
endif()

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()

endif()
endfunction()

function(b_embed_proxy_target MAIN_TARGET PROXY_TARGET)
Expand Down
Loading