Files
whetstone_DSL/editor/src/ArchitectBuildAwareness.h

209 lines
8.0 KiB
C++

#pragma once
// Step 479: Dependency + Build System Awareness
// Adds build/dependency annotations from scaffold module dependencies.
#include "ArchitectSkeletonGenerator.h"
#include <algorithm>
#include <functional>
#include <map>
#include <set>
#include <string>
#include <vector>
struct BuildDependencyAnnotation {
std::string moduleName;
std::string language;
std::vector<std::string> moduleDependencies;
std::vector<std::string> importHints;
std::vector<std::string> packageDependencies;
std::string manifestSnippet;
int migrationOrder = 0; // SQL-only: 1-based order
std::vector<std::string> notes;
};
struct BuildAwarenessReport {
std::vector<BuildDependencyAnnotation> modules;
std::vector<std::string> globalNotes;
bool hasModule(const std::string& moduleName) const {
for (const auto& m : modules) if (m.moduleName == moduleName) return true;
return false;
}
BuildDependencyAnnotation getModule(const std::string& moduleName) const {
for (const auto& m : modules) if (m.moduleName == moduleName) return m;
return {};
}
};
class ArchitectBuildAwareness {
public:
static BuildAwarenessReport annotate(const SkeletonProjectSpec& skeleton) {
BuildAwarenessReport out;
const auto sqlOrder = computeSqlMigrationOrder(skeleton);
for (const auto& m : skeleton.modules) {
BuildDependencyAnnotation ann;
ann.moduleName = m.moduleName;
ann.language = lower(m.language);
ann.moduleDependencies = m.dependencies;
if (ann.language == "python") {
annotatePython(m, ann);
} else if (ann.language == "rust") {
annotateRust(m, ann);
} else if (ann.language == "typescript" || ann.language == "javascript") {
annotateNode(m, ann);
} else if (ann.language == "cpp" || ann.language == "c") {
annotateCpp(m, ann);
} else if (ann.language == "sql") {
annotateSql(m, ann, sqlOrder);
} else {
annotateGeneric(m, ann);
}
out.modules.push_back(std::move(ann));
}
out.globalNotes.push_back("Build awareness report generated from module dependencies");
out.globalNotes.push_back("Snippets are annotation-level hints, not executable build configs");
return out;
}
private:
static std::string lower(const std::string& s) {
std::string out = s;
std::transform(out.begin(), out.end(), out.begin(),
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return out;
}
static void annotatePython(const SkeletonModuleSpec& m, BuildDependencyAnnotation& ann) {
for (const auto& dep : m.dependencies) {
ann.importHints.push_back("from " + dep + " import *");
}
ann.packageDependencies.push_back("typing-extensions");
if (m.moduleName.find("api") != std::string::npos) ann.packageDependencies.push_back("fastapi");
if (m.moduleName.find("data") != std::string::npos) ann.packageDependencies.push_back("sqlalchemy");
ann.manifestSnippet = "dependencies = [";
for (size_t i = 0; i < ann.packageDependencies.size(); ++i) {
if (i) ann.manifestSnippet += ", ";
ann.manifestSnippet += "\"" + ann.packageDependencies[i] + "\"";
}
ann.manifestSnippet += "]";
ann.notes.push_back("Python imports mapped from module graph");
}
static void annotateRust(const SkeletonModuleSpec& m, BuildDependencyAnnotation& ann) {
ann.packageDependencies.push_back("serde = \"1\"");
for (const auto& dep : m.dependencies) {
ann.packageDependencies.push_back(dep + " = { path = \"../" + dep + "\" }");
ann.importHints.push_back("use crate::" + dep + "::*;");
}
ann.manifestSnippet = "[dependencies]\n";
for (const auto& p : ann.packageDependencies) ann.manifestSnippet += p + "\n";
if (m.dependencies.empty()) ann.manifestSnippet += "# no local module dependencies\n";
ann.notes.push_back("Cargo dependency hints generated from module dependencies");
}
static void annotateNode(const SkeletonModuleSpec& m, BuildDependencyAnnotation& ann) {
ann.packageDependencies.push_back("typescript");
if (m.moduleName.find("api") != std::string::npos) ann.packageDependencies.push_back("express");
for (const auto& dep : m.dependencies) {
ann.importHints.push_back("import * as " + dep + " from \"../" + dep + "/" + dep + "\";");
}
ann.manifestSnippet = "\"dependencies\": {";
for (size_t i = 0; i < ann.packageDependencies.size(); ++i) {
if (i) ann.manifestSnippet += ", ";
ann.manifestSnippet += "\"" + ann.packageDependencies[i] + "\": \"*\"";
}
ann.manifestSnippet += "}";
ann.notes.push_back("Node package and import hints generated");
}
static void annotateCpp(const SkeletonModuleSpec& m, BuildDependencyAnnotation& ann) {
for (const auto& dep : m.dependencies) {
ann.importHints.push_back("#include \"" + dep + ".h\"");
}
ann.packageDependencies.push_back("cmake-target:" + m.moduleName);
std::string snippet = "add_library(" + m.moduleName + " INTERFACE)\n";
if (!m.dependencies.empty()) {
snippet += "target_link_libraries(" + m.moduleName + " INTERFACE";
for (const auto& dep : m.dependencies) snippet += " " + dep;
snippet += ")\n";
}
ann.manifestSnippet = snippet;
ann.notes.push_back("CMake target/link hints generated from dependencies");
}
static void annotateSql(const SkeletonModuleSpec& m, BuildDependencyAnnotation& ann,
const std::map<std::string, int>& sqlOrder) {
auto it = sqlOrder.find(m.moduleName);
ann.migrationOrder = (it == sqlOrder.end()) ? 0 : it->second;
ann.manifestSnippet = "-- migration_order: " + std::to_string(ann.migrationOrder);
for (const auto& dep : m.dependencies) {
ann.importHints.push_back("-- depends_on: " + dep);
}
ann.notes.push_back("SQL migration ordering derived from dependency graph");
}
static void annotateGeneric(const SkeletonModuleSpec& m, BuildDependencyAnnotation& ann) {
ann.manifestSnippet = "# build-hints unavailable for language: " + lower(m.language);
if (m.dependencies.empty()) ann.notes.push_back("No dependencies recorded");
else ann.notes.push_back("Dependency links preserved for manual review");
}
static std::map<std::string, int> computeSqlMigrationOrder(const SkeletonProjectSpec& sk) {
std::set<std::string> sql;
for (const auto& m : sk.modules) {
if (lower(m.language) == "sql") sql.insert(m.moduleName);
}
std::vector<std::string> ordered;
std::set<std::string> temp, perm;
bool hasCycle = false;
std::function<void(const std::string&)> visit = [&](const std::string& node) {
if (perm.count(node) || hasCycle) return;
if (temp.count(node)) {
hasCycle = true;
return;
}
temp.insert(node);
for (const auto& m : sk.modules) {
if (m.moduleName != node) continue;
for (const auto& dep : m.dependencies) {
if (sql.count(dep)) visit(dep);
}
break;
}
temp.erase(node);
perm.insert(node);
ordered.push_back(node);
};
for (const auto& n : sql) {
if (!perm.count(n)) visit(n);
}
// Cycle fallback: deterministic lexical order.
if (hasCycle) {
ordered.assign(sql.begin(), sql.end());
std::sort(ordered.begin(), ordered.end());
}
std::map<std::string, int> out;
for (size_t i = 0; i < ordered.size(); ++i) out[ordered[i]] = (int)i + 1;
return out;
}
};