From 50ae80a206c3c7af857bcfc3288929d1fb601d36 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Thu, 30 Apr 2026 04:15:29 +0200 Subject: [PATCH] ArgQuery: out-of-line CRAFTER_API methods for Windows DLL crossing In-class inline methods on a module-exported class get the @ linkage attachment, and clang does not emit their bodies into consumers; the resulting external reference fails to resolve when a project.dll on Windows tries to call ArgQuery::Has after consuming ApplyStandardArgs's return value. Move the bodies to the implementation unit and dllexport them. Co-Authored-By: Claude Sonnet 4.6 --- implementations/Crafter.Build-Clang.cpp | 12 ++++++++++++ interfaces/Crafter.Build-Clang.cppm | 21 +++++++-------------- 2 files changed, 19 insertions(+), 14 deletions(-) diff --git a/implementations/Crafter.Build-Clang.cpp b/implementations/Crafter.Build-Clang.cpp index c17f6db..fe106bc 100644 --- a/implementations/Crafter.Build-Clang.cpp +++ b/implementations/Crafter.Build-Clang.cpp @@ -865,6 +865,18 @@ std::string Crafter::HostTarget() { return cached; } +bool Crafter::ArgQuery::Has(std::string_view flag) const { + for (std::string_view a : args) if (a == flag) return true; + return false; +} + +std::optional Crafter::ArgQuery::Get(std::string_view prefix) const { + for (std::string_view a : args) { + if (a.starts_with(prefix)) return std::string(a.substr(prefix.size())); + } + return std::nullopt; +} + ArgQuery Crafter::ApplyStandardArgs(Configuration& cfg, std::span args) { if (const char* envMarch = std::getenv("CRAFTER_BUILD_MARCH"); envMarch && *envMarch) { cfg.march = envMarch; diff --git a/interfaces/Crafter.Build-Clang.cppm b/interfaces/Crafter.Build-Clang.cppm index 7d22d72..f988623 100644 --- a/interfaces/Crafter.Build-Clang.cppm +++ b/interfaces/Crafter.Build-Clang.cppm @@ -168,22 +168,15 @@ export namespace Crafter { // outputName so renaming the binary later requires another call. CRAFTER_API void EnableWasiBrowserRuntime(Configuration& cfg); - // View over the project's args with simple query helpers — kept inline - // so it works across the DLL boundary without an extra ABI hop. Use - // Has(flag) for boolean switches and Get(prefix) for valued options - // (e.g. Get("--prefix=") returns the substring after the equals). + // View over the project's args with simple query helpers. Has(flag) for + // boolean switches; Get(prefix) for valued options (e.g. Get("--prefix=") + // returns the substring after the equals). Definitions are out-of-line + // and CRAFTER_API so they cross the Windows DLL boundary cleanly — clang + // does not emit module-attached in-class inline bodies into consumers. struct ArgQuery { std::span args; - bool Has(std::string_view flag) const { - for (std::string_view a : args) if (a == flag) return true; - return false; - } - std::optional Get(std::string_view prefix) const { - for (std::string_view a : args) { - if (a.starts_with(prefix)) return std::string(a.substr(prefix.size())); - } - return std::nullopt; - } + CRAFTER_API bool Has(std::string_view flag) const; + CRAFTER_API std::optional Get(std::string_view prefix) const; }; // Apply the framework's standard CLI args + env vars onto cfg: