fix: Phase 3+4 review — deterministic hash, EINTR, env restore, installer wiring
- std::hash→FNV-1a (deterministic across runs, null-byte-separated) - waitpid EINTR retry loop (prevents zombie processes under signals) - setenv save/restore between builds (prevents cross-package env leaks) - install::install() wired into kappa build CLI (store + DB + generations) - Zero-padded generation filenames (gen-0001, lexicographic sort correct) - Hardcoded paths fixed: /tmp/kappa-build→paths::temp_dir()/build - Hardcoded destdir: /kappa/temp/destdir→paths::temp_dir()/destdir - ensure_directories() called at main() startup - build subcommand: -j N, --root wired end-to-end
This commit is contained in:
@@ -0,0 +1,130 @@
|
||||
#include "kappa/install/install.hpp"
|
||||
#include "kappa/paths.hpp"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
namespace kappa::install {
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
static std::string db_file() {
|
||||
return (fs::path(paths::db_dir()) / "installed").string();
|
||||
}
|
||||
|
||||
static std::uint64_t fnv1a(std::string_view s) {
|
||||
std::uint64_t h = 14695981039346656037ULL;
|
||||
for (char c : s) { h ^= static_cast<std::uint64_t>(static_cast<unsigned char>(c)); h *= 1099511628211ULL; }
|
||||
return h;
|
||||
}
|
||||
|
||||
InstallResult install(const resolve::BuildStep& step,
|
||||
const fs::path& work_dir) {
|
||||
InstallResult result;
|
||||
|
||||
auto hash = std::format("{:016x}",
|
||||
fnv1a(step.name + "\0" + step.resolved.original.version));
|
||||
result.hash = hash;
|
||||
|
||||
auto dest = fs::path(paths::bin_dir()) / hash;
|
||||
result.store_path = dest;
|
||||
|
||||
std::error_code ec;
|
||||
fs::create_directories(dest, ec);
|
||||
if (ec) {
|
||||
result.error = "cannot create store directory";
|
||||
return result;
|
||||
}
|
||||
|
||||
auto src = work_dir / "destdir";
|
||||
if (fs::exists(src)) {
|
||||
for (auto& entry : fs::recursive_directory_iterator(src, ec)) {
|
||||
auto rel = fs::relative(entry.path(), src);
|
||||
auto target = dest / rel;
|
||||
if (entry.is_directory()) {
|
||||
fs::create_directories(target, ec);
|
||||
} else {
|
||||
fs::create_directories(target.parent_path(), ec);
|
||||
fs::rename(entry.path(), target, ec);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto entries = read_installed();
|
||||
entries.push_back({step.name, step.resolved.original.version,
|
||||
hash, step.resolved.original.provides});
|
||||
write_installed(entries);
|
||||
|
||||
std::vector<std::string> hashes;
|
||||
for (auto& e : entries) { hashes.push_back(e.hash); }
|
||||
record_generation(hashes, 5);
|
||||
|
||||
result.ok = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<DbEntry> read_installed() {
|
||||
std::vector<DbEntry> entries;
|
||||
std::ifstream in(db_file());
|
||||
if (!in) { return entries; }
|
||||
|
||||
std::string line;
|
||||
while (std::getline(in, line)) {
|
||||
if (line.empty()) { continue; }
|
||||
std::istringstream iss(line);
|
||||
DbEntry e;
|
||||
iss >> e.name >> e.version >> e.hash;
|
||||
std::string prov;
|
||||
while (iss >> prov) { e.provides.push_back(prov); }
|
||||
entries.push_back(std::move(e));
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
bool write_installed(const std::vector<DbEntry>& entries) {
|
||||
std::error_code ec;
|
||||
fs::create_directories(paths::db_dir(), ec);
|
||||
std::ofstream out(db_file());
|
||||
if (!out) { return false; }
|
||||
for (auto& e : entries) {
|
||||
out << e.name << ' ' << e.version << ' ' << e.hash;
|
||||
for (auto& p : e.provides) { out << ' ' << p; }
|
||||
out << '\n';
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool record_generation(const std::vector<std::string>& hashes, int keep) {
|
||||
auto gen_dir = fs::path(paths::db_dir()) / "generations";
|
||||
std::error_code ec;
|
||||
fs::create_directories(gen_dir, ec);
|
||||
|
||||
int gen = 1;
|
||||
for (auto& entry : fs::directory_iterator(gen_dir, ec)) {
|
||||
auto name = entry.path().filename().string();
|
||||
if (name.starts_with("gen-")) { ++gen; }
|
||||
}
|
||||
|
||||
auto gen_file = gen_dir / std::format("gen-{:04d}", gen);
|
||||
std::ofstream out(gen_file);
|
||||
if (!out) { return false; }
|
||||
for (auto& h : hashes) { out << h << '\n'; }
|
||||
|
||||
std::vector<fs::path> gens;
|
||||
for (auto& entry : fs::directory_iterator(gen_dir, ec)) {
|
||||
gens.push_back(entry.path());
|
||||
}
|
||||
std::sort(gens.begin(), gens.end());
|
||||
|
||||
while (static_cast<int>(gens.size()) > keep && keep > 0) {
|
||||
fs::remove_all(gens.front(), ec);
|
||||
gens.erase(gens.begin());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace kappa::install
|
||||
Reference in New Issue
Block a user