Skip to content

Commit 41e710e

Browse files
committed
chore(ci): add nph linting (#77)
1 parent f3dfe4d commit 41e710e

19 files changed

Lines changed: 205 additions & 209 deletions

.github/workflows/linters.yml

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
name: Linters
2+
3+
on:
4+
pull_request:
5+
merge_group:
6+
7+
concurrency:
8+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
9+
cancel-in-progress: true
10+
11+
jobs:
12+
nph:
13+
name: nph
14+
runs-on: ubuntu-latest
15+
steps:
16+
- name: Checkout repository
17+
uses: actions/checkout@v4
18+
with:
19+
fetch-depth: 2 # In PR, has extra merge commit: ^1 = PR, ^2 = base
20+
21+
- name: Check nph formatting
22+
uses: arnetheduck/nph-action@v1
23+
with:
24+
version: 0.7.0
25+
options: "./. *.nim*"
26+
fail: true
27+
suggest: true

config.nims

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ switch("path", thisDir())
66
--noNimblePath
77
when withDir(thisDir(), system.fileExists("nimble.paths")):
88
include "nimble.paths"
9-
# end Nimble config
9+
# end Nimble config

examples/echo/echo.nimble

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
version = "0.1.0"
22
packageName = "echo"
33
author = "Institute of Free Technology"
4-
description = "Second nim-ffi example library, used as the cross-library partner of the timer example in C++ e2e tests"
4+
description =
5+
"Second nim-ffi example library, used as the cross-library partner of the timer example in C++ e2e tests"
56
license = "MIT or Apache License 2.0"
67

78
requires "nim >= 2.2.4"
@@ -13,8 +14,7 @@ requires "https://github.com/logos-messaging/nim-ffi >= 0.2.0"
1314
const nimFlags = "--mm:orc -d:chronicles_log_level=WARN"
1415

1516
task build, "Compile the echo library":
16-
exec "nim c " & nimFlags &
17-
" --app:lib --noMain --nimMainPrefix:libecho echo.nim"
17+
exec "nim c " & nimFlags & " --app:lib --noMain --nimMainPrefix:libecho echo.nim"
1818

1919
task genbindings_cpp, "Generate C++ bindings for the echo example":
2020
exec "nim c " & nimFlags & " --app:lib --noMain --nimMainPrefix:libecho" &

examples/timer/timer.nimble

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ requires "https://github.com/logos-messaging/nim-ffi >= 0.2.0"
1313
const nimFlags = "--mm:orc -d:chronicles_log_level=WARN"
1414

1515
task build, "Compile the timer library":
16-
exec "nim c " & nimFlags &
17-
" --app:lib --noMain --nimMainPrefix:libmy_timer timer.nim"
16+
exec "nim c " & nimFlags & " --app:lib --noMain --nimMainPrefix:libmy_timer timer.nim"
1817

1918
task genbindings_rust, "Generate Rust bindings for the timer example":
2019
exec "nim c " & nimFlags & " --app:lib --noMain --nimMainPrefix:libmy_timer" &

ffi.nimble

Lines changed: 48 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -56,17 +56,12 @@ proc sanFlags(san: string): string =
5656
of "none", "":
5757
""
5858
of "asan-ubsan":
59-
" --passC:-fsanitize=address,undefined" &
60-
" --passC:-fno-sanitize-recover=all" &
61-
" --passC:-fno-omit-frame-pointer" &
62-
" --passC:-g" &
63-
" --passL:-fsanitize=address,undefined"
59+
" --passC:-fsanitize=address,undefined" & " --passC:-fno-sanitize-recover=all" &
60+
" --passC:-fno-omit-frame-pointer" & " --passC:-g" &
61+
" --passL:-fsanitize=address,undefined"
6462
of "tsan":
65-
" --passC:-fsanitize=thread" &
66-
" --passC:-fno-omit-frame-pointer" &
67-
" --passC:-g" &
68-
" --passC:-O1" &
69-
" --passL:-fsanitize=thread"
63+
" --passC:-fsanitize=thread" & " --passC:-fno-omit-frame-pointer" & " --passC:-g" &
64+
" --passC:-O1" & " --passL:-fsanitize=thread"
7065
else:
7166
raise newException(ValueError, "unknown NIM_FFI_SAN: " & san)
7267

@@ -95,17 +90,24 @@ task test_cpp_e2e, "Build and run the C++ end-to-end tests for the timer example
9590
runOrQuit "nimble genbindings_cpp"
9691
runOrQuit "nimble genbindings_cpp_echo"
9792
runOrQuit "cmake -S tests/e2e/cpp -B tests/e2e/cpp/build"
98-
runOrQuit "cmake --build tests/e2e/cpp/build"
99-
runOrQuit "ctest --test-dir tests/e2e/cpp/build --output-on-failure"
100-
101-
task test_sanitized, "Run all unit tests under a sanitizer (NIM_FFI_SAN) and mm (NIM_FFI_MM)":
93+
runOrQuit "cmake --build tests/e2e/cpp/build --config Debug"
94+
# `-C Debug` is required on Windows multi-config generators because
95+
# gtest_discover_tests(PRE_TEST) loads per-config include files; harmless on
96+
# single-config generators (Make/Ninja) on Linux/macOS.
97+
runOrQuit "ctest --test-dir tests/e2e/cpp/build --output-on-failure -C Debug"
98+
99+
task test_sanitized,
100+
"Run all unit tests under a sanitizer (NIM_FFI_SAN) and mm (NIM_FFI_MM)":
102101
let san = getEnv("NIM_FFI_SAN", "none")
103-
let mm = getEnv("NIM_FFI_MM", "")
102+
let mm = getEnv("NIM_FFI_MM", "")
104103
let extra = sanFlags(san)
105104
let modes =
106-
if mm == "orc": @[nimFlagsOrc]
107-
elif mm == "refc": @[nimFlagsRefc]
108-
else: @[nimFlagsOrc, nimFlagsRefc]
105+
if mm == "orc":
106+
@[nimFlagsOrc]
107+
elif mm == "refc":
108+
@[nimFlagsRefc]
109+
else:
110+
@[nimFlagsOrc, nimFlagsRefc]
109111
if san == "tsan":
110112
let suppPath = thisDir() & "/tsan.supp"
111113
let existing = getEnv("TSAN_OPTIONS")
@@ -117,82 +119,67 @@ task test_sanitized, "Run all unit tests under a sanitizer (NIM_FFI_SAN) and mm
117119
for t in unitTests:
118120
exec "nim c -r " & flags & extra & " tests/unit/" & t & ".nim"
119121

120-
task test_cpp_e2e_sanitized, "Build and run the C++ e2e tests with a sanitizer (NIM_FFI_SAN) and mm (NIM_FFI_MM)":
121-
let mm = getEnv("NIM_FFI_MM", "orc")
122+
task test_cpp_e2e_sanitized,
123+
"Build and run the C++ e2e tests with a sanitizer (NIM_FFI_SAN) and mm (NIM_FFI_MM)":
124+
let mm = getEnv("NIM_FFI_MM", "orc")
122125
let san = getEnv("NIM_FFI_SAN", "none")
123126
runOrQuit "nimble genbindings_cpp"
124127
runOrQuit "nimble genbindings_cpp_echo"
125-
runOrQuit "cmake -S tests/e2e/cpp -B tests/e2e/cpp/build" &
126-
" -DNIM_FFI_MM=" & mm &
127-
" -DNIM_FFI_SANITIZER=" & san
128-
runOrQuit "cmake --build tests/e2e/cpp/build -j"
129-
runOrQuit "ctest --test-dir tests/e2e/cpp/build --output-on-failure"
128+
runOrQuit "cmake -S tests/e2e/cpp -B tests/e2e/cpp/build" & " -DNIM_FFI_MM=" & mm &
129+
" -DNIM_FFI_SANITIZER=" & san
130+
runOrQuit "cmake --build tests/e2e/cpp/build --config Debug -j"
131+
runOrQuit "ctest --test-dir tests/e2e/cpp/build --output-on-failure -C Debug"
130132

131133
task genbindings_example, "Generate Rust bindings for the timer example":
132-
exec "nim c " & nimFlagsOrc & " --app:lib --noMain --nimMainPrefix:libmy_timer -d:ffiGenBindings -o:/dev/null examples/timer/timer.nim"
133-
exec "nim c " & nimFlagsRefc & " --app:lib --noMain --nimMainPrefix:libmy_timer -d:ffiGenBindings -o:/dev/null examples/timer/timer.nim"
134+
exec "nim c " & nimFlagsOrc &
135+
" --app:lib --noMain --nimMainPrefix:libmy_timer -d:ffiGenBindings -o:/dev/null examples/timer/timer.nim"
136+
exec "nim c " & nimFlagsRefc &
137+
" --app:lib --noMain --nimMainPrefix:libmy_timer -d:ffiGenBindings -o:/dev/null examples/timer/timer.nim"
134138

135139
task genbindings_rust, "Generate Rust bindings for the timer example":
136-
exec "nim c " & nimFlagsOrc &
137-
" --app:lib --noMain --nimMainPrefix:libmy_timer" &
140+
exec "nim c " & nimFlagsOrc & " --app:lib --noMain --nimMainPrefix:libmy_timer" &
138141
" -d:ffiGenBindings -d:targetLang=rust" &
139-
" -d:ffiOutputDir=examples/timer/rust_bindings" &
140-
" -d:ffiSrcPath=../timer.nim" &
142+
" -d:ffiOutputDir=examples/timer/rust_bindings" & " -d:ffiSrcPath=../timer.nim" &
141143
" -o:/dev/null examples/timer/timer.nim"
142-
exec "nim c " & nimFlagsRefc &
143-
" --app:lib --noMain --nimMainPrefix:libmy_timer" &
144+
exec "nim c " & nimFlagsRefc & " --app:lib --noMain --nimMainPrefix:libmy_timer" &
144145
" -d:ffiGenBindings -d:targetLang=rust" &
145-
" -d:ffiOutputDir=examples/timer/rust_bindings" &
146-
" -d:ffiSrcPath=../timer.nim" &
146+
" -d:ffiOutputDir=examples/timer/rust_bindings" & " -d:ffiSrcPath=../timer.nim" &
147147
" -o:/dev/null examples/timer/timer.nim"
148148

149149
task genbindings_cddl, "Generate CDDL schema for the timer example":
150-
exec "nim c " & nimFlagsOrc &
151-
" --app:lib --noMain --nimMainPrefix:libtimer" &
150+
exec "nim c " & nimFlagsOrc & " --app:lib --noMain --nimMainPrefix:libtimer" &
152151
" -d:ffiGenBindings -d:targetLang=cddl" &
153-
" -d:ffiOutputDir=examples/timer/cddl_bindings" &
154-
" -d:ffiSrcPath=../timer.nim" &
152+
" -d:ffiOutputDir=examples/timer/cddl_bindings" & " -d:ffiSrcPath=../timer.nim" &
155153
" -o:/dev/null examples/timer/timer.nim"
156154

157155
task genbindings_cpp, "Generate C++ bindings for the timer example":
158-
exec "nim c " & nimFlagsOrc &
159-
" --app:lib --noMain --nimMainPrefix:libmy_timer" &
156+
exec "nim c " & nimFlagsOrc & " --app:lib --noMain --nimMainPrefix:libmy_timer" &
160157
" -d:ffiGenBindings -d:targetLang=cpp" &
161-
" -d:ffiOutputDir=examples/timer/cpp_bindings" &
162-
" -d:ffiSrcPath=../timer.nim" &
158+
" -d:ffiOutputDir=examples/timer/cpp_bindings" & " -d:ffiSrcPath=../timer.nim" &
163159
" -o:/dev/null examples/timer/timer.nim"
164-
exec "nim c " & nimFlagsRefc &
165-
" --app:lib --noMain --nimMainPrefix:libmy_timer" &
160+
exec "nim c " & nimFlagsRefc & " --app:lib --noMain --nimMainPrefix:libmy_timer" &
166161
" -d:ffiGenBindings -d:targetLang=cpp" &
167-
" -d:ffiOutputDir=examples/timer/cpp_bindings" &
168-
" -d:ffiSrcPath=../timer.nim" &
162+
" -d:ffiOutputDir=examples/timer/cpp_bindings" & " -d:ffiSrcPath=../timer.nim" &
169163
" -o:/dev/null examples/timer/timer.nim"
170164

171165
task genbindings_cpp_echo, "Generate C++ bindings for the echo example":
172-
exec "nim c " & nimFlagsOrc &
173-
" --app:lib --noMain --nimMainPrefix:libecho" &
166+
exec "nim c " & nimFlagsOrc & " --app:lib --noMain --nimMainPrefix:libecho" &
174167
" -d:ffiGenBindings -d:targetLang=cpp" &
175-
" -d:ffiOutputDir=examples/echo/cpp_bindings" &
176-
" -d:ffiSrcPath=../echo.nim" &
168+
" -d:ffiOutputDir=examples/echo/cpp_bindings" & " -d:ffiSrcPath=../echo.nim" &
177169
" -o:/dev/null examples/echo/echo.nim"
178-
exec "nim c " & nimFlagsRefc &
179-
" --app:lib --noMain --nimMainPrefix:libecho" &
170+
exec "nim c " & nimFlagsRefc & " --app:lib --noMain --nimMainPrefix:libecho" &
180171
" -d:ffiGenBindings -d:targetLang=cpp" &
181-
" -d:ffiOutputDir=examples/echo/cpp_bindings" &
182-
" -d:ffiSrcPath=../echo.nim" &
172+
" -d:ffiOutputDir=examples/echo/cpp_bindings" & " -d:ffiSrcPath=../echo.nim" &
183173
" -o:/dev/null examples/echo/echo.nim"
184174

185175
task check_bindings_rust, "Verify checked-in Rust bindings match Nim source":
186176
exec "nimble genbindings_rust"
187-
exec "git diff --exit-code --" &
188-
" examples/timer/rust_bindings/Cargo.toml" &
189-
" examples/timer/rust_bindings/build.rs" &
190-
" examples/timer/rust_bindings/src"
177+
exec "git diff --exit-code --" & " examples/timer/rust_bindings/Cargo.toml" &
178+
" examples/timer/rust_bindings/build.rs" & " examples/timer/rust_bindings/src"
191179

192180
task check_bindings_cpp, "Verify checked-in C++ bindings match Nim source":
193181
exec "nimble genbindings_cpp"
194-
exec "git diff --exit-code --" &
195-
" examples/timer/cpp_bindings/my_timer.hpp" &
182+
exec "git diff --exit-code --" & " examples/timer/cpp_bindings/my_timer.hpp" &
196183
" examples/timer/cpp_bindings/CMakeLists.txt"
197184

198185
task check_bindings, "Verify all checked-in example bindings match Nim source":

ffi/cbor_serial.nim

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,6 @@ export cbor_serialization, options, results
3535
const CborNullByte*: byte = 0xf6'u8
3636
## CBOR encoding of `null` — used as the wire sentinel for empty OK payloads.
3737

38-
3938
proc cborEncode*[T](x: T): seq[byte] =
4039
## CBOR-encode any cbor_serialization-supported type (plus `pointer` / `ptr T`
4140
## via our custom writers) into a fresh `seq[byte]`.

ffi/codegen/cpp.nim

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,9 @@ proc emitEventDispatcher(
158158
## until `removeEventListener` removes it.
159159
if events.len == 0:
160160
return
161-
lines.add(" // ── Event listener API ──────────────────────────────────")
161+
lines.add(
162+
" // ── Event listener API ──────────────────────────────────"
163+
)
162164
lines.add(" struct ListenerHandle { std::uint64_t id = 0; };")
163165
lines.add("")
164166
# Per-event typed registration helpers.
@@ -174,9 +176,7 @@ proc emitEventDispatcher(
174176
[ev.payloadTypeName]
175177
)
176178
lines.add(" auto* raw = owned.get();")
177-
lines.add(
178-
" const auto id = $1_add_event_listener(" % [libName]
179-
)
179+
lines.add(" const auto id = $1_add_event_listener(" % [libName])
180180
lines.add(
181181
" ptr_, \"$1\", &$2::typedTrampoline<$3>, raw);" %
182182
[ev.wireName, ctxTypeName, ev.payloadTypeName]
@@ -197,9 +197,7 @@ proc emitEventDispatcher(
197197
lines.add(" }")
198198
lines.add("")
199199

200-
proc emitEventTrampoline(
201-
lines: var seq[string], events: seq[FFIEventMeta]
202-
) =
200+
proc emitEventTrampoline(lines: var seq[string], events: seq[FFIEventMeta]) =
203201
## Private listener machinery for the public API emitted by
204202
## `emitEventDispatcher`:
205203
##
@@ -217,12 +215,16 @@ proc emitEventTrampoline(
217215
lines.add(" template <class T>")
218216
lines.add(" struct TypedListener : ListenerBase {")
219217
lines.add(" std::function<void(const T&)> fn;")
220-
lines.add(" explicit TypedListener(std::function<void(const T&)> f) : fn(std::move(f)) {}")
218+
lines.add(
219+
" explicit TypedListener(std::function<void(const T&)> f) : fn(std::move(f)) {}"
220+
)
221221
lines.add(" };")
222222
lines.add("")
223223
# Typed trampoline — one instantiation per payload type, all sharing a body.
224224
lines.add(" template <class T>")
225-
lines.add(" static void typedTrampoline(int ret, const char* msg, std::size_t len, void* ud) {")
225+
lines.add(
226+
" static void typedTrampoline(int ret, const char* msg, std::size_t len, void* ud) {"
227+
)
226228
lines.add(" if (!ud || ret != 0 || !msg || len == 0) return;")
227229
lines.add(" auto* listener = static_cast<TypedListener<T>*>(ud);")
228230
lines.add(" if (!listener->fn) return;")
@@ -236,9 +238,7 @@ proc emitEventTrampoline(
236238
" if (cbor_value_map_find_value(&it, \"payload\", &payloadField) != CborNoError) return;"
237239
)
238240
lines.add(" T payload{};")
239-
lines.add(
240-
" if (decode_cbor(payloadField, payload) != CborNoError) return;"
241-
)
241+
lines.add(" if (decode_cbor(payloadField, payload) != CborNoError) return;")
242242
lines.add(" listener->fn(payload);")
243243
lines.add(" }")
244244
lines.add("")
@@ -416,12 +416,12 @@ proc generateCppHeader*(
416416
# itself (see ContextRuleOf5Tpl) and hand out ownership through a
417417
# smart pointer that callers can move, store in containers, etc.
418418
let createRet = "Result<std::unique_ptr<$1>>" % [ctxTypeName]
419-
lines.add(
420-
" static $1 create($2) {" % [createRet, ctorParamsWithTimeout]
421-
)
419+
lines.add(" static $1 create($2) {" % [createRet, ctorParamsWithTimeout])
422420
lines.add(" const auto ffi_req_ = $1;" % [reqInit])
423421
lines.add(" auto ffi_enc_ = encodeCborFFI(ffi_req_);")
424-
lines.add(" if (ffi_enc_.isErr()) return $1::err(ffi_enc_.error());" % [createRet])
422+
lines.add(
423+
" if (ffi_enc_.isErr()) return $1::err(ffi_enc_.error());" % [createRet]
424+
)
425425
lines.add(" const auto& ffi_req_bytes_ = ffi_enc_.value();")
426426
lines.add(" auto ffi_raw_ = ffi_call_([&](FFICallback cb, void* ud) {")
427427
lines.add(
@@ -430,9 +430,13 @@ proc generateCppHeader*(
430430
)
431431
lines.add(" return 0;")
432432
lines.add(" }, timeout);")
433-
lines.add(" if (ffi_raw_.isErr()) return $1::err(ffi_raw_.error());" % [createRet])
433+
lines.add(
434+
" if (ffi_raw_.isErr()) return $1::err(ffi_raw_.error());" % [createRet]
435+
)
434436
lines.add(" auto ffi_addr_ = decodeCborFFI<std::string>(ffi_raw_.value());")
435-
lines.add(" if (ffi_addr_.isErr()) return $1::err(ffi_addr_.error());" % [createRet])
437+
lines.add(
438+
" if (ffi_addr_.isErr()) return $1::err(ffi_addr_.error());" % [createRet]
439+
)
436440
lines.add(" const auto& addr_str = ffi_addr_.value();")
437441
# Parse the ctx address without exceptions: std::stoull would throw on a
438442
# non-numeric payload, so use std::from_chars and surface the failure as
@@ -515,15 +519,19 @@ proc generateCppHeader*(
515519
lines.add(" $1 $2($3) const {" % [methRet, methodName, methParamsStr])
516520
lines.add(" const auto ffi_req_ = $1;" % [reqInit])
517521
lines.add(" auto ffi_enc_ = encodeCborFFI(ffi_req_);")
518-
lines.add(" if (ffi_enc_.isErr()) return $1::err(ffi_enc_.error());" % [methRet])
522+
lines.add(
523+
" if (ffi_enc_.isErr()) return $1::err(ffi_enc_.error());" % [methRet]
524+
)
519525
lines.add(" const auto& ffi_req_bytes_ = ffi_enc_.value();")
520526
lines.add(" auto ffi_raw_ = ffi_call_([&](FFICallback cb, void* ud) {")
521527
lines.add(
522528
" return $1(ptr_, cb, ud, ffi_req_bytes_.data(), ffi_req_bytes_.size());" %
523529
[m.procName]
524530
)
525531
lines.add(" }, timeout_);")
526-
lines.add(" if (ffi_raw_.isErr()) return $1::err(ffi_raw_.error());" % [methRet])
532+
lines.add(
533+
" if (ffi_raw_.isErr()) return $1::err(ffi_raw_.error());" % [methRet]
534+
)
527535
lines.add(" return decodeCborFFI<$1>(ffi_raw_.value());" % [retCppType])
528536
lines.add(" }")
529537
lines.add("")
@@ -533,8 +541,7 @@ proc generateCppHeader*(
533541
# parse as invoking the `schedule` parameter).
534542
if methParamsStr.len > 0:
535543
lines.add(
536-
" std::future<$1> $2Async($3) const {" %
537-
[methRet, methodName, methParamsStr]
544+
" std::future<$1> $2Async($3) const {" % [methRet, methodName, methParamsStr]
538545
)
539546
lines.add(
540547
" return std::async(std::launch::async, [this, $1]() { return this->$2($3); });" %
@@ -591,7 +598,6 @@ proc generateCppBindings*(
591598
) =
592599
createDir(outputDir)
593600
writeFile(
594-
outputDir / (libName & ".hpp"),
595-
generateCppHeader(procs, types, libName, events),
601+
outputDir / (libName & ".hpp"), generateCppHeader(procs, types, libName, events)
596602
)
597603
writeFile(outputDir / "CMakeLists.txt", generateCppCMakeLists(libName, nimSrcRelPath))

0 commit comments

Comments
 (0)