Skip to content

feat: add pure C shim layer for cross-compiler ABI safety#1

Merged
boybook merged 8 commits intomainfrom
feat/shim-layer
Mar 7, 2026
Merged

feat: add pure C shim layer for cross-compiler ABI safety#1
boybook merged 8 commits intomainfrom
feat/shim-layer

Conversation

@boybook
Copy link
Owner

@boybook boybook commented Mar 7, 2026

Summary

  • Introduce a pure C shim layer (src/shim/) that wraps all ~95 Hamlib API calls with opaque handles, eliminating the MSVC/MinGW struct layout incompatibility that caused immediate crashes on Windows
  • Rewrite hamlib.cpp to use shim_rig_* functions exclusively — no conditional compilation, all platforms use the same code path
  • Simplify binding.gyp by removing complex Hamlib path search logic, pthread dependency, and MinGW conditional compilation
  • Update CI with MSYS2/MinGW setup for building the shim DLL on Windows

Architecture

Node.js → hamlib.node (C++ NAPI, MSVC) → hamlib_shim.dll (pure C, MinGW) → libhamlib-4.dll
  • Linux/macOS: shim compiled as static library (.a), linked into addon
  • Windows: shim compiled as DLL (MinGW), addon (MSVC) dynamically loads it via import lib

Test plan

  • Windows local build: module loads successfully with official Node.js
  • All 84 API methods registered and callable (80 instance + 4 static)
  • test_loader.js: 35/35 tests pass
  • test_dummy_complete.js: all methods execute without crashes (failures are expected "Rig is not open" without rigctld)
  • CI: verify all 5 platform builds pass (linux-x64, linux-arm64, darwin-arm64, darwin-x64, win32-x64)

🤖 Generated with Claude Code

boybook and others added 8 commits March 7, 2026 13:30
Introduce a shim layer between the Node.js addon (MSVC) and Hamlib
(MinGW), eliminating the MSVC/MinGW struct layout incompatibility
that caused immediate crashes on Windows when importing the module.

Architecture:
  Node.js → hamlib.node (C++ NAPI, MSVC) → hamlib_shim (pure C) → libhamlib

Key changes:
- New src/shim/hamlib_shim.{h,c}: ~95 pure C wrapper functions with
  opaque handle pattern, replacing all direct Hamlib struct accesses
- Rewrite src/hamlib.cpp to use shim_rig_* functions exclusively
  (no conditional compilation, all platforms use same code path)
- New scripts/build-shim.js: cross-platform shim build script
  (static .a on Linux/macOS, DLL on Windows via MinGW)
- Simplified binding.gyp: removed complex Hamlib path search logic,
  pthread dependency, and MinGW conditional compilation for Windows
- Updated CI: added MSYS2/MinGW setup for Windows shim DLL build
- Removed obsolete hamlib_compat.h and 7 temporary debug scripts

Verified on Windows: module loads and all 84 API methods work correctly.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
On Unix, build-all.js handles hamlib setup + shim build + addon build
in sequence. On Windows, shim DLL is built separately via MSYS2 before
the MSVC addon build.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The MSYS2 shell doesn't have Node.js in PATH. Use pwsh shell instead
and set MINGW_CC to point to the MSYS2 MinGW gcc. Also add fail-fast:
false to allow all platform builds to complete independently.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
prebuildify internally runs node-gyp rebuild which cleans the build/
directory, deleting the previously built shim .a/.lib files. Moving
shim output to a separate shim-build/ directory prevents this.

Also adds pip install setuptools to CI for Python 3.12 compatibility
with node-gyp 9.4.1 (distutils was removed in Python 3.12).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The dynamic linker needs the soname-versioned file (e.g., libhamlib.so.5)
but the bundler was only copying a single file. Now copies all
libhamlib.so* files from the lib directory, resolving symlinks.

Also improved findHamlibSo() to search for any version (so.4, so.5, etc.)
instead of hardcoding so.4.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Adds test_ci_functional.js that validates the compiled binary actually
works by exercising core API operations against the Hamlib Dummy device
(Model 1). Tests VFO, frequency, PTT, serial config, power control,
and instance lifecycle - no rigctld or hardware needed.

CI now runs both test_loader.js (API existence) and test_ci_functional.js
(API functionality) to ensure binaries are usable.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replaces the manual package/release flow with automated publishing:
- Tag push (v*) triggers: build → validate → npm publish → GitHub Release
- Validates all 5 platform prebuilds before publishing
- Uses NPM_TOKEN secret for npm authentication
- Creates GitHub Release with per-platform tar.gz archives
- Removes cleanup job (artifacts auto-expire)
- Adds PUBLISHING.md with release instructions

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Rewrites the project guide to reflect the current shim-layer
architecture. Includes clear step-by-step instructions for adding
new API methods across all 4 layers (shim → C++ → JS → TS).
Condensed from 333 to 131 lines while retaining all essential info.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@boybook boybook merged commit a66ac8c into main Mar 7, 2026
6 checks passed
@boybook boybook deleted the feat/shim-layer branch March 7, 2026 06:30
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant