refactor: drop Windows host support
crafter-build is moving to a single Flatpak distribution, and there is no Windows host in that picture: no build.cmd, no MSVC or mingw host build, no DLL plus launcher. Delete every host-Windows path instead of leaving it to rot unverified: the cmd.exe command runner and quoting, the LOCALAPPDATA cache, the MSVC and mingw-host LoadProject variants with their LIBCXX_DIR and CRAFTER_MINGW_DIR plumbing, the winsock port probe, the LoadLibrary libclang loader, the console-width and isatty shims, and the preprocessor guards around the Linux bodies. The x86_64-w64-mingw32 cross-compile target stays. It is target logic (output names, -femulated-tls, the libstdc++ link extras, copying a dependency DLL next to a mingw exe, wine as the runner) and did not depend on any of the removed code. The msvc target goes with the host that was the only thing able to use it. Three target-path warts fixed on the way: ExpectedOutput named a mingw static library <name>.lib while the archive step wrote lib<name>.a, so those libraries relinked on every run; the import lib is no longer copied next to a mingw exe, since the dependency's own bin dir is already on the link line; and llvm-ar now writes every archive, not only the LTO ones, so Debug and Release archives are made the same way. CRAFTER_API and Crafter.Build-Api.h existed only for the DLL boundary. Nothing downstream used them, so they are removed rather than kept as an empty macro nobody can verify. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
f60ba809b5
commit
46082a3117
24 changed files with 138 additions and 1053 deletions
|
|
@ -2,17 +2,9 @@
|
|||
// 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)
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
// <rpc.h> (pulled in transitively) defines `interface` as a macro for `struct`,
|
||||
// which collides with local variables named `interface` in this TU.
|
||||
#undef interface
|
||||
#else
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
export module Crafter.Build:Clang_impl;
|
||||
import std;
|
||||
import :Clang;
|
||||
|
|
@ -267,17 +259,19 @@ fs::path Crafter::StdPcmDir(const Configuration& config) {
|
|||
|
||||
fs::path Crafter::ExpectedOutput(const Configuration& config) {
|
||||
const fs::path dir = config.BinDir();
|
||||
const bool windowsTarget = config.target == "x86_64-w64-mingw32" || config.target == "x86_64-pc-windows-msvc";
|
||||
const bool mingwTarget = config.target == "x86_64-w64-mingw32";
|
||||
if (config.type == ConfigurationType::Executable) {
|
||||
if (config.target.starts_with("wasm32")) return dir / std::format("{}.wasm", config.outputName);
|
||||
return config.target == "x86_64-w64-mingw32" ? dir / std::format("{}.exe", config.outputName) : dir / config.outputName;
|
||||
return mingwTarget ? dir / std::format("{}.exe", config.outputName) : dir / config.outputName;
|
||||
}
|
||||
if (config.type == ConfigurationType::LibraryStatic) {
|
||||
return windowsTarget ? dir / std::format("{}.lib", config.outputName) : dir / std::format("lib{}.a", config.outputName);
|
||||
// The archive step writes lib<name>.a for every target, mingw included:
|
||||
// llvm-ar does not care about the object format.
|
||||
return dir / std::format("lib{}.a", config.outputName);
|
||||
}
|
||||
// LibraryDynamic — the .dll on Windows targets so an mtime check sees the
|
||||
// LibraryDynamic — the .dll on the mingw target so an mtime check sees the
|
||||
// freshly linked library; on Unix the .so is that file.
|
||||
return windowsTarget ? dir / std::format("{}.dll", config.outputName) : dir / std::format("lib{}.so", config.outputName);
|
||||
return mingwTarget ? dir / std::format("{}.dll", config.outputName) : dir / std::format("lib{}.so", config.outputName);
|
||||
}
|
||||
|
||||
CompileCommand Crafter::GetCompileCommand(const Configuration& config) {
|
||||
|
|
@ -322,22 +316,9 @@ CompileCommand Crafter::GetCompileCommand(const Configuration& config) {
|
|||
}
|
||||
|
||||
if(config.type == ConfigurationType::LibraryDynamic) {
|
||||
#ifdef __linux__
|
||||
out.command += " -fPIC -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
#endif
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
out.command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_SHARED_LIBRARY";
|
||||
#endif
|
||||
} else if(config.type == ConfigurationType::Executable) {
|
||||
out.command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_EXECUTABLE";
|
||||
// On Windows targets the API uses __declspec(dllimport) when consuming
|
||||
// a DLL. Set the macro for executables so CRAFTER_API resolves to
|
||||
// dllimport in their PCM cache (separate from the lib's PCM cache,
|
||||
// which gets dllexport). Harmless if the exe doesn't actually link a
|
||||
// crafter DLL — CRAFTER_API only matters at API call sites.
|
||||
if (config.target == "x86_64-w64-mingw32" || config.target == "x86_64-pc-windows-msvc") {
|
||||
out.command += " -D CRAFTER_BUILD_DLL_IMPORT";
|
||||
}
|
||||
} else {
|
||||
out.command += " -D CRAFTER_BUILD_CONFIGURATION_TYPE_LIBRARY";
|
||||
}
|
||||
|
|
@ -464,15 +445,12 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
config.ResolvePendingImports();
|
||||
|
||||
// Auto-detect the WASI sysroot before any compile step runs so BuildStdPcm
|
||||
// and the main compile command see the same value. Linux-only — Windows
|
||||
// users supply cfg.sysroot pointing at their wasi-sdk install. Covers all
|
||||
// wasm32-* triples (wasi, wasip1, wasip2, ...); the sysroot's per-triple
|
||||
// subdirs handle the differences.
|
||||
#ifdef __linux__
|
||||
// and the main compile command see the same value. Covers all wasm32-*
|
||||
// triples (wasi, wasip1, wasip2, ...); the sysroot's per-triple subdirs
|
||||
// handle the differences.
|
||||
if (config.sysroot.empty() && config.target.starts_with("wasm32")) {
|
||||
config.sysroot = "/usr/share/wasi-sysroot";
|
||||
}
|
||||
#endif
|
||||
|
||||
fs::path buildDir = config.BuildDir();
|
||||
fs::path outputDir = config.BinDir();
|
||||
|
|
@ -705,7 +683,6 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
const std::string& ltoCompileFlags = compile.ltoCompileFlags;
|
||||
const std::string& ltoLinkFlags = compile.ltoLinkFlags;
|
||||
const bool isWasm = config.target.starts_with("wasm32");
|
||||
const bool useLto = compile.useLto;
|
||||
|
||||
std::string files;
|
||||
std::unordered_set<std::string> libSet;
|
||||
|
|
@ -1125,7 +1102,6 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
if(buildResult.repack) {
|
||||
Progress::Task task(std::format("Linking {}", config.outputName));
|
||||
if(config.type == ConfigurationType::Executable) {
|
||||
#ifdef __linux__
|
||||
if(config.target == "x86_64-w64-mingw32") {
|
||||
try {
|
||||
// Copy any LibraryDynamic dependency DLLs alongside
|
||||
|
|
@ -1136,15 +1112,12 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
std::function<void(Configuration*)> copyDepDlls = [&](Configuration* dep) {
|
||||
if (!dllSeen.insert(dep).second) return;
|
||||
if (dep->type == ConfigurationType::LibraryDynamic && dep->target == "x86_64-w64-mingw32") {
|
||||
fs::path depDir = dep->BinDir();
|
||||
// The DLL itself (Windows resolves it from the
|
||||
// exe's directory at load time) and the mingw
|
||||
// import lib (so a downstream `crafter-build.exe`
|
||||
// can link a fresh project.dll against it without
|
||||
// hunting through sibling output dirs).
|
||||
for (auto fname : {std::format("{}.dll", dep->outputName), std::format("lib{}.dll.a", dep->outputName)}) {
|
||||
fs::path src = depDir / fname;
|
||||
if (!fs::exists(src)) continue;
|
||||
// Only the DLL: Windows resolves it from the exe's
|
||||
// directory at load time. The import lib stays in
|
||||
// the dependency's own bin dir, which is already
|
||||
// on the link line through buildResult.libs.
|
||||
fs::path src = dep->BinDir() / std::format("{}.dll", dep->outputName);
|
||||
if (fs::exists(src)) {
|
||||
fs::path dest = outputDir / src.filename();
|
||||
if (!fs::exists(dest) || fs::last_write_time(src) > fs::last_write_time(dest)) {
|
||||
fs::copy(src, dest, fs::copy_options::overwrite_existing);
|
||||
|
|
@ -1163,65 +1136,22 @@ BuildResult Crafter::Build(Configuration& config, std::unordered_map<fs::path, s
|
|||
} else {
|
||||
buildResult.result = RunBuildCommand(std::format("{}{} -o {} -fuse-ld=lld{}", command, files, (outputDir/config.outputName).string(), linkExtras));
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
if (config.target == "x86_64-w64-mingw32") {
|
||||
// Windows host, mingw target: same shape as the Linux→mingw
|
||||
// path (no LIBCXX_DIR / -lc++ / -nostdlib++ — those are MSVC
|
||||
// libc++ flags). Copy LibraryDynamic dep DLLs + import libs
|
||||
// alongside the launcher exe so Windows resolves them from
|
||||
// the exe's own directory at load time. Runtime DLLs (libstdc++,
|
||||
// libgcc, libwinpthread) come from msys2 on PATH.
|
||||
std::unordered_set<Configuration*> dllSeen;
|
||||
std::function<void(Configuration*)> copyDepDlls = [&](Configuration* dep) {
|
||||
if (!dllSeen.insert(dep).second) return;
|
||||
if (dep->type == ConfigurationType::LibraryDynamic && dep->target == "x86_64-w64-mingw32") {
|
||||
fs::path depDir = dep->BinDir();
|
||||
for (auto fname : {std::format("{}.dll", dep->outputName), std::format("lib{}.dll.a", dep->outputName)}) {
|
||||
fs::path src = depDir / fname;
|
||||
if (!fs::exists(src)) continue;
|
||||
fs::path dest = outputDir / src.filename();
|
||||
if (!fs::exists(dest) || fs::last_write_time(src) > fs::last_write_time(dest)) {
|
||||
fs::copy(src, dest, fs::copy_options::overwrite_existing);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (Configuration* sub : dep->dependencies) copyDepDlls(sub);
|
||||
};
|
||||
for (Configuration* dep : config.dependencies) copyDepDlls(dep);
|
||||
|
||||
buildResult.result = RunBuildCommand(std::format("{}{} -o {} -fuse-ld=lld{}", command, files, (outputDir/config.outputName).string(), linkExtras));
|
||||
} else {
|
||||
std::system(std::format("copy \"%LIBCXX_DIR%\\lib\\c++.dll\" \"{}\\c++.dll\"", outputDir.string()).c_str());
|
||||
buildResult.result = RunBuildCommand(std::format("{}{} -o {}.exe -fuse-ld=lld -L %LIBCXX_DIR%\\lib -lc++ -nostdinc++ -nostdlib++{}", command, files, (outputDir/config.outputName).string(), linkExtras));
|
||||
}
|
||||
#endif
|
||||
} else if(config.type == ConfigurationType::LibraryStatic) {
|
||||
#ifdef __linux__
|
||||
// ThinLTO emits LLVM bitcode objects; plain `ar` writes an archive
|
||||
// index that omits their symbols, so a consumer's lld link can't
|
||||
// pull the needed members. llvm-ar writes a bitcode-aware index.
|
||||
buildResult.result = RunBuildCommand(std::format("{} rcs {}.a {}", useLto ? "llvm-ar" : "ar", (outputDir/fs::path(std::string("lib")+config.outputName)).string(), files));
|
||||
#endif
|
||||
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
buildResult.result = RunBuildCommand(std::format("llvm-lib.exe {} /OUT:{}.lib", files, (outputDir/fs::path(config.outputName)).string()));
|
||||
#endif
|
||||
// pull the needed members. llvm-ar writes a bitcode-aware index
|
||||
// and handles plain ELF and COFF objects just as well, so every
|
||||
// build uses it.
|
||||
buildResult.result = RunBuildCommand(std::format("llvm-ar rcs {}.a {}", (outputDir/fs::path(std::string("lib")+config.outputName)).string(), files));
|
||||
} else {
|
||||
// LibraryDynamic. Output names follow each target's convention so
|
||||
// consumers can link via the standard linker search:
|
||||
// mingw: <name>.dll + lib<name>.dll.a (lld --out-implib)
|
||||
// msvc: <name>.dll + <name>.lib (lld /IMPLIB)
|
||||
// unix: lib<name>.so (rpath $ORIGIN)
|
||||
if (config.target == "x86_64-w64-mingw32") {
|
||||
fs::path dll = outputDir / std::format("{}.dll", config.outputName);
|
||||
fs::path implib = outputDir / std::format("lib{}.dll.a", config.outputName);
|
||||
buildResult.result = RunBuildCommand(std::format("{}{} -shared -o {} -Wl,--out-implib,{} -fuse-ld=lld{}", command, files, dll.string(), implib.string(), linkExtras));
|
||||
} else if (config.target == "x86_64-pc-windows-msvc") {
|
||||
fs::path dll = outputDir / std::format("{}.dll", config.outputName);
|
||||
fs::path implib = outputDir / std::format("{}.lib", config.outputName);
|
||||
buildResult.result = RunBuildCommand(std::format("{}{} -shared -o {} -Wl,/IMPLIB:{} -fuse-ld=lld{}", command, files, dll.string(), implib.string(), linkExtras));
|
||||
} else {
|
||||
buildResult.result = RunBuildCommand(std::format("{}{} -shared -o {}.so -Wl,-rpath,'$ORIGIN' -fuse-ld=lld{}", command, files, (outputDir/(std::string("lib")+config.outputName)).string(), linkExtras));
|
||||
}
|
||||
|
|
@ -1544,11 +1474,9 @@ ArgQuery Crafter::ApplyStandardArgs(Configuration& cfg, std::span<const std::str
|
|||
// includes it. (Build() also runs this once more for callers that bypassed
|
||||
// ApplyStandardArgs, but doing it here makes dep PcmDirs consistent
|
||||
// between the consumer's command-construction and the dep's own build.)
|
||||
#ifdef __linux__
|
||||
if (cfg.sysroot.empty() && cfg.target.starts_with("wasm32")) {
|
||||
cfg.sysroot = "/usr/share/wasi-sysroot";
|
||||
}
|
||||
#endif
|
||||
return ArgQuery{args};
|
||||
}
|
||||
|
||||
|
|
@ -1644,7 +1572,6 @@ Environment:
|
|||
Replace '-' and '.' with '_' in the
|
||||
triple. CLI --runner= overrides this.
|
||||
CRAFTER_MINGW_DIR Override mingw-w64 sysroot auto-detect.
|
||||
LIBCXX_DIR Windows libc++ install (MSVC ABI builds).
|
||||
|
||||
Exit status:
|
||||
0 success / all non-skipped tests passed / lint clean / format applied
|
||||
|
|
@ -1789,11 +1716,7 @@ std::int32_t Crafter::Run(std::int32_t argc, char** argv) {
|
|||
if (config.target.starts_with("wasm32")) {
|
||||
const bool browserBuild = fs::exists(absDir / "index.html");
|
||||
auto have = [](std::string_view exe) {
|
||||
#ifdef _WIN32
|
||||
std::string probe = std::format("where {} > NUL 2>&1", exe);
|
||||
#else
|
||||
std::string probe = std::format("command -v {} > /dev/null 2>&1", exe);
|
||||
#endif
|
||||
return std::system(probe.c_str()) == 0;
|
||||
};
|
||||
if (browserBuild) {
|
||||
|
|
@ -1801,17 +1724,9 @@ std::int32_t Crafter::Run(std::int32_t argc, char** argv) {
|
|||
// user has another dev server running we shift up rather
|
||||
// than letting caddy/python exit with EADDRINUSE.
|
||||
auto findFreePort = [](std::int32_t basePort, std::int32_t span) -> std::int32_t {
|
||||
#if defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_windows_msvc) || defined(CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32)
|
||||
static const bool WsaInit = []{ WSADATA d; return WSAStartup(MAKEWORD(2, 2), &d) == 0; }();
|
||||
if (!WsaInit) return basePort;
|
||||
using Sock = SOCKET;
|
||||
const Sock invalid = INVALID_SOCKET;
|
||||
auto closesock = [](Sock s){ closesocket(s); };
|
||||
#else
|
||||
using Sock = std::int32_t;
|
||||
const Sock invalid = -1;
|
||||
auto closesock = [](Sock s){ ::close(s); };
|
||||
#endif
|
||||
for (std::int32_t p = basePort; p < basePort + span; ++p) {
|
||||
Sock s = ::socket(AF_INET, SOCK_STREAM, 0);
|
||||
if (s == invalid) return basePort;
|
||||
|
|
@ -1934,11 +1849,7 @@ std::int32_t Crafter::Run(std::int32_t argc, char** argv) {
|
|||
// Silence the backend's own banner/request logs so the
|
||||
// experience is identical regardless of which server is
|
||||
// picked — the line above is the only output.
|
||||
#ifdef _WIN32
|
||||
cmd += " > NUL 2>&1";
|
||||
#else
|
||||
cmd += " > /dev/null 2>&1";
|
||||
#endif
|
||||
return std::system(cmd.c_str()) == 0 ? 0 : 1;
|
||||
}
|
||||
// wasi-cli wasm — needs a standalone runtime.
|
||||
|
|
@ -1952,10 +1863,6 @@ std::int32_t Crafter::Run(std::int32_t argc, char** argv) {
|
|||
return 1;
|
||||
}
|
||||
|
||||
// Resolve to absolute — cmd.exe on Windows mishandles a leading
|
||||
// "./" by trying to interpret it as a command. system() invokes
|
||||
// through cmd /c, so the relative-prefixed path makes cmd error
|
||||
// with "'.' is not recognized as an internal or external command".
|
||||
// Run from the artifact's own directory so relative file opens
|
||||
// (shaders, assets copied alongside the exe via cfg.files) resolve
|
||||
// against the bin dir rather than the user's cwd. We exit the
|
||||
|
|
|
|||
Loading…
Reference in a new issue