This commit is contained in:
parent
bc9ceb8f24
commit
0ab30a1d81
5 changed files with 249 additions and 18 deletions
|
|
@ -21,6 +21,8 @@ module Crafter.Build:External_impl;
|
|||
import std;
|
||||
import :External;
|
||||
import :Platform;
|
||||
import :Progress;
|
||||
import :Clang;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
|
|
@ -76,6 +78,14 @@ std::string FetchGit(const GitSource& source, const fs::path& cloneDir) {
|
|||
}
|
||||
return "";
|
||||
} else {
|
||||
// No commit pinned — pull latest. The user opted into "track latest"
|
||||
// by not pinning; the cost is one network round-trip per build.
|
||||
// Pin source.commit to a SHA for reproducible, offline-capable builds.
|
||||
if (!source.branch.empty()) {
|
||||
if (auto err = runGit(std::format("git -C {} pull origin {}", ShellQuote(cloneDir.string()), ShellQuote(source.branch)))) return *err;
|
||||
} else {
|
||||
if (auto err = runGit(std::format("git -C {} pull", ShellQuote(cloneDir.string())))) return *err;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
@ -265,3 +275,131 @@ ExternalBuildResult Crafter::BuildExternal(
|
|||
|
||||
return result;
|
||||
}
|
||||
|
||||
namespace {
|
||||
std::mutex projectCacheMutex;
|
||||
std::unordered_map<std::string, std::unique_ptr<Configuration>> projectCache;
|
||||
|
||||
// Run the dep's project.cpp from its own directory so its relative paths
|
||||
// (cfg.path = "./", interfaces/, lib/, ...) resolve against the dep
|
||||
// root, then rebase cfg.path and all other relative path collections to
|
||||
// absolute so Build() can run from any cwd without misresolving them.
|
||||
// The mutex is NOT held across LoadProject — the dep may itself call
|
||||
// GitProject/LocalProject, and a non-recursive lock would deadlock.
|
||||
Configuration LoadProjectFromRoot(const fs::path& projectPath, const fs::path& depRoot, std::span<const std::string_view> args) {
|
||||
fs::path savedCwd = fs::current_path();
|
||||
Configuration cfg;
|
||||
fs::current_path(depRoot);
|
||||
try {
|
||||
cfg = LoadProject(projectPath, args);
|
||||
} catch (...) {
|
||||
fs::current_path(savedCwd);
|
||||
throw;
|
||||
}
|
||||
fs::current_path(savedCwd);
|
||||
cfg.path = fs::absolute(depRoot / cfg.path).lexically_normal();
|
||||
auto abs = [&](const fs::path& p) -> fs::path {
|
||||
return p.is_absolute() ? p : fs::absolute(depRoot / p).lexically_normal();
|
||||
};
|
||||
for (fs::path& p : cfg.cFiles) p = abs(p);
|
||||
for (fs::path& p : cfg.cuda) p = abs(p);
|
||||
for (fs::path& p : cfg.files) p = abs(p);
|
||||
for (Shader& s : cfg.shaders) s.path = abs(s.path);
|
||||
return cfg;
|
||||
}
|
||||
}
|
||||
|
||||
Configuration* Crafter::GitProject(const GitProjectSpec& spec) {
|
||||
// Two hashes: the clone is shared across specs that point at the same
|
||||
// source revision (forwarded args don't change what gets cloned, just
|
||||
// how the dep's project.cpp interprets them at runtime), while the
|
||||
// in-memory Configuration is keyed by the full spec so each (args)
|
||||
// permutation gets its own Configuration object.
|
||||
std::string srcKey = std::format("git|{}|{}|{}|{}",
|
||||
spec.source.url, spec.source.branch, spec.source.commit, spec.projectFile.string());
|
||||
std::string srcHash = std::format("{:016x}", std::hash<std::string>{}(srcKey));
|
||||
|
||||
std::string fullKey = srcKey;
|
||||
for (const std::string& a : spec.args) {
|
||||
fullKey += '|';
|
||||
fullKey += a;
|
||||
}
|
||||
std::string fullHash = std::format("{:016x}", std::hash<std::string>{}(fullKey));
|
||||
|
||||
{
|
||||
std::lock_guard lock(projectCacheMutex);
|
||||
if (auto it = projectCache.find(fullHash); it != projectCache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
std::string name = DeriveName(spec.source);
|
||||
if (name.empty()) {
|
||||
throw std::runtime_error(std::format("GitProject: could not derive name from URL '{}'", spec.source.url));
|
||||
}
|
||||
|
||||
fs::path externalRoot = GetCacheDir() / "external";
|
||||
fs::create_directories(externalRoot);
|
||||
fs::path cloneDir = externalRoot / std::format("{}-{}", name, srcHash);
|
||||
|
||||
{
|
||||
bool exists = fs::exists(cloneDir);
|
||||
Progress::Task task(std::format("{} {}", exists ? "Updating" : "Cloning", name));
|
||||
if (std::string err = FetchGit(spec.source, cloneDir); !err.empty()) {
|
||||
throw std::runtime_error(std::format("GitProject({}): {}", spec.source.url, err));
|
||||
}
|
||||
}
|
||||
|
||||
fs::path projectPath = cloneDir / spec.projectFile;
|
||||
if (!fs::exists(projectPath)) {
|
||||
throw std::runtime_error(std::format(
|
||||
"GitProject({}): {} not found in clone", spec.source.url, spec.projectFile.string()));
|
||||
}
|
||||
|
||||
std::vector<std::string_view> argViews(spec.args.begin(), spec.args.end());
|
||||
Configuration cfg = LoadProjectFromRoot(projectPath, cloneDir, argViews);
|
||||
|
||||
std::lock_guard lock(projectCacheMutex);
|
||||
if (auto it = projectCache.find(fullHash); it != projectCache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
auto stored = std::make_unique<Configuration>(std::move(cfg));
|
||||
Configuration* ptr = stored.get();
|
||||
projectCache.emplace(std::move(fullHash), std::move(stored));
|
||||
return ptr;
|
||||
}
|
||||
|
||||
Configuration* Crafter::LocalProject(const LocalProjectSpec& spec) {
|
||||
fs::path projectPath = fs::absolute(spec.projectFile).lexically_normal();
|
||||
if (!fs::exists(projectPath)) {
|
||||
throw std::runtime_error(std::format("LocalProject: {} not found", projectPath.string()));
|
||||
}
|
||||
fs::path canonical = fs::canonical(projectPath);
|
||||
|
||||
std::string fullKey = std::format("local|{}", canonical.string());
|
||||
for (const std::string& a : spec.args) {
|
||||
fullKey += '|';
|
||||
fullKey += a;
|
||||
}
|
||||
std::string fullHash = std::format("{:016x}", std::hash<std::string>{}(fullKey));
|
||||
|
||||
{
|
||||
std::lock_guard lock(projectCacheMutex);
|
||||
if (auto it = projectCache.find(fullHash); it != projectCache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
}
|
||||
|
||||
fs::path depRoot = canonical.parent_path();
|
||||
std::vector<std::string_view> argViews(spec.args.begin(), spec.args.end());
|
||||
Configuration cfg = LoadProjectFromRoot(canonical, depRoot, argViews);
|
||||
|
||||
std::lock_guard lock(projectCacheMutex);
|
||||
if (auto it = projectCache.find(fullHash); it != projectCache.end()) {
|
||||
return it->second.get();
|
||||
}
|
||||
auto stored = std::make_unique<Configuration>(std::move(cfg));
|
||||
Configuration* ptr = stored.get();
|
||||
projectCache.emplace(std::move(fullHash), std::move(stored));
|
||||
return ptr;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue