test runner, cross-target runners, lib/exe split
- subprocess-isolated test runner (replaces V1 dlopen-RunTest); Pass/Fail/Crash/Timeout/Skipped outcomes via :Test partition - TestRunner abstraction with command templates: Local, Ssh, SshWin (cmd.exe-shell), QemuUser, FromEnv; probe-based skip when runner unreachable - transitive PCM-path propagation in Build(); resolveImport walks deps recursively; depResults cache keyed by PcmDir() so per-target builds don't collide - cfg.sysroot threaded through BuildStdPcm + base compile/link command (enables aarch64 cross via Arch Linux ARM rootfs) - lib + exe split: project.cpp defines crafterBuildLib (LibraryStatic) + crafterBuildExe (Executable depending on it); build.sh produces lib/libcrafter-build.a alongside bin/crafter-build for downstream static-link consumers - Windows DLL+launcher: CRAFTER_API macro, /EXPORT flag for project.dll's CrafterBuildProject; Crafter::Run as the real entry point with main.cpp as a thin wrapper - 18 tests: HelloWorld/WithModule/Defines/CrossProjectModule/ Diamond × (Linux + sshwin:winvm), plus Incremental, BuildError, Libraries, RunnerClassification, QemuUser, SshRunner, WindowsViaSsh, CrossArchAarch64 - single ./bin/crafter-build test runs everything; Windows variants skip gracefully if winvm SSH alias unreachable Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f13671b2be
commit
cdfdb976c8
60 changed files with 2029 additions and 104 deletions
|
|
@ -21,6 +21,7 @@ export module Crafter.Build:Clang_impl;
|
|||
import std;
|
||||
import :Clang;
|
||||
import :Platform;
|
||||
import :Test;
|
||||
namespace fs = std::filesystem;
|
||||
using namespace Crafter;
|
||||
|
||||
|
|
@ -35,7 +36,10 @@ void Configuration::GetInterfacesAndImplementations(std::span<fs::path> interfac
|
|||
return true;
|
||||
}
|
||||
}
|
||||
for(Configuration* depCfg : this->dependencies) {
|
||||
|
||||
std::unordered_set<Configuration*> seen;
|
||||
std::function<bool(Configuration*)> walk = [&](Configuration* depCfg) -> bool {
|
||||
if (!seen.insert(depCfg).second) return false;
|
||||
for(const std::unique_ptr<Module>& depInterface : depCfg->interfaces) {
|
||||
if(depInterface->name == importName) {
|
||||
fs::path depPcmPath = (depCfg->PcmDir() / depInterface->path.filename()).string() + ".pcm";
|
||||
|
|
@ -43,6 +47,14 @@ void Configuration::GetInterfacesAndImplementations(std::span<fs::path> interfac
|
|||
return true;
|
||||
}
|
||||
}
|
||||
for(Configuration* sub : depCfg->dependencies) {
|
||||
if (walk(sub)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
for(Configuration* depCfg : this->dependencies) {
|
||||
if (walk(depCfg)) return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
|
@ -314,6 +326,10 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
|
||||
std::string command = std::format("{} --target={} -march={} -mtune={} -std=c++26 -D CRAFTER_BUILD_CONFIGURATION_TARGET=\\\"{}\\\" -D CRAFTER_BUILD_CONFIGURATION_TARGET_{} -fprebuilt-module-path={} -fprebuilt-module-path={}", GetBaseCommand(config), config.target, config.march, config.mtune, editedTarget, editedTarget, stdPcmDir.string(), pcmDir.string());
|
||||
|
||||
if (!config.sysroot.empty()) {
|
||||
command += std::format(" --sysroot={}", config.sysroot);
|
||||
}
|
||||
|
||||
if(config.type == ConfigurationType::LibraryDynamic) {
|
||||
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
|
||||
command += " -fPIC -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
|
|
@ -334,15 +350,26 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
depThreads.reserve(config.dependencies.size());
|
||||
std::atomic<bool> repack(false);
|
||||
|
||||
for(Configuration* dep : config.dependencies) {
|
||||
for (const auto& entry : fs::recursive_directory_iterator(dep->path)) {
|
||||
if (entry.is_directory() && entry.path().filename() == "include") {
|
||||
command += " -I" + entry.path().string();
|
||||
{
|
||||
std::unordered_set<Configuration*> seen;
|
||||
std::function<void(Configuration*)> addFlags = [&](Configuration* dep) {
|
||||
if (!seen.insert(dep).second) return;
|
||||
for (const auto& entry : fs::recursive_directory_iterator(dep->path)) {
|
||||
if (entry.is_directory() && entry.path().filename() == "include") {
|
||||
command += " -I" + entry.path().string();
|
||||
}
|
||||
}
|
||||
command += std::format(" -I{} -fprebuilt-module-path={}", dep->path.string(), dep->PcmDir().string());
|
||||
for (Configuration* sub : dep->dependencies) {
|
||||
addFlags(sub);
|
||||
}
|
||||
};
|
||||
for (Configuration* dep : config.dependencies) {
|
||||
addFlags(dep);
|
||||
}
|
||||
}
|
||||
|
||||
command += std::format(" -I{} -fprebuilt-module-path={}", dep->path.string(), dep->PcmDir().string());
|
||||
|
||||
for(Configuration* dep : config.dependencies) {
|
||||
depThreads.emplace_back([&, dep](){
|
||||
try {
|
||||
if (buildCancelled.load(std::memory_order_relaxed)) return;
|
||||
|
|
@ -352,12 +379,13 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
bool isBuilder = false;
|
||||
|
||||
depMutex.lock();
|
||||
auto it = depResults.find(dep->path);
|
||||
fs::path cacheKey = dep->PcmDir();
|
||||
auto it = depResults.find(cacheKey);
|
||||
if (it == depResults.end()) {
|
||||
isBuilder = true;
|
||||
promise = std::make_shared<std::promise<BuildResult>>();
|
||||
resultFuture = promise->get_future().share();
|
||||
depResults.emplace(dep->path, resultFuture);
|
||||
depResults.emplace(cacheKey, resultFuture);
|
||||
} else {
|
||||
resultFuture = it->second;
|
||||
}
|
||||
|
|
@ -594,10 +622,23 @@ int Crafter::Run(int argc, char** argv) {
|
|||
std::vector<std::string_view> projectArgs;
|
||||
projectArgs.reserve(argc);
|
||||
|
||||
bool runTests = false;
|
||||
RunTestsOptions testOpts;
|
||||
|
||||
for (int i = 1; i < argc; ++i) {
|
||||
std::string_view arg = argv[i];
|
||||
if (arg.starts_with("--project=")) {
|
||||
if (arg == "test") {
|
||||
runTests = true;
|
||||
} else if (arg.starts_with("--project=")) {
|
||||
projectFile = arg.substr(std::string_view("--project=").size());
|
||||
} else if (runTests && arg.starts_with("--jobs=")) {
|
||||
testOpts.jobs = std::stoi(std::string(arg.substr(std::string_view("--jobs=").size())));
|
||||
} else if (runTests && arg.starts_with("--timeout=")) {
|
||||
testOpts.timeoutOverride = std::chrono::seconds(std::stoi(std::string(arg.substr(std::string_view("--timeout=").size()))));
|
||||
} else if (runTests && arg == "--list") {
|
||||
testOpts.listOnly = true;
|
||||
} else if (runTests && !arg.starts_with("-")) {
|
||||
testOpts.globs.emplace_back(arg);
|
||||
} else {
|
||||
projectArgs.push_back(arg);
|
||||
}
|
||||
|
|
@ -610,6 +651,11 @@ int Crafter::Run(int argc, char** argv) {
|
|||
|
||||
Configuration config = LoadProject(projectFile, projectArgs);
|
||||
|
||||
if (runTests) {
|
||||
TestSummary summary = RunTests(config, testOpts);
|
||||
return summary.AllPassed() ? 0 : 1;
|
||||
}
|
||||
|
||||
std::unordered_map<fs::path, std::shared_future<BuildResult>> depResults;
|
||||
std::mutex depMutex;
|
||||
BuildResult result = Build(config, depResults, depMutex);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue