Stage topic 'bin2c'

Topic-id: 11536
Topic-url: https://gitlab.kitware.com/cmake/cmake/-/merge_requests/11536
This commit is contained in:
Kyle Edwards 2026-01-17 15:38:21 +00:00 committed by Kitware Robot
commit 6b9ebd0447
125 changed files with 6029 additions and 0 deletions

View File

@ -6,6 +6,7 @@ set(CMake_TEST_CTestUpdate_GIT "ON" CACHE BOOL "")
set(CMake_TEST_CTestUpdate_HG "ON" CACHE BOOL "")
set(CMake_TEST_CTestUpdate_SVN "ON" CACHE BOOL "")
if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
set(CMake_TEST_BIN2C_LARGE_FILE "ON" CACHE BOOL "")
set(CMake_TEST_CTestUpdate_P4 "ON" CACHE BOOL "")
endif()

View File

@ -2,6 +2,7 @@ set(CMake_TEST_C_STANDARDS "90;99;11;17;23" CACHE STRING "")
set(CMake_TEST_CXX_STANDARDS "98;11;14;17;20;23" CACHE STRING "")
if (NOT "$ENV{CMAKE_CI_NIGHTLY}" STREQUAL "")
set(CMake_TEST_BIN2C_LARGE_FILE "ON" CACHE BOOL "")
set(CMake_TEST_CPACK_INNOSETUP "ON" CACHE STRING "")
set(CMake_TEST_CPACK_NUGET "ON" CACHE STRING "")
set(CMake_TEST_IAR_TOOLCHAINS "$ENV{CI_PROJECT_DIR}/.gitlab/iar" CACHE PATH "")

View File

