Resolver now pulls in transitive dependencies automatically.
Declaring 'packages { root {} }' where root→mid→leaf resolves
all three packages in correct topological order. Previously
every package had to be listed explicitly in the config.
build_registry pre-populates from $KAPPA_ROOT/cache/packages/
so transitive deps can be found without remote fetching.
This commit is contained in:
@@ -111,6 +111,24 @@ static void handle_parse_error(const char* path,
|
|||||||
static resolve::Registry build_registry(const dsl::SystemConfig& cfg) {
|
static resolve::Registry build_registry(const dsl::SystemConfig& cfg) {
|
||||||
resolve::Registry registry;
|
resolve::Registry registry;
|
||||||
|
|
||||||
|
// Pre-populate from cached packages so transitive deps can be resolved
|
||||||
|
auto pkg_dir = paths::packages_dir();
|
||||||
|
std::error_code ec;
|
||||||
|
if (std::filesystem::exists(pkg_dir)) {
|
||||||
|
for (auto& entry : std::filesystem::directory_iterator(pkg_dir, ec)) {
|
||||||
|
if (ec) break;
|
||||||
|
if (entry.path().extension() != ".kap") continue;
|
||||||
|
std::ifstream in(entry.path());
|
||||||
|
if (!in) continue;
|
||||||
|
std::ostringstream buf;
|
||||||
|
buf << in.rdbuf();
|
||||||
|
try {
|
||||||
|
auto pkg = dsl::parse(buf.str());
|
||||||
|
registry.try_emplace(pkg.name, std::move(pkg));
|
||||||
|
} catch (...) { continue; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (const auto& pref : cfg.packages) {
|
for (const auto& pref : cfg.packages) {
|
||||||
bool found = false;
|
bool found = false;
|
||||||
|
|
||||||
|
|||||||
+52
-1
@@ -42,7 +42,58 @@ BuildPlan resolve(const dsl::SystemConfig& cfg, const Registry& registry) {
|
|||||||
nodes.push_back(std::move(step));
|
nodes.push_back(std::move(step));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detect conflicts between selected packages
|
// Transitive dependency discovery: pull in deps that are in the
|
||||||
|
// registry but not yet in the plan. Continue until no new deps
|
||||||
|
// are discovered — handles arbitrarily deep dependency chains.
|
||||||
|
bool changed = true;
|
||||||
|
while (changed) {
|
||||||
|
changed = false;
|
||||||
|
std::size_t current_size = nodes.size();
|
||||||
|
|
||||||
|
for (std::size_t i = 0; i < current_size; ++i) {
|
||||||
|
if (!nodes[i].package) continue;
|
||||||
|
|
||||||
|
for (auto& dep : nodes[i].package->depends) {
|
||||||
|
if (name_to_idx.contains(dep.name)) continue;
|
||||||
|
|
||||||
|
// Skip feature-conditional deps that aren't enabled
|
||||||
|
if (!dep.feature.empty()) {
|
||||||
|
auto fit = nodes[i].resolved.features.find(dep.feature);
|
||||||
|
if (fit == nodes[i].resolved.features.end() || !fit->second.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rit = registry.find(dep.name);
|
||||||
|
if (rit == registry.end()) continue;
|
||||||
|
|
||||||
|
auto& pkg = rit->second;
|
||||||
|
auto resolved = config::resolve_package(pkg, cfg.system, {});
|
||||||
|
|
||||||
|
BuildStep step;
|
||||||
|
step.name = pkg.name;
|
||||||
|
step.package = &pkg;
|
||||||
|
step.resolved = std::move(resolved);
|
||||||
|
step.enabled_init = cfg.boot.init;
|
||||||
|
|
||||||
|
for (auto& ddep : pkg.depends) {
|
||||||
|
if (!ddep.feature.empty()) {
|
||||||
|
auto fit = step.resolved.features.find(ddep.feature);
|
||||||
|
if (fit == step.resolved.features.end() || !fit->second.enabled) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
step.dependencies.push_back({ddep.name, ddep.version});
|
||||||
|
}
|
||||||
|
|
||||||
|
name_to_idx[step.name] = nodes.size();
|
||||||
|
nodes.push_back(std::move(step));
|
||||||
|
changed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detect conflicts between all selected packages (including transitive)
|
||||||
std::unordered_set<std::string> selected;
|
std::unordered_set<std::string> selected;
|
||||||
for (auto& node : nodes) { selected.insert(node.name); }
|
for (auto& node : nodes) { selected.insert(node.name); }
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user