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) {
|
||||
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) {
|
||||
bool found = false;
|
||||
|
||||
|
||||
+52
-1
@@ -42,7 +42,58 @@ BuildPlan resolve(const dsl::SystemConfig& cfg, const Registry& registry) {
|
||||
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;
|
||||
for (auto& node : nodes) { selected.insert(node.name); }
|
||||
|
||||
|
||||
Reference in New Issue
Block a user