Initial commit
Bin2CPP / Sonarqube (push) Successful in 5m3s
Bin2CPP / Build & Test Debug with ./ConanProfiles/clang (Unity Build: OFF) (push) Successful in 4m45s
Bin2CPP / Build & Test Debug with ./ConanProfiles/clang (Unity Build: ON) (push) Successful in 5m13s
Bin2CPP / Build & Test Debug with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Successful in 4m50s
Bin2CPP / Build & Test Debug with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Successful in 4m48s
Bin2CPP / Build & Test RelWithDebInfo with ./ConanProfiles/clang (Unity Build: OFF) (push) Successful in 4m56s
Bin2CPP / Build & Test RelWithDebInfo with ./ConanProfiles/clang (Unity Build: ON) (push) Successful in 4m55s
Bin2CPP / Build & Test RelWithDebInfo with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Successful in 5m6s
Bin2CPP / Build & Test RelWithDebInfo with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Successful in 5m4s
Bin2CPP / Build & Test Release with ./ConanProfiles/clang (Unity Build: OFF) (push) Successful in 4m53s
Bin2CPP / Build & Test Release with ./ConanProfiles/clang (Unity Build: ON) (push) Successful in 4m52s
Bin2CPP / Build & Test Release with ./ConanProfiles/clang_asan (Unity Build: OFF) (push) Successful in 5m2s
Bin2CPP / Build & Test Release with ./ConanProfiles/clang_asan (Unity Build: ON) (push) Successful in 5m0s
Bin2CPP / Clang Format Checks (push) Successful in 6s
Conan Packaging / Package Bin2CPP/1.0.0 (push) Successful in 4m56s

This commit is contained in:
2026-08-17 01:05:55 +02:00
commit f9c9207fc6
48 changed files with 2461 additions and 0 deletions
+122
View File
@@ -0,0 +1,122 @@
---
AlignAfterOpenBracket: Align
AlignArrayOfStructures: None
AccessModifierOffset: -2
AlignConsecutiveBitFields:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
PadOperators: true
AlignConsecutiveMacros:
Enabled: true
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
PadOperators: true
AlignConsecutiveShortCaseStatements:
Enabled: true
AcrossEmptyLines: true
AcrossComments: true
AlignCaseColons: true
AlignEscapedNewlines: Right
AlignOperands: Align
AlignTrailingComments:
Kind: Always
OverEmptyLines: 2
AllowAllArgumentsOnNextLine: false
AllowAllParametersOfDeclarationOnNextLine: false
AllowShortBlocksOnASingleLine: Empty
AllowShortCaseLabelsOnASingleLine: false
AllowShortEnumsOnASingleLine: false
AllowShortFunctionsOnASingleLine: Empty
AllowShortIfStatementsOnASingleLine: Never
AllowShortLambdasOnASingleLine: Empty
AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: Yes
BinPackArguments: false
BinPackParameters: false
BitFieldColonSpacing: After
BreakBeforeBraces: Custom
BracedInitializerIndentWidth: 2
BreakAfterAttributes: Always
BreakArrays: false
BreakBeforeTernaryOperators: true
BreakConstructorInitializers: AfterColon
BreakInheritanceList: BeforeComma
BreakStringLiterals: true
CompactNamespaces: false
Cpp11BracedListStyle: true
EmptyLineBeforeAccessModifier: Always
FixNamespaceComments: true
IncludeBlocks: Preserve
IndentCaseBlocks: true
IndentCaseLabels: false
IndentPPDirectives: None
IndentRequiresClause: true
IndentWidth: 4
IndentWrappedFunctionNames: true
InsertBraces: true
InsertNewlineAtEOF: true
IntegerLiteralSeparator:
Binary: -1
Decimal: 3
DecimalMinDigits: 5
Hex: -1
LambdaBodyIndentation: Signature
Language: Cpp
NamespaceIndentation: None
PointerAlignment: Left
QualifierAlignment: Leave
ReferenceAlignment: Left
UseTab: Never
Standard: Auto
BraceWrapping:
AfterNamespace: true
AfterCaseLabel: true
AfterClass: true
AfterControlStatement: Always
AfterEnum: true
AfterFunction: true
AfterStruct: true
AfterUnion: true
BeforeElse: true
BeforeLambdaBody: true
BeforeWhile: false
IndentBraces: false
SplitEmptyFunction: true
SplitEmptyRecord: true
SplitEmptyNamespace: true
ColumnLimit: 120
SpaceAfterTemplateKeyword: false
SortIncludes: CaseSensitive
PenaltyReturnTypeOnItsOwnLine: 500
SpaceBeforeAssignmentOperators: true
SpaceBeforeCaseColon: false
SpaceBeforeCtorInitializerColon: false
SpaceBeforeInheritanceColon: false
SpaceBeforeRangeBasedForLoopColon: false
SpaceBeforeSquareBrackets: false
SpacesInAngles: Never
SpacesInConditionalStatement: false
SpaceBeforeParens: ControlStatements
SpacesInContainerLiterals: false
SpacesInSquareBrackets: false
SpacesBeforeTrailingComments: 3
PackConstructorInitializers: Never
SeparateDefinitionBlocks: Always
MaxEmptyLinesToKeep: 1
BreakBeforeBinaryOperators: None
AlignConsecutiveAssignments:
Enabled: false
AcrossEmptyLines: false
AcrossComments: true
AlignCompound: true
PadOperators: true
SpaceAroundPointerQualifiers: Default
SpaceBeforeCpp11BracedList: true
SpaceInEmptyBlock: true
PenaltyBreakAssignment: 200
KeepEmptyLinesAtTheStartOfBlocks: false
+22
View File
@@ -0,0 +1,22 @@
# .clang-tidy
---
Checks: >
-*,
bugprone-*,
clang-analyzer-*,
cppcoreguidelines-*,
modernize-*,
performance-*,
readability-*,
portability-*,
-modernize-use-trailing-return-type,
-readability-avoid-const-params-in-decls,
-cppcoreguidelines-macro-usage,
-cppcoreguidelines-avoid-do-while
ExcludeHeaderFilterRegex: '_generated.*'
CheckOptions:
- key: readability-implicit-bool-conversion.AllowPointerConditions
value: true
+3
View File
@@ -0,0 +1,3 @@
* text=auto eol=lf
*.{cmd,[cC][mM][dD]} text eol=crlf
*.{bat,[bB][aA][tT]} text eol=crlf
+59
View File
@@ -0,0 +1,59 @@
name: Bin2CPP
on:
push:
branches:
- '**'
workflow_dispatch:
jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:main
strategy:
matrix:
build_type: ["Debug", "RelWithDebInfo", "Release"]
unity_build: ["ON", "OFF"]
conan_profile: ["./ConanProfiles/clang", "./ConanProfiles/clang_asan"]
name: "Build & Test ${{ matrix.build_type }} with ${{ matrix.conan_profile }} (Unity Build: ${{ matrix.unity_build }})"
steps:
- name: Install Node.js
run: apt-get update && apt-get install -y nodejs
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive
- name: Build
run: |
conan install . --remote=bigfootpackages -pr:h=${{ matrix.conan_profile }} -pr:b=./ConanProfiles/Tools/clang --build=* -s:h build_type=${{ matrix.build_type }}
. ./build/${{ matrix.build_type }}/generators/conanbuild.sh
cmake -S . -B ./build/${{ matrix.build_type }} --toolchain ./build/${{ matrix.build_type }}/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=${{ matrix.build_type }} -DCMAKE_UNITY_BUILD=${{ matrix.unity_build }} -G "Ninja"
cmake --build build/${{ matrix.build_type }} --parallel $(nproc)
. ./build/${{ matrix.build_type }}/generators/deactivate_conanbuild.sh
- name: Unit Tests
run: |
cd ./build/${{ matrix.build_type }}
ctest . --output-on-failure
clang-format:
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:main
name: "Clang Format Checks"
steps:
- name: Install Node.js
run: apt-get update && apt-get install -y nodejs
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive
- name: Clang Format Checks
run: chmod +x format.sh && ./format.sh --check Bin2CPP
+46
View File
@@ -0,0 +1,46 @@
name: Conan Packaging
on:
push:
branches:
- '**'
workflow_dispatch:
jobs:
conan-packages:
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:main
env:
ARTIFACTORY_USER: ${{ secrets.ARTIFACTORY_USER }}
ARTIFACTORY_PASSWORD: ${{ secrets.ARTIFACTORY_PASSWORD }}
name: "Package Bin2CPP/1.0.0"
steps:
- name: Set BRANCH_NAME
run: echo "BRANCH_NAME=$(echo $GITHUB_REF_NAME | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Install Node.js
run: apt-get update && apt-get install -y nodejs
- name: Checkout repository
uses: actions/checkout@v6
with:
submodules: recursive
- name: Run Conan packaging
run: |
echo "Building Bin2CPP@$1.0.0"
CONAN_LOGIN_USERNAME=${ARTIFACTORY_USER} \
CONAN_PASSWORD=${ARTIFACTORY_PASSWORD} \
conan create . \
--version=1.0.0 \
--user=bigfootdev \
--channel=${{ env.BRANCH_NAME }} \
-pr:b=./ConanProfiles/Tools/clang -pr:h=./ConanProfiles/clang \
--build=* --remote=bigfootpackages
CONAN_LOGIN_USERNAME=${ARTIFACTORY_USER} \
CONAN_PASSWORD=${ARTIFACTORY_PASSWORD} \
conan upload bin2cpp/1.0.0@bigfootdev/${{ env.BRANCH_NAME }} \
--only-recipe --remote=bigfootpackages
+50
View File
@@ -0,0 +1,50 @@
name: Bin2CPP
on:
push:
branches:
- main
- Development
jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 120
container:
image: git.romainboullard.com/bigfootdev/linuxcppbuilder:main
name: "Sonarqube"
steps:
- name: Install Node.js
run: apt-get update && apt-get install -y nodejs
- name: Checkout repository
uses: actions/checkout@v6
with:
fetch-depth: 0
submodules: recursive
- name: Generate
run: |
conan profile detect
conan install . --remote=bigfootpackages -pr:h=./ConanProfiles/clang_coverage -pr:b=./ConanProfiles/Tools/clang --build=* -s:h build_type=Debug
. ./build/Debug/generators/conanbuild.sh
cmake -S . -B ./build/Debug --toolchain ./build/Debug/generators/conan_toolchain.cmake -DCMAKE_BUILD_TYPE=Debug -G "Ninja"
cmake --build build/Debug --parallel $(nproc)
. ./build/Debug/generators/deactivate_conanbuild.sh
- name: Clang-Tidy
run: run-clang-tidy -p ./build/Debug/ >> tidy_result.txt
- name: Infer
run: infer run --compilation-database build/Debug/compile_commands.json
- name: SonarQube Scan
if: github.head_ref == 'main' || github.ref_name == 'main'
uses: SonarSource/sonarqube-scan-action@v7.0.0
with:
args: >
-Dsonar.cxx.jsonCompilationDatabase=./build/Debug/compile_commands.json
-Dsonar.verbose=true
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST }}
+21
View File
@@ -0,0 +1,21 @@
[Bb]uild*
Environment/output
*.generated.h
.scannerwork
SonarqubeResult
*.profraw
conan_imports_manifest.txt
CMakeUserPresets.json
conan.lock
conanbuildinfo.txt
conaninfo.txt
graph_info.json
.idea
test_package/Vendor
graphviz
+3
View File
@@ -0,0 +1,3 @@
{
}
+66
View File
@@ -0,0 +1,66 @@
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${PackageName})
add_executable(${PROJECT_NAME})
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
file(GLOB_RECURSE BIN2CPPEXE_SOURCES
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
file(GLOB_RECURSE BIN2CPPEXE_HEADERS
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.h
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp
)
target_sources(${PROJECT_NAME}
PRIVATE
${BIN2CPPEXE_SOURCES}
PRIVATE
FILE_SET HEADERS
BASE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/Include
FILES
${BIN2CPPEXE_HEADERS}
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
Bin2CPPCompileAndLinkFlags
Bin2CPPLib
CLI11::CLI11
${CMAKE_DL_LIBS})
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Src FILES ${BIN2CPPEXE_SOURCES} ${BIN2CPPEXE_HEADERS})
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Bin2CPP/${ParentFolder})
##################ASAN SETUP###################
if(${ASAN})
if(MSVC)
get_filename_component(MSVC_BIN_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY)
set(ASAN_DLL "${MSVC_BIN_DIR}/clang_rt.asan_dynamic-x86_64.dll")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp"
DEPENDS "${ASAN_DLL}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ASAN_DLL}" "$<TARGET_FILE_DIR:${PROJECT_NAME}>"
COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp"
COMMENT "Copying ASan DLL for ${PROJECT_NAME}"
)
add_custom_target(${PROJECT_NAME}Asan
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp"
)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}Asan)
set_target_properties(${PROJECT_NAME}Asan PROPERTIES FOLDER UtilityTargets/Bin2CPP/${ParentFolder})
endif()
endif()
########INSTALL
install(TARGETS ${PROJECT_NAME} DESTINATION bin)
+68
View File
@@ -0,0 +1,68 @@
#include <Generator.hpp>
#include <Log.hpp>
#include <CLI/CLI.hpp>
int main(int argc, char** argv)
{
CLI::App app {"Bin2CPP allows you to generate a C++ header containing the binary content of an input file"};
argv = app.ensure_utf8(argv);
std::string input;
app.add_option("-f,--input", input, "The input file")->required();
std::string output;
app.add_option("-o,--output", output, "The output file")->required();
std::optional<std::string> arrayType;
app.add_option("--arrayType", arrayType, "The type of the array");
std::optional<std::string> arrayInclude;
app.add_option("--arrayInclude", arrayInclude, "Include for the array type");
std::optional<std::string> arrayName;
app.add_option("--arrayName", arrayName, "The array name");
std::optional<std::string> customNamespace;
app.add_option("--namespace", customNamespace, "The namespace");
bool verbose = false;
app.add_option("--verbose", verbose, "Activate logging");
CLI11_PARSE(app, argc, argv);
std::unique_ptr<Bin2CPP::Singleton<Bin2CPP::Log>::Lifetime> loggerLifetime =
verbose ? std::make_unique<Bin2CPP::Singleton<Bin2CPP::Log>::Lifetime>() : nullptr;
Bin2CPP::Generator generator(input);
if (arrayType)
{
generator.SetMapping(Bin2CPP::MappingKey::ARRAY_TYPE, arrayType.value());
}
if (arrayInclude)
{
generator.SetMapping(Bin2CPP::MappingKey::ARRAY_TYPE_INCLUDE, arrayInclude.value());
}
if (arrayName)
{
generator.SetMapping(Bin2CPP::MappingKey::ARRAY_NAME, arrayName.value());
}
if (customNamespace)
{
generator.SetMapping(Bin2CPP::MappingKey::NAMESPACE, customNamespace.value());
}
if (!generator.Generate())
{
BIN2CPP_LOG_ERROR("Failed to generate '{}'", input);
return 1;
}
std::ofstream out(output);
if (!out)
{
BIN2CPP_LOG_ERROR("Failed to open '{}'", output);
return 1;
}
out << generator.Get();
out.close();
BIN2CPP_LOG_INFO("'{}' Generated !", output);
return 0;
}
+49
View File
@@ -0,0 +1,49 @@
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${PackageName})
add_library(${PROJECT_NAME} STATIC)
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
file(GLOB_RECURSE BIN2CPPLIB_SOURCES
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
file(GLOB_RECURSE BIN2CPPLIB_HEADERS
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.h
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp
)
file(GLOB_RECURSE BIN2CPPLIB_OTHERS
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp.in
)
target_sources(${PROJECT_NAME}
PRIVATE
${BIN2CPPLIB_SOURCES}
PUBLIC
FILE_SET HEADERS
BASE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/Include
FILES
${BIN2CPPLIB_HEADERS}
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
Bin2CPPCompileAndLinkFlags
PUBLIC
quill::quill
magic_enum::magic_enum)
target_compile_definitions(${PROJECT_NAME}
PUBLIC
QUILL_DISABLE_NON_PREFIXED_MACROS)
set_source_files_properties(${CMAKE_CURRENT_SOURCE_DIR}/MimallocImpl.cpp PROPERTIES SKIP_UNITY_BUILD_INCLUSION ON)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Src FILES ${BIN2CPPLIB_SOURCES} ${BIN2CPPLIB_HEADERS} ${BIN2CPPLIB_OTHERS})
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Bin2CPP/${ParentFolder})
+280
View File
@@ -0,0 +1,280 @@
/*********************************************************************
* \file Generator.cpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#include <Generator.hpp>
#include <Assert.hpp>
#include <GeneratedTemplate.hpp>
#include <algorithm>
#include <filesystem>
#include <fstream>
#include <regex>
#include <utility>
#include <vector>
namespace Bin2CPP
{
Generator::Generator(const std::string_view p_inputFile):
m_inputFile(p_inputFile)
{
}
/****************************************************************************************/
void Generator::SetMapping(const MappingKey p_key, const std::string_view p_value)
{
const std::optional<std::size_t> index = magic_enum::enum_index(p_key);
BIN2CPP_CRITICAL_ASSERT(index.has_value(), "Incorrect mapping key! Got {}", magic_enum::enum_integer(p_key));
m_mappingTable[index.value()] = p_value;
}
/****************************************************************************************/
bool Generator::Generate()
{
if (!ComputeMappings())
{
BIN2CPP_LOG_ERROR("Invalid mappings! Can't generate file!");
return false;
}
m_result = g_generatedTemplate;
for (std::uint32_t i = 0; i < m_mappingTable.size(); ++i)
{
const std::string KEY = magic_enum::enum_name(magic_enum::enum_value<MappingKey>(i)).data();
const std::string& VALUE = m_mappingTable[i];
const std::string pattern = "\\{\\{" + KEY + "\\}\\}";
std::regex placeholderRegex(pattern);
m_result = std::regex_replace(m_result, placeholderRegex, VALUE);
}
return Validate();
}
/****************************************************************************************/
std::string_view Generator::Get() const
{
return m_result;
}
/****************************************************************************************/
bool Generator::ComputeMappings()
{
std::filesystem::path file {m_inputFile.data()};
if (!std::filesystem::exists(file))
{
BIN2CPP_LOG_ERROR("File '{}' does not exists!", m_inputFile);
return false;
}
if (std::filesystem::is_directory(file))
{
BIN2CPP_LOG_ERROR("'{}' is not a file!", m_inputFile);
return false;
}
if (!ComputeFilename())
{
BIN2CPP_LOG_ERROR("Failed to compute filename");
return false;
}
if (!ComputeGuardName())
{
BIN2CPP_LOG_ERROR("Failed to compute GuardName");
return false;
}
if (!ComputeArrayTypeInclude())
{
BIN2CPP_LOG_ERROR("Failed to compute ArrayTypeInclude");
return false;
}
if (!ComputeArrayType())
{
BIN2CPP_LOG_ERROR("Failed to compute ArrayType");
return false;
}
if (!ComputeArrayName())
{
BIN2CPP_LOG_ERROR("Failed to compute ArrayName");
return false;
}
std::ifstream in {m_inputFile.data(), std::ios::binary};
if (!in)
{
BIN2CPP_LOG_ERROR("Failed to open '{}'", m_inputFile);
return false;
}
in.seekg(0, std::ios::end);
const size_t fileSize = static_cast<size_t>(in.tellg());
in.seekg(0, std::ios::beg);
std::vector<std::byte> data(fileSize);
in.read(std::bit_cast<char*>(data.data()), fileSize);
if (!ComputeArraySize(data))
{
BIN2CPP_LOG_ERROR("Failed to compute ArraySize");
return false;
}
if (!ComputeData(data))
{
BIN2CPP_LOG_ERROR("Failed to compute Data");
return false;
}
return true;
}
/****************************************************************************************/
bool Generator::ComputeFilename()
{
std::filesystem::path file {m_inputFile.data()};
m_mappingTable[magic_enum::enum_index(MappingKey::FILENAME).value()] = file.filename().string();
return true;
}
/****************************************************************************************/
bool Generator::ComputeGuardName()
{
std::filesystem::path file {m_inputFile.data()};
std::string guardName = file.filename().string();
std::transform(guardName.begin(),
guardName.end(),
guardName.begin(),
[](const char c)
{
if (!std::isalnum(c))
{
return static_cast<int>('_');
}
else
{
return std::toupper(c);
}
});
m_mappingTable[magic_enum::enum_index(MappingKey::GUARD_NAME).value()] = std::format("{}_HPP", guardName);
return true;
}
/****************************************************************************************/
bool Generator::ComputeArrayTypeInclude()
{
std::string& value = m_mappingTable[magic_enum::enum_index(MappingKey::ARRAY_TYPE_INCLUDE).value()];
if (value.empty())
{
value = "<array>";
}
return true;
}
/****************************************************************************************/
bool Generator::ComputeArrayType()
{
std::string& value = m_mappingTable[magic_enum::enum_index(MappingKey::ARRAY_TYPE).value()];
if (value.empty())
{
value = "std::array";
}
return true;
}
/****************************************************************************************/
bool Generator::ComputeArrayName()
{
std::string& value = m_mappingTable[magic_enum::enum_index(MappingKey::ARRAY_NAME).value()];
if (value.empty())
{
std::filesystem::path file {m_inputFile.data()};
value = std::format("g_{}", file.filename().string());
std::transform(value.begin(),
value.end(),
value.begin(),
[](const char c)
{
if (!std::isalnum(c))
{
return static_cast<int>('_');
}
else
{
return static_cast<int>(c);
}
});
}
return true;
}
/****************************************************************************************/
bool Generator::ComputeArraySize(const std::span<std::byte> p_data)
{
m_mappingTable[magic_enum::enum_index(MappingKey::ARRAY_SIZE).value()] = std::to_string(p_data.size());
return true;
}
/****************************************************************************************/
bool Generator::ComputeData(const std::span<std::byte> p_data)
{
std::string& value = m_mappingTable[magic_enum::enum_index(MappingKey::DATA).value()];
value.clear();
constexpr std::size_t bytesPerLine = 16;
constexpr std::string_view linePrefix = "\n ";
for (std::size_t i = 0; i < p_data.size(); ++i)
{
if (i > 0 && i % bytesPerLine == 0)
{
value.append(linePrefix.data(), linePrefix.size());
}
const std::string element = std::format("std::byte{{0x{:02X}}}", std::to_integer<std::uint8_t>(p_data[i]));
value.append(element.data(), element.size());
if (i + 1 < p_data.size())
{
value.append(", ");
}
}
return true;
}
/****************************************************************************************/
bool Generator::Validate()
{
std::regex unreplacedRegex(R"(\{\{[A-Z_]+\}\})");
std::smatch match;
if (std::regex_search(m_result, match, unreplacedRegex))
{
BIN2CPP_LOG_ERROR("Warning: Unreplaced placeholder found: {}", match.str());
return false;
}
return true;
}
} // namespace Bin2CPP
@@ -0,0 +1,121 @@
/*********************************************************************
* \file Assert.hpp
*
* \author Romain BOULLARD
* \date October 2025
*********************************************************************/
#ifndef BIN2CPP_ASSERT_HPP
#define BIN2CPP_ASSERT_HPP
#include <Log.hpp>
#if defined BIN2CPP_NOT_OPTIMIZED
#include <source_location>
#include <string>
#if defined BIN2CPP_LINUX
#include <csignal>
#endif
namespace Bin2CPP
{
class AssertHandler
{
public:
AssertHandler() = delete;
AssertHandler(const AssertHandler& p_handler) = delete;
AssertHandler(AssertHandler&& p_handler) = delete;
~AssertHandler() = delete;
/**
* Handle an assertion.
*
* \param p_location Location of the assertion.
* \param p_format Format string for the assertion message.
* \param p_args Arguments for the format string.
*/
template<typename... ARGS>
static void Handle(const std::source_location& p_location, std::format_string<ARGS...> p_format, ARGS&&... p_args)
{
BIN2CPP_LOG_FATAL("Assert: {} (File:{}, Line:{}, Function:{})",
std::format(p_format, std::forward<ARGS>(p_args)...),
p_location.file_name(),
p_location.line(),
p_location.function_name());
}
AssertHandler& operator=(const AssertHandler& p_handler) = delete;
AssertHandler& operator=(AssertHandler&& p_handler) = delete;
};
} // namespace Bin2CPP
#if defined BIN2CPP_WINDOWS
#define BIN2CPP_BREAK \
do \
{ \
__debugbreak(); \
} while (false)
#elif defined BIN2CPP_LINUX
#define BIN2CPP_BREAK \
do \
{ \
std::raise(SIGTRAP); \
} while (false)
#endif
#define BIN2CPP_ASSERT(p_assert, p_message, ...) \
do \
{ \
constexpr std::source_location location = std::source_location::current(); \
if (!(p_assert)) [[unlikely]] \
{ \
Bin2CPP::AssertHandler::Handle(location, p_message __VA_OPT__(, ) __VA_ARGS__); \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
Bin2CPP::Singleton<Bin2CPP::Log>::Instance().Flush(); \
} \
BIN2CPP_BREAK; \
} \
} while (false)
#define BIN2CPP_SOFT_ASSERT(p_assert, p_message, ...) \
do \
{ \
constexpr std::source_location location = std::source_location::current(); \
if (!(p_assert)) [[unlikely]] \
{ \
Bin2CPP::AssertHandler::Handle(location, p_message __VA_OPT__(, ) __VA_ARGS__); \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
Bin2CPP::Singleton<Bin2CPP::Log>::Instance().Flush(); \
} \
BIN2CPP_BREAK; \
} \
} while (false)
#define BIN2CPP_CRITICAL_ASSERT(p_assert, p_message, ...) \
do \
{ \
constexpr std::source_location location = std::source_location::current(); \
if (!(p_assert)) [[unlikely]] \
{ \
Bin2CPP::AssertHandler::Handle(location, p_message __VA_OPT__(, ) __VA_ARGS__); \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
Bin2CPP::Singleton<Bin2CPP::Log>::Instance().Flush(); \
} \
BIN2CPP_BREAK; \
std::abort(); \
} \
} while (false)
#else
#define BIN2CPP_ASSERT(p_assert, p_message, ...)
#define BIN2CPP_SOFT_ASSERT(p_assert, p_message, ...)
#define BIN2CPP_CRITICAL_ASSERT(p_assert, p_message, ...)
#endif
#endif
@@ -0,0 +1,36 @@
/*********************************************************************
* \file GeneratedTemplate.hpp
*
* \author Romain BOULLARD
* \date March 2026
*********************************************************************/
#ifndef BIN2CPP_GENERATED_TEMPLATE_HPP
#define BIN2CPP_GENERATED_TEMPLATE_HPP
#include <string_view>
namespace Bin2CPP
{
constexpr std::string_view g_generatedTemplate = R"(/**
* Auto-generated header from: {{FILENAME}}
* Generated by Bin2CPP
*
* DO NOT TOUCH
*/
#ifndef {{GUARD_NAME}}
#define {{GUARD_NAME}}
#include {{ARRAY_TYPE_INCLUDE}}
#include <cstddef>
namespace {{NAMESPACE}}
{
inline constexpr {{ARRAY_TYPE}}<std::byte, {{ARRAY_SIZE}}> {{ARRAY_NAME}} = {
{{DATA}}
};
} // namespace {{NAMESPACE}}
#endif // {{GUARD_NAME}}
)";
} // namespace Bin2CPP
#endif
@@ -0,0 +1,157 @@
/*********************************************************************
* \file Generator.hpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#ifndef BIN2CPP_GENERATOR_HPP
#define BIN2CPP_GENERATOR_HPP
#include <magic_enum/magic_enum.hpp>
#include <array>
#include <span>
#include <string>
namespace Bin2CPP
{
enum class MappingKey
{
FILENAME,
GUARD_NAME,
NAMESPACE,
ARRAY_TYPE_INCLUDE,
ARRAY_TYPE,
ARRAY_SIZE,
ARRAY_NAME,
DATA
};
class Generator
{
public:
/**
* Constructor
*
* \param p_inputFile the Input file
*/
Generator(const std::string_view p_inputFile);
Generator(const Generator& p_generator) = default;
Generator(Generator&& p_generator) = default;
/**
* Set a mapping
*
*/
void SetMapping(const MappingKey p_key, const std::string_view p_value);
/**
* Generate
*
* \return True if successful, false otherwise
*/
[[nodiscard]]
bool Generate();
/**
* Get
*
*/
[[nodiscard]]
std::string_view Get() const;
~Generator() = default;
Generator& operator=(const Generator& p_generator) = default;
Generator& operator=(Generator&& p_generator) = default;
private:
/**
* Compute the mappings
*
* \return True if all mappings have valid values, false otherwise
*/
[[nodiscard]]
bool ComputeMappings();
/**
* Compute the filename mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeFilename();
/**
* Compute the guardName mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeGuardName();
/**
* Compute the arrayTypeInclude mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeArrayTypeInclude();
/**
* Compute the arrayType mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeArrayType();
/**
* Compute the arrayName mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeArrayName();
/**
* Compute the arraySize mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeArraySize(const std::span<std::byte> p_data);
/**
* Compute the data mapping
*
* \return True if success, false otherwise
*/
[[nodiscard]]
bool ComputeData(const std::span<std::byte> p_data);
/**
* Validate generated is correct (no remaining tags)
*
* \return True if valid, false otherwise
*/
[[nodiscard]]
bool Validate();
/**
* The mapping table
*/
std::array<std::string, magic_enum::enum_count<MappingKey>()> m_mappingTable;
/**
* The input file
*/
std::string m_inputFile;
/**
* The result
*/
std::string m_result;
};
} // namespace Bin2CPP
#endif
+133
View File
@@ -0,0 +1,133 @@
/*********************************************************************
* \file Log.hpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#ifndef BIN2CPP_LOG_HPP
#define BIN2CPP_LOG_HPP
#include <Singleton.hpp>
#include <array>
#ifdef BIN2CPP_WINDOWS
#pragma warning(disable: 4702)
#endif
#include <quill/Backend.h>
#include <quill/Frontend.h>
#include <quill/LogMacros.h>
#include <quill/Logger.h>
#include <quill/sinks/ConsoleSink.h>
#if defined BIN2CPP_WINDOWS
#pragma warning(default: 4702)
#endif
namespace Bin2CPP
{
class Log
{
public:
Log();
Log(const Log& p_logger) = delete;
Log(Log&& p_logger) = delete;
/**
* Register a logger.
*
* \return The logger, nullptr if it does not exist
*/
[[nodiscard]]
quill::Logger* GetLogger();
/*
* Flush all the loggers
*
*/
void Flush();
~Log();
Log& operator=(const Log& p_logger) = delete;
Log& operator=(Log&& p_logger) = delete;
private:
/*
* The sinks
*/
std::array<std::shared_ptr<quill::Sink>, 1> m_sinks;
};
} // namespace Bin2CPP
#define BIN2CPP_LOG_DEBUG(fmt, ...) \
do \
{ \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
if (quill::Logger* logger = Bin2CPP::Singleton<Bin2CPP::Log>::Instance().GetLogger()) \
{ \
QUILL_LOG_DEBUG(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} \
} while (0)
#define BIN2CPP_LOG_TRACE(fmt, ...) \
do \
{ \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
if (quill::Logger* logger = Bin2CPP::Singleton<Bin2CPP::Log>::Instance().GetLogger()) \
{ \
QUILL_LOG_TRACE_L3(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} \
} while (0)
#define BIN2CPP_LOG_INFO(fmt, ...) \
do \
{ \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
if (quill::Logger* logger = Bin2CPP::Singleton<Bin2CPP::Log>::Instance().GetLogger()) \
{ \
QUILL_LOG_INFO(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} \
} while (0)
#define BIN2CPP_LOG_WARN(fmt, ...) \
do \
{ \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
if (quill::Logger* logger = Bin2CPP::Singleton<Bin2CPP::Log>::Instance().GetLogger()) \
{ \
QUILL_LOG_WARNING(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} \
} while (0)
#define BIN2CPP_LOG_ERROR(fmt, ...) \
do \
{ \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
if (quill::Logger* logger = Bin2CPP::Singleton<Bin2CPP::Log>::Instance().GetLogger()) \
{ \
QUILL_LOG_ERROR(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} \
} while (0)
#define BIN2CPP_LOG_FATAL(fmt, ...) \
do \
{ \
if (Bin2CPP::Singleton<Bin2CPP::Log>::HasInstance()) \
{ \
if (quill::Logger* logger = Bin2CPP::Singleton<Bin2CPP::Log>::Instance().GetLogger()) \
{ \
QUILL_LOG_CRITICAL(logger, fmt __VA_OPT__(, ) __VA_ARGS__); \
} \
} \
} while (0)
#endif
@@ -0,0 +1,104 @@
/*********************************************************************
* \file Singleton.hpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#ifndef BIN2CPP_SINGLETON_HPP
#define BIN2CPP_SINGLETON_HPP
#include <optional>
namespace Bin2CPP
{
template<class TYPE>
class Singleton
{
public:
Singleton() = delete;
Singleton(const Singleton& p_singleton) = delete;
Singleton(Singleton&& p_singleton) = delete;
~Singleton() = delete;
/**
* Get the instance.
*
* \return The instance
*/
static TYPE& Instance()
{
return ms_instance.value();
}
/**
* Is the instance initialized
*
* \return True if initialized, false otherwise
*/
static bool HasInstance()
{
return ms_instance.has_value();
}
class Lifetime
{
public:
/**
* Constructor.
*
* \param p_args Arguments for the singleton
*/
template<typename... ARGS>
explicit Lifetime(ARGS&&... p_args)
{
Initialize(std::forward<ARGS>(p_args)...);
}
Lifetime(const Lifetime& p_lifetime) = delete;
Lifetime(Lifetime&& p_lifetime) = delete;
~Lifetime()
{
Finalize();
}
[[nodiscard]]
Lifetime& operator=(const Lifetime& p_lifetime) = delete;
[[nodiscard]]
Lifetime& operator=(Lifetime&& p_lifetime) = delete;
};
[[nodiscard]]
Singleton& operator=(const Singleton& p_singleton) = delete;
[[nodiscard]]
Singleton& operator=(Singleton&& p_singleton) = delete;
private:
/**
* Initialize the singleton.
*
* \param p_args Arguments for the singleton
*/
template<typename... ARGS>
static void Initialize(ARGS&&... p_args)
{
ms_instance.emplace(std::forward<ARGS>(p_args)...);
}
/**
* Finalize the singleton.
*
*/
static void Finalize()
{
ms_instance.reset();
}
/**
* The singleton.
*/
inline static std::optional<TYPE> ms_instance;
};
} // namespace Bin2CPP
#endif
+51
View File
@@ -0,0 +1,51 @@
/*******************************************************************
* \file Log.cpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#include <Log.hpp>
namespace Bin2CPP
{
Log::Log()
{
quill::Backend::start();
m_sinks[0] = quill::Frontend::create_or_get_sink<quill::ConsoleSink>("ConsoleSink");
quill::Logger* logger = quill::Frontend::create_or_get_logger("Bin2CPP", m_sinks[0]);
logger->set_log_level(quill::LogLevel::TraceL3);
}
/****************************************************************************************/
quill::Logger* Log::GetLogger()
{
return quill::Frontend::get_logger("Bin2CPP");
}
/****************************************************************************************/
void Log::Flush()
{
for (quill::Logger* logger: quill::Frontend::get_all_loggers())
{
logger->flush_log();
}
}
/****************************************************************************************/
Log::~Log()
{
Flush();
for (quill::Logger* logger: quill::Frontend::get_all_loggers())
{
quill::Frontend::remove_logger(logger);
}
quill::Backend::stop();
}
} // namespace Bin2CPP
+2
View File
@@ -0,0 +1,2 @@
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bin2CPPLib)
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bin2CPP)
+90
View File
@@ -0,0 +1,90 @@
get_filename_component(PackageName ${CMAKE_CURRENT_SOURCE_DIR} NAME)
project(${PackageName}Tests)
add_executable(${PROJECT_NAME})
target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_20)
file(GLOB_RECURSE BIN2CPPLIBTESTS_SOURCES
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.cpp
)
file(GLOB_RECURSE BIN2CPPLIBTESTS_HEADERS
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/*.h
${CMAKE_CURRENT_SOURCE_DIR}/*.hpp
)
target_sources(${PROJECT_NAME}
PRIVATE
${BIN2CPPLIBTESTS_SOURCES}
PRIVATE
FILE_SET HEADERS
BASE_DIRS
${CMAKE_CURRENT_SOURCE_DIR}/Include
FILES
${BIN2CPPLIBTESTS_HEADERS}
)
target_link_libraries(${PROJECT_NAME}
PRIVATE
Bin2CPPCompileAndLinkFlags
Bin2CPPLib
gtest::gtest)
include(GoogleTest)
gtest_discover_tests(${PROJECT_NAME} XML_OUTPUT_DIR ${CMAKE_BINARY_DIR}/TestResults/)
source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} PREFIX Src/ FILES ${BIN2CPPLIBTESTS_SOURCES} ${BIN2CPPLIBTESTS_HEADERS})
set_target_properties(${PROJECT_NAME} PROPERTIES FOLDER Tests/Bin2CPP/${ParentFolder})
##################ASAN SETUP###################
if(${ASAN})
if(MSVC)
get_filename_component(MSVC_BIN_DIR "${CMAKE_CXX_COMPILER}" DIRECTORY)
set(ASAN_DLL "${MSVC_BIN_DIR}/clang_rt.asan_dynamic-x86_64.dll")
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp"
DEPENDS "${ASAN_DLL}"
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ASAN_DLL}" "$<TARGET_FILE_DIR:${PROJECT_NAME}>"
COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp"
COMMENT "Copying ASan DLL for ${PROJECT_NAME}"
)
add_custom_target(${PROJECT_NAME}Asan
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-asan.timestamp"
)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}Asan)
set_target_properties(${PROJECT_NAME}Asan PROPERTIES FOLDER UtilityTargets/Tests/Bin2CPP)
endif()
endif()
##################COPY FIXTURE FOLDER###################
if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/Fixture)
file(MAKE_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/Fixture)
endif()
file(GLOB_RECURSE FIXTURE_FILES
CONFIGURE_DEPENDS
${CMAKE_CURRENT_SOURCE_DIR}/Fixture/*
)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-fixture.timestamp"
DEPENDS ${FIXTURE_FILES}
COMMAND ${CMAKE_COMMAND} -E remove_directory $<TARGET_FILE_DIR:${PROJECT_NAME}>/Fixture
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_SOURCE_DIR}/Fixture $<TARGET_FILE_DIR:${PROJECT_NAME}>/Fixture
COMMAND ${CMAKE_COMMAND} -E touch "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-fixture.timestamp"
COMMENT "Copying Fixture folder for ${PROJECT_NAME}"
)
add_custom_target(${PROJECT_NAME}Fixture
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}-fixture.timestamp"
)
add_dependencies(${PROJECT_NAME} ${PROJECT_NAME}Fixture)
set_target_properties(${PROJECT_NAME}Fixture PROPERTIES FOLDER UtilityTargets/Tests/Bin2CPP)
@@ -0,0 +1 @@
Hello World
+133
View File
@@ -0,0 +1,133 @@
/*********************************************************************
* \file Generator.cpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#include <Generator.hpp>
#include <Log.hpp>
#include <gtest/gtest.h>
namespace Bin2CPP
{
class GeneratorFixture: public ::testing::Test
{
protected:
Singleton<Log>::Lifetime m_lifetime;
};
/****************************************************************************************/
TEST_F(GeneratorFixture, GenerateDefault)
{
Generator generator("Fixture/toto.txt");
EXPECT_TRUE(generator.Generate());
constexpr std::string_view expectedResult = R"(/**
* Auto-generated header from: toto.txt
* Generated by Bin2CPP
*
* DO NOT TOUCH
*/
#ifndef TOTO_TXT_HPP
#define TOTO_TXT_HPP
#include <array>
#include <cstddef>
namespace
{
inline constexpr std::array<std::byte, 11> g_toto_txt = {
std::byte{0x48}, std::byte{0x65}, std::byte{0x6C}, std::byte{0x6C}, std::byte{0x6F}, std::byte{0x20}, std::byte{0x57}, std::byte{0x6F}, std::byte{0x72}, std::byte{0x6C}, std::byte{0x64}
};
} // namespace
#endif // TOTO_TXT_HPP
)";
EXPECT_STREQ(expectedResult.data(), generator.Get().data());
}
/****************************************************************************************/
TEST_F(GeneratorFixture, GenerateNamespace)
{
Generator generator("Fixture/toto.txt");
generator.SetMapping(MappingKey::NAMESPACE, "Test");
EXPECT_TRUE(generator.Generate());
constexpr std::string_view expectedResult = R"(/**
* Auto-generated header from: toto.txt
* Generated by Bin2CPP
*
* DO NOT TOUCH
*/
#ifndef TOTO_TXT_HPP
#define TOTO_TXT_HPP
#include <array>
#include <cstddef>
namespace Test
{
inline constexpr std::array<std::byte, 11> g_toto_txt = {
std::byte{0x48}, std::byte{0x65}, std::byte{0x6C}, std::byte{0x6C}, std::byte{0x6F}, std::byte{0x20}, std::byte{0x57}, std::byte{0x6F}, std::byte{0x72}, std::byte{0x6C}, std::byte{0x64}
};
} // namespace Test
#endif // TOTO_TXT_HPP
)";
EXPECT_STREQ(expectedResult.data(), generator.Get().data());
}
/****************************************************************************************/
TEST_F(GeneratorFixture, GenerateCustomArray)
{
Generator generator("Fixture/toto.txt");
generator.SetMapping(MappingKey::ARRAY_TYPE, "eastl::array");
generator.SetMapping(MappingKey::ARRAY_TYPE_INCLUDE, "<EASTL/array.h>");
generator.SetMapping(MappingKey::ARRAY_NAME, "g_myArray");
EXPECT_TRUE(generator.Generate());
constexpr std::string_view expectedResult = R"(/**
* Auto-generated header from: toto.txt
* Generated by Bin2CPP
*
* DO NOT TOUCH
*/
#ifndef TOTO_TXT_HPP
#define TOTO_TXT_HPP
#include <EASTL/array.h>
#include <cstddef>
namespace
{
inline constexpr eastl::array<std::byte, 11> g_myArray = {
std::byte{0x48}, std::byte{0x65}, std::byte{0x6C}, std::byte{0x6C}, std::byte{0x6F}, std::byte{0x20}, std::byte{0x57}, std::byte{0x6F}, std::byte{0x72}, std::byte{0x6C}, std::byte{0x64}
};
} // namespace
#endif // TOTO_TXT_HPP
)";
EXPECT_STREQ(expectedResult.data(), generator.Get().data());
}
/****************************************************************************************/
TEST_F(GeneratorFixture, NonExistentFile)
{
Generator generator("Fixture/tata.txt");
EXPECT_FALSE(generator.Generate());
}
/****************************************************************************************/
TEST_F(GeneratorFixture, Directory)
{
Generator generator("Fixture/");
EXPECT_FALSE(generator.Generate());
}
} // namespace Bin2CPP
+76
View File
@@ -0,0 +1,76 @@
/*********************************************************************
* \file Log.cpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#include <Log.hpp>
#include <gtest/gtest.h>
#include <Assert.hpp>
namespace Bin2CPP
{
class LogFixture: public ::testing::Test
{
protected:
Log m_log;
};
/****************************************************************************************/
TEST_F(LogFixture, GetLogger_ShouldReturnTheLogger)
{
EXPECT_TRUE(m_log.GetLogger());
}
/****************************************************************************************/
TEST_F(LogFixture, LogDebug)
{
Singleton<Log>::Lifetime singletonLifetime;
BIN2CPP_LOG_DEBUG("Hello");
}
/****************************************************************************************/
TEST_F(LogFixture, LogTrace)
{
Singleton<Log>::Lifetime singletonLifetime;
BIN2CPP_LOG_TRACE("Hello");
}
/****************************************************************************************/
TEST_F(LogFixture, LogInfo)
{
Singleton<Log>::Lifetime singletonLifetime;
BIN2CPP_LOG_INFO("Hello");
}
/****************************************************************************************/
TEST_F(LogFixture, LogWarn)
{
Singleton<Log>::Lifetime singletonLifetime;
BIN2CPP_LOG_WARN("Hello");
}
/****************************************************************************************/
TEST_F(LogFixture, LogError)
{
Singleton<Log>::Lifetime singletonLifetime;
BIN2CPP_LOG_ERROR("Hello");
}
/****************************************************************************************/
TEST_F(LogFixture, LogFatal)
{
Singleton<Log>::Lifetime singletonLifetime;
BIN2CPP_LOG_FATAL("Hello");
}
} // namespace Bin2CPP
+39
View File
@@ -0,0 +1,39 @@
/*********************************************************************
* \file Singleton.cpp
*
* \author Romain BOULLARD
* \date February 2026
*********************************************************************/
#include <Singleton.hpp>
#include <gtest/gtest.h>
namespace Bin2CPP
{
class SingletonFixture: public ::testing::Test
{
protected:
Singleton<std::uint8_t>::Lifetime m_lifetime {8};
};
/****************************************************************************************/
TEST_F(SingletonFixture, HasInstance_ShouldReturnTrueIfSingletonIsInitialized)
{
EXPECT_TRUE(Singleton<std::uint8_t>::HasInstance());
}
/****************************************************************************************/
TEST_F(SingletonFixture, HasInstance_ShouldReturnFaleIfSingletonIsNotInitialized)
{
EXPECT_FALSE(Singleton<std::uint32_t>::HasInstance());
}
/****************************************************************************************/
TEST_F(SingletonFixture, Instance_ShouldReturnTheInstance)
{
EXPECT_EQ(Singleton<std::uint8_t>::Instance(), 8);
}
} // namespace Bin2CPP
+1
View File
@@ -0,0 +1 @@
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bin2CPPLib)
+23
View File
@@ -0,0 +1,23 @@
if(NOT TARGET Bin2CPP)
if(CMAKE_CROSSCOMPILING)
find_program(BIN2CPP_EXECUTABLE
NAMES Bin2CPP
PATHS ENV PATH
NO_DEFAULT_PATH
)
else()
find_program(BIN2CPP_EXECUTABLE
NAMES Bin2CPP
PATHS "${CMAKE_CURRENT_LIST_DIR}"
NO_DEFAULT_PATH
)
endif()
if(BIN2CPP_EXECUTABLE)
get_filename_component(BIN2CPP_EXECUTABLE "${BIN2CPP_EXECUTABLE}" ABSOLUTE)
add_executable(Bin2CPP IMPORTED)
set_property(TARGET Bin2CPP PROPERTY IMPORTED_LOCATION ${BIN2CPP_EXECUTABLE})
else()
message(WARNING "Bin2CPP executable not found in ${CMAKE_CURRENT_LIST_DIR}")
endif()
endif()
+19
View File
@@ -0,0 +1,19 @@
add_library(Bin2CPPCompileAndLinkFlags INTERFACE)
target_compile_options(Bin2CPPCompileAndLinkFlags INTERFACE
$<$<CXX_COMPILER_ID:MSVC>:/W4 /WX>
$<$<CXX_COMPILER_ID:Clang,GNU>:-Wall -Wextra -Wpedantic -Werror>
$<$<AND:$<BOOL:${COVERAGE}>,$<CXX_COMPILER_ID:Clang>>:-fprofile-instr-generate -fcoverage-mapping>
)
target_link_options(Bin2CPPCompileAndLinkFlags INTERFACE
$<$<AND:$<BOOL:${COVERAGE}>,$<CXX_COMPILER_ID:Clang>>:-fprofile-instr-generate>
)
target_compile_definitions(Bin2CPPCompileAndLinkFlags INTERFACE
$<$<CONFIG:Release>:BIN2CPP_OPTIMIZED>
$<$<CONFIG:Debug,RelWithDebInfo>:BIN2CPP_NOT_OPTIMIZED>
$<$<PLATFORM_ID:Windows>:BIN2CPP_WINDOWS>
$<$<PLATFORM_ID:Linux>:BIN2CPP_LINUX>
$<$<PLATFORM_ID:Windows>:NOMINMAX>
$<$<PLATFORM_ID:Windows>:WIN32_LEAN_AND_MEAN>)
+14
View File
@@ -0,0 +1,14 @@
find_package(Python3 COMPONENTS Interpreter Development)
if(CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(THREADS_PREFER_PTHREAD_FLAG ON)
find_package(Threads REQUIRED)
endif()
find_package(magic_enum REQUIRED)
find_package(CLI11 REQUIRED)
find_package(quill REQUIRED)
if(BUILD_TESTS)
find_package(GTest REQUIRED)
endif()
+36
View File
@@ -0,0 +1,36 @@
cmake_minimum_required(VERSION 3.26)
project(Bin2CPP VERSION 1.0.0
DESCRIPTION "Bin2CPP"
LANGUAGES CXX)
set(CMAKE_POLICY_DEFAULT_CMP0077 NEW)
get_property(IS_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
include(${CMAKE_SOURCE_DIR}/CMake/CustomTargets.cmake)
include(${CMAKE_SOURCE_DIR}/CMake/FindDependencies.cmake)
set(CMAKE_CONFIGURATION_TYPES "Release;RelWithDebInfo;Debug" CACHE STRING "" FORCE)
option(BUILD_TESTS OFF)
option(ASAN OFF)
option(COVERAGE OFF)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_OPTIMIZE_DEPENDENCIES 1)
if(BUILD_TESTS)
enable_testing()
endif()
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
SET(CMAKE_SKIP_BUILD_RPATH FALSE)
SET(CMAKE_BUILD_WITH_INSTALL_RPATH TRUE)
SET(CMAKE_INSTALL_RPATH "\${ORIGIN}")
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bin2CPP/Sources)
if(${BUILD_TESTS})
add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/Bin2CPP/Tests)
endif()
+5
View File
@@ -0,0 +1,5 @@
include_guard()
set(CMAKE_POLICY_DEFAULT_CMP0069 NEW)
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION_RELEASE TRUE)
+2
View File
@@ -0,0 +1,2 @@
[built-in options]
b_lto = true
+9
View File
@@ -0,0 +1,9 @@
include_guard()
find_program(MOLD_PROGRAM mold)
if (MOLD_PROGRAM)
set(CMAKE_LINKER_TYPE MOLD)
message(STATUS "mold linker found: ${MOLD_PROGRAM}")
else ()
message(WARNING "mold linker not found")
endif ()
+19
View File
@@ -0,0 +1,19 @@
[settings]
os=Linux
arch=x86_64
compiler=clang
compiler.version=20
compiler.libcxx=libstdc++11
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Release
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/../Toolchains/ipo.cmake
tools.meson.mesontoolchain:extra_machine_files+={{profile_dir}}/../Toolchains/ipo.ini
tools.system.package_manager:mode=install
tools.system.package_manager:sudo=True
tools.build:compiler_executables={"c": "clang", "cpp": "clang++"}
+18
View File
@@ -0,0 +1,18 @@
[settings]
os=Windows
arch=x86_64
compiler=msvc
compiler.version=195
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Release
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/../Toolchains/ipo.cmake
tools.meson.mesontoolchain:extra_machine_files+={{profile_dir}}/../Toolchains/ipo.ini
tools.build:cflags=["/Zc:preprocessor", "/Zc:__STDC__", "/D_CRT_DECLARE_NONSTDC_NAMES=1"]
tools.build:cxxflags=["/Zc:preprocessor", "/permissive-", "/Zc:__cplusplus", "/Zc:enumTypes", "/Zc:templateScope"]
tools.env.virtualenv:powershell=powershell.exe
+30
View File
@@ -0,0 +1,30 @@
[settings]
os=Linux
arch=x86_64
compiler=clang
compiler.version=20
compiler.libcxx=libstdc++11
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Release
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/Toolchains/mold.cmake
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/Toolchains/ipo.cmake
tools.meson.mesontoolchain:extra_machine_files+={{profile_dir}}/Toolchains/ipo.ini
tools.system.package_manager:mode=install
tools.system.package_manager:sudo=True
tools.build:compiler_executables={"c": "clang", "cpp": "clang++"}
tools.cmake.cmaketoolchain:generator=Ninja
[tool_requires]
!cmake/*: cmake/4.3.2
!mold/*: mold/2.41.0@bigfootdev/main
!ninja/*: ninja/1.13.2
[options]
bin2cpp/*:build_tests=True
+35
View File
@@ -0,0 +1,35 @@
[settings]
os=Linux
arch=x86_64
compiler=clang
compiler.version=20
compiler.libcxx=libstdc++11
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Debug
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/Toolchains/mold.cmake
tools.system.package_manager:mode=install
tools.system.package_manager:sudo=True
tools.build:exelinkflags=["-fsanitize=address,undefined,leak"]
tools.build:sharedlinkflags=["-fsanitize=address,undefined,leak"]
tools.build:cflags=["-fsanitize=address,undefined,leak", "-fno-sanitize-recover=all"]
tools.build:cxxflags=["-fsanitize=address,undefined,leak", "-fno-sanitize-recover=all"]
tools.cmake.cmaketoolchain:generator=Ninja
tools.build:compiler_executables={"c": "clang", "cpp": "clang++"}
[tool_requires]
!cmake/*: cmake/4.3.2
!mold/*: mold/2.41.0@bigfootdev/main
!ninja/*: ninja/1.13.2
[options]
bin2cpp/*:asan=True
bin2cpp/*:build_tests=True
+29
View File
@@ -0,0 +1,29 @@
[settings]
os=Linux
arch=x86_64
compiler=clang
compiler.version=20
compiler.libcxx=libstdc++11
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Debug
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/Toolchains/mold.cmake
tools.system.package_manager:mode=install
tools.system.package_manager:sudo=True
tools.cmake.cmaketoolchain:generator=Ninja
tools.build:compiler_executables={"c": "clang", "cpp": "clang++"}
[tool_requires]
!cmake/*: cmake/4.3.2
!mold/*: mold/2.41.0@bigfootdev/main
!ninja/*: ninja/1.13.2
[options]
bin2cpp/*:build_tests=True
bin2cpp/*:coverage=True
+24
View File
@@ -0,0 +1,24 @@
[settings]
os=Windows
arch=x86_64
compiler=msvc
compiler.version=195
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Release
[conf]
tools.cmake.cmaketoolchain:user_toolchain+={{profile_dir}}/Toolchains/ipo.cmake
tools.meson.mesontoolchain:extra_machine_files+={{profile_dir}}/Toolchains/ipo.ini
tools.build:cflags=["/Zc:preprocessor", "/Zc:__STDC__", "/D_CRT_DECLARE_NONSTDC_NAMES=1"]
tools.build:cxxflags=["/Zc:preprocessor", "/permissive-", "/Zc:__cplusplus", "/Zc:enumTypes", "/Zc:templateScope", "/Zc:throwingNew"]
tools.env.virtualenv:powershell=powershell.exe
[tool_requires]
!cmake/*: cmake/4.3.2
[options]
bin2cpp/*:build_tests=True
+22
View File
@@ -0,0 +1,22 @@
[settings]
os=Windows
arch=x86_64
compiler=msvc
compiler.version=195
compiler.cppstd=20
compiler.cstd=17
compiler.runtime=static
build_type=Debug
[conf]
tools.build:cflags=["/Zc:preprocessor", "/Zc:__STDC__", "/D_CRT_DECLARE_NONSTDC_NAMES=1", "/fsanitize=address"]
tools.build:cxxflags=["/Zc:preprocessor", "/permissive-", "/Zc:__cplusplus", "/Zc:enumTypes", "/Zc:templateScope", "/Zc:throwingNew", "/fsanitize=address"]
tools.env.virtualenv:powershell=powershell.exe
[tool_requires]
!cmake/*: cmake/4.3.2
[options]
bin2cpp/*:asan=True
bin2cpp/*:build_tests=True
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2026 Romain BOULLARD
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
+84
View File
@@ -0,0 +1,84 @@
from conan import ConanFile
from conan.tools.cmake import CMakeToolchain, CMake, cmake_layout
from conan.tools.files import copy
import os
required_conan_version = ">=1.33.0"
class Bin2CPP(ConanFile):
name = "bin2cpp"
homepage = "https://git.romainboullard.com/rboullard/Bin2CPP"
description = "A utility that converts files to CPP headers"
topics = ("utility")
license = "MIT"
version = "1.0.0"
package_type = "application"
# Binary configuration
settings = "os", "compiler", "build_type", "arch"
options = {
"shared": [True, False],
"fPIC": [True, False],
"build_tests": [True, False],
"asan": [True, False],
"coverage": [True, False]
}
default_options = {
"shared": False,
"fPIC": True,
"build_tests": False,
"asan": False,
"coverage": False
}
generators = "CMakeDeps"
def export_sources(self):
copy(self, "CMakeLists.txt", self.recipe_folder, self.export_sources_folder)
copy(self, os.path.join("Bin2CPP", "*"), self.recipe_folder, self.export_sources_folder)
copy(self, os.path.join("CMake", "*"), self.recipe_folder, self.export_sources_folder)
def layout(self):
cmake_layout(self)
def configure(self):
if self.settings.os == "Windows":
del self.options.fPIC
def requirements(self):
self.requires("quill/11.0.2", transitive_headers=True)
self.requires("magic_enum/0.9.7", transitive_headers=True)
self.requires("cli11/2.6.1@bigfootdev/main")
if(self.options.build_tests):
self.test_requires("gtest/1.17.0")
def generate(self):
tc = CMakeToolchain(self)
tc.variables["BUILD_TESTS"] = self.options.build_tests
tc.variables["ASAN"] = self.options.asan
tc.variables["COVERAGE"] = self.options.coverage
tc.generate()
def build(self):
cmake = CMake(self)
cmake.configure()
cmake.build()
def package(self):
copy(self, pattern="LICENSE", dst=os.path.join(self.package_folder, "licenses"), src=self.source_folder)
cmake = CMake(self)
cmake.install()
copy(self, "Bin2CPPTargets.cmake",
src=os.path.join(self.source_folder, "CMake"),
dst=os.path.join(self.package_folder, "bin"))
@property
def _module_path(self):
return os.path.join("bin", "Bin2CPPTargets.cmake")
def package_info(self):
self.cpp_info.set_property("cmake_build_modules", [self._module_path])
+71
View File
@@ -0,0 +1,71 @@
@echo off
SETLOCAL ENABLEDELAYEDEXPANSION
REM =========================
REM Variables
REM =========================
SET FMT=clang-format
SET MODE=
SET EXIT_CODE=0
REM =========================
REM Parse arguments
REM =========================
:parse_args
IF "%~1"=="" GOTO end_parse_args
IF "%~1"=="--check" (
SET MODE=check
) ELSE IF "%~1"=="--fix" (
SET MODE=fix
) ELSE (
REM Accumulate directories
SET DIRS=!DIRS! "%~1"
)
SHIFT
GOTO parse_args
:end_parse_args
IF "%MODE%"=="" (
ECHO Usage: %~nx0 --check|--fix <directory> [<directory> ...]
EXIT /B 1
)
IF "%DIRS%"=="" (
ECHO Please provide at least one directory.
EXIT /B 1
)
REM =========================
REM Iterate over directories
REM =========================
FOR %%D IN (%DIRS%) DO (
IF NOT EXIST "%%~D" (
ECHO %%~D is not a valid directory.
SET EXIT_CODE=1
GOTO :continue_dirs
)
REM Recursively find source files
FOR /R "%%~D" %%F IN (*.h *.hpp *.c *.cpp *.m *.mm) DO (
SET FILE=%%F
REM Skip *_generated* files
ECHO !FILE! | FINDSTR /I "_generated" >nul
IF ERRORLEVEL 1 (
IF "%MODE%"=="fix" (
ECHO Formatting !FILE!
%FMT% -i "!FILE!"
) ELSE (
ECHO Checking !FILE!
%FMT% --dry-run --Werror "!FILE!" 2>nul
IF ERRORLEVEL 1 (
REM clang-format will already print diagnostics
SET EXIT_CODE=1
)
)
)
)
:continue_dirs
)
EXIT /B %EXIT_CODE%
+68
View File
@@ -0,0 +1,68 @@
#!/bin/bash
set -euo pipefail
FMT="clang-format"
MODE=""
DIRS=()
EXIT_CODE=0
# =========================
# Parse arguments
# =========================
for arg in "$@"; do
case "$arg" in
--check)
MODE="check"
;;
--fix)
MODE="fix"
;;
*)
DIRS+=("$arg")
;;
esac
done
# Ensure mode is set
if [[ -z "$MODE" ]]; then
echo "Usage: $0 --check|--fix <directory> [<directory> ...]"
exit 1
fi
# Ensure at least one directory
if [[ ${#DIRS[@]} -eq 0 ]]; then
echo "Please provide at least one directory."
exit 1
fi
# =========================
# Process directories
# =========================
for DIR in "${DIRS[@]}"; do
if [[ ! -d "$DIR" ]]; then
echo "$DIR is not a valid directory."
EXIT_CODE=1
continue
fi
# Find all source files safely (null-separated)
while IFS= read -r -d '' FILE; do
[[ "$FILE" == *_generated* ]] && continue
if [[ "$MODE" == "fix" ]]; then
echo "Formatting $FILE"
"$FMT" -i "$FILE"
else
echo "Checking $FILE"
# Check mode: clang-format diagnostic only
if ! "$FMT" --dry-run --Werror "$FILE"; then
# clang-format prints: file:line:col: error ...
EXIT_CODE=1
fi
fi
done < <(
find "$DIR" \( -name '*.h' -o -name '*.hpp' -o -name '*.c' -o -name '*.cpp' -o -name '*.m' -o -name '*.mm' \) -print0
)
done
exit $EXIT_CODE
+60
View File
@@ -0,0 +1,60 @@
@echo off
setlocal
REM ─── Validate argument ───────────────────────────────────────────────────────
if "%~1"=="force" set "build_option=--build=*" & goto :start
if "%~1"=="missing" set "build_option=--build=missing" & goto :start
echo Usage: %~n0 [force^|missing]
echo force - Rebuild all packages from source
echo missing - Only build packages not already cached
exit /b 1
:start
REM ─── Register remote (skip if already registered) ────────────────────────────
conan remote list | findstr /i "bigfootpackages" >nul 2>&1
if errorlevel 1 (
echo Adding Conan remote: bigfootpackages
conan remote add bigfootpackages https://conan.romainboullard.com/artifactory/api/conan/BigfootPackages
if errorlevel 1 (
echo ERROR: Failed to add Conan remote.
exit /b 1
)
) else (
echo Conan remote 'bigfootpackages' already registered, skipping.
)
REM ─── Shared flags ────────────────────────────────────────────────────────────
set "conan_common=--remote=bigfootpackages -pr:h=./ConanProfiles/msvc -pr:b=./ConanProfiles/Tools/msvc -of build"
REM ─── Install dependencies for each build type ────────────────────────────────
for %%C in (Release RelWithDebInfo Debug) do (
echo.
echo [%%C] Installing dependencies...
conan install . %conan_common% %build_option% -s:h build_type=%%C
if errorlevel 1 (
echo ERROR: conan install failed for build type %%C
exit /b 1
)
echo [%%C] Done.
)
echo.
echo All build types installed successfully.
REM ─── Activate build environment and run CMake ────────────────────────────────
echo.
echo Activating build environment and configuring CMake...
mkdir graphviz 2>nul
powershell -ExecutionPolicy Bypass -Command "& 'build/build/generators/conanbuild.ps1'; cmake -S . -B build --toolchain build/build/generators/conan_toolchain.cmake --graphviz=graphviz/graph.dot; if ($LASTEXITCODE -ne 0) { Write-Host 'ERROR: CMake configuration failed'; exit 1 }"
if errorlevel 1 (
echo ERROR: Build environment or CMake step failed.
exit /b 1
)
echo.
echo CMake configuration successful.
endlocal
+100
View File
@@ -0,0 +1,100 @@
#!/usr/bin/env bash
set -euo pipefail
# ─── Validate arguments ───────────────────────────────────────────────────────
if [[ $# -lt 1 ]]; then
echo "Usage: $(basename "$0") [force|missing] [Release|RelWithDebInfo|Debug]"
echo " force - Rebuild all packages from source"
echo " missing - Only build packages not already cached"
echo " build_type (optional) - One of: Release, RelWithDebInfo, Debug"
echo " If omitted, all build types are processed"
exit 1
fi
case "${1:-}" in
force) build_option="--build=*" ;;
missing) build_option="--build=missing" ;;
*)
echo "Usage: $(basename "$0") [force|missing] [Release|RelWithDebInfo|Debug]"
echo " force - Rebuild all packages from source"
echo " missing - Only build packages not already cached"
exit 1
;;
esac
# ─── Validate optional build_type argument ────────────────────────────────────
all_build_types=(Release RelWithDebInfo Debug)
if [[ -n "${2:-}" ]]; then
case "${2}" in
Release|RelWithDebInfo|Debug)
build_types=("${2}")
;;
*)
echo "ERROR: Invalid build type '${2}'. Must be one of: Release, RelWithDebInfo, Debug"
exit 1
;;
esac
else
build_types=("${all_build_types[@]}")
fi
# ─── Register remote (skip if already registered) ─────────────────────────────
if conan remote list 2>/dev/null | grep -qi "bigfootpackages"; then
echo "Conan remote 'bigfootpackages' already registered, skipping."
else
echo "Adding Conan remote: bigfootpackages"
if ! conan remote add bigfootpackages https://conan.romainboullard.com/artifactory/api/conan/BigfootPackages; then
echo "ERROR: Failed to add Conan remote."
exit 1
fi
fi
# ─── Shared flags ─────────────────────────────────────────────────────────────
conan_common=(
--remote=bigfootpackages
-pr:h=./ConanProfiles/clang
-pr:b=./ConanProfiles/Tools/clang
-of build
)
# ─── Install dependencies for each build type ─────────────────────────────────
for build_type in "${build_types[@]}"; do
echo
echo "[${build_type}] Installing dependencies..."
if ! conan install . "${conan_common[@]}" ${build_option} -s:h "build_type=${build_type}"; then
echo "ERROR: conan install failed for build type ${build_type}"
exit 1
fi
echo "[${build_type}] Done."
done
echo
echo "All selected build types installed successfully."
# ─── Configure CMake for each build type ─────────────────────────────────────
echo
echo "Configuring CMake for each build type..."
for build_type in "${build_types[@]}"; do
echo
echo "[${build_type}] Activating build environment..."
# shellcheck disable=SC1090
source "build/build/${build_type}/generators/conanbuild.sh"
mkdir -p "graphviz/${build_type}"
echo "[${build_type}] Running CMake..."
if ! cmake -S . -B "build/${build_type}" \
--toolchain "build/build/${build_type}/generators/conan_toolchain.cmake" \
-DCMAKE_BUILD_TYPE="${build_type}" \
-G "Ninja" \
--graphviz="graphviz/${build_type}/graph.dot"; then
echo "ERROR: CMake configuration failed for build type ${build_type}."
exit 1
fi
echo "[${build_type}] Done."
done
echo
echo "CMake configuration successful."
+14
View File
@@ -0,0 +1,14 @@
sonar.projectKey=Bin2CPP
sonar.projectVersion=1.0.0
sonar.sourceEncoding=UTF-8
sonar.sources=Bin2CPP/Sources
sonar.tests=Bin2CPP/Tests
sonar.cxx.file.suffixes=.hpp,.cpp,.h
sonar.cxx.clangtidy.reportPaths=tidy_result.txt
sonar.cxx.infer.reportPaths=infer-out/report.json
sonar.cxx.jsonCompilationDatabase.analyzeOnlyContainedFiles=True
+7
View File
@@ -0,0 +1,7 @@
cmake_minimum_required(VERSION 3.15)
project(test_package LANGUAGES C)
find_package(Bin2CPP REQUIRED CONFIG)
find_program(BIN2CPP_EXECUTABLE NAMES Bin2CPP)
execute_process(COMMAND ${BIN2CPP_EXECUTABLE} -h)
+18
View File
@@ -0,0 +1,18 @@
from conan import ConanFile
from conan.tools.build import can_run
from conan.tools.cmake import CMake, cmake_layout
import os
class TestPackageConan(ConanFile):
settings = "os", "arch", "compiler", "build_type"
generators = "CMakeToolchain", "CMakeDeps", "VirtualRunEnv"
def layout(self):
cmake_layout(self)
def requirements(self):
self.requires(self.tested_reference_str)
def test(self):
self.run("Bin2CPP --help", env="conanrun")