Skip to content

Instantly share code, notes, and snippets.

@sivachandran
Last active December 7, 2023 22:19
Show Gist options
  • Star 16 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save sivachandran/3a0de157dccef822a230 to your computer and use it in GitHub Desktop.
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.
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()
@dagamayank
Copy link

dagamayank commented Aug 15, 2016

@sivachandran Hi - we intend to use the bin2h utility for one of the libraries that we are developing. I wanted to know if we have your permission for doing so? If yes, is there a license attached to this code? I did not find a license either here or on your blog. Our eventual plan is to open-source the library.

@amir-saniyan
Copy link

To increase speed for large file sizes, you can wrap strings like the following code:

string(REPEAT "[0-9a-f]" 32 column_pattern)
string(REGEX REPLACE "(${column_pattern})" "\\1\n" content "${hex_content}")

@StevenAWhite
Copy link

I was just about to write this myself after noticing the FILE(READ HEX) option. I would like to integrate this functionality in to https://github.com/biogearsengine/core and wanted to verify you consider this compatible with the APACHE 2.0 license.

@sivachandran
Copy link
Author

@StevenAWhite I have created GitHub repo for bin2h here and included license(MIT). Feel free to use it in your projects.

@sivachandran
Copy link
Author

@dagamayank I missed your message somehow and just seeing it. Feel free to use it in your projects.

@sivachandran
Copy link
Author

@amir-saniyan Thanks for your suggestion. I will try it and update.

@StevenAWhite
Copy link

@StevenAWhite I have created GitHub repo for bin2h here and included license(MIT). Feel free to use it in your projects.

Thank you

@mcejp
Copy link

mcejp commented Jan 26, 2023

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?

@sivachandran
Copy link
Author

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.

@mcejp
Copy link

mcejp commented Dec 6, 2023

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?

@StevenAWhite
Copy link

StevenAWhite commented Dec 7, 2023 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment