Resolver (Phase 1): - resolve::resolve() takes SystemConfig + Registry → BuildPlan - Three-layer feature/config merge (uses config::resolve_package) - Feature-gated dependencies: deps with feature=X skipped if feature disabled - Topological sort via Kahn's algorithm (BFS on in-degree) - Cycle detection, missing package warnings - CLI: kappa resolve <config> [<package>] Formatter: - format_package() + format_config() → canonical output - Consistent 4-space indent, canonical declaration order Doctor: - check_package() + check_config() → warnings for common issues - Missing fields, empty configs, root shell, feature warnings Assertion evaluation: - evaluate_assertions() resolves dotted field paths - Supports == and != operators for config validation - kappa validate now runs assertion checks Review fixes: - plan.steps.empty() exit code corrected to 0 - Removed unused <unordered_set> include - Eliminated duplicate merge logic (uses config::resolve_package)
82 lines
2.3 KiB
C++
82 lines
2.3 KiB
C++
#include "kappa/resolve/plan.hpp"
|
|
#include "kappa/config/merge.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <queue>
|
|
|
|
namespace kappa::resolve {
|
|
|
|
BuildPlan resolve(const dsl::SystemConfig& cfg, const Registry& registry) {
|
|
BuildPlan plan;
|
|
std::unordered_map<std::string, std::size_t> name_to_idx;
|
|
std::vector<BuildStep> nodes;
|
|
|
|
for (auto& pref : cfg.packages) {
|
|
auto rit = registry.find(pref.name);
|
|
if (rit == registry.end()) {
|
|
plan.missing.push_back(pref.name);
|
|
continue;
|
|
}
|
|
|
|
auto& pkg = rit->second;
|
|
auto resolved = config::resolve_package(pkg, cfg.system, pref);
|
|
|
|
BuildStep step;
|
|
step.name = pkg.name;
|
|
step.package = pkg;
|
|
step.features = resolved.features;
|
|
step.config = resolved.config;
|
|
|
|
for (auto& dep : pkg.depends) {
|
|
if (!dep.feature.empty()) {
|
|
auto fit = step.features.find(dep.feature);
|
|
if (fit == step.features.end() || !fit->second.enabled) {
|
|
continue; // feature-gated and disabled
|
|
}
|
|
}
|
|
step.dependencies.push_back({dep.name, dep.version});
|
|
}
|
|
|
|
name_to_idx[step.name] = nodes.size();
|
|
nodes.push_back(std::move(step));
|
|
}
|
|
|
|
std::vector<int> in_degree(nodes.size(), 0);
|
|
std::vector<std::vector<std::size_t>> adj(nodes.size());
|
|
|
|
for (std::size_t i = 0; i < nodes.size(); ++i) {
|
|
for (auto& dep : nodes[i].dependencies) {
|
|
auto it = name_to_idx.find(dep.name);
|
|
if (it != name_to_idx.end()) {
|
|
adj[it->second].push_back(i);
|
|
in_degree[i]++;
|
|
}
|
|
}
|
|
}
|
|
|
|
std::queue<std::size_t> q;
|
|
for (std::size_t i = 0; i < nodes.size(); ++i) {
|
|
if (in_degree[i] == 0) { q.push(i); }
|
|
}
|
|
|
|
std::vector<bool> visited(nodes.size(), false);
|
|
while (!q.empty()) {
|
|
auto u = q.front(); q.pop();
|
|
visited[u] = true;
|
|
plan.steps.push_back(nodes[u]);
|
|
for (auto v : adj[u]) {
|
|
if (--in_degree[v] == 0) { q.push(v); }
|
|
}
|
|
}
|
|
|
|
for (std::size_t i = 0; i < nodes.size(); ++i) {
|
|
if (!visited[i]) {
|
|
plan.cycles.push_back(nodes[i].name);
|
|
}
|
|
}
|
|
|
|
return plan;
|
|
}
|
|
|
|
} // namespace kappa::resolve
|