I’ve found a neat way to embed files in a C++ header file using CMake and thought nit might be useful to share it for other extension devs.
I adapted a CMake file I found online here: How to Embed Files in Your C++ Project - That One Game Dev and changed it so that it would also use the file extension as part of the variable name used to reference the file contents.
My version is this:
function(target_embed_files TARGET_NAME)
set(OPTIONS)
set(SINGLE_VALUE)
set(MULTIPLE_VALUE FILES)
cmake_parse_arguments(
EF
"${OPTIONS}"
"${SINGLE_VALUE}"
"${MULTIPLE_VALUE}"
${ARGN}
)
string(TOUPPER "${TARGET_NAME}" TARGET_NAME_UPPER)
string(MAKE_C_IDENTIFIER "${TARGET_NAME_UPPER}" TARGET_NAME_UPPER)
string(TOLOWER "${TARGET_NAME}" TARGET_NAME_LOWER)
string(MAKE_C_IDENTIFIER "${TARGET_NAME_LOWER}" TARGET_NAME_LOWER)
set(GENERATE_HEADER_LOCATION "${CMAKE_BINARY_DIR}/embed/include")
set(EMBED_HEADERS_LOCATION "${GENERATE_HEADER_LOCATION}/embed")
set(HEADER_FILENAME "${TARGET_NAME_LOWER}.h")
set(ARRAY_DECLARATIONS)
foreach(INPUT_FILE IN LISTS EF_FILES)
get_filename_component(FILEFULLNAME "${INPUT_FILE}" NAME)
string(REPLACE "." "_" FILENAME "${FILEFULLNAME}")
string(TOLOWER "${FILENAME}" FILENAME)
string(MAKE_C_IDENTIFIER "${FILENAME}" FILENAME)
file(READ "${INPUT_FILE}" BYTES HEX)
string(REGEX REPLACE "(..)" "0x\\1, " BYTES "${BYTES}")
string(CONFIGURE "const std::uint8_t ${FILENAME}[] = { ${BYTES} }\;" CURRENT_DECLARATION)
list(APPEND ARRAY_DECLARATIONS "${CURRENT_DECLARATION}")
endforeach()
string(JOIN "\n" ARRAY_DECLARATIONS ${ARRAY_DECLARATIONS})
set(BASIC_HEADER "\
#ifndef ${TARGET_NAME_UPPER}_EMBED_FILES
#define ${TARGET_NAME_UPPER}_EMBED_FILES
#include <cstdint>
${ARRAY_DECLARATIONS}
#endif
")
string(CONFIGURE "${BASIC_HEADER}" BASIC_HEADER)
file(WRITE "${EMBED_HEADERS_LOCATION}/${HEADER_FILENAME}" "${BASIC_HEADER}")
target_include_directories(${TARGET_NAME} PUBLIC "${GENERATE_HEADER_LOCATION}")
endfunction()
then in your CMakeLists.txt
file, you include it and then call the function thus:
include("embed.cmake") # routine to create .h headers with embedded file contents
# include embedded web content
target_embed_files(${PROJECT_NAME} FILES "content/css/bootstrap.min.css"
"content/css/songchooser.css"
"content/js/bootstrap.bundle.min.js"
"content/js/songchooser.js"
"content/icon/bootstrap-icons.css"
"content/icon/fonts/bootstrap-icons.woff"
"content/icon/fonts/bootstrap-icons.woff2"
"content/BouldenDigitalSiteIcon.png"
"content/BouldenDigitalLogo-1024.png"
"content/favicon.ico"
"content/index.html"
)
and when you build your project is built, you end up with a file in the build directory looking a bit like this:
#ifndef MYEXTENSION_EMBED_FILES
#define MYEXTENSION_EMBED_FILES
#include <cstdint>
const std::uint8_t bootstrap_min_css[] = { 0x40, 0x63, 0x68, 0x61, 0x72, 0x73, 0x65, 0x74, 0x20, 0x22, 0x55, 0x54, 0x46, 0x2d, 0x38, 0x22, 0x3b, 0x2f, 0x2a, 0x21, 0x0d, 0x0a, 0x20, 0x2a, 0x20, 0x42, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x20, 0x20, 0x76, 0x35, 0x2e, 0x33, 0x2e, 0x30, 0x2d, 0x61, 0x6c, 0x70, 0x68, 0x61, 0x33, 0x20, 0x28, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x67, 0x65, 0x74, 0x62, 0x6f, 0x6f, 0x74, 0x73, 0x74, 0x72, 0x61, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x29, 0x0d, 0x0a, 0x20, 0x2a, 0x20, 0x43, 0x6f, 0x70, 0x79, 0x72, 0x69, 0x67, 0x68, 0x74, 0x20, 0x32, 0x30, 0x31, 0x31, 0x2d, 0x32, 0x30, 0x32, 0x33, 0x20, 0x54, };
#endif
which you can then #include
into your extension code and convert the array to whatever format you need to deploy/serve it. I am using it to serve image, JavaScript and stylesheet assets to the WebView in my upcoming Advanced Song Selector extension.