diff options
Diffstat (limited to 'buildtools/codegen')
-rw-r--r-- | buildtools/codegen/CodegenOutput.inl | 19 | ||||
-rw-r--r-- | buildtools/codegen/CodegenUtils.inl | 73 | ||||
-rw-r--r-- | buildtools/codegen/main.cpp | 154 |
3 files changed, 134 insertions, 112 deletions
diff --git a/buildtools/codegen/CodegenOutput.inl b/buildtools/codegen/CodegenOutput.inl index edd9abc..ff7b912 100644 --- a/buildtools/codegen/CodegenOutput.inl +++ b/buildtools/codegen/CodegenOutput.inl @@ -3,6 +3,9 @@ #include "CodegenDecl.hpp" #include "CodegenMacros.hpp" +#include <Utils.hpp> + +#include <robin_hood.h> #include <algorithm> #include <cstdio> #include <cstdlib> @@ -16,6 +19,7 @@ struct CodegenOutputThing { class CodegenOutput { private: + robin_hood::unordered_set<std::string, StringHash, StringEqual> mRequestIncludes; std::vector<CodegenOutputThing> mOutThings; std::vector<DeclStruct> mOutStructs; std::vector<DeclEnum> mOutEnums; @@ -27,6 +31,12 @@ public: bool optionAutoAddPrefix : 1 = false; public: + void AddRequestInclude(std::string_view include) { + if (!mRequestIncludes.contains(include)) { + mRequestIncludes.insert(std::string(include)); + } + } + void AddOutputThing(CodegenOutputThing thing) { mOutThings.push_back(std::move(thing)); } @@ -39,15 +49,20 @@ public: } void Write(FILE* file) const { + for (auto& include : mRequestIncludes) { + // TODO how to resolve to the correct include paths? + WRITE_FMT_LN(file, "#include <%s>", include.c_str()); + } + for (auto& thing : mOutThings) { fwrite(thing.text.c_str(), sizeof(char), thing.text.size(), file); WRITE_LIT(file, "\n"); } for (auto& declStruct : mOutStructs) { - WRITE_FMT(file, "struct %s {\n", declStruct.name.c_str()); + WRITE_FMT_LN(file, "struct %s {", declStruct.name.c_str()); // TODO - WRITE_LIT(file, "}\n"); + WRITE_LIT_LN(file, "};"); } for (auto& declEnum : mOutEnums) { diff --git a/buildtools/codegen/CodegenUtils.inl b/buildtools/codegen/CodegenUtils.inl new file mode 100644 index 0000000..ea46ac1 --- /dev/null +++ b/buildtools/codegen/CodegenUtils.inl @@ -0,0 +1,73 @@ +#pragma once + +#include "CodegenConfig.hpp" +#include "CodegenMacros.hpp" + +#include "CodegenOutput.inl" + +#include <Macros.hpp> +#include <ScopeGuard.hpp> + +#include <cstdio> +#include <cstdlib> +#include <filesystem> + +namespace Utils { + +std::string ReadFileAsString(const std::filesystem::path& path) { + auto file = Utils::OpenCstdioFile(path, Utils::Read); + if (!file) throw std::runtime_error("Failed to open source file."); + DEFER { fclose(file); }; + + fseek(file, 0, SEEK_END); + auto fileSize = ftell(file); + rewind(file); + + std::string result(fileSize, '\0'); + fread(result.data(), fileSize, 1, file); + + return result; +} + +bool WriteOutputFile(const CodegenOutput& output, std::string_view dir, std::string_view filename, std::string_view additionalSuffix = {}) { + char path[2048]; + snprintf(path, sizeof(path), "%.*s/%.*s%.*s", PRINTF_STRING_VIEW(dir), PRINTF_STRING_VIEW(filename), PRINTF_STRING_VIEW(additionalSuffix)); + + auto outputFile = Utils::OpenCstdioFile(path, Utils::WriteTruncate); + if (!outputFile) { + printf("[ERROR] unable to open output file %s\n", path); + return false; + } + DEFER { fclose(outputFile); }; + + DEBUG_PRINTF("Writing output %s\n", path); + output.Write(outputFile); + + return true; +} + +void ProduceGeneratedHeaderFileHeader(CodegenOutput& output) { + output.AddOutputThing(CodegenOutputThing{ + .text = &R"""( +// This file is generated. Any changes will be overidden when building. +#pragma once + +#include <MetadataBase.hpp> +)"""[1], + }); +} + +void ProduceGeneratedSourceFileHeader(CodegenOutput& output) { + output.AddOutputThing(CodegenOutputThing{ + .text = &R"""( +// This file is generated. Any changes will be overidden when building. +#include "GeneratedCode.hpp" + +#include <frozen/string.h> +#include <frozen/unordered_map.h> +using namespace std::literals; + )"""[1], + }); +} + +} // namespace Utils diff --git a/buildtools/codegen/main.cpp b/buildtools/codegen/main.cpp index 298f19e..3a3e443 100644 --- a/buildtools/codegen/main.cpp +++ b/buildtools/codegen/main.cpp @@ -4,6 +4,7 @@ #include "CodegenInput.inl" #include "CodegenOutput.inl" +#include "CodegenUtils.inl" #include <Enum.hpp> #include <LookupTable.hpp> @@ -27,10 +28,12 @@ using namespace std::literals; namespace fs = std::filesystem; // TODO handle namespace +// TODO support codegen target in .cpp files struct AppState { - CodegenOutput headerOutput; - CodegenOutput sourceOutput; + std::string_view outputDir; + CodegenOutput mainHeaderOutput; + CodegenOutput mainSourceOutput; }; enum { @@ -113,7 +116,7 @@ bool StbTokenIsMultiChar(int lexerToken) { void CheckBraceDepth(int braceDpeth) { if (braceDpeth < 0) { - printf("[WARNING] unbalanced brace"); + printf("[WARNING] unbalanced brace\n"); } } @@ -197,7 +200,7 @@ std::vector<StbLexerToken> RecordTokens(std::string_view source) { } if (lexer.token == CLEX_parse_error) { - printf("[ERROR] stb_c_lexer countered a parse error."); + printf("[ERROR] stb_c_lexer countered a parse error.\n"); // TODO how to handle? continue; } @@ -292,11 +295,6 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D maxVal = decl.elements[decl.elements.size() - 2].value; } - CodegenOutputThing lookupFunctionDecl; - auto& o1 = lookupFunctionDecl.text; - APPEND_LIT_LN(o1, "template <>"); - APPEND_FMT_LN(o1, "std::string_view Metadata::EnumToString<%s>(%s);", decl.name.c_str(), decl.name.c_str()); - CodegenOutputThing lookupFunctionDef; auto& o2 = lookupFunctionDef.text; APPEND_LIT_LN(o2, "template <>"); @@ -305,7 +303,6 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D APPEND_FMT_LN(o2, " return %s[value - %d];", arrayName.c_str(), minVal); APPEND_LIT_LN(o2, "}"); - headerOut.AddOutputThing(std::move(lookupFunctionDecl)); sourceOut.AddOutputThing(std::move(lookupFunctionDef)); } break; @@ -337,11 +334,6 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D APPEND_LIT_LN(o1, "};"); // Generate lookup function - CodegenOutputThing lookupFunctionDecl; - auto& o2 = lookupFunctionDecl.text; - APPEND_LIT_LN(o2, "template <>"); - APPEND_FMT_LN(o2, "std::optional<%s> Metadata::EnumFromString<%s>(std::string_view);", decl.name.c_str(), decl.name.c_str()); - CodegenOutputThing lookupFunctionDef; auto& o3 = lookupFunctionDef.text; APPEND_LIT_LN(o3, "template <>"); @@ -355,12 +347,11 @@ void GenerateForEnum(CodegenOutput& headerOut, CodegenOutput& sourceOut, const D APPEND_LIT_LN(o3, "}"); sourceOut.AddOutputThing(std::move(lookupTable)); - headerOut.AddOutputThing(std::move(lookupFunctionDecl)); sourceOut.AddOutputThing(std::move(lookupFunctionDef)); } } -void HandleInputFile(AppState& state, std::string_view source) { +void HandleInputFile(AppState& state, std::string_view filenameStem, std::string_view source) { auto tokens = RecordTokens(source); size_t idx = 0; @@ -373,8 +364,10 @@ void HandleInputFile(AppState& state, std::string_view source) { #endif CodegenInput cgInput; - auto& cgHeaderOutput = state.headerOutput; - auto& cgSourceOutput = state.sourceOutput; + CodegenOutput cgHeaderOutput; + Utils::ProduceGeneratedHeaderFileHeader(cgHeaderOutput); + CodegenOutput cgSourceOutput; + Utils::ProduceGeneratedSourceFileHeader(cgSourceOutput); int bracePairDepth = 0; while (idx < tokens.size()) { @@ -545,21 +538,9 @@ void HandleInputFile(AppState& state, std::string_view source) { if (bracePairDepth != 0) { printf("[WARNING] unbalanced brace at end of file."); } -} - -std::string ReadFileAtOnce(const fs::path& path) { - auto file = Utils::OpenCstdioFile(path, Utils::Read); - if (!file) throw std::runtime_error("Failed to open source file."); - DEFER { fclose(file); }; - fseek(file, 0, SEEK_END); - auto fileSize = ftell(file); - rewind(file); - - std::string result(fileSize, '\0'); - fread(result.data(), fileSize, 1, file); - - return result; + Utils::WriteOutputFile(cgHeaderOutput, state.outputDir, filenameStem, ".gh.inl"sv); + Utils::WriteOutputFile(cgSourceOutput, state.outputDir, filenameStem, ".gs.inl"sv); } enum InputOpcode { @@ -571,12 +552,17 @@ enum InputOpcode { void HandleArgument(AppState& state, InputOpcode opcode, std::string_view operand) { switch (opcode) { case IOP_ProcessSingleFile: { - fs::path filePath(operand); - auto source = ReadFileAtOnce(filePath); - HandleInputFile(state, source); + DEBUG_PRINTF("Processing single file %.*s\n", PRINTF_STRING_VIEW(operand)); + + fs::path path(operand); + auto filenameStem = path.stem().string(); + auto source = Utils::ReadFileAsString(path); + HandleInputFile(state, filenameStem, source); } break; case IOP_ProcessRecursively: { + DEBUG_PRINTF("Recursively processing folder %.*s\n", PRINTF_STRING_VIEW(operand)); + fs::path startPath(operand); for (auto& item : fs::recursive_directory_iterator(startPath)) { if (!item.is_regular_file()) { @@ -584,15 +570,19 @@ void HandleArgument(AppState& state, InputOpcode opcode, std::string_view operan } auto& path = item.path(); - auto filename = path.filename().string(); - if (filename != ".c" || - filename != ".cpp") + auto pathExt = path.extension(); + auto pathStem = path.stem(); + if (pathExt != ".h" && + pathExt != ".hpp") { continue; } - auto source = ReadFileAtOnce(path); - HandleInputFile(state, source); + DEBUG_PRINTF("Processing subfile %s\n", path.string().c_str()); + + auto filenameStem = pathStem.string(); + auto source = Utils::ReadFileAsString(path); + HandleInputFile(state, filenameStem, source); } } break; @@ -605,25 +595,10 @@ InputOpcode ParseInputOpcode(std::string_view text) { return IOP_ProcessSingleFile; } else if (text == "rec"sv) { return IOP_ProcessRecursively; + } else { + DEBUG_PRINTF("Unknown input opcode %s\n", text.data()); + throw std::runtime_error("Unknown input opcode"); } - return IOP_COUNT; -} - -bool WriteOutputFile(const CodegenOutput& output, const char* dirPath, const char* fileName) { - char path[2048]; - snprintf(path, sizeof(path), "%s/%s", dirPath, fileName); - - auto outputFile = Utils::OpenCstdioFile(path, Utils::WriteTruncate); - if (!outputFile) { - printf("[ERROR] unable to open output file %s", path); - return false; - } - DEFER { fclose(outputFile); }; - - DEBUG_PRINTF("Writing output %s", path); - output.Write(outputFile); - - return true; } int main(int argc, char* argv[]) { @@ -639,25 +614,8 @@ int main(int argc, char* argv[]) { AppState state; - state.headerOutput.AddOutputThing(CodegenOutputThing{ - .text = &R"""( -// This file is generated. Any changes will be overidden when building. -#pragma once - -#include <MetadataBase.hpp> -)"""[1], - }); - - state.sourceOutput.AddOutputThing(CodegenOutputThing{ - .text = &R"""( -// This file is generated. Any changes will be overidden when building. -#include "MetadataImpl.hpp" - -#include <frozen/string.h> -#include <frozen/unordered_map.h> -using namespace std::literals; - )"""[1], - }); + Utils::ProduceGeneratedHeaderFileHeader(state.mainHeaderOutput); + Utils::ProduceGeneratedSourceFileHeader(state.mainSourceOutput); // If no cli is provided (argv[0] conventionally but not mandatorily the cli), this will do thing // Otherwise, start with the 2nd element in the array, which is the 1st actual argument @@ -668,55 +626,31 @@ USAGE: codegen.exe <output path> [<opcode>:<input path>]... where <output path>: the directory to write generated contents to. This will NOT automatically create the directory. <opcode> is one of: "single" process this <input path> file only - "rec" starting at the given directory <input path>, recursively process all .h .c .hpp .cpp files + "rec" starting at the given directory <input path>, recursively process all .h .hpp files )"""[1]); return -1; } - const char* outputDir = argv[1]; + state.outputDir = std::string_view(argv[1]); DEBUG_PRINTF("Outputting to directory %s.\n", outputDir); - { - char path[2048]; - snprintf(path, sizeof(path), "%s/%s", outputDir, "Metadata.hpp"); - - auto fileContent = R"""( -// This file is generated. Any changes will be overidden when building. -// This file is an umbrella header for all generated metadata. End users simply include this file to get access to everything. -#pragma once - -#include <MetadataBase.hpp> - -#include <generated/MetadataImpl.hpp> -)"""sv.substr(1); // Get rid of the \n at the beginning - - auto file = Utils::OpenCstdioFile(path, Utils::WriteTruncate); - if (!file) { - printf("[ERROR] unable to open output file %s", path); - return -1; - } - DEFER { fclose(file); }; - - DEBUG_PRINTF("Writing output file %s", path); - fwrite(fileContent.data(), sizeof(decltype(fileContent)::value_type), fileContent.size(), file); - } - for (int i = 2; i < argc; ++i) { - std::string_view arg(argv[i]); + const char* argRaw = argv[i]; + std::string_view arg(argRaw); + DEBUG_PRINTF("Processing input command %s\n", argRaw); + auto separatorLoc = arg.find(':'); if (separatorLoc != std::string_view::npos) { auto opcodeString = arg.substr(0, separatorLoc); auto opcode = ParseInputOpcode(opcodeString); auto operand = arg.substr(separatorLoc + 1); - DEBUG_PRINTF("Processing input command %.*s at path %.*s\n", (int)opcodeString.size(), opcodeString.data(), (int)operand.size(), operand.data()); - HandleArgument(state, opcode, operand); } } - WriteOutputFile(state.headerOutput, outputDir, "MetadataImpl.hpp"); - WriteOutputFile(state.sourceOutput, outputDir, "MetadataImpl.cpp"); + Utils::WriteOutputFile(state.mainHeaderOutput, state.outputDir, "GeneratedCode.hpp"sv); + Utils::WriteOutputFile(state.mainSourceOutput, state.outputDir, "GeneratedCode.cpp"sv); return 0; } |