ArgQuery: out-of-line CRAFTER_API methods for Windows DLL crossing
All checks were successful
CI / build-test-release (push) Successful in 14m37s

In-class inline methods on a module-exported class get the @<module>
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 <noreply@anthropic.com>
This commit is contained in:
Jorijn van der Graaf 2026-04-30 04:15:29 +02:00
commit 50ae80a206
2 changed files with 19 additions and 14 deletions

View file

@ -865,6 +865,18 @@ std::string Crafter::HostTarget() {
return cached; 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<std::string> 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<const std::string_view> args) { ArgQuery Crafter::ApplyStandardArgs(Configuration& cfg, std::span<const std::string_view> args) {
if (const char* envMarch = std::getenv("CRAFTER_BUILD_MARCH"); envMarch && *envMarch) { if (const char* envMarch = std::getenv("CRAFTER_BUILD_MARCH"); envMarch && *envMarch) {
cfg.march = envMarch; cfg.march = envMarch;

View file

@ -168,22 +168,15 @@ export namespace Crafter {
// outputName so renaming the binary later requires another call. // outputName so renaming the binary later requires another call.
CRAFTER_API void EnableWasiBrowserRuntime(Configuration& cfg); CRAFTER_API void EnableWasiBrowserRuntime(Configuration& cfg);
// View over the project's args with simple query helpers — kept inline // View over the project's args with simple query helpers. Has(flag) for
// so it works across the DLL boundary without an extra ABI hop. Use // boolean switches; Get(prefix) for valued options (e.g. Get("--prefix=")
// Has(flag) for boolean switches and Get(prefix) for valued options // returns the substring after the equals). Definitions are out-of-line
// (e.g. Get("--prefix=") returns the substring after the equals). // 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 { struct ArgQuery {
std::span<const std::string_view> args; std::span<const std::string_view> args;
bool Has(std::string_view flag) const { CRAFTER_API bool Has(std::string_view flag) const;
for (std::string_view a : args) if (a == flag) return true; CRAFTER_API std::optional<std::string> Get(std::string_view prefix) const;
return false;
}
std::optional<std::string> 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;
}
}; };
// Apply the framework's standard CLI args + env vars onto cfg: // Apply the framework's standard CLI args + env vars onto cfg: