linting
Some checks failed
CI / build-test-release (push) Failing after 7m15s

This commit is contained in:
Jorijn van der Graaf 2026-07-23 01:24:42 +02:00
commit 8892154b28
70 changed files with 2780 additions and 596 deletions

View file

@ -1,5 +1,5 @@
//SPDX-License-Identifier: LGPL-3.0-only
//SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
// SPDX-License-Identifier: LGPL-3.0-only
// SPDX-FileCopyrightText: Copyright (C) 2026 Catcrafts®
module;
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
@ -42,7 +42,7 @@ namespace {
// don't keep the lock alive past our own release.
class CacheLock {
#ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu
int fd_ = -1;
std::int32_t fd_ = -1;
public:
explicit CacheLock(const fs::path& cacheDir) {
fd_ = ::open((cacheDir / ".lock").c_str(), O_RDWR | O_CREAT | O_CLOEXEC, 0644);
@ -59,9 +59,7 @@ namespace {
HANDLE handle_ = INVALID_HANDLE_VALUE;
public:
explicit CacheLock(const fs::path& cacheDir) {
handle_ = CreateFileW((cacheDir / ".lock").wstring().c_str(),
GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE,
nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
handle_ = CreateFileW((cacheDir / ".lock").wstring().c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (handle_ != INVALID_HANDLE_VALUE) {
OVERLAPPED ov{};
LockFileEx(handle_, LOCKFILE_EXCLUSIVE_LOCK, 0, MAXDWORD, MAXDWORD, &ov);
@ -111,15 +109,43 @@ fs::path Crafter::GetCrafterBuildHome() {
if (parent == dir) break;
dir = parent;
}
std::string msg = std::format(
"could not locate crafter-build runtime assets relative to {} (set CRAFTER_BUILD_HOME). Tried:",
hostExe.string());
std::string msg = std::format("could not locate crafter-build runtime assets relative to {} (set CRAFTER_BUILD_HOME). Tried:", hostExe.string());
for (const auto& p : tried) {
msg += "\n " + p.string();
msg += std::format("\n {}", p.string());
}
throw std::runtime_error(msg);
}
bool Crafter::MatchGlob(std::string_view glob, std::string_view name) {
std::size_t gi = 0;
std::size_t ni = 0;
std::size_t star = std::string_view::npos;
std::size_t mark = 0;
while (ni < name.size()) {
if (gi < glob.size() && (glob[gi] == '?' || glob[gi] == name[ni])) {
++gi; ++ni;
} else if (gi < glob.size() && glob[gi] == '*') {
star = gi++;
mark = ni;
} else if (star != std::string_view::npos) {
gi = star + 1;
ni = ++mark;
} else {
return false;
}
}
while (gi < glob.size() && glob[gi] == '*') ++gi;
return gi == glob.size();
}
bool Crafter::MatchAny(std::span<const std::string> globs, std::string_view name) {
if (globs.empty()) return true;
for (const auto& g : globs) {
if (MatchGlob(g, name)) return true;
}
return false;
}
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
std::string Crafter::RunCommand(const std::string_view cmd) {
Progress::EchoCommand(cmd);
@ -127,14 +153,14 @@ std::string Crafter::RunCommand(const std::string_view cmd) {
std::string result;
// Use cmd.exe to interpret redirection
std::string with = "cmd /C \"" + std::string(cmd) + " 2>&1\"";
std::string with = std::format("cmd /C \"{} 2>&1\"", std::string(cmd));
FILE* pipe = _popen(with.c_str(), "r");
if (!pipe) {
throw std::runtime_error("_popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe) != nullptr) {
while (fgets(buffer.data(), static_cast<std::int32_t>(buffer.size()), pipe) != nullptr) {
result += buffer.data();
}
@ -146,14 +172,14 @@ CommandResult Crafter::RunCommandChecked(std::string_view cmd) {
std::array<char, 128> buffer;
CommandResult result{};
std::string with = "cmd /C \"" + std::string(cmd) + " 2>&1\"";
std::string with = std::format("cmd /C \"{} 2>&1\"", std::string(cmd));
FILE* pipe = _popen(with.c_str(), "r");
if (!pipe) {
throw std::runtime_error("_popen() failed!");
}
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe) != nullptr) {
while (fgets(buffer.data(), static_cast<std::int32_t>(buffer.size()), pipe) != nullptr) {
result.output += buffer.data();
}
@ -165,7 +191,8 @@ CommandResult Crafter::RunCommandWithTimeout(std::string_view cmd, std::chrono::
CommandResult result{};
SECURITY_ATTRIBUTES sa{ sizeof(sa), nullptr, TRUE };
HANDLE readEnd = nullptr, writeEnd = nullptr;
HANDLE readEnd = nullptr;
HANDLE writeEnd = nullptr;
if (!CreatePipe(&readEnd, &writeEnd, &sa, 0)) {
throw std::runtime_error("CreatePipe failed");
}
@ -196,14 +223,11 @@ CommandResult Crafter::RunCommandWithTimeout(std::string_view cmd, std::chrono::
// &&, ||) as popen("/bin/sh -c …") does on Linux. cmd's special-case
// /C parsing strips the outer quote pair when the inner text contains
// additional quotes, which is the common case here.
std::string wrapped = "cmd /C \"" + std::string(cmd) + "\"";
std::string wrapped = std::format("cmd /C \"{}\"", std::string(cmd));
std::vector<char> cmdBuf(wrapped.begin(), wrapped.end());
cmdBuf.push_back('\0');
BOOL ok = CreateProcessA(
nullptr, cmdBuf.data(), nullptr, nullptr, TRUE,
CREATE_NO_WINDOW | CREATE_SUSPENDED,
nullptr, nullptr, &si, &pi);
BOOL ok = CreateProcessA(nullptr, cmdBuf.data(), nullptr, nullptr, TRUE, CREATE_NO_WINDOW | CREATE_SUSPENDED, nullptr, nullptr, &si, &pi);
if (!ok) {
CloseHandle(readEnd);
CloseHandle(writeEnd);
@ -226,9 +250,7 @@ CommandResult Crafter::RunCommandWithTimeout(std::string_view cmd, std::chrono::
}
});
DWORD waitMs = static_cast<DWORD>(std::min<long long>(
static_cast<long long>(timeout.count()) * 1000LL,
static_cast<long long>(INFINITE) - 1));
DWORD waitMs = static_cast<DWORD>(std::min<std::int64_t>(static_cast<std::int64_t>(timeout.count()) * 1000LL, static_cast<std::int64_t>(INFINITE) - 1));
DWORD waitResult = WaitForSingleObject(pi.hProcess, waitMs);
if (waitResult == WAIT_TIMEOUT) {
@ -245,9 +267,9 @@ CommandResult Crafter::RunCommandWithTimeout(std::string_view cmd, std::chrono::
// so the runner shows 💥 instead of a numeric exit code.
if ((exit & 0xC0000000) == 0xC0000000) {
result.crashed = true;
result.signal = static_cast<int>(exit);
result.signal = static_cast<std::int32_t>(exit);
}
result.exitCode = static_cast<int>(exit);
result.exitCode = static_cast<std::int32_t>(exit);
}
reader.join();
@ -269,7 +291,7 @@ fs::path Crafter::GetCacheDir() {
}
namespace {
constexpr std::array<std::string_view, 10> kCrafterBuildModules = {
constexpr std::array<std::string_view, 11> CrafterBuildModules = {
"Crafter.Build-Shader",
"Crafter.Build-Platform",
"Crafter.Build-Interface",
@ -277,6 +299,7 @@ namespace {
"Crafter.Build-External",
"Crafter.Build-Clang",
"Crafter.Build-Test",
"Crafter.Build-Lint",
"Crafter.Build-Progress",
"Crafter.Build-Asset",
"Crafter.Build",
@ -305,9 +328,9 @@ std::string Crafter::GetBaseCommand(const Configuration& config) {
namespace {
void EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir) {
CacheLock lock(cacheDir);
for (std::string_view name : kCrafterBuildModules) {
fs::path cppmPath = sourceDir / (std::string(name) + ".cppm");
fs::path pcmPath = cacheDir / (std::string(name) + ".pcm");
for (std::string_view name : CrafterBuildModules) {
fs::path cppmPath = sourceDir / std::format("{}.cppm", name);
fs::path pcmPath = cacheDir / std::format("{}.pcm", name);
if (!fs::exists(cppmPath)) {
throw std::runtime_error(std::format("module source {} not found in {} (set CRAFTER_BUILD_HOME)", name, sourceDir.string()));
}
@ -336,7 +359,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
if (!fs::exists(buildDir)) {
fs::create_directories(buildDir);
}
fs::path dllPath = buildDir / (absProject.stem().string() + ".dll");
fs::path dllPath = buildDir / std::format("{}.dll", absProject.stem().string());
char hostExeBuf[MAX_PATH];
DWORD hostExeLen = GetModuleFileNameA(nullptr, hostExeBuf, MAX_PATH);
@ -361,9 +384,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
EnsureCrafterBuildPcms(sourceDir, cacheDir);
bool stale = !fs::exists(dllPath)
|| fs::last_write_time(dllPath) < fs::last_write_time(absProject)
|| fs::last_write_time(dllPath) < fs::last_write_time(hostExe);
bool stale = !fs::exists(dllPath) || fs::last_write_time(dllPath) < fs::last_write_time(absProject) || fs::last_write_time(dllPath) < fs::last_write_time(hostExe);
if (stale) {
fs::path crafterBuildLib = hostExe.parent_path() / "crafter-build.lib";
@ -427,9 +448,7 @@ namespace {
std::string MingwGccVersion() {
fs::path includeRoot = MingwPrefix() / "include" / "c++";
if (!fs::exists(includeRoot)) {
throw std::runtime_error(std::format(
"mingw-w64 not found at {} (install msys2 mingw-w64-x86_64-toolchain or set CRAFTER_MINGW_DIR)",
MingwPrefix().string()));
throw std::runtime_error(std::format("mingw-w64 not found at {} (install msys2 mingw-w64-x86_64-toolchain or set CRAFTER_MINGW_DIR)", MingwPrefix().string()));
}
std::vector<std::string> versions;
for (const auto& entry : fs::directory_iterator(includeRoot)) {
@ -505,13 +524,11 @@ namespace {
void EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir) {
CacheLock lock(cacheDir);
fs::path prefix = MingwPrefix();
for (std::string_view name : kCrafterBuildModules) {
fs::path cppmPath = sourceDir / (std::string(name) + ".cppm");
fs::path pcmPath = cacheDir / (std::string(name) + ".pcm");
for (std::string_view name : CrafterBuildModules) {
fs::path cppmPath = sourceDir / std::format("{}.cppm", name);
fs::path pcmPath = cacheDir / std::format("{}.pcm", name);
if (!fs::exists(cppmPath)) {
throw std::runtime_error(std::format(
"module source {} not found in {} (set CRAFTER_BUILD_HOME)",
name, sourceDir.string()));
throw std::runtime_error(std::format("module source {} not found in {} (set CRAFTER_BUILD_HOME)", name, sourceDir.string()));
}
if (fs::exists(pcmPath) && fs::last_write_time(cppmPath) < fs::last_write_time(pcmPath)) {
continue;
@ -526,8 +543,7 @@ namespace {
prefix.string(), cacheDir.string(), cppmPath.string(), pcmPath.string());
CommandResult r = Crafter::RunCommandChecked(cmd);
if (r.exitCode != 0) {
throw std::runtime_error(std::format(
"Failed to precompile {} (exit {}): {}", name, r.exitCode, r.output));
throw std::runtime_error(std::format("Failed to precompile {} (exit {}): {}", name, r.exitCode, r.output));
}
}
}
@ -537,7 +553,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
fs::path absProject = fs::canonical(projectFile);
fs::path buildDir = absProject.parent_path() / "build";
if (!fs::exists(buildDir)) fs::create_directories(buildDir);
fs::path dllPath = buildDir / (absProject.stem().string() + ".dll");
fs::path dllPath = buildDir / std::format("{}.dll", absProject.stem().string());
char hostExeBuf[MAX_PATH];
DWORD hostExeLen = GetModuleFileNameA(nullptr, hostExeBuf, MAX_PATH);
@ -562,9 +578,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
EnsureCrafterBuildPcms(sourceDir, cacheDir);
bool stale = !fs::exists(dllPath)
|| fs::last_write_time(dllPath) < fs::last_write_time(absProject)
|| fs::last_write_time(dllPath) < fs::last_write_time(hostExe);
bool stale = !fs::exists(dllPath) || fs::last_write_time(dllPath) < fs::last_write_time(absProject) || fs::last_write_time(dllPath) < fs::last_write_time(hostExe);
if (stale) {
// The import lib lives next to the launcher exe (Build()'s mingw
@ -589,8 +603,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
std::string result = RunCommand(compileCmd);
if (!result.empty()) {
throw std::runtime_error(std::format(
"Failed to compile project {}: {}", absProject.string(), result));
throw std::runtime_error(std::format("Failed to compile project {}: {}", absProject.string(), result));
}
}
@ -604,15 +617,13 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
HMODULE handle = LoadLibraryA(dllPath.string().c_str());
if (!handle) {
throw std::runtime_error(std::format(
"Failed to load project {}: error {}", dllPath.string(), GetLastError()));
throw std::runtime_error(std::format("Failed to load project {}: error {}", dllPath.string(), GetLastError()));
}
using ProjectFn = Configuration (*)(std::span<const std::string_view>);
auto fn = reinterpret_cast<ProjectFn>(GetProcAddress(handle, "CrafterBuildProject"));
if (!fn) {
throw std::runtime_error(std::format(
"CrafterBuildProject not found in {}: error {}", dllPath.string(), GetLastError()));
throw std::runtime_error(std::format("CrafterBuildProject not found in {}: error {}", dllPath.string(), GetLastError()));
}
return fn(args);
@ -626,7 +637,7 @@ std::string Crafter::RunCommand(const std::string_view cmd) {
std::array<char, 128> buffer;
std::string result;
std::string with = std::string(cmd) + " 2>&1";
std::string with = std::format("{} 2>&1", cmd);
// Open pipe to file
FILE* pipe = popen(with.c_str(), "r");
if (!pipe) throw std::runtime_error("popen() failed!");
@ -645,7 +656,7 @@ CommandResult Crafter::RunCommandChecked(std::string_view cmd) {
std::array<char, 128> buffer;
CommandResult result{};
std::string with = std::string(cmd) + " 2>&1";
std::string with = std::format("{} 2>&1", cmd);
FILE* pipe = popen(with.c_str(), "r");
if (!pipe) throw std::runtime_error("popen() failed!");
@ -653,7 +664,7 @@ CommandResult Crafter::RunCommandChecked(std::string_view cmd) {
result.output += buffer.data();
}
int status = pclose(pipe);
std::int32_t status = pclose(pipe);
if (WIFEXITED(status)) {
result.exitCode = WEXITSTATUS(status);
} else if (WIFSIGNALED(status)) {
@ -670,9 +681,7 @@ CommandResult Crafter::RunCommandWithTimeout(std::string_view cmd, std::chrono::
std::array<char, 128> buffer;
CommandResult result{};
std::string wrapped = std::format(
"timeout --kill-after=2 {} {} 2>&1",
timeout.count(), cmd);
std::string wrapped = std::format("timeout --kill-after=2 {} {} 2>&1", timeout.count(), cmd);
FILE* pipe = popen(wrapped.c_str(), "r");
if (!pipe) throw std::runtime_error("popen() failed!");
@ -681,9 +690,9 @@ CommandResult Crafter::RunCommandWithTimeout(std::string_view cmd, std::chrono::
result.output += buffer.data();
}
int status = pclose(pipe);
std::int32_t status = pclose(pipe);
if (WIFEXITED(status)) {
int code = WEXITSTATUS(status);
std::int32_t code = WEXITSTATUS(status);
if (code == 124) {
result.timedOut = true;
result.exitCode = 124;
@ -774,7 +783,7 @@ std::string Crafter::BuildStdPcm(const Configuration& config, fs::path stdPcm) {
// (e.g. -mrelaxed-simd) so the std BMI is feature-compatible. wasm only;
// wasmVariantFlags is empty on every other path.
if (isWasm) {
for (const std::string& f : config.wasmVariantFlags) archFlags += " " + f;
for (const std::string& f : config.wasmVariantFlags) archFlags += std::format(" {}", f);
}
// non-wasm cross builds: the driver ranks the host toolchain's own
// libc++ headers (<install>/../include/c++/v1) above --sysroot, so
@ -814,7 +823,7 @@ std::string Crafter::GetBaseCommand(const Configuration& config) {
}
namespace {
constexpr std::array<std::string_view, 10> kCrafterBuildModules = {
constexpr std::array<std::string_view, 11> CrafterBuildModules = {
"Crafter.Build-Shader",
"Crafter.Build-Platform",
"Crafter.Build-Interface",
@ -822,6 +831,7 @@ namespace {
"Crafter.Build-External",
"Crafter.Build-Clang",
"Crafter.Build-Test",
"Crafter.Build-Lint",
"Crafter.Build-Progress",
"Crafter.Build-Asset",
"Crafter.Build",
@ -829,9 +839,9 @@ namespace {
void EnsureCrafterBuildPcms(const fs::path& sourceDir, const fs::path& cacheDir) {
CacheLock lock(cacheDir);
for (std::string_view name : kCrafterBuildModules) {
fs::path cppmPath = sourceDir / (std::string(name) + ".cppm");
fs::path pcmPath = cacheDir / (std::string(name) + ".pcm");
for (std::string_view name : CrafterBuildModules) {
fs::path cppmPath = sourceDir / std::format("{}.cppm", name);
fs::path pcmPath = cacheDir / std::format("{}.pcm", name);
if (!fs::exists(cppmPath)) {
throw std::runtime_error(std::format("module source {} not found in {} (set CRAFTER_BUILD_HOME)", name, sourceDir.string()));
}
@ -859,7 +869,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
if (!fs::exists(buildDir)) {
fs::create_directories(buildDir);
}
fs::path soPath = buildDir / (absProject.stem().string() + ".so");
fs::path soPath = buildDir / std::format("{}.so", absProject.stem().string());
fs::path hostExe = fs::read_symlink("/proc/self/exe");
@ -879,9 +889,7 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
EnsureCrafterBuildPcms(sourceDir, cacheDir);
bool stale = !fs::exists(soPath)
|| fs::last_write_time(soPath) < fs::last_write_time(absProject)
|| fs::last_write_time(soPath) < fs::last_write_time(hostExe);
bool stale = !fs::exists(soPath) || fs::last_write_time(soPath) < fs::last_write_time(absProject) || fs::last_write_time(soPath) < fs::last_write_time(hostExe);
if (stale) {
std::string compileCmd = std::format(
@ -914,4 +922,4 @@ Configuration Crafter::LoadProject(const fs::path& projectFile, std::span<const
return fn(args);
}
#endif
#endif