Last active
March 10, 2025 03:16
-
-
Save sivachandran/3a0de157dccef822a230 to your computer and use it in GitHub Desktop.
Pure CMake function to convert any file into C/C++ header, implemented with only CMake commands.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
include(CMakeParseArguments) | |
# Function to wrap a given string into multiple lines at the given column position. | |
# Parameters: | |
# VARIABLE - The name of the CMake variable holding the string. | |
# AT_COLUMN - The column position at which string will be wrapped. | |
function(WRAP_STRING) | |
set(oneValueArgs VARIABLE AT_COLUMN) | |
cmake_parse_arguments(WRAP_STRING "${options}" "${oneValueArgs}" "" ${ARGN}) | |
string(LENGTH ${${WRAP_STRING_VARIABLE}} stringLength) | |
math(EXPR offset "0") | |
while(stringLength GREATER 0) | |
if(stringLength GREATER ${WRAP_STRING_AT_COLUMN}) | |
math(EXPR length "${WRAP_STRING_AT_COLUMN}") | |
else() | |
math(EXPR length "${stringLength}") | |
endif() | |
string(SUBSTRING ${${WRAP_STRING_VARIABLE}} ${offset} ${length} line) | |
set(lines "${lines}\n${line}") | |
math(EXPR stringLength "${stringLength} - ${length}") | |
math(EXPR offset "${offset} + ${length}") | |
endwhile() | |
set(${WRAP_STRING_VARIABLE} "${lines}" PARENT_SCOPE) | |
endfunction() | |
# Function to embed contents of a file as byte array in C/C++ header file(.h). The header file | |
# will contain a byte array and integer variable holding the size of the array. | |
# Parameters | |
# SOURCE_FILE - The path of source file whose contents will be embedded in the header file. | |
# VARIABLE_NAME - The name of the variable for the byte array. The string "_SIZE" will be append | |
# to this name and will be used a variable name for size variable. | |
# HEADER_FILE - The path of header file. | |
# APPEND - If specified appends to the header file instead of overwriting it | |
# NULL_TERMINATE - If specified a null byte(zero) will be append to the byte array. This will be | |
# useful if the source file is a text file and we want to use the file contents | |
# as string. But the size variable holds size of the byte array without this | |
# null byte. | |
# Usage: | |
# bin2h(SOURCE_FILE "Logo.png" HEADER_FILE "Logo.h" VARIABLE_NAME "LOGO_PNG") | |
function(BIN2H) | |
set(options APPEND NULL_TERMINATE) | |
set(oneValueArgs SOURCE_FILE VARIABLE_NAME HEADER_FILE) | |
cmake_parse_arguments(BIN2H "${options}" "${oneValueArgs}" "" ${ARGN}) | |
# reads source file contents as hex string | |
file(READ ${BIN2H_SOURCE_FILE} hexString HEX) | |
string(LENGTH ${hexString} hexStringLength) | |
# appends null byte if asked | |
if(BIN2H_NULL_TERMINATE) | |
set(hexString "${hexString}00") | |
endif() | |
# wraps the hex string into multiple lines at column 32(i.e. 16 bytes per line) | |
wrap_string(VARIABLE hexString AT_COLUMN 32) | |
math(EXPR arraySize "${hexStringLength} / 2") | |
# adds '0x' prefix and comma suffix before and after every byte respectively | |
string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " arrayValues ${hexString}) | |
# removes trailing comma | |
string(REGEX REPLACE ", $" "" arrayValues ${arrayValues}) | |
# converts the variable name into proper C identifier | |
string(MAKE_C_IDENTIFIER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) | |
string(TOUPPER "${BIN2H_VARIABLE_NAME}" BIN2H_VARIABLE_NAME) | |
# declares byte array and the length variables | |
set(arrayDefinition "const unsigned char ${BIN2H_VARIABLE_NAME}[] = { ${arrayValues} };") | |
set(arraySizeDefinition "const size_t ${BIN2H_VARIABLE_NAME}_SIZE = ${arraySize};") | |
set(declarations "${arrayDefinition}\n\n${arraySizeDefinition}\n\n") | |
if(BIN2H_APPEND) | |
file(APPEND ${BIN2H_HEADER_FILE} "${declarations}") | |
else() | |
file(WRITE ${BIN2H_HEADER_FILE} "${declarations}") | |
endif() | |
endfunction() |
Am I wrong that this executes at configuration time rather than build time? So it cannot be used in cases where the input file is also built by the project?
It depends on when you call bin2h function.
It depends on when you call bin2h function.
Respectfully, I don't see how that could be the case -- isn't CMake code always evaluated at configure time?
I think you should browse the docs a little more. CMake has a two phase
generation step Configure and Generate. The generate phase knows a lot
more about the outputs, but it also has the ability to support custom build
commands and the CMake executable has a command line interface that
abstracts syntax for basic shell operations.
Custom commands support artifacts for DEP tracking to allow for incremental
building. There is a lot going on there that allows things to work in the
build phase.
…On Wed, Dec 6, 2023, 08:15 Martin Cejp ***@***.***> wrote:
***@***.**** commented on this gist.
------------------------------
It depends on when you call bin2h function.
Respectfully, I don't see how that could be the case -- isn't CMake code
*always* evaluated at configure time?
—
Reply to this email directly, view it on GitHub
<https://gist.github.com/sivachandran/3a0de157dccef822a230#gistcomment-4784300>
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AJKBY3SDWRIXP6G7E6DNVTTYIBVXHBFKMF2HI4TJMJ2XIZLTSKBKK5TBNR2WLJDHNFZXJJDOMFWWLK3UNBZGKYLEL52HS4DFQKSXMYLMOVS2I5DSOVS2I3TBNVS3W5DIOJSWCZC7OBQXE5DJMNUXAYLOORPWCY3UNF3GS5DZVRZXKYTKMVRXIX3UPFYGLK2HNFZXIQ3PNVWWK3TUUZ2G64DJMNZZDAVEOR4XAZNEM5UXG5FFOZQWY5LFVAYTOMRTGM4DSOFHORZGSZ3HMVZKMY3SMVQXIZI>
.
You are receiving this email because you commented on the thread.
Triage notifications on the go with GitHub Mobile for iOS
<https://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675>
or Android
<https://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub>
.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Am I wrong that this executes at configuration time rather than build time? So it cannot be used in cases where the input file is also built by the project?