openblocks/autogen/src/main.cpp

118 lines
No EOL
3.9 KiB
C++

#include <clang-c/CXDiagnostic.h>
#include <clang-c/CXFile.h>
#include <clang-c/CXSourceLocation.h>
#include <clang-c/CXString.h>
#include <clang-c/Index.h>
#include <cstdio>
#include <fstream>
#include <filesystem>
#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;
}
fs::create_directories(outPath.parent_path()); // Make sure generated dir exists before we try writing to it
// 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);
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 <src-root> <src-file> <out-dir>\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);
}