|
- /*
- ==============================================================================
-
- This file is part of the JUCE library.
- Copyright (c) 2022 - Raw Material Software Limited
-
- JUCE is an open source library subject to commercial or open-source
- licensing.
-
- By using JUCE, you agree to the terms of both the JUCE 7 End-User License
- Agreement and JUCE Privacy Policy.
-
- End User License Agreement: www.juce.com/juce-7-licence
- Privacy Policy: www.juce.com/juce-privacy-policy
-
- Or: You may also use this code under the terms of the GPL v3 (see
- www.gnu.org/licenses).
-
- JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
- EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
- DISCLAIMED.
-
- ==============================================================================
- */
-
- #include <sys/stat.h>
- #include <unistd.h>
-
- #include <algorithm>
- #include <cstdint>
- #include <fstream>
- #include <iomanip>
- #include <iostream>
- #include <optional>
- #include <vector>
-
- //==============================================================================
- struct FileHelpers
- {
- static std::string getCurrentWorkingDirectory()
- {
- std::vector<char> buffer (1024);
-
- while (getcwd (buffer.data(), buffer.size() - 1) == nullptr && errno == ERANGE)
- buffer.resize (buffer.size() * 2 / 3);
-
- return { buffer.data() };
- }
-
- static bool endsWith (const std::string& s, char c)
- {
- if (s.length() == 0)
- return false;
-
- return *s.rbegin() == c;
- }
-
- static std::string appendedPaths (const std::string& first, const std::string& second)
- {
- return endsWith (first, '/') ? first + second : first + "/" + second;
- }
-
- static bool exists (const std::string& path)
- {
- return ! path.empty() && access (path.c_str(), F_OK) == 0;
- }
-
- static bool deleteFile (const std::string& path)
- {
- if (! exists (path))
- return true;
-
- return remove (path.c_str()) == 0;
- }
-
- static std::string getFilename (const std::string& path)
- {
- return { std::find_if (path.rbegin(), path.rend(), [] (auto c) { return c == '/'; }).base(),
- path.end() };
- }
-
- static bool isDirectory (const std::string& path)
- {
- #if defined (__FreeBSD__) || defined (__OpenBSD__)
- #define JUCE_STAT stat
- #else
- #define JUCE_STAT stat64
- #endif
-
- struct JUCE_STAT info;
-
- return ! path.empty()
- && JUCE_STAT (path.c_str(), &info) == 0
- && ((info.st_mode & S_IFDIR) != 0);
- }
-
- static std::string getParentDirectory (const std::string& path)
- {
- std::string p { path.begin(),
- std::find_if (path.rbegin(),
- path.rend(),
- [] (auto c) { return c == '/'; }).base() };
-
- // Trim the ending slash, but only if not root
- if (endsWith (p, '/') && p.length() > 1)
- return { p.begin(), p.end() - 1 };
-
- return p;
- }
-
- static bool createDirectory (const std::string& path)
- {
- if (isDirectory (path))
- return true;
-
- const auto parentDir = getParentDirectory (path);
-
- if (path == parentDir)
- return false;
-
- if (createDirectory (parentDir))
- return mkdir (path.c_str(), 0777) != -1;
-
- return false;
- }
- };
-
- //==============================================================================
- struct StringHelpers
- {
- static bool isQuoteCharacter (char c)
- {
- return c == '"' || c == '\'';
- }
-
- static std::string unquoted (const std::string& str)
- {
- if (str.length() == 0 || (! isQuoteCharacter (str[0])))
- return str;
-
- return str.substr (1, str.length() - (isQuoteCharacter (str[str.length() - 1]) ? 1 : 0));
- }
-
- static void ltrim (std::string& s)
- {
- s.erase (s.begin(), std::find_if (s.begin(), s.end(), [] (int c) { return ! std::isspace (c); }));
- }
-
- static void rtrim (std::string& s)
- {
- s.erase (std::find_if (s.rbegin(), s.rend(), [] (int c) { return ! std::isspace (c); }).base(), s.end());
- }
-
- static std::string trimmed (const std::string& str)
- {
- auto result = str;
- ltrim (result);
- rtrim (result);
- return result;
- }
-
- static std::string replaced (const std::string& str, char charToReplace, char replaceWith)
- {
- auto result = str;
- std::replace (result.begin(), result.end(), charToReplace, replaceWith);
- return result;
- }
- };
-
- //==============================================================================
- static bool addFile (const std::string& filePath,
- const std::string& binaryNamespace,
- std::ofstream& headerStream,
- std::ofstream& cppStream,
- bool verbose)
- {
- std::ifstream fileStream (filePath, std::ios::in | std::ios::binary | std::ios::ate);
-
- if (! fileStream.is_open())
- {
- std::cerr << "Failed to open input file " << filePath << std::endl;
- return false;
- }
-
- std::vector<char> buffer ((size_t) fileStream.tellg());
- fileStream.seekg (0);
- fileStream.read (buffer.data(), static_cast<std::streamsize> (buffer.size()));
-
- const auto variableName = StringHelpers::replaced (StringHelpers::replaced (FileHelpers::getFilename (filePath),
- ' ',
- '_'),
- '.',
- '_');
-
- if (verbose)
- {
- std::cout << "Adding " << variableName << ": "
- << buffer.size() << " bytes" << std::endl;
- }
-
- headerStream << " extern const char* " << variableName << ";" << std::endl
- << " const int " << variableName << "Size = "
- << buffer.size() << ";" << std::endl;
-
- cppStream << "static const unsigned char temp0[] = {";
-
- auto* data = (const uint8_t*) buffer.data();
-
- for (size_t i = 0; i < buffer.size() - 1; ++i)
- {
- cppStream << (int) data[i] << ",";
-
- if ((i % 40) == 39)
- cppStream << std::endl << " ";
- }
-
- cppStream << (int) data[buffer.size() - 1] << ",0,0};" << std::endl;
- cppStream << "const char* " << binaryNamespace << "::" << variableName
- << " = (const char*) temp0" << ";" << std::endl << std::endl;
-
- return true;
- }
-
- //==============================================================================
- class Arguments
- {
- public:
- enum class PositionalArguments
- {
- sourceFile = 0,
- targetDirectory,
- targetFilename,
- binaryNamespace
- };
-
- static std::optional<Arguments> create (int argc, char* argv[])
- {
- std::vector<std::string> arguments;
- bool verbose = false;
-
- for (int i = 1; i < argc; ++i)
- {
- std::string arg { argv[i] };
-
- if (arg == "-v" || arg == "--verbose")
- verbose = true;
- else
- arguments.emplace_back (std::move (arg));
- }
-
- if (arguments.size() != static_cast<size_t> (PositionalArguments::binaryNamespace) + 1)
- return std::nullopt;
-
- return Arguments { std::move (arguments), verbose };
- }
-
- std::string get (PositionalArguments argument) const
- {
- return arguments[static_cast<size_t> (argument)];
- }
-
- bool isVerbose() const
- {
- return verbose;
- }
-
- private:
- Arguments (std::vector<std::string> args, bool verboseIn)
- : arguments (std::move (args)), verbose (verboseIn)
- {
- }
-
- std::vector<std::string> arguments;
- bool verbose = false;
- };
-
- //==============================================================================
- int main (int argc, char* argv[])
- {
- const auto arguments = Arguments::create (argc, argv);
-
- if (! arguments.has_value())
- {
- std::cout << " Usage: SimpleBinaryBuilder [-v | --verbose] sourcefile targetdirectory targetfilename namespace"
- << std::endl << std::endl
- << " SimpleBinaryBuilder will encode the provided source file into" << std::endl
- << " two files called (targetfilename).cpp and (targetfilename).h," << std::endl
- << " which it will write into the specified target directory." << std::endl
- << " The target directory will be automatically created if necessary. The binary" << std::endl
- << " resource will be available in the given namespace." << std::endl << std::endl;
-
- return 0;
- }
-
- const auto currentWorkingDirectory = FileHelpers::getCurrentWorkingDirectory();
-
- using ArgType = Arguments::PositionalArguments;
-
- const auto sourceFile = FileHelpers::appendedPaths (currentWorkingDirectory,
- StringHelpers::unquoted (arguments->get (ArgType::sourceFile)));
-
- if (! FileHelpers::exists (sourceFile))
- {
- std::cerr << "Source file doesn't exist: "
- << sourceFile
- << std::endl << std::endl;
-
- return 1;
- }
-
- const auto targetDirectory = FileHelpers::appendedPaths (currentWorkingDirectory,
- StringHelpers::unquoted (arguments->get (ArgType::targetDirectory)));
-
- if (! FileHelpers::exists (targetDirectory))
- {
- if (! FileHelpers::createDirectory (targetDirectory))
- {
- std::cerr << "Failed to create target directory: " << targetDirectory << std::endl;
- return 1;
- }
- }
-
- const auto className = StringHelpers::trimmed (arguments->get (ArgType::targetFilename));
- const auto binaryNamespace = StringHelpers::trimmed (arguments->get (ArgType::binaryNamespace));
-
- const auto headerFilePath = FileHelpers::appendedPaths (targetDirectory, className + ".h");
- const auto cppFilePath = FileHelpers::appendedPaths (targetDirectory, className + ".cpp");
-
- if (arguments->isVerbose())
- {
- std::cout << "Creating " << headerFilePath
- << " and " << cppFilePath
- << " from file " << sourceFile
- << "..." << std::endl << std::endl;
- }
-
- if (! FileHelpers::deleteFile (headerFilePath))
- {
- std::cerr << "Failed to remove old header file: " << headerFilePath << std::endl;
- return 1;
- }
-
- if (! FileHelpers::deleteFile (cppFilePath))
- {
- std::cerr << "Failed to remove old source file: " << cppFilePath << std::endl;
- return 1;
- }
-
- std::ofstream header (headerFilePath);
-
- if (! header.is_open())
- {
- std::cerr << "Failed to open " << headerFilePath << std::endl;
-
- return 1;
- }
-
- std::ofstream cpp (cppFilePath);
-
- if (! cpp.is_open())
- {
- std::cerr << "Failed to open " << headerFilePath << std::endl;
-
- return 1;
- }
-
- header << "/* (Auto-generated binary data file). */" << std::endl << std::endl
- << "#pragma once" << std::endl << std::endl
- << "namespace " << binaryNamespace << std::endl
- << "{" << std::endl;
-
- cpp << "/* (Auto-generated binary data file). */" << std::endl << std::endl
- << "#include " << std::quoted (className + ".h") << std::endl << std::endl;
-
- if (! addFile (sourceFile, binaryNamespace, header, cpp, arguments->isVerbose()))
- return 1;
-
- header << "}" << std::endl << std::endl;
-
- return 0;
- }
|