mirror of
https://github.com/logos-messaging/nim-ffi.git
synced 2026-08-05 22:43:17 +00:00
fix: ci failing
This commit is contained in:
parent
265f38208f
commit
800d7f19ca
23
README.md
23
README.md
@ -171,17 +171,18 @@ header shape from the library's ABI format. It carries two honest limits today:
|
||||
An `abi = c` proc whose whole signature is scalar — fixed-width integer, float,
|
||||
or bool params (a `string` return is fine, a `string` param is not) and no
|
||||
structs, handles, or pointers — dispatches through a CBOR-free scalar fast path.
|
||||
The `-d:targetLang=c_abi` generator emits real bindings for that shape: the
|
||||
wrapper passes the scalar args inline (no request struct) and adapts the
|
||||
raw-bytes reply into the same typed callback surface the flat-struct methods
|
||||
use. The CBOR-speaking targets (`c`, `cpp`, `rust`, `cddl`) have no scalar
|
||||
codegen, so under `-d:ffiGenBindings` they would omit such a proc from the
|
||||
generated bindings — and `genBindings()` fails with an error naming the
|
||||
affected procs. Resolve it by generating with `-d:targetLang=c_abi`, switching
|
||||
the proc to `abi = cbor`, adding a non-scalar param so it takes the CBOR wire
|
||||
shape, or passing `-d:ffiAllowScalarSkip` to accept the omission (the proc
|
||||
still works over the scalar fast path; it's just absent from the generated
|
||||
foreign bindings).
|
||||
The `abi = c` C header (an `abi = c` library generated with `-d:targetLang=c`)
|
||||
emits real bindings for that shape: the wrapper passes the scalar args inline
|
||||
(no request struct) and adapts the raw-bytes reply into the same typed callback
|
||||
surface the flat-struct methods use. The CBOR-speaking outputs — the CBOR C
|
||||
header and the `cpp`, `rust`, `cddl` targets — have no scalar codegen, so under
|
||||
`-d:ffiGenBindings` they would omit such a proc from the generated bindings —
|
||||
and `genBindings()` fails with an error naming the affected procs. Resolve it by
|
||||
making the whole library `abi = c` and generating C bindings, switching the proc
|
||||
to `abi = cbor`, adding a non-scalar param so it takes the CBOR wire shape, or
|
||||
passing `-d:ffiAllowScalarSkip` to accept the omission (the proc still works
|
||||
over the scalar fast path; it's just absent from the generated foreign
|
||||
bindings).
|
||||
|
||||
## Placement of `genBindings()`
|
||||
|
||||
|
||||
@ -1018,7 +1018,7 @@ func abiScalarRawFnName(libType: string): string =
|
||||
## per-method trampoline converts into the typed reply.
|
||||
return libType & "ScalarRawFn"
|
||||
|
||||
proc abiScalarArgParams(m: FFIProcMeta): seq[string] =
|
||||
func abiScalarArgParams(m: FFIProcMeta): seq[string] =
|
||||
## C parameters for a scalar method's args — passed inline by value, in both
|
||||
## the raw export and the high-level wrapper (no Req struct).
|
||||
var params: seq[string] = @[]
|
||||
|
||||
@ -34,9 +34,9 @@ type
|
||||
scalarFastPath*: bool
|
||||
## True for an `abi = c` proc whose whole signature is scalar (see
|
||||
## `isScalarOnly`): dispatches through the CBOR-free scalar fast path.
|
||||
## Only the `c_abi` generator emits foreign bindings for it (inline
|
||||
## scalar args + raw-bytes reply trampoline); the CBOR-speaking
|
||||
## generators drop it (see `bindableProcs`).
|
||||
## Only the `abi = c` C header emits a foreign binding for it (inline
|
||||
## scalar args + raw-bytes reply trampoline); every other target — and
|
||||
## the CBOR C header — drops it (see `bindableProcs`).
|
||||
|
||||
FFIFieldMeta* = object
|
||||
name*: string
|
||||
@ -138,5 +138,8 @@ const ffiOutputDir* {.strdefine.} = ""
|
||||
# Nim src path override relative to outputDir (-d:ffiSrcPath); empty derives it.
|
||||
const ffiSrcPath* {.strdefine.} = ""
|
||||
|
||||
# When true, scalar-only `abi = c` procs are silently omitted rather than failing the build. Off by default so the drop is loud; see genBindings().
|
||||
# When set, scalar-only `abi = c` procs are silently omitted from bindings
|
||||
# generated by targets without scalar codegen (every target except the
|
||||
# `abi = c` C header) instead of failing the build. Off by default so the drop
|
||||
# is loud; see genBindings().
|
||||
const ffiAllowScalarSkip* {.booldefine.} = false
|
||||
|
||||
@ -1519,9 +1519,10 @@ macro ffiEvent*(args: varargs[untyped]): untyped =
|
||||
return generated
|
||||
|
||||
proc reportScalarFastPathDrops(procs: seq[FFIProcMeta]) {.compileTime.} =
|
||||
## Only the `c_abi` generator emits foreign bindings for scalar-fast-path
|
||||
## procs; every other target drops them. Fail loudly, naming them, unless
|
||||
## `-d:ffiAllowScalarSkip` opts into the silent omission (then just hint).
|
||||
## Only the `abi = c` C header emits foreign bindings for scalar-fast-path
|
||||
## procs; every other target (and the CBOR C header) drops them. Fail loudly,
|
||||
## naming them, unless `-d:ffiAllowScalarSkip` opts into the silent omission
|
||||
## (then just hint).
|
||||
var skipped: seq[string] = @[]
|
||||
for p in procs:
|
||||
if p.scalarFastPath:
|
||||
@ -1537,11 +1538,12 @@ proc reportScalarFastPathDrops(procs: seq[FFIProcMeta]) {.compileTime.} =
|
||||
return
|
||||
error(
|
||||
"genBindings: this target has no foreign-binding codegen for " &
|
||||
"scalar-fast-path `abi = c` procs (only -d:targetLang=c_abi emits them), " &
|
||||
"so these would be silently omitted from the generated bindings: " &
|
||||
skipped.join(", ") & ".\n" & "Fix by one of:\n" &
|
||||
" - generate with -d:targetLang=c_abi, or\n" &
|
||||
" - switch the proc to `abi = cbor`, or\n" &
|
||||
"scalar-fast-path `abi = c` procs, so these would be silently omitted " &
|
||||
"from the generated bindings: " & skipped.join(", ") &
|
||||
".\nThey are emitted only into the `abi = c` C header (an `abi = c` " &
|
||||
"library generated with -d:targetLang=c).\n" & "Fix by one of:\n" &
|
||||
" - make the library `abi = c` (declareLibrary(..., \"c\")) and generate " &
|
||||
"C bindings, or\n" & " - switch the proc to `abi = cbor`, or\n" &
|
||||
" - add a non-scalar param (e.g. a struct or handle) so it takes the " &
|
||||
"CBOR wire shape, or\n" & " - pass -d:ffiAllowScalarSkip to accept the omission."
|
||||
)
|
||||
@ -1602,14 +1604,16 @@ macro genBindings*(
|
||||
let lang = string_helpers.toLower(rawLang.strip())
|
||||
if lang.len == 0:
|
||||
continue
|
||||
# `c_abi` is the one target with scalar-fast-path codegen, so it binds the
|
||||
# full registry; the others drop scalar procs (loudly, unless skipped).
|
||||
# The `abi = c` C header is the one output with scalar-fast-path codegen,
|
||||
# so it binds the full registry. Every other target — and the CBOR C
|
||||
# header — drops scalar procs (loudly, unless skipped).
|
||||
let emitsScalars = lang == "c" and currentDefaultABIFormat == ABIFormat.C
|
||||
let genProcs =
|
||||
if lang == "c_abi":
|
||||
if emitsScalars:
|
||||
ffiProcRegistry
|
||||
else:
|
||||
bindableProcs(ffiProcRegistry)
|
||||
if lang != "c_abi":
|
||||
if not emitsScalars:
|
||||
reportScalarFastPathDrops(ffiProcRegistry)
|
||||
let outDir = bindingsOutputDir(lang, outputDir)
|
||||
emitBindingsFor(
|
||||
|
||||
@ -35,7 +35,7 @@ func bindableProcs*(procs: seq[FFIProcMeta]): seq[FFIProcMeta] =
|
||||
## The procs the CBOR-speaking foreign-binding generators emit for.
|
||||
## Scalar-fast-path procs are dropped: their C export takes inline scalar
|
||||
## args, not the CBOR `(reqCbor, reqCborLen)` shape those backends assume, so
|
||||
## emitting a CBOR caller for them would be wrong. Only the `c_abi` generator
|
||||
## emitting a CBOR caller for them would be wrong. Only the `abi = c` C header
|
||||
## has scalar codegen and binds the full registry (see genBindings()).
|
||||
var kept: seq[FFIProcMeta] = @[]
|
||||
for p in procs:
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
## Compile fixture for the scalar-fast-path genBindings() behavior (see
|
||||
## tests/unit/test_scalar_skip_gen.nim). Under `-d:ffiGenBindings` only the
|
||||
## `c_abi` target has foreign-binding codegen for the scalar `abi = c` proc
|
||||
## below; any other target must fail — unless `-d:ffiAllowScalarSkip` is
|
||||
## passed, which downgrades the drop to a hint.
|
||||
## Compile fixture for the scalar-fast-path drop error (see
|
||||
## tests/unit/test_scalar_skip_gen.nim). This is a CBOR-default library with one
|
||||
## stray scalar `abi = c` proc, so no target can emit a binding for it: under
|
||||
## `-d:ffiGenBindings` genBindings() must fail — unless `-d:ffiAllowScalarSkip`
|
||||
## is passed, which downgrades the drop to a hint.
|
||||
|
||||
import ffi, chronos
|
||||
|
||||
@ -20,8 +20,8 @@ proc scalarskip_create*(cfg: SkipConfig): Future[Result[SkipLib, string]] {.ffiC
|
||||
proc scalarskip_add*(
|
||||
lib: SkipLib, a: int, b: int
|
||||
): Future[Result[int, string]] {.ffi: "abi = c".} =
|
||||
## All-scalar signature: dispatches through the CBOR-free fast path; only the
|
||||
## `c_abi` target generates a foreign binding for it.
|
||||
## All-scalar signature: dispatches through the CBOR-free fast path. In this
|
||||
## CBOR-default library no target can emit a foreign binding for it.
|
||||
return ok(lib.base + a + b)
|
||||
|
||||
genBindings()
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
## Drives the scalar-fast-path genBindings() behavior end to end: compiles
|
||||
## `fixtures/scalar_skip_fixture.nim` (a library with an all-scalar `abi = c`
|
||||
## proc) with `-d:ffiGenBindings` and asserts a CBOR-speaking target
|
||||
## (`targetLang=c`) fails loudly, `-d:ffiAllowScalarSkip` downgrades the drop
|
||||
## to a clean build, and `targetLang=c_abi` needs no skip at all — it emits a
|
||||
## real binding for the scalar proc.
|
||||
## Drives the scalar-fast-path drop error end to end: compiles
|
||||
## `fixtures/scalar_skip_fixture.nim` (a CBOR-default library with a stray
|
||||
## all-scalar `abi = c` proc) with `-d:ffiGenBindings` and asserts genBindings()
|
||||
## fails loudly, and that `-d:ffiAllowScalarSkip` downgrades the drop to a clean
|
||||
## build. The positive path — the `abi = c` C header emitting a real scalar
|
||||
## binding — is covered by test_c_abi_codegen and the echo c_abi e2e.
|
||||
##
|
||||
## The fixture is compiled in a child `nim check` (search paths and compiler
|
||||
## captured at compile time) so an expected failure is observed as a test
|
||||
## captured at compile time) so its expected failure is observed as a test
|
||||
## assertion, not this file's own compile error.
|
||||
|
||||
import std/[os, osproc, strutils, compilesettings]
|
||||
@ -17,15 +17,14 @@ const
|
||||
nimExe = getCurrentCompilerExe()
|
||||
ffiSearchPaths = querySettingSeq(searchPaths)
|
||||
|
||||
proc genFixture(
|
||||
lang: string, extraDefs: seq[string], outDir: string
|
||||
): tuple[output: string, exitCode: int] =
|
||||
proc genFixture(extraDefs: seq[string]): tuple[output: string, exitCode: int] =
|
||||
let outDir = getTempDir() / "ffi_scalar_skip_out"
|
||||
let cacheDir = getTempDir() / "ffi_scalar_skip_cache"
|
||||
createDir(outDir)
|
||||
var cmd = quoteShell(nimExe) & " check --hints:off --warnings:off"
|
||||
for p in ffiSearchPaths:
|
||||
cmd.add(" --path:" & quoteShell(p))
|
||||
cmd.add(" -d:ffiGenBindings -d:targetLang=" & lang)
|
||||
cmd.add(" -d:ffiGenBindings -d:targetLang=c")
|
||||
cmd.add(" -d:ffiOutputDir=" & quoteShell(outDir))
|
||||
for d in extraDefs:
|
||||
cmd.add(" " & d)
|
||||
@ -34,26 +33,14 @@ proc genFixture(
|
||||
execCmdEx(cmd)
|
||||
|
||||
suite "scalar-fast-path drop is loud under -d:ffiGenBindings":
|
||||
test "a CBOR target errors and names the dropped scalar proc":
|
||||
let (output, code) = genFixture("c", @[], getTempDir() / "ffi_scalar_skip_out_c")
|
||||
test "genBindings errors and names the dropped scalar proc":
|
||||
let (output, code) = genFixture(@[])
|
||||
check code != 0
|
||||
check output.contains("scalarskip_add")
|
||||
check output.contains("scalar-fast-path")
|
||||
check output.contains("targetLang=c_abi")
|
||||
check output.contains("-d:ffiAllowScalarSkip")
|
||||
|
||||
test "-d:ffiAllowScalarSkip downgrades the drop to a clean build":
|
||||
let (output, code) = genFixture(
|
||||
"c", @["-d:ffiAllowScalarSkip"], getTempDir() / "ffi_scalar_skip_out_c_skip"
|
||||
)
|
||||
check code == 0
|
||||
check not output.contains("Error")
|
||||
|
||||
test "targetLang=c_abi needs no skip: the scalar proc has real codegen":
|
||||
# `nim check` runs genBindings() but VM file writes are skipped, so this
|
||||
# asserts the clean build only; the emitted wrapper text is covered by
|
||||
# test_c_abi_codegen and the checked-in echo c_abi bindings.
|
||||
let (output, code) =
|
||||
genFixture("c_abi", @[], getTempDir() / "ffi_scalar_skip_out_c_abi")
|
||||
let (output, code) = genFixture(@["-d:ffiAllowScalarSkip"])
|
||||
check code == 0
|
||||
check not output.contains("Error")
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user