@ -1027,6 +1027,138 @@ CMake provides builtin command-line tools through the signature
Available commands are:
.. option:: bin2c [<options>...] [--] [<input file> [<output file>]]
.. versionadded:: 4.3
Convert a binary file to a C array. If input file is unspecified or ``-``,
read from standard input instead of a file. If output file is unspecified or
``-``, write to standard output instead of a file.
By default, this prints only the bytes and does not define an enclosing
array. An array can optionally be defined by specifying ``--array-name``.
Or, the bytes can be combined with a header and footer by calling
``cmake -E cat``. You can also ``#include`` the bytes from another file,
acting as a drop-in replacement for the ``#embed`` directive from C23 and
C++26:
.. code-block:: c
unsigned char my_bytes[] = {
/* #embed "bin2c_input.bin" */
#include "bin2c_output.c.txt"
};
.. program:: cmake-E_bin2c
.. option:: --array-name <array name>
Name of the enclosing array. Can be specified as a class member, for
example ``myclass::contents``.
.. option:: --size-name <size name>
Name of a ``size_t`` holding the number of elements in the array (not the
``sizeof`` the array). Can be specified as a class member, for example
``myclass::size``.
.. option:: --signed
Print the bytes as signed integers rather than unsigned.
.. option:: --static
Define the array and optional size as ``static``. Cannot be used if the
array or size is a class member.
.. option:: --const
Define the array and optional size as ``const``.
.. option:: --namespace [<namespace name>]
Specify the namespace to enclose the array and optional size in. Can be
nested, for example ``ns::nested``. If ``--namespace`` is passed with no
namespace name, an anonymous namespace is used.
.. option:: --extern-c
Define the array and optional size as ``extern "C"``. Cannot be used if
the array or size is a class member.
.. option:: --macro <macro>
Prepend a macro to the definition of the array and optional size.
.. option:: --include <include file>
``#include`` a header at the top of the file. If the argument is not
enclosed in quotes (``"``) or brackets (``<``, ``>``) then quotes are
automatically added.
.. option:: --no-auto-gen-comment
Do not prepend the file with a comment stating that the file was
automatically generated by ``cmake -E bin2c``.
.. option:: --trailing-comma
Append a trailing comma after the last byte (not included by default.)
.. option:: --array-type <type>
Set the type of the array elements. Set to ``unsigned char`` (or ``char``
if ``--signed`` is used) by default.
.. option:: --array-static
Same as ``--static``, but only for the array.
.. option:: --array-const
Same as ``--const``, but only for the array.
.. option:: --array-namespace [<namespace name>]
Same as ``--namespace``, but only for the array.
.. option:: --array-extern-c
Same as ``--extern-c``, but only for the array.
.. option:: --array-macro <macro>
Same as ``--macro``, but only for the array.
.. option:: --size-type <type>
Set the type of the size. Set to ``size_t`` by default. If not set,
``<stdlib.h>`` will automatically be included for ``size_t``. If
explicitly set to ``size_t``, ``<stdlib.h>`` will not be automatically
included, and must be specified with ``--include``.
.. option:: --size-static
Same as ``--static``, but only for the size.
.. option:: --size-const
Same as ``--const``, but only for the size.
.. option:: --size-namespace [<namespace name>]
Same as ``--namespace``, but only for the size.
.. option:: --size-extern-c
Same as ``--extern-c``, but only for the size.
.. option:: --size-macro <macro>
Same as ``--macro``, but only for the size.
.. program:: cmake-E
.. option:: capabilities
.. versionadded:: 3.7

View File

@ -0,0 +1,4 @@
E_bin2c
-------
:manual:`cmake -E <cmake(1)>` gained a new ``bin2c`` mode.

View File

@ -4376,3 +4376,23 @@ char cmSystemTools::GetSystemPathlistSeparator()
return ':';
#endif
}
bool cmSystemTools::IsValidCIdentifier(cm::string_view str)
{
if (str.empty()) {
return false;
}
auto const isAlphaUnderscore = [](char c) -> bool {
return c == '_' || (c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z');
};
auto const isAlphaNumericUnderscore = [&isAlphaUnderscore](char c) -> bool {
return isAlphaUnderscore(c) || (c >= '0' && c <= '9');
};
if (!isAlphaUnderscore(str.front())) {
return false;
}
auto const rest = str.substr(1);
return std::all_of(rest.begin(), rest.end(), isAlphaNumericUnderscore);
}

View File

@ -705,6 +705,9 @@ public:
/** Get the system path separator character */
static char GetSystemPathlistSeparator();
/** Determine whether a string is a valid C identifier */
static bool IsValidCIdentifier(cm::string_view str);
private:
static bool s_ForceUnixPaths;
static bool s_RunCommandHideConsole;

View File

@ -2,6 +2,7 @@
file LICENSE.rst or https://cmake.org/licensing for details. */
#include "cmcmd.h"
#include <cassert>
#include <functional>
#include <iomanip>
#include <iterator>
@ -9,6 +10,7 @@
#include <cm/optional>
#include <cmext/algorithm>
#include <cmext/string_view>
#include <cm3p/uv.h>
#include <fcntl.h>
@ -16,6 +18,7 @@
#include "cmCommandLineArgument.h"
#include "cmCryptoHash.h"
#include "cmDuration.h"
#include "cmGeneratedFileStream.h"
#include "cmGlobalGenerator.h"
#include "cmList.h"
#include "cmLocalGenerator.h"
@ -60,6 +63,7 @@
#include <array>
#include <chrono>
#include <cstdint>
#include <cstdio>
#include <cstdlib>
#include <cstring>
@ -86,10 +90,18 @@ int cmcmd_cmake_module_compile_db(
std::vector<std::string>::const_iterator argBeg,
std::vector<std::string>::const_iterator argEnd);
std::ostream& operator<<(
std::ostream& stream,
std::function<std::ostream&(std::ostream&)> const& func)
{
return func(stream);
}
namespace {
// ATTENTION If you add new commands, change here,
// and in `cmakemain.cxx` in the options table
char const* const HELP_AVAILABLE_COMMANDS = R"(Available commands:
bin2c - Turn a binary file into a C array
capabilities - Report capabilities built into cmake in JSON format
cat [--] <files>... - concat the files and print them to the standard output
chdir dir cmd [args...] - run command in a given directory
@ -676,6 +688,336 @@ struct CoCompileJob
std::string Command;
CoCompileHandler Handler;
};
struct Bin2CDefinition
{
cm::optional<std::string> Type;
std::string Name;
bool Static = false;
bool Const = false;
cm::optional<std::vector<cm::string_view>> Namespace;
bool ExternC = false;
cm::optional<std::string> Macro;
};
class Bin2CSectionTracker
{
public:
Bin2CSectionTracker(std::ostream& sout)
: Sout(sout)
{
}
Bin2CSectionTracker(Bin2CSectionTracker const&) = delete;
Bin2CSectionTracker(Bin2CSectionTracker&&) = delete;
void Start()
{
assert(this->CurrentState != State::InProgress);
if (this->CurrentState == State::End) {
this->Sout << "\n";
}
this->CurrentState = State::InProgress;
}
void End()
{
#if defined(__GNUC__) && !defined(__clang__)
# define CM_GCC_diagnostic_push_Wmaybe_uninitialized
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
#endif
assert(this->CurrentState == State::InProgress);
this->CurrentState = State::End;
#ifdef CM_GCC_diagnostic_push_Wmaybe_uninitialized
# pragma GCC diagnostic pop
# undef CM_GCC_diagnostic_push_Wmaybe_uninitialized
#endif
}
private:
enum class State
{
Start,
InProgress,
End,
};
std::ostream& Sout;
State CurrentState = State::Start;
};
class Bin2CSectionGuard
{
public:
Bin2CSectionGuard(Bin2CSectionTracker& tracker)
: Tracker(tracker)
{
this->Tracker.Start();
}
Bin2CSectionGuard(Bin2CSectionGuard const&) = delete;
~Bin2CSectionGuard() { this->Tracker.End(); }
private:
Bin2CSectionTracker& Tracker;
};
class Bin2CDefGuard
{
public:
Bin2CDefGuard(std::ostream& sout, Bin2CSectionTracker& sectionTracker,
Bin2CDefinition const& def, cm::string_view defaultType,
cm::string_view arraySuffix = ""_s)
: Sout(sout)
, SectionTracker(sectionTracker)
, Def(def)
{
if (this->Def.ExternC || this->Def.Namespace) {
Bin2CSectionGuard guard{ this->SectionTracker };
(void)guard;
if (this->Def.ExternC) {
this->Sout << "#ifdef __cplusplus\nextern \"C\" {\n";
}
if (this->Def.Namespace) {
if (this->Def.Namespace->empty()) {
this->Sout << "namespace {\n";
} else {
for (auto const& component : *this->Def.Namespace) {
this->Sout << "namespace " << component << " {\n";
}
}
}
if (this->Def.ExternC) {
this->Sout << "#endif /* __cplusplus */\n";
}
}
this->Guard.emplace(this->SectionTracker);
if (this->Def.Macro) {
this->Sout << *def.Macro << " ";
}
if (this->Def.Static) {
this->Sout << "static ";
}
if (this->Def.Const) {
this->Sout << "const ";
}
this->Sout << (this->Def.Type ? *this->Def.Type : defaultType) << " "
<< this->Def.Name << arraySuffix << " = ";
}
Bin2CDefGuard(Bin2CDefGuard const&) = delete;
~Bin2CDefGuard()
{
this->Sout << ";";
if (this->EndComment) {
this->Sout << " /* " << *this->EndComment << " */";
}
this->Sout << "\n";
this->Guard.reset();
if (this->Def.ExternC || this->Def.Namespace) {
Bin2CSectionGuard guard{ this->SectionTracker };
(void)guard;
if (this->Def.ExternC) {
this->Sout << "#ifdef __cplusplus\n";
}
if (this->Def.Namespace) {
if (this->Def.Namespace->empty()) {
this->Sout << "} /* namespace */\n";
} else {
for (auto const& component : cmMakeRange(
this->Def.Namespace->rbegin(), this->Def.Namespace->rend())) {
this->Sout << "} /* namespace " << component << " */\n";
}
}
}
if (this->Def.ExternC) {
this->Sout << "} /* extern \"C\" */\n#endif /* __cplusplus */\n";
}
}
}
cm::optional<cm::string_view> EndComment;
private:
std::ostream& Sout;
Bin2CSectionTracker& SectionTracker;
cm::optional<Bin2CSectionGuard> Guard;
Bin2CDefinition const& Def;
};
std::size_t const BIN2C_BUFFER_SIZE = 16384;
std::size_t const BIN2C_ROW_WIDTH = 32;
inline std::size_t Bin2CColumnPos(std::size_t pos)
{
return cmStrLen(" ") + (pos % BIN2C_ROW_WIDTH) * cmStrLen(" 0x__,");
}
inline void Bin2CPrintCharToBuffer(bool printSigned, std::string& line,
unsigned char byte, std::uint64_t pos)
{
static char const hextable[] = "0123456789ABCDEF";
if (printSigned) {
if (byte & 0x80) {
line[Bin2CColumnPos(pos)] = '-';
byte = ~byte + 1;
} else {
line[Bin2CColumnPos(pos)] = ' ';
}
}
line[Bin2CColumnPos(pos) + cmStrLen(" 0x")] = hextable[byte >> 4];
line[Bin2CColumnPos(pos) + cmStrLen(" 0x_")] = hextable[byte & 0xF];
}
void Bin2CPrintChar(std::ostream& sout, bool printSigned,
bool enclosingBrackets,
Bin2CSectionTracker& sectionTracker,
cm::optional<Bin2CSectionGuard>& guard, std::string& line,
unsigned char byte, std::uint64_t& pos, bool& any)
{
if (!any) {
if (enclosingBrackets) {
sout << "\n";
} else {
guard.emplace(sectionTracker);
}
} else if (pos % BIN2C_ROW_WIDTH == 0) {
sout << line;
}
any = true;
Bin2CPrintCharToBuffer(printSigned, line, byte, pos);
pos++;
}
std::function<std::ostream&(std::ostream& sout)> Bin2CPrintChars(
std::istream& sin, Bin2CSectionTracker& sectionTracker,
bool enclosingBrackets, bool trailingComma, bool printSigned,
std::uint64_t& pos, bool& any)
{
return [&sin, &sectionTracker, enclosingBrackets, trailingComma, printSigned,
&any, &pos](std::ostream& sout) -> std::ostream& {
std::size_t readSize;
std::vector<std::uint8_t> buffer(BIN2C_BUFFER_SIZE);
cm::optional<Bin2CSectionGuard> guard;
if (enclosingBrackets) {
sout << '{';
}
// Construct a line buffer and modify the characters within it. This is
// an order of magnitude faster than `sout <<`-ing everything.
std::string line = " ";
line.reserve(cmStrLen(" ") + cmStrLen(" 0x__,") * BIN2C_ROW_WIDTH +
cmStrLen("\n"));
for (std::size_t i = 0; i < BIN2C_ROW_WIDTH; i++) {
line = cmStrCat(line, " 0x__,");
}
line = cmStrCat(line, '\n');
any = false;
pos = 0;
do {
sin.read(reinterpret_cast<char*>(buffer.data()), BIN2C_BUFFER_SIZE);
readSize = sin.gcount();
for (std::size_t i = 0; i < readSize; i++) {
Bin2CPrintChar(sout, printSigned, enclosingBrackets, sectionTracker,
guard, line, buffer[i], pos, any);
}
} while (readSize >= BIN2C_BUFFER_SIZE);
if (any) {
sout << line.substr(0,
pos % BIN2C_ROW_WIDTH == 0
? cmStrLen(" ") +
(BIN2C_ROW_WIDTH - 1) * cmStrLen("0x__, ") +
cmStrLen("0x__")
: Bin2CColumnPos(pos) - cmStrLen(","))
<< (trailingComma ? ",\n" : "\n");
}
if (enclosingBrackets) {
sout << '}';
}
return sout;
};
}
void Bin2C(std::ostream& sout, std::istream& sin,
cm::optional<Bin2CDefinition> const& array,
cm::optional<Bin2CDefinition> const& size,
std::vector<std::string> const& include, bool autoGenComment,
bool trailingComma, bool printSigned)
{
Bin2CSectionTracker sectionTracker{ sout };
if (autoGenComment) {
Bin2CSectionGuard guard{ sectionTracker };
(void)guard;
sout << "/* Automatically generated by cmake -E bin2c. Do not edit! */\n";
}
bool any = false;
std::uint64_t pos = 0;
if (array) {
if ((size && !size->Type) || !include.empty()) {
Bin2CSectionGuard guard{ sectionTracker };
(void)guard;
for (auto const& inc : include) {
sout << "#include " << inc << " /* specified on command line */\n";
}
if (size && !size->Type) {
sout << "#include <stdlib.h> /* for size_t */\n";
}
}
{
Bin2CDefGuard guard{ sout, sectionTracker, *array,
printSigned ? "char"_s : "unsigned char"_s,
"[]"_s };
sout << Bin2CPrintChars(sin, sectionTracker, true, trailingComma,
printSigned, pos, any);
if (any) {
guard.EndComment = cm::make_optional<cm::string_view>(array->Name);
}
}
} else {
sout << Bin2CPrintChars(sin, sectionTracker, false, trailingComma,
printSigned, pos, any);
}
if (size) {
Bin2CDefGuard guard{ sout, sectionTracker, *size, "size_t"_s };
(void)guard;
sout << pos;
}
sout.flush();
}
cm::optional<std::vector<cm::string_view>> ParseCPPNamespace(
cm::string_view str)
{
std::vector<cm::string_view> components;
std::size_t componentStart = 0;
std::size_t colons;
while ((colons = str.find("::", componentStart)) != std::string::npos) {
auto component = str.substr(componentStart, colons - componentStart);
if (!cmSystemTools::IsValidCIdentifier(component)) {
return cm::nullopt;
}
components.push_back(component);
componentStart = colons + 2;
}
auto component = str.substr(componentStart);
if (!cmSystemTools::IsValidCIdentifier(component)) {
return cm::nullopt;
}
components.push_back(component);
return cm::make_optional<std::vector<cm::string_view>>(
std::move(components));
}
}
// called when args[0] == "__run_co_compile"
@ -1910,6 +2252,348 @@ int cmcmd::ExecuteCMakeCommand(std::vector<std::string> const& args,
return 0;
}
// bin2c
if (args[1] == "bin2c"_s) {
auto const usage = []() {
std::cerr << "bin2c Usage: -E bin2c "
"[<options>...] "
"[--] [<input file> [<output file>]]\n";
return 1;
};
auto const isFilename = [](std::string const& arg) -> bool {
return arg == "-"_s || !cmHasLiteralPrefix(arg, "-");
};
std::string inputFile = "-";
std::string outputFile = "-";
cm::optional<Bin2CDefinition> array;
cm::optional<std::string> arrayNamespace;
cm::optional<Bin2CDefinition> size;
cm::optional<std::string> sizeNamespace;
bool staticArg = false;
bool constArg = false;
cm::optional<std::string> namespaceArg;
bool externCArg = false;
cm::optional<std::string> macroArg;
std::vector<std::string> include;
bool noAutoGenComment = false;
bool trailingComma = false;
bool printSigned = false;
auto const ensure =
[](cm::optional<Bin2CDefinition>& def) -> Bin2CDefinition& {
if (!def) {
def.emplace();
}
return *def;
};
using CommandArgument =
cmCommandLineArgument<bool(std::string const& value)>;
std::vector<CommandArgument> arguments = {
CommandArgument{ "--array-name", CommandArgument::Values::One,
[&ensure, &array](std::string const& arg) -> bool {
ensure(array).Name = arg;
return true;
} },
CommandArgument{ "--array-static", CommandArgument::Values::Zero,
[&ensure, &array](std::string const&) -> bool {
ensure(array).Static = true;
return true;
} },
CommandArgument{ "--array-const", CommandArgument::Values::Zero,
[&ensure, &array](std::string const&) -> bool {
ensure(array).Const = true;
return true;
} },
CommandArgument{ "--array-namespace",
CommandArgument::Values::ZeroOrOne,
CommandArgument::setToValue(arrayNamespace) },
CommandArgument{ "--array-extern-c", CommandArgument::Values::Zero,
[&ensure, &array](std::string const&) -> bool {
ensure(array).ExternC = true;
return true;
} },
CommandArgument{ "--array-macro", CommandArgument::Values::One,
[&ensure, &array](std::string const& arg) -> bool {
ensure(array).Macro = arg;
return true;
} },
CommandArgument{ "--array-type", CommandArgument::Values::One,
[&ensure, &array](std::string const& arg) -> bool {
ensure(array).Type = arg;
return true;
} },
CommandArgument{ "--size-name", CommandArgument::Values::One,
[&ensure, &size](std::string const& arg) -> bool {
ensure(size).Name = arg;
return true;
} },
CommandArgument{ "--size-static", CommandArgument::Values::Zero,
[&ensure, &size](std::string const&) -> bool {
ensure(size).Static = true;
return true;
} },
CommandArgument{ "--size-const", CommandArgument::Values::Zero,
[&ensure, &size](std::string const&) -> bool {
ensure(size).Const = true;
return true;
} },
CommandArgument{ "--size-namespace",
CommandArgument::Values::ZeroOrOne,
CommandArgument::setToValue(sizeNamespace) },
CommandArgument{ "--size-extern-c", CommandArgument::Values::Zero,
[&ensure, &size](std::string const&) -> bool {
ensure(size).ExternC = true;
return true;
} },
CommandArgument{ "--size-macro", CommandArgument::Values::One,
[&ensure, &size](std::string const& arg) -> bool {
ensure(size).Macro = arg;
return true;
} },
CommandArgument{ "--size-type", CommandArgument::Values::One,
[&ensure, &size](std::string const& arg) -> bool {
ensure(size).Type = arg;
return true;
} },
CommandArgument{ "--static", CommandArgument::Values::Zero,
CommandArgument::setToTrue(staticArg) },
CommandArgument{ "--const", CommandArgument::Values::Zero,
CommandArgument::setToTrue(constArg) },
CommandArgument{ "--namespace", CommandArgument::Values::ZeroOrOne,
CommandArgument::setToValue(namespaceArg) },
CommandArgument{ "--extern-c", CommandArgument::Values::Zero,
CommandArgument::setToTrue(externCArg) },
CommandArgument{ "--macro", CommandArgument::Values::One,
CommandArgument::setToValue(macroArg) },
CommandArgument{ "--include", CommandArgument::Values::One,
[&include](std::string const& arg) -> bool {
if ((arg.front() == '"' && arg.back() == '"') ||
(arg.front() == '<' && arg.back() == '>')) {
include.push_back(arg);
} else {
include.push_back(cmStrCat('"', arg, '"'));
}
return true;
} },
CommandArgument{ "--no-auto-gen-comment",
CommandArgument::Values::Zero,
CommandArgument::setToTrue(noAutoGenComment) },
CommandArgument{ "--trailing-comma", CommandArgument::Values::Zero,
CommandArgument::setToTrue(trailingComma) },
CommandArgument{ "--signed", CommandArgument::Values::Zero,
CommandArgument::setToTrue(printSigned) },
};
size_t i;
for (i = 2; i < args.size(); i++) {
bool matched = false;
auto const& arg = args[i];
if (arg == "--"_s) {
i++;
break;
}
for (auto const& argument : arguments) {
if (argument.matches(arg)) {
matched = true;
if (!argument.parse(arg, i, args)) {
std::cerr << "\n";
return usage();
}
break;
}
}
if (isFilename(arg)) {
break;
}
if (!matched) {
return usage();
}
}
if (i < args.size() - 2) {
return usage();
}
if (i < args.size()) {
if (!isFilename(args[i])) {
return usage();
}
inputFile = args[i];
i++;
}
if (i < args.size()) {
if (!isFilename(args[i])) {
return usage();
}
outputFile = args[i];
i++;
}
auto const checkAndPopulateDef =
[&staticArg, &constArg, &namespaceArg, &externCArg, &macroArg](
cm::static_string_view defType, cm::optional<Bin2CDefinition>& def,
cm::optional<std::string> const& ns) -> bool {
if (def && !def->Name.empty()) {
if (staticArg) {
def->Static = true;
}
if (constArg) {
def->Const = true;
}
if (externCArg) {
def->ExternC = true;
}
if (!cmSystemTools::IsValidCIdentifier(def->Name)) {
// If not a valid C identifier, assume it's a class member. We
// can't parse full C++.
if (def->Static) {
std::cerr << "Cannot use --static or --" << defType
<< "-static if --" << defType
<< "-name is a class member\n\n";
return false;
}
if (def->ExternC) {
std::cerr << "Cannot use --extern-c or --" << defType
<< "-extern-c if --" << defType
<< "-name is a class member\n\n";
return false;
}
}
cm::optional<cm::string_view> nsView;
if (ns) {
nsView = *ns;
} else if (namespaceArg) {
nsView = *namespaceArg;
}
if (nsView) {
if (nsView->empty()) {
def->Namespace.emplace();
} else if (!(def->Namespace = ParseCPPNamespace(*nsView))) {
std::cerr << "Invalid " << defType << " namespace: \"" << *nsView
<< "\"\n\n";
return false;
}
}
if (!def->Macro) {
def->Macro = macroArg;
}
} else {
if (def && def->Static) {
std::cerr << "Cannot use --" << defType << "-static without --"
<< defType << "-name\n\n";
return false;
}
if (def && def->Const) {
std::cerr << "Cannot use --" << defType << "-const without --"
<< defType << "-name\n\n";
return false;
}
if (ns) {
std::cerr << "Cannot use --" << defType << "-namespace without --"
<< defType << "-name\n\n";
return false;
}
if (def && def->ExternC) {
std::cerr << "Cannot use --" << defType << "-extern-c without --"
<< defType << "-name\n\n";
return false;
}
if (def && def->Macro) {
std::cerr << "Cannot use --" << defType << "-macro without --"
<< defType << "-name\n\n";
return false;
}
if (def && def->Type) {
std::cerr << "Cannot use --" << defType << "-type without --"
<< defType << "-name\n\n";
return false;
}
}
return true;
};
if (!array || array->Name.empty()) {
if (!include.empty()) {
std::cerr << "Cannot use --include without --array-name\n\n";
return usage();
}
if (staticArg) {
std::cerr << "Cannot use --static without --array-name\n\n";
return usage();
}
if (constArg) {
std::cerr << "Cannot use --const without --array-name\n\n";
return usage();
}
if (namespaceArg) {
std::cerr << "Cannot use --namespace without --array-name\n\n";
return usage();
}
if (externCArg) {
std::cerr << "Cannot use --extern-c without --array-name\n\n";
return usage();
}
if (macroArg) {
std::cerr << "Cannot use --macro without --array-name\n\n";
return usage();
}
}
if (!checkAndPopulateDef("array"_s, array, arrayNamespace)) {
return usage();
}
if (size && !size->Name.empty() && !array) {
std::cerr << "Cannot use --size-name without --array-name\n\n";
return usage();
}
if (!checkAndPopulateDef("size"_s, size, sizeNamespace)) {
return usage();
}
std::istream* sin = &std::cin;
cmsys::ifstream fin;
if (inputFile != "-"_s) {
fin.open(inputFile.c_str(), std::ios::in | std::ios::binary);
if (!fin) {
std::cerr << "Could not open file for reading: \"" << inputFile
<< "\"\n";
return 1;
}
sin = &fin;
#ifdef _WIN32
} else {
_setmode(fileno(stdin), _O_BINARY);
#endif
}
std::ostream* sout = &std::cout;
cmGeneratedFileStream fout;
if (outputFile != "-"_s) {
fout.Open(outputFile);
if (!fout) {
std::cerr << "Could not open file for writing: \"" << outputFile
<< "\"\n";
return 1;
}
fout.SetCopyIfDifferent(true);
sout = &fout;
}
Bin2C(*sout, *sin, array, size, include, !noAutoGenComment,
trailingComma, printSigned);
if (fout) {
fout.Close();
}
return 0;
}
if (args[1] == "server") {
cmSystemTools::Error(
"CMake server mode has been removed in favor of the file-api.");

View File

@ -6,6 +6,9 @@
#include <string>
#include <vector>
#include <cm/string_view>
#include <cmext/string_view>
#include <stddef.h>
#include "cmSystemTools.h"
@ -162,6 +165,84 @@ static bool testMakeTempDirectory()
return true;
}
static bool testIsValidCIdentifier()
{
std::cout << "testIsValidCIdentifier()\n";
struct
{
cm::static_string_view Str;
bool Expected;
} const testCases[] = {
{
""_s,
false,
},
{
"0"_s,
false,
},
{
"9"_s,
false,
},
{
"-"_s,
false,
},
{
"A"_s,
true,
},
{
"Z"_s,
true,
},
{
"a"_s,
true,
},
{
"z"_s,
true,
},
{
"_"_s,
true,
},
{
"a0"_s,
true,
},
{
"a9"_s,
true,
},
{
"a-"_s,
false,
},
{
"longIdentifierWith1Number"_s,
true,
},
{
"long_identifier_with_underscores"_s,
true,
},
};
bool success = true;
for (auto const& testCase : testCases) {
if (cmSystemTools::IsValidCIdentifier(testCase.Str) != testCase.Expected) {
std::cout << "cmSystemTools::IsValidCIdentifier failed on \""
<< testCase.Str << "\"\n";
success = false;
}
}
return success;
}
int testSystemTools(int /*unused*/, char* /*unused*/[])
{
return runTests({
@ -170,5 +251,6 @@ int testSystemTools(int /*unused*/, char* /*unused*/[])
testStrVersCmp,
testRelativeIfUnder,
testMakeTempDirectory,
testIsValidCIdentifier,
});
}

View File

@ -1526,3 +1526,8 @@ add_RunCMake_test(MsvcCharsetDefines
-DCMake_TEST_CUDA=${CMake_TEST_CUDA}
)
set_property(TEST RunCMake.MsvcCharsetDefines APPEND PROPERTY LABELS "CUDA")
add_RunCMake_test(cmake-E-bin2c -DCMake_TEST_BIN2C_LARGE_FILE=${CMake_TEST_BIN2C_LARGE_FILE})
if(CMake_TEST_BIN2C_LARGE_FILE)
set_property(TEST RunCMake.cmake-E-bin2c PROPERTY TIMEOUT ${CMAKE_LONG_TEST_TIMEOUT})
endif()

View File

@ -0,0 +1,3 @@
cmake_minimum_required(VERSION 4.2)
project(${RunCMake_TEST} NONE)
include(${RunCMake_TEST}.cmake)

View File

@ -0,0 +1,151 @@
include(RunCMake)
function(run_bin2c name contents_name)
string(REPLACE ";" "\\;" ARGS "${ARGN}")
run_cmake_command(${name}
"${CMAKE_COMMAND}"
"-DNAME=${name}"
"-DINPUT_FILE=${RunCMake_TEST_BINARY_DIR}/${contents_name}.bin"
"-DARGS=${ARGS}"
-P "${RunCMake_SOURCE_DIR}/run_bin2c.cmake"
)
endfunction()
function(run_bin2c_basic name)
run_bin2c("${name}" basic ${ARGN})
endfunction()
set(RunCMake_TEST_BINARY_DIR "${RunCMake_BINARY_DIR}/generate_binaries-build")
run_cmake_with_options(generate_binaries "-DCMake_TEST_BIN2C_LARGE_FILE:BOOL=${CMake_TEST_BIN2C_LARGE_FILE}")
set(RunCMake_TEST_NO_CLEAN 1)
run_cmake_command(generate_binaries-build "${CMAKE_COMMAND}" --build .)
run_bin2c(basic basic)
run_bin2c(empty empty)
run_bin2c(text_lf text_lf)
run_bin2c(text_crlf text_crlf)
run_bin2c(text_align text_align)
run_bin2c(long long)
run_bin2c_basic(double_hyphen --)
run_bin2c_basic(array --array-name basic_contents)
run_bin2c_basic(array_signed --array-name basic_contents --signed)
run_bin2c_basic(array_double_hyphen --array-name basic_contents --)
run_bin2c(array_empty empty --array-name empty_contents)
run_bin2c_basic(signed --signed)
run_bin2c(long_signed long --signed)
run_bin2c_basic(static --array-name basic_contents --static)
run_bin2c_basic(const --array-name basic_contents --const)
run_bin2c_basic(static_const --array-name basic_contents --static --const)
run_bin2c_basic(namespace --array-name basic_contents --namespace ns)
run_bin2c_basic(namespace_nested --array-name basic_contents --namespace ns::nested)
run_bin2c_basic(namespace_anonymous --namespace "" --array-name basic_contents)
run_bin2c_basic(namespace_anonymous_no_arg --namespace --array-name basic_contents)
run_bin2c_basic(namespace_anonymous_no_arg_double_hyphen --array-name basic_contents --namespace --)
run_bin2c_basic(extern_c --array-name basic_contents --extern-c)
run_bin2c_basic(macro --array-name basic_contents --macro "MY_MACRO(true)")
run_bin2c_basic(array_type --array-name basic_contents --array-type std::uint8_t)
run_bin2c_basic(size --array-name basic_contents --size-name basic_size)
run_bin2c_basic(size_type --array-name basic_contents --size-name basic_size --size-type "unsigned long")
run_bin2c_basic(size_type_size_t --array-name basic_contents --size-name basic_size --size-type size_t)
run_bin2c_basic(size_include --array-name basic_contents --size-name basic_size --include "header.h")
run_bin2c_basic(trailing_comma --trailing-comma)
run_bin2c_basic(trailing_comma_array --array-name basic_contents --trailing-comma)
run_bin2c(trailing_comma_empty empty --trailing-comma)
run_bin2c(trailing_comma_array_empty empty --array-name empty_contents --trailing-comma)
run_bin2c(trailing_comma_text_align text_align --trailing-comma)
run_bin2c_basic(include --array-name basic_contents --include "<brackets.h>" --include "\"quotes.h\"" --include "noquotes.h")
run_bin2c_basic(class_member --array-name "basic_class<true>::basic_contents" --size-name "basic_class<true>::basic_size")
run_bin2c_basic(everything
--array-name basic_contents
--size-name basic_size
--namespace ns::nested
--static
--const
--extern-c
--macro "MY_MACRO(true)"
--array-type std::uint8_t
--size-type "unsigned long"
--include "<brackets.h>"
--include "\"quotes.h\""
--include "noquotes.h"
--trailing-comma
)
run_bin2c_basic(everything_array_override
--array-name basic_contents
--size-name basic_size
--namespace ns::nested
--macro GLOBAL_MACRO
--array-namespace ns::array
--array-static
--array-const
--array-extern-c
--array-macro ARRAY_MACRO
)
run_bin2c_basic(everything_array_only
--array-name basic_contents
--namespace ns::nested
--macro GLOBAL_MACRO
--array-namespace ns::array
--array-static
--array-const
--array-extern-c
--array-macro ARRAY_MACRO
)
run_bin2c_basic(everything_size_override
--array-name basic_contents
--size-name basic_size
--namespace ns::nested
--macro GLOBAL_MACRO
--size-namespace ns::size
--size-static
--size-const
--size-extern-c
--size-macro SIZE_MACRO
)
run_bin2c_basic(array_namespace_anonymous --array-name basic_contents --array-namespace --size-name basic_size)
run_bin2c_basic(size_namespace_anonymous --array-name basic_contents --size-namespace --size-name basic_size)
run_bin2c_basic(no_auto_gen_comment --no-auto-gen-comment)
run_bin2c_basic(no_auto_gen_comment_array --array-name basic_contents --no-auto-gen-comment)
run_bin2c_basic(no_auto_gen_comment_namespace --array-name basic_contents --namespace ns --no-auto-gen-comment)
run_bin2c_basic(no_auto_gen_comment_extern_c --array-name basic_contents --extern-c --no-auto-gen-comment)
run_bin2c_basic(no_auto_gen_comment_include --array-name basic_contents --include header.h --no-auto-gen-comment)
run_bin2c(no_auto_gen_comment_empty empty --no-auto-gen-comment)
run_cmake_command(too_many_files ${CMAKE_COMMAND} -E bin2c a a a)
run_cmake_command(input_noexist ${CMAKE_COMMAND} -E bin2c noexist.bin)
run_cmake_command(size_no_array ${CMAKE_COMMAND} -E bin2c --size-name size)
run_cmake_command(include_no_array ${CMAKE_COMMAND} -E bin2c --include header.h)
run_cmake_command(static_no_array ${CMAKE_COMMAND} -E bin2c --static)
run_cmake_command(const_no_array ${CMAKE_COMMAND} -E bin2c --const)
run_cmake_command(namespace_no_array ${CMAKE_COMMAND} -E bin2c --namespace ns)
run_cmake_command(extern_c_no_array ${CMAKE_COMMAND} -E bin2c --extern-c)
run_cmake_command(macro_no_array ${CMAKE_COMMAND} -E bin2c --macro MY_MACRO)
run_cmake_command(array_static_no_array ${CMAKE_COMMAND} -E bin2c --array-static)
run_cmake_command(array_const_no_array ${CMAKE_COMMAND} -E bin2c --array-const)
run_cmake_command(array_namespace_no_array ${CMAKE_COMMAND} -E bin2c --array-namespace ns)
run_cmake_command(array_extern_c_no_array ${CMAKE_COMMAND} -E bin2c --array-extern-c)
run_cmake_command(array_macro_no_array ${CMAKE_COMMAND} -E bin2c --array-macro MY_MACRO)
run_cmake_command(array_type_no_array ${CMAKE_COMMAND} -E bin2c --array-type std::uint8_t)
run_cmake_command(size_static_no_size ${CMAKE_COMMAND} -E bin2c --array-name arr --size-static)
run_cmake_command(size_const_no_size ${CMAKE_COMMAND} -E bin2c --array-name arr --size-const)
run_cmake_command(size_namespace_no_size ${CMAKE_COMMAND} -E bin2c --array-name arr --size-namespace ns)
run_cmake_command(size_extern_c_no_size ${CMAKE_COMMAND} -E bin2c --array-name arr --size-extern-c)
run_cmake_command(size_macro_no_size ${CMAKE_COMMAND} -E bin2c --array-name arr --size-macro MY_MACRO)
run_cmake_command(array_namespace_invalid ${CMAKE_COMMAND} -E bin2c --array-name arr --array-namespace 0)
run_cmake_command(array_static_class_member ${CMAKE_COMMAND} -E bin2c --array-name cls::arr --array-static)
run_cmake_command(array_extern_c_class_member ${CMAKE_COMMAND} -E bin2c --array-name cls::arr --array-extern-c)
run_cmake_command(size_namespace_invalid ${CMAKE_COMMAND} -E bin2c --array-name arr --size-name size --size-namespace 0)
run_cmake_command(size_static_class_member ${CMAKE_COMMAND} -E bin2c --array-name arr --size-name cls::size --size-static)
run_cmake_command(size_extern_c_class_member ${CMAKE_COMMAND} -E bin2c --array-name arr --size-name cls::size --size-extern-c)
run_cmake_command(arg_after_double_hyphen ${CMAKE_COMMAND} -E bin2c --array-name arr -- --size-name size)
if(CMake_TEST_BIN2C_LARGE_FILE)
run_cmake_command(generate_binaries-verify_very_long_hash
"${CMAKE_COMMAND}"
--build .
--target verify_very_long_hash
)
run_cmake_command(very_long
"${CMAKE_COMMAND}"
-P "${RunCMake_SOURCE_DIR}/very_long.cmake"
)
endif()

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
^bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,5 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --array-const without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,5 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1,3 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
unsigned char empty_contents[] = {};

View File

@ -0,0 +1,3 @@
^Cannot use --extern-c or --array-extern-c if --array-name is a class member
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --array-extern-c without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --array-macro without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,13 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <stdlib.h> /* for size_t */
namespace {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace */
size_t basic_size = 8;

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Invalid array namespace: "0"
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,3 @@
^Cannot use --array-namespace without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,5 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
char basic_contents[] = {
-0x04,-0x03,-0x02,-0x01, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1,3 @@
^Cannot use --static or --array-static if --array-name is a class member
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --array-static without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,5 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
std::uint8_t basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --array-type without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,3 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <stdlib.h> /* for size_t */
unsigned char basic_class<true>::basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_class<true>::basic_contents */
size_t basic_class<true>::basic_size = 8;

View File

@ -0,0 +1,5 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
const unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --const without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,3 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03

View File

@ -0,0 +1 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */

View File

@ -0,0 +1,35 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <brackets.h> /* specified on command line */
#include "quotes.h" /* specified on command line */
#include "noquotes.h" /* specified on command line */
#ifdef __cplusplus
extern "C" {
namespace ns {
namespace nested {
#endif /* __cplusplus */
MY_MACRO(true) static const std::uint8_t basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03,
}; /* basic_contents */
#ifdef __cplusplus
} /* namespace nested */
} /* namespace ns */
} /* extern "C" */
#endif /* __cplusplus */
#ifdef __cplusplus
extern "C" {
namespace ns {
namespace nested {
#endif /* __cplusplus */
MY_MACRO(true) static const unsigned long basic_size = 8;
#ifdef __cplusplus
} /* namespace nested */
} /* namespace ns */
} /* extern "C" */
#endif /* __cplusplus */

View File

@ -0,0 +1,17 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#ifdef __cplusplus
extern "C" {
namespace ns {
namespace array {
#endif /* __cplusplus */
ARRAY_MACRO static const unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
#ifdef __cplusplus
} /* namespace array */
} /* namespace ns */
} /* extern "C" */
#endif /* __cplusplus */

View File

@ -0,0 +1,27 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <stdlib.h> /* for size_t */
#ifdef __cplusplus
extern "C" {
namespace ns {
namespace array {
#endif /* __cplusplus */
ARRAY_MACRO static const unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
#ifdef __cplusplus
} /* namespace array */
} /* namespace ns */
} /* extern "C" */
#endif /* __cplusplus */
namespace ns {
namespace nested {
GLOBAL_MACRO size_t basic_size = 8;
} /* namespace nested */
} /* namespace ns */

View File

@ -0,0 +1,27 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <stdlib.h> /* for size_t */
namespace ns {
namespace nested {
GLOBAL_MACRO unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace nested */
} /* namespace ns */
#ifdef __cplusplus
extern "C" {
namespace ns {
namespace size {
#endif /* __cplusplus */
SIZE_MACRO static const size_t basic_size = 8;
#ifdef __cplusplus
} /* namespace size */
} /* namespace ns */
} /* extern "C" */
#endif /* __cplusplus */

View File

@ -0,0 +1,13 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --extern-c without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,64 @@
enable_language(C)
enable_language(CXX)
include("${CMAKE_CURRENT_SOURCE_DIR}/hashes.cmake")
function(verify_long_c filename expected_lf_hash expected_crlf_hash)
file(SHA256 "${CMAKE_CURRENT_SOURCE_DIR}/${filename}" actual_hash)
if(NOT actual_hash STREQUAL expected_lf_hash AND NOT actual_hash STREQUAL expected_crlf_hash)
message(FATAL_ERROR "File ${CMAKE_CURRENT_SOURCE_DIR}/${filename} does not match expected hash and has likely been manually edited. Edit and re-run ${CMAKE_CURRENT_SOURCE_DIR}/generate_files.sh instead.")
endif()
endfunction()
verify_long_c(long.c.txt "${long_c_lf_hash}" "${long_c_crlf_hash}")
verify_long_c(long_signed.c.txt "${long_signed_c_lf_hash}" "${long_signed_c_crlf_hash}")
add_executable(generate_binary generate_binary.cpp)
function(generate_binary name)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.bin"
COMMAND generate_binary "${name}" "${CMAKE_CURRENT_BINARY_DIR}/${name}.bin"
DEPENDS generate_binary
)
add_custom_target(
verify_${name} ALL
COMMAND "${CMAKE_COMMAND}"
"-DFILENAME=${CMAKE_CURRENT_BINARY_DIR}/${name}.bin"
"-DSHA256SUM=${${name}_hash}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/verify_binary.cmake"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/${name}.bin"
)
endfunction()
foreach(name IN ITEMS basic empty text_lf text_crlf text_align long)
generate_binary("${name}")
endforeach()
add_executable(verify_long_signed verify_long_signed.c)
add_custom_target(verify_long_signed_contents ALL COMMAND verify_long_signed)
if(CMake_TEST_BIN2C_LARGE_FILE)
include("${CMAKE_CURRENT_SOURCE_DIR}/jack_count.cmake")
add_executable(generate_very_long generate_very_long.cpp)
target_compile_definitions(generate_very_long PRIVATE JACK_COUNT=${jack_count})
add_custom_target(
verify_very_long_hash
COMMAND "${CMAKE_COMMAND}"
"-DGENERATE_VERY_LONG=$<TARGET_FILE:generate_very_long>"
"-DSHA256SUM=${very_long_hash}"
-P "${CMAKE_CURRENT_SOURCE_DIR}/verify_very_long_hash.cmake"
)
add_custom_command(
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/very_long_executables.cmake"
COMMAND "${CMAKE_COMMAND}"
"-DGENERATE_VERY_LONG=$<TARGET_FILE:generate_very_long>"
-P "${CMAKE_CURRENT_SOURCE_DIR}/record_very_long.cmake"
)
add_custom_target(
record_very_long ALL
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/very_long_executables.cmake"
)
endif()

View File

@ -0,0 +1,35 @@
#include <fstream>
#include <ios>
#include <string>
static unsigned char const long_contents[] = {
#include "long.c.txt"
};
int main(int argc, char** argv)
{
if (argc != 3) {
return 1;
}
std::string name = argv[1];
std::ofstream fout(argv[2], std::ios::out | std::ios::binary);
if (name == "basic") {
fout.write("\xFC\xFD\xFE\xFF\x00\x01\x02\x03", 8);
} else if (name == "empty") {
// Write nothing
} else if (name == "text_lf") {
fout << "All work and no play makes Jack a dull boy.\n";
} else if (name == "text_crlf") {
fout << "All work and no play makes Jack a dull boy.\r\n";
} else if (name == "text_align") {
fout << "This exactly 32 characters long!";
} else if (name == "long") {
fout.write(reinterpret_cast<char const*>(long_contents),
sizeof(long_contents));
}
fout.flush();
fout.close();
return 0;
}

View File

@ -0,0 +1,127 @@
#!/bin/bash
# This script generates the following files:
#
# - long.c.txt
# - long_signed.c.txt
# - jack_count.cmake
# - hashes.cmake
#
# Any changes to those files should be made here, and then the script rerun.
set -euo pipefail
readonly dir="$(realpath "$(dirname "$0")")"
readonly jack_count="$(((1024 * 1024 * 1024 * 4 / 64) + 1))"
readonly jack_byte_count="$((jack_count * 64))"
random_bytes() {
python3 -c 'import random; import sys; sys.stdout.buffer.write(random.Random(12345).randbytes(65536))'
}
bin2c() {
od -vtx1 -Anone -w32 \
| awk '{ print toupper($0) }' \
| sed -E \
-e 's/^ / 0x/g' \
-e 's/([0-9A-F]) ([0-9A-F])/\1, 0x\2/g' \
-e 's/$/,/g' \
-e '$ s/,$//g'
}
bin2c_signed() {
local sed_args=()
local i
for i in {128..255}; do
sed_args+=(-e "$(python3 -c 'import sys; n = int(sys.argv[1]); print(f"s/ 0x{n:02X}/-0x{256-n:02X}/g")' "$i")")
done
bin2c | sed -E "${sed_args[@]}"
}
cmake_hash() {
local name="$1"
echo " Computing ${name}_hash..." >&2
local hash="$(sha256sum | cut -d' ' -f1)"
echo "set(${name}_hash $hash)"
}
very_long_c() {
local nl="$1"
echo -n "/* Automatically generated by cmake -E bin2c. Do not edit! */$nl"
echo -n "$nl"
echo -n "#include <stdlib.h> /* for size_t */$nl"
echo -n "$nl"
echo -n "unsigned char very_long_contents[] = {$nl"
python3 -c '
import sys
jack_count = int(sys.argv[1])
nl = sys.argv[2]
def jack(trailing_comma):
sys.stdout.buffer.write(
(
" "
"0x20, 0x41, 0x6C, 0x6C, 0x20, 0x20, 0x20, 0x77, "
"0x6F, 0x72, 0x6B, 0x20, 0x20, 0x20, 0x61, 0x6E, "
"0x64, 0x20, 0x20, 0x20, 0x6E, 0x6F, 0x20, 0x20, "
"0x20, 0x70, 0x6C, 0x61, 0x79, 0x20, 0x20, 0x20,"
f"{nl}"
" "
"0x6D, 0x61, 0x6B, 0x65, 0x73, 0x20, 0x20, 0x20, "
"0x4A, 0x61, 0x63, 0x6B, 0x20, 0x20, 0x20, 0x61, "
"0x20, 0x20, 0x20, 0x64, 0x75, 0x6C, 0x6C, 0x20, "
f"0x20, 0x20, 0x62, 0x6F, 0x79, 0x2E, 0x20, 0x0A{trailing_comma}"
f"{nl}"
).encode()
)
for _ in range(jack_count - 1):
jack(",")
jack("")
' "$jack_count" "$nl"
echo -n "}; /* very_long_contents */$nl"
echo -n "$nl"
echo -n "size_t very_long_size = $jack_byte_count;$nl"
}
echo "Generating long.c.txt..." >&2
(
echo -en '/* Automatically generated by cmake -E bin2c. Do not edit! */\n\n'
random_bytes | bin2c
) > "$dir/long.c.txt"
echo "Generating long_signed.c.txt..." >&2
(
echo -en '/* Automatically generated by cmake -E bin2c. Do not edit! */\n\n'
random_bytes | bin2c_signed
) > "$dir/long_signed.c.txt"
echo "Generating jack_count.cmake..." >&2
(
echo -en '# Automatically generated by generate_files.sh. Do not edit!\n\n'
echo "set(jack_count $jack_count)"
) > "$dir/jack_count.cmake"
echo "Generating hashes.cmake..." >&2
(
echo -en '# Automatically generated by generate_files.sh. Do not edit!\n\n'
echo -en '\xFC\xFD\xFE\xFF\x00\x01\x02\x03' | cmake_hash basic
echo -en '' | cmake_hash empty
echo -en 'All work and no play makes Jack a dull boy.\n' | cmake_hash text_lf
echo -en 'All work and no play makes Jack a dull boy.\r\n' | cmake_hash text_crlf
echo -en 'This exactly 32 characters long!' | cmake_hash text_align
random_bytes | cmake_hash long
cat "$dir/long.c.txt" | cmake_hash long_c_lf
cat "$dir/long.c.txt" | unix2dos | cmake_hash long_c_crlf
cat "$dir/long_signed.c.txt" | cmake_hash long_signed_c_lf
cat "$dir/long_signed.c.txt" | unix2dos | cmake_hash long_signed_c_crlf
python3 -c '
import sys
for _ in range(int(sys.argv[1])):
sys.stdout.buffer.write(b" All work and no play makes Jack a dull boy. \n")
' "$jack_count" | cmake_hash very_long
very_long_c $'\n' | cmake_hash very_long_c_lf
very_long_c $'\r\n' | cmake_hash very_long_c_crlf
) > "$dir/hashes.cmake"

View File

@ -0,0 +1,21 @@
#include <iostream>
#ifdef _WIN32
# include <fcntl.h> // for _O_BINARY
# include <io.h> // for _setmode
# include <stdio.h> // for _fileno
#endif
int main()
{
#ifdef _WIN32
_setmode(_fileno(stdout), _O_BINARY);
#endif
for (unsigned long i = 0; i < JACK_COUNT; i++) {
std::cout
<< " All work and no play makes Jack a dull boy. \n";
}
return 0;
}

View File

@ -0,0 +1,15 @@
# Automatically generated by generate_files.sh. Do not edit!
set(basic_hash 31ca2bb38aec879274fa64fafcb97acddd2d99fd819c53a7a9ae1348fbad2dfb)
set(empty_hash e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855)
set(text_lf_hash 4489c0acf5973358718c763d3c20287308c7e31e3cd46fdc1a72c8cc0b0a5ef4)
set(text_crlf_hash e09ddeeb9ac62ef0d166e6fb1fe7c61f10c3a762f95b041aaa165dddde85c5b3)
set(text_align_hash b8a6df236221d02cc182d8b804ce9b1e81fd249315720285f6c76b334b79bb49)
set(long_hash bddb0b20d82ce70b45ab5eea8b995b5167b0cd0d5d576f742dbc193c9ec2aaa4)
set(long_c_lf_hash 5547540dbcfd2f404088db290871f7948ad7c25eaf817067038ada17c91b0b83)
set(long_c_crlf_hash c4e7a2e976ec28198f50cffa2246cdb1fd8cb1edaf4a46780449528c364bb1ca)
set(long_signed_c_lf_hash cd455c62387682132e14b6795d93bec053021dc82fda5dd4d1bd70a0e020a159)
set(long_signed_c_crlf_hash 81b305ac5d5f3ed30ba3c158dff3cd2f43a2ccf33db7fccadc9ebef5a81156c4)
set(very_long_hash 2a380908971dada85a00911e27ec09e3d7b2bb06d8ae27666124b4f1e083c789)
set(very_long_c_lf_hash 55597a2785835b26699ea3e1a999342dabceed8459a57d2151eff949ae0d1657)
set(very_long_c_crlf_hash dd23964b837b1df80079e9ad3fb191129b2f5318294a3852df3b7f24dcc5dd05)

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <brackets.h> /* specified on command line */
#include "quotes.h" /* specified on command line */
#include "noquotes.h" /* specified on command line */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --include without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1 @@
^Could not open file for reading: "noexist\.bin"$

View File

@ -0,0 +1,3 @@
# Automatically generated by generate_files.sh. Do not edit!
set(jack_count 67108865)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
MY_MACRO(true) unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --macro without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
namespace ns {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace ns */

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
namespace {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace */

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
namespace {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace */

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
namespace {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace */

View File

@ -0,0 +1,11 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
namespace ns {
namespace nested {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace nested */
} /* namespace ns */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --namespace without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03

View File

@ -0,0 +1,3 @@
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1,11 @@
#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
#ifdef __cplusplus
} /* extern "C" */
#endif /* __cplusplus */

View File

@ -0,0 +1,5 @@
#include "header.h" /* specified on command line */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */

View File

@ -0,0 +1,7 @@
namespace ns {
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
} /* namespace ns */

View File

@ -0,0 +1,6 @@
file(CONFIGURE
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/very_long_executables.cmake"
CONTENT [==[set(generate_very_long [====[@GENERATE_VERY_LONG@]====])
]==]
@ONLY
)

View File

@ -0,0 +1,64 @@
set(expected_output_file "${CMAKE_CURRENT_LIST_DIR}/${NAME}.c.txt")
function(run_bin2c output_file)
execute_process(
${ARGN}
COMMAND_ERROR_IS_FATAL ANY
)
execute_process(
COMMAND ${CMAKE_COMMAND} -E compare_files --ignore-eol "${expected_output_file}" "${output_file}"
RESULT_VARIABLE result
)
if(result)
file(READ "${expected_output_file}" expected_output)
file(READ "${output_file}" actual_output)
string(REPLACE "\n" "\n " formatted_expected_output "${expected_output}")
string(REPLACE "\n" "\n " formatted_actual_output "${actual_output}")
set(formatted_binary_input)
file(READ "${INPUT_FILE}" binary_contents HEX)
string(LENGTH "${binary_contents}" binary_length)
if(binary_length LESS 256)
set(formatted_binary_input "\nHexadecimal contents of ${INPUT_FILE}:\n ${binary_contents}")
endif()
message(FATAL_ERROR "${output_file} does not match ${expected_output_file}.\nExpected output:\n ${formatted_expected_output}\nActual output:\n ${formatted_actual_output}${formatted_binary_input}")
endif()
endfunction()
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-no-input-arg-no-output-arg.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS}
INPUT_FILE "${INPUT_FILE}"
OUTPUT_FILE "${output_file}"
)
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-stdin-no-output-arg.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS} -
INPUT_FILE "${INPUT_FILE}"
OUTPUT_FILE "${output_file}"
)
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-input-file-no-output-arg.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS} "${INPUT_FILE}"
OUTPUT_FILE "${output_file}"
)
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-stdin-stdout.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS} - -
INPUT_FILE "${INPUT_FILE}"
OUTPUT_FILE "${output_file}"
)
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-input-file-stdout.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS} "${INPUT_FILE}" -
OUTPUT_FILE "${output_file}"
)
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-stdin-output-file.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS} - "${output_file}"
INPUT_FILE "${INPUT_FILE}"
)
set(output_file "${CMAKE_CURRENT_BINARY_DIR}/${NAME}-input-file-output-file.c.txt")
run_bin2c("${output_file}"
COMMAND ${CMAKE_COMMAND} -E bin2c ${ARGS} "${INPUT_FILE}" "${output_file}"
)

View File

@ -0,0 +1,3 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
-0x04,-0x03,-0x02,-0x01, 0x00, 0x01, 0x02, 0x03

View File

@ -0,0 +1,9 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <stdlib.h> /* for size_t */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
size_t basic_size = 8;

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --size-const without --size-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,3 @@
^Cannot use --extern-c or --size-extern-c if --size-name is a class member
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --size-extern-c without --size-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,10 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include "header.h" /* specified on command line */
#include <stdlib.h> /* for size_t */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
size_t basic_size = 8;

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --size-macro without --size-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1,13 @@
/* Automatically generated by cmake -E bin2c. Do not edit! */
#include <stdlib.h> /* for size_t */
unsigned char basic_contents[] = {
0xFC, 0xFD, 0xFE, 0xFF, 0x00, 0x01, 0x02, 0x03
}; /* basic_contents */
namespace {
size_t basic_size = 8;
} /* namespace */

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Invalid size namespace: "0"
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --size-namespace without --size-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

View File

@ -0,0 +1 @@
1

View File

@ -0,0 +1,3 @@
^Cannot use --size-name without --array-name
bin2c Usage: -E bin2c \[<options>\.\.\.\] \[--\] \[<input file> \[<output file>\]\]$

Some files were not shown because too many files have changed in this diff Show More