#include #include #include #include #include #include #include #include #include "enum/analysis.h" #include "enum/codegen.h" #include "object/analysis.h" #include "object/codegen.h" #include "data/analysis.h" #include "data/codegen.h" #include "util.h" // namespace data { // #include "data/analysis.h" // #include "data/codegen.h" // } namespace fs = std::filesystem; // https://clang.llvm.org/docs/LibClang.html int processHeader(fs::path srcRoot, fs::path srcPath, fs::path outPath) { std::string srcRootStr = string_of(srcRoot); std::string srcPathStr = string_of(srcPath); std::string outPathStr = string_of(outPath); std::string logFileStr = outPathStr + ".log"; const char* cargs[] = { "-xc++", "-std=c++17", "-I", srcRootStr.c_str(), "-D__AUTOGEN__", 0 }; // THANK YOU SO MUCH THIS STACKOVERFLOW ANSWER IS SO HELPFUL // https://stackoverflow.com/a/59206378/16255372 CXIndex index = clang_createIndex(0, 0); CXTranslationUnit unit = clang_parseTranslationUnit( index, srcPathStr.c_str(), cargs, 5, nullptr, 0, CXTranslationUnit_None); if (!unit) { fprintf(stderr, "Failed to parse file\n"); return 1; } // We write to a special log file instead of stdout/stderr to // 1. avoid confusion // 2. prevent MSBuild from reading the word "error" and detecting there's a problem with the program (there isn't) FILE* logout = fopen(logFileStr.c_str(), "w"); // Print errors int ndiags = clang_getNumDiagnostics(unit); for (int i = 0; i < ndiags; i++) { CXDiagnostic diag = clang_getDiagnostic(unit, i); CXString str = clang_formatDiagnostic(diag, 0); fprintf(logout, "diag: %s\n", clang_getCString(str)); clang_disposeString(str); clang_disposeDiagnostic(diag); } fclose(logout); CXCursor cursor = clang_getTranslationUnitCursor(unit); object::AnalysisState objectAnlyState; data::AnalysisState dataAnlyState; enum_::AnalysisState enumAnlyState; fs::path relpath = fs::relative(srcPath, srcRoot); std::string relpathStr = string_of(relpath); printf("[AUTOGEN] Processing file %s...\n", relpathStr.c_str()); object::analyzeClasses(cursor, srcRootStr, &objectAnlyState); data::analyzeClasses(cursor, srcRootStr, &dataAnlyState); enum_::analyzeClasses(cursor, srcRootStr, &enumAnlyState); fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it printf("[AUTOGEN] Generating file %s...\n", relpathStr.c_str()); std::ofstream outStream(outPathStr); if (!objectAnlyState.classes.empty() || !dataAnlyState.classes.empty()) { outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n"; outStream << "// This file was automatically generated by autogen, and should not be edited manually //\n"; outStream << "/////////////////////////////////////////////////////////////////////////////////////////\n\n"; } for (auto& [_, clazz] : objectAnlyState.classes) { object::writeCodeForClass(outStream, relpathStr, clazz); } for (auto& [_, clazz] : dataAnlyState.classes) { data::writeCodeForClass(outStream, relpathStr, clazz); } for (auto& [_, clazz] : enumAnlyState.classes) { enum_::writeCodeForClass(outStream, relpathStr, clazz); } outStream.close(); return 0; } int main(int argc, char** argv) { if (argc < 4) { fprintf(stderr, "Usage: autogen \n"); return 1; } fs::path srcRoot = argv[1]; fs::path srcPath = argv[2]; fs::path outPath = argv[3]; // fprintf(stderr, "Some error here\n"); // return 0; return processHeader(srcRoot, srcPath, outPath); }