cleanup resgen code
A test for how Gemini can clean-up code. Changes: - Configuration Struct: Global variables have been grouped into an AppConfig struct. This makes the program's configuration explicit and avoids polluting the global namespace. - Clearer Argument Parsing: The handleArguments function now returns an AppConfig object, encapsulating all parsing logic and making the main function cleaner. - Helper Functions: Repetitive tasks like string replacement, file I/O, and writing file entries have been extracted into small, well-defined helper functions (replaceAll, openOutputFile, readFile, etc.). - Simplified main Function: The main function is now a high-level coordinator, delegating work to helper functions. This makes the overall program flow much easier to follow. - Modern C++ Idioms: The code now consistently uses C++ streams (std::cout, std::ofstream) and std::string objects, which is more idiomatic than a mix of C and C++ styles. - added short comments
This commit is contained in:
committed by
Mathias Agopian
parent
3a5f558874
commit
ddca795cc9
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2018 The Android Open Source Project
|
||||
* Copyright (C) 2025 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -19,28 +19,37 @@
|
||||
#include <getopt/getopt.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <string.h>
|
||||
|
||||
using namespace std;
|
||||
using namespace utils;
|
||||
|
||||
static const char* g_jsonMagicString = "__RESGEN__";
|
||||
static const char* g_packageName = "resources";
|
||||
static const char* g_deployDir = ".";
|
||||
static bool g_keepExtension = false;
|
||||
static bool g_appendNull = false;
|
||||
static bool g_generateC = false;
|
||||
static bool g_quietMode = false;
|
||||
static bool g_embedJson = false;
|
||||
// Holds all command-line configuration for the resource generator.
|
||||
struct AppConfig {
|
||||
const char* packageName = "resources";
|
||||
const char* deployDir = ".";
|
||||
bool keepExtension = false;
|
||||
bool appendNull = false;
|
||||
bool generateC = false;
|
||||
bool quietMode = false;
|
||||
bool embedJson = false;
|
||||
std::vector<Path> inputPaths;
|
||||
};
|
||||
|
||||
// A special marker used to identify where to insert the JSON summary blob.
|
||||
static const char* g_jsonMagicString = "__RESGEN__";
|
||||
|
||||
// Defines the help message text displayed to the user.
|
||||
static const char* USAGE = R"TXT(
|
||||
RESGEN aggregates a sequence of binary blobs, each of which becomes a "resource" whose id
|
||||
is the basename of the input file. It produces the following set of files:
|
||||
@@ -82,6 +91,7 @@ Examples:
|
||||
TEXTURES_BEACH_DATA, TEXTURES_BEACH_SIZE
|
||||
)TXT";
|
||||
|
||||
// Template for the Apple-specific assembly file.
|
||||
static const char* APPLE_ASM_TEMPLATE = R"ASM(
|
||||
.global _{RESOURCES}PACKAGE
|
||||
.section __TEXT,__const
|
||||
@@ -89,6 +99,7 @@ _{RESOURCES}PACKAGE:
|
||||
.incbin "{resources}.bin"
|
||||
)ASM";
|
||||
|
||||
// Template for the standard assembly file.
|
||||
static const char* ASM_TEMPLATE = R"ASM(
|
||||
.global {RESOURCES}PACKAGE
|
||||
.section .rodata
|
||||
@@ -96,287 +107,304 @@ static const char* ASM_TEMPLATE = R"ASM(
|
||||
.incbin "{resources}.bin"
|
||||
)ASM";
|
||||
|
||||
// Prints the usage string, replacing the placeholder with the actual executable name.
|
||||
static void printUsage(const char* name) {
|
||||
std::string execName(Path(name).getName());
|
||||
std::string const execName(Path(name).getName());
|
||||
const std::string from("RESGEN");
|
||||
std::string usage(USAGE);
|
||||
for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) {
|
||||
std::size_t pos = 0;
|
||||
while ((pos = usage.find(from, pos)) != std::string::npos) {
|
||||
usage.replace(pos, from.length(), execName);
|
||||
pos += execName.length();
|
||||
}
|
||||
puts(usage.c_str());
|
||||
std::cout << usage;
|
||||
}
|
||||
|
||||
// Prints the license text to the console.
|
||||
static void license() {
|
||||
static const char *license[] = {
|
||||
#include "licenses/licenses.inc"
|
||||
nullptr
|
||||
};
|
||||
|
||||
const char **p = &license[0];
|
||||
while (*p)
|
||||
std::cout << *p++ << std::endl;
|
||||
for (const char **p = &license[0]; *p; ++p) {
|
||||
std::cout << *p << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
static int handleArguments(int argc, char* argv[]) {
|
||||
// A helper function to replace all occurrences of a substring within a string.
|
||||
static std::string& replaceAll(std::string& context, const std::string& from, const std::string& to) {
|
||||
std::size_t pos = 0;
|
||||
while ((pos = context.find(from, pos)) != std::string::npos) {
|
||||
context.replace(pos, from.length(), to);
|
||||
pos += to.length();
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
// Parses command-line arguments using getopt and populates the AppConfig struct.
|
||||
static AppConfig handleArguments(int const argc, char* argv[]) {
|
||||
static constexpr const char* OPTSTR = "hLp:x:ktcqj";
|
||||
static const struct option OPTIONS[] = {
|
||||
{ "help", no_argument, 0, 'h' },
|
||||
{ "license", no_argument, 0, 'L' },
|
||||
{ "package", required_argument, 0, 'p' },
|
||||
{ "deploy", required_argument, 0, 'x' },
|
||||
{ "keep", no_argument, 0, 'k' },
|
||||
{ "text", no_argument, 0, 't' },
|
||||
{ "cfile", no_argument, 0, 'c' },
|
||||
{ "quiet", no_argument, 0, 'q' },
|
||||
{ "json", no_argument, 0, 'j' },
|
||||
{ 0, 0, 0, 0 } // termination of the option list
|
||||
static const option OPTIONS[] = {
|
||||
{ "help", no_argument, nullptr, 'h' },
|
||||
{ "license", no_argument, nullptr, 'L' },
|
||||
{ "package", required_argument, nullptr, 'p' },
|
||||
{ "deploy", required_argument, nullptr, 'x' },
|
||||
{ "keep", no_argument, nullptr, 'k' },
|
||||
{ "text", no_argument, nullptr, 't' },
|
||||
{ "cfile", no_argument, nullptr, 'c' },
|
||||
{ "quiet", no_argument, nullptr, 'q' },
|
||||
{ "json", no_argument, nullptr, 'j' },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
AppConfig config;
|
||||
int opt;
|
||||
int optionIndex = 0;
|
||||
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, &optionIndex)) >= 0) {
|
||||
std::string arg(optarg ? optarg : "");
|
||||
while ((opt = getopt_long(argc, argv, OPTSTR, OPTIONS, nullptr)) >= 0) {
|
||||
switch (opt) {
|
||||
default:
|
||||
case 'h':
|
||||
printUsage(argv[0]);
|
||||
exit(0);
|
||||
std::exit(0);
|
||||
case 'L':
|
||||
license();
|
||||
exit(0);
|
||||
std::exit(0);
|
||||
case 'p':
|
||||
g_packageName = optarg;
|
||||
config.packageName = optarg;
|
||||
break;
|
||||
case 'x':
|
||||
g_deployDir = optarg;
|
||||
config.deployDir = optarg;
|
||||
break;
|
||||
case 'k':
|
||||
g_keepExtension = true;
|
||||
config.keepExtension = true;
|
||||
break;
|
||||
case 't':
|
||||
g_appendNull = true;
|
||||
config.appendNull = true;
|
||||
break;
|
||||
case 'c':
|
||||
g_generateC = true;
|
||||
config.generateC = true;
|
||||
break;
|
||||
case 'q':
|
||||
g_quietMode = true;
|
||||
config.quietMode = true;
|
||||
break;
|
||||
case 'j':
|
||||
g_embedJson = true;
|
||||
config.embedJson = true;
|
||||
break;
|
||||
default:
|
||||
printUsage(argv[0]);
|
||||
std::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
return optind;
|
||||
// Treat remaining arguments as input file paths.
|
||||
for (int i = optind; i < argc; ++i) {
|
||||
config.inputPaths.emplace_back(argv[i]);
|
||||
}
|
||||
|
||||
return config;
|
||||
}
|
||||
|
||||
// Opens a file for writing, printing an error and exiting on failure.
|
||||
static std::ofstream openOutputFile(const Path& path, std::ios_base::openmode const mode = std::ios_base::out) {
|
||||
std::ofstream stream(path.getPath(), mode);
|
||||
if (!stream) {
|
||||
std::cerr << "Unable to open " << path << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
return stream;
|
||||
}
|
||||
|
||||
// Reads an entire binary file into a vector of bytes.
|
||||
static std::vector<std::uint8_t> readFile(const Path& path) {
|
||||
std::ifstream inStream(path.getPath(), std::ios::binary);
|
||||
if (!inStream) {
|
||||
std::cerr << "Unable to open " << path << std::endl;
|
||||
std::exit(1);
|
||||
}
|
||||
return std::vector<std::uint8_t>((std::istreambuf_iterator<char>(inStream)),
|
||||
(std::istreambuf_iterator<char>()));
|
||||
}
|
||||
|
||||
// Writes the generated header file, but only if its content has changed.
|
||||
// This helps avoid unnecessary recompilation in build systems.
|
||||
static void writeHeaderIfChanged(const Path& path, const std::string& newContents) {
|
||||
std::ifstream headerInStream(path.getPath(), std::ifstream::ate);
|
||||
if (headerInStream) {
|
||||
long const fileSize = headerInStream.tellg();
|
||||
if (fileSize == newContents.size()) {
|
||||
std::vector<char> previous(fileSize);
|
||||
headerInStream.seekg(0);
|
||||
headerInStream.read(previous.data(), fileSize);
|
||||
if (0 == memcmp(previous.data(), newContents.c_str(), fileSize)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
std::ofstream headerOutStream = openOutputFile(path);
|
||||
headerOutStream << newContents;
|
||||
}
|
||||
|
||||
// Writes a resource's data to a C-style char array (similar to 'xxd -i').
|
||||
static void writeXxdEntry(std::ofstream& xxdStream, const std::string& resourceName,
|
||||
const std::vector<std::uint8_t>& content) {
|
||||
xxdStream << "// " << resourceName << "\n";
|
||||
xxdStream << std::setfill('0') << std::hex;
|
||||
for (std::size_t i = 0; i < content.size(); i++) {
|
||||
if (i > 0 && i % 20 == 0) {
|
||||
xxdStream << "\n";
|
||||
}
|
||||
xxdStream << "0x" << std::setw(2) << int(content[i]) << ", ";
|
||||
}
|
||||
if (!content.empty() && content.size() % 20 != 0) {
|
||||
xxdStream << "\n";
|
||||
}
|
||||
xxdStream << "\n";
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[]) {
|
||||
const int optionIndex = handleArguments(argc, argv);
|
||||
const int numArgs = argc - optionIndex;
|
||||
if (numArgs < 1) {
|
||||
// Parse arguments and check for input files.
|
||||
const AppConfig config = handleArguments(argc, argv);
|
||||
if (config.inputPaths.empty()) {
|
||||
printUsage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vector<Path> inputPaths;
|
||||
for (int argIndex = optionIndex; argIndex < argc; ++argIndex) {
|
||||
inputPaths.emplace_back(argv[argIndex]);
|
||||
}
|
||||
if (g_embedJson) {
|
||||
inputPaths.push_back(g_jsonMagicString);
|
||||
// If JSON embedding is enabled, add a placeholder to the input list.
|
||||
std::vector<Path> inputPaths = config.inputPaths;
|
||||
if (config.embedJson) {
|
||||
inputPaths.emplace_back(g_jsonMagicString);
|
||||
}
|
||||
|
||||
std::string packageFile = g_packageName;
|
||||
std::string packagePrefix = std::string(g_packageName) + "_";
|
||||
transform(packagePrefix.begin(), packagePrefix.end(), packagePrefix.begin(), ::toupper);
|
||||
std::string package = packagePrefix + "PACKAGE";
|
||||
// Generate names and symbols based on the package name.
|
||||
const std::string packageFile = config.packageName;
|
||||
std::string packagePrefix = std::string(config.packageName) + "_";
|
||||
std::transform(packagePrefix.begin(), packagePrefix.end(), packagePrefix.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||
const std::string packageSymbol = packagePrefix + "PACKAGE";
|
||||
|
||||
const Path deployDir(g_deployDir);
|
||||
// Create the deployment directory if it doesn't exist.
|
||||
const Path deployDir(config.deployDir);
|
||||
if (!deployDir.exists()) {
|
||||
deployDir.mkdirRecursive();
|
||||
}
|
||||
|
||||
// Define all output file paths.
|
||||
const Path appleAsmPath(deployDir + (packageFile + ".apple.S"));
|
||||
const Path asmPath(deployDir + (packageFile + ".S"));
|
||||
const Path binPath(deployDir + (packageFile + ".bin"));
|
||||
const Path headerPath(deployDir + (packageFile + ".h"));
|
||||
const Path xxdPath(deployDir + (packageFile + ".c"));
|
||||
|
||||
// In the assembly language templates, replace {RESOURCES} with packagePrefix and replace
|
||||
// {resources} with packageFile.
|
||||
const std::string k1("{RESOURCES}");
|
||||
const std::string k2("{resources}");
|
||||
// Prepare assembly file content by replacing placeholders.
|
||||
std::string aasmstr(APPLE_ASM_TEMPLATE);
|
||||
replaceAll(aasmstr, "{RESOURCES}", packagePrefix);
|
||||
replaceAll(aasmstr, "{resources}", packageFile);
|
||||
|
||||
std::string asmstr(ASM_TEMPLATE);
|
||||
for (size_t pos = aasmstr.find(k1); pos != std::string::npos; pos = aasmstr.find(k1, pos))
|
||||
aasmstr.replace(pos, k1.length(), packagePrefix);
|
||||
for (size_t pos = aasmstr.find(k2); pos != std::string::npos; pos = aasmstr.find(k2, pos))
|
||||
aasmstr.replace(pos, k2.length(), packageFile);
|
||||
for (size_t pos = asmstr.find(k1); pos != std::string::npos; pos = asmstr.find(k1, pos))
|
||||
asmstr.replace(pos, k1.length(), packagePrefix);
|
||||
for (size_t pos = asmstr.find(k2); pos != std::string::npos; pos = asmstr.find(k2, pos))
|
||||
asmstr.replace(pos, k2.length(), packageFile);
|
||||
replaceAll(asmstr, "{RESOURCES}", packagePrefix);
|
||||
replaceAll(asmstr, "{resources}", packageFile);
|
||||
|
||||
// Open the Apple-friendly assembly language file.
|
||||
ofstream appleAsmStream(appleAsmPath.getPath());
|
||||
if (!appleAsmStream) {
|
||||
cerr << "Unable to open " << appleAsmPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Open all output file streams.
|
||||
auto appleAsmStream = openOutputFile(appleAsmPath);
|
||||
auto asmStream = openOutputFile(asmPath);
|
||||
auto binStream = openOutputFile(binPath, std::ios::binary);
|
||||
|
||||
// Open the non-Apple assembly language file.
|
||||
ofstream asmStream(asmPath.getPath());
|
||||
if (!asmStream) {
|
||||
cerr << "Unable to open " << asmPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// Begin constructing the C header file content.
|
||||
std::ostringstream headerStream;
|
||||
headerStream << "#ifndef " << packagePrefix << "H_\n"
|
||||
<< "#define " << packagePrefix << "H_\n\n"
|
||||
<< "#include <stdint.h>\n\n"
|
||||
<< "extern \"C\" {\n"
|
||||
<< " extern const uint8_t " << packageSymbol << "[];\n";
|
||||
|
||||
// Open the bin file for writing.
|
||||
ofstream binStream(binPath.getPath(), ios::binary);
|
||||
if (!binStream) {
|
||||
cerr << "Unable to open " << binPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// String streams to accumulate header macros and the JSON summary.
|
||||
std::ostringstream headerMacros;
|
||||
std::ostringstream jsonStream;
|
||||
|
||||
// Open the header file stream for writing.
|
||||
ostringstream headerStream;
|
||||
headerStream << "#ifndef " << packagePrefix << "H_" << endl
|
||||
<< "#define " << packagePrefix << "H_" << endl << endl
|
||||
<< "#include <stdint.h>\n" << endl
|
||||
<< "extern \"C\" {" << endl
|
||||
<< " extern const uint8_t " << package << "[];" << endl;
|
||||
|
||||
ostringstream headerMacros;
|
||||
ostringstream appleDataAsmStream;
|
||||
ostringstream dataAsmStream;
|
||||
ostringstream jsonStream;
|
||||
|
||||
// Open the generated C file for writing.
|
||||
ofstream xxdStream;
|
||||
if (g_generateC) {
|
||||
xxdStream = ofstream(xxdPath.getPath());
|
||||
if (!xxdStream) {
|
||||
cerr << "Unable to open " << xxdPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
// If generating a C file, open the stream and write the initial boilerplate.
|
||||
std::ofstream xxdStream;
|
||||
if (config.generateC) {
|
||||
xxdStream = openOutputFile(xxdPath);
|
||||
xxdStream << "#include <stdint.h>\n"
|
||||
<< "const uint8_t " << package << "[] = {\n";
|
||||
<< "const uint8_t " << packageSymbol << "[] = {\n";
|
||||
}
|
||||
|
||||
// Consume each input file and write it back out into the various output streams.
|
||||
// Process each input file to build the resource collection.
|
||||
jsonStream << "{";
|
||||
size_t offset = 0;
|
||||
std::size_t offset = 0;
|
||||
for (const auto& inPath : inputPaths) {
|
||||
vector<uint8_t> content;
|
||||
std::vector<std::uint8_t> content;
|
||||
if (inPath != g_jsonMagicString) {
|
||||
ifstream inStream(inPath.getPath(), ios::binary);
|
||||
if (!inStream) {
|
||||
cerr << "Unable to open " << inPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
content = vector<uint8_t>((istreambuf_iterator<char>(inStream)), {});
|
||||
// For a regular file, read its binary content.
|
||||
content = readFile(inPath);
|
||||
} else {
|
||||
// To finalize the JSON string, we replace the trailing comma with an end bracket and
|
||||
// prefix it with the magic identifier and string size.
|
||||
// For the JSON placeholder, finalize and embed the JSON summary blob.
|
||||
// The blob is formatted as: __RESGEN__\0<size>\0{...json...}
|
||||
std::string jsonString = jsonStream.str();
|
||||
jsonString[jsonString.size()-1] = '}';
|
||||
ostringstream jsonBlob;
|
||||
jsonBlob << g_jsonMagicString << "\0";
|
||||
jsonBlob << jsonString.size() << "\0";
|
||||
jsonString[jsonString.size() - 1] = '}';
|
||||
std::ostringstream jsonBlob;
|
||||
jsonBlob << g_jsonMagicString << '\0';
|
||||
jsonBlob << jsonString.size() << '\0';
|
||||
jsonBlob << jsonString;
|
||||
jsonString = jsonBlob.str();
|
||||
uint8_t const* jsonPtr = (uint8_t const*) jsonString.c_str();
|
||||
content = vector<uint8_t>(jsonPtr, jsonPtr + jsonBlob.str().size());
|
||||
const auto* jsonPtr = reinterpret_cast<const std::uint8_t*>(jsonString.c_str());
|
||||
content.assign(jsonPtr, jsonPtr + jsonBlob.str().size());
|
||||
}
|
||||
if (g_appendNull) {
|
||||
|
||||
// Optionally append a null terminator, useful for text resources.
|
||||
if (config.appendNull) {
|
||||
content.push_back(0);
|
||||
}
|
||||
|
||||
// Formulate the resource name and the prefixed resource name.
|
||||
std::string rname = g_keepExtension ? inPath.getName() : inPath.getNameWithoutExtension();
|
||||
replace(rname.begin(), rname.end(), '.', '_');
|
||||
transform(rname.begin(), rname.end(), rname.begin(), ::toupper);
|
||||
// Generate the resource's symbol name from its file name.
|
||||
std::string rname = config.keepExtension ? inPath.getName() : inPath.getNameWithoutExtension();
|
||||
replaceAll(rname, ".", "_");
|
||||
std::transform(rname.begin(), rname.end(), rname.begin(), [](unsigned char c) { return std::toupper(c); });
|
||||
const std::string prname = packagePrefix + rname;
|
||||
|
||||
// Write the binary blob into the bin file.
|
||||
binStream.write((const char*) content.data(), content.size());
|
||||
// Write the resource content to the aggregate binary file.
|
||||
binStream.write(reinterpret_cast<const char*>(content.data()), content.size());
|
||||
|
||||
// Write the offsets and sizes.
|
||||
headerMacros
|
||||
<< "#define " << prname << "_OFFSET " << offset << "\n"
|
||||
<< "#define " << prname << "_SIZE " << content.size() << "\n"
|
||||
<< "#define " << prname << "_DATA (" << package << " + " << prname << "_OFFSET)\n\n";
|
||||
// Generate C preprocessor macros for the resource's offset, size, and data pointer.
|
||||
headerMacros << "#define " << prname << "_OFFSET " << offset << "\n"
|
||||
<< "#define " << prname << "_SIZE " << content.size() << "\n"
|
||||
<< "#define " << prname << "_DATA (" << packageSymbol << " + " << prname << "_OFFSET)\n\n";
|
||||
|
||||
// Write the xxd-style ASCII array, followed by a blank line.
|
||||
if (g_generateC) {
|
||||
xxdStream << "// " << rname << "\n";
|
||||
xxdStream << setfill('0') << hex;
|
||||
size_t i = 0;
|
||||
for (; i < content.size(); i++) {
|
||||
if (i > 0 && i % 20 == 0) {
|
||||
xxdStream << "\n";
|
||||
}
|
||||
xxdStream << "0x" << setw(2) << (int) content[i] << ", ";
|
||||
}
|
||||
if (i % 20 != 0) xxdStream << "\n";
|
||||
xxdStream << "\n";
|
||||
// If enabled, write the content to the C source file.
|
||||
if (config.generateC) {
|
||||
writeXxdEntry(xxdStream, rname, content);
|
||||
}
|
||||
|
||||
// Add an entry to the JSON summary and update the running offset.
|
||||
jsonStream << "\"" << rname << "\":" << content.size() << ",";
|
||||
offset += content.size();
|
||||
}
|
||||
|
||||
// Finalize the header file content.
|
||||
headerStream << "}\n\n";
|
||||
headerStream << headerMacros.str();
|
||||
headerStream << "#endif\n";
|
||||
|
||||
// To optimize builds, avoid overwriting the header file if nothing has changed.
|
||||
bool headerIsDirty = true;
|
||||
ifstream headerInStream(headerPath.getPath(), std::ifstream::ate);
|
||||
string headerContents = headerStream.str();
|
||||
if (headerInStream) {
|
||||
long fileSize = static_cast<long>(headerInStream.tellg());
|
||||
if (fileSize == headerContents.size()) {
|
||||
vector<char> previous(fileSize);
|
||||
headerInStream.seekg(0);
|
||||
headerInStream.read(previous.data(), fileSize);
|
||||
headerIsDirty = 0 != memcmp(previous.data(), headerContents.c_str(), fileSize);
|
||||
}
|
||||
// Write the header and assembly files.
|
||||
writeHeaderIfChanged(headerPath, headerStream.str());
|
||||
asmStream << asmstr << std::endl;
|
||||
appleAsmStream << aasmstr << std::endl;
|
||||
|
||||
// Report generated files to the console unless in quiet mode.
|
||||
if (!config.quietMode) {
|
||||
std::cout << "Generated files: " << headerPath << " " << asmPath << " " << appleAsmPath << " "
|
||||
<< binPath;
|
||||
}
|
||||
|
||||
if (headerIsDirty) {
|
||||
ofstream headerOutStream(headerPath.getPath());
|
||||
if (!headerOutStream) {
|
||||
cerr << "Unable to open " << headerPath << endl;
|
||||
exit(1);
|
||||
}
|
||||
headerOutStream << headerContents;
|
||||
}
|
||||
|
||||
asmStream << asmstr << dataAsmStream.str() << endl;
|
||||
asmStream.close();
|
||||
|
||||
appleAsmStream << aasmstr << appleDataAsmStream.str() << endl;
|
||||
appleAsmStream.close();
|
||||
|
||||
if (!g_quietMode) {
|
||||
cout << "Generated files: "
|
||||
<< headerPath << " "
|
||||
<< asmPath << " "
|
||||
<< appleAsmPath << " "
|
||||
<< binPath;
|
||||
}
|
||||
|
||||
if (g_generateC) {
|
||||
// Finalize the C file and report it.
|
||||
if (config.generateC) {
|
||||
xxdStream << "};\n\n";
|
||||
if (!g_quietMode) {
|
||||
cout << " " << xxdPath;
|
||||
if (!config.quietMode) {
|
||||
std::cout << " " << xxdPath;
|
||||
}
|
||||
}
|
||||
|
||||
if (!g_quietMode) {
|
||||
cout << endl;
|
||||
if (!config.quietMode) {
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user