Skip to content

Add Linux x64 support (net8.0)#2

Merged
pjanec merged 7 commits into
mainfrom
claude/linux-support-pr-ezm5al
Jul 12, 2026
Merged

Add Linux x64 support (net8.0)#2
pjanec merged 7 commits into
mainfrom
claude/linux-support-pr-ezm5al

Conversation

@pjanec

@pjanec pjanec commented Jul 12, 2026

Copy link
Copy Markdown
Owner

Adds cross-platform native build & packaging so the bindings run on Linux x64 alongside Windows x64, keeping net8.0 as the target framework. Inspired by the earlier (closed) PR #1, but decoupled from that PR's net8.0 → net10.0 bump and verified end-to-end on Linux.

What's included

Linux native build & packaging

  • build/native-linux.sh — compiles the CycloneDDS submodule + idlc, stages libddsc/libcycloneddsidl* .so and idlc into artifacts/native/linux-x64/, and rewrites RPATH to $ORIGIN (via patchelf) so the flat NuGet tools/ layout resolves its .so dependencies.
  • CycloneDDS.Runtime.csproj packs libddsc.so under runtimes/linux-x64/native and idlc + .so deps under tools/ (Exists-guarded so a Windows-only local pack still succeeds).
  • Core.csproj and the native-consuming test/example projects OS-guard the native-asset copy via IsOSPlatform.
  • P/Invoke needed no change — [DllImport("ddsc")] resolves to libddsc.so automatically.

Cross-platform code generator

  • IdlcRunner is now platform-aware: locates idlc/idlc.exe under the correct RID, sets LD_LIBRARY_PATH, and restores the Unix execute bit NuGet drops.

Bugs found by actually building/testing on Linux

  • DDS matched-status events never fired on Linux. The publication/subscription-matched listener callbacks passed their 24-byte status struct by ref, which matches the Windows x64 ABI but not Linux System V (>16-byte structs passed by value on the stack) — arg was read from the wrong register, GCHandle.FromIntPtr threw, and the callback was silently swallowed. Now passed by value so the marshaller emits the correct ABI on both platforms. PublicationMatched/SubscriptionMatched/WaitForReaderAsync now work on Linux.
  • Mis-cased ..\..\Src\ project references that only resolved on case-insensitive (Windows) filesystems — corrected to src.

CI

  • A linux-build job builds the native libs, then dotnet builds the solution (exercising Linux idlc codegen) and runs the runtime tests; the Windows job runs the existing pack.ps1 after downloading the Linux native artifacts to produce a single cross-platform package.
  • Installs both the .NET 8 and .NET 10 SDKs: the code uses C# 13 features that require the newer compiler, while the target framework stays net8.0.

Versioning

  • Bumps version.json minor to 0.3 (new platform + a public interop signature change). Nerdbank.GitVersioning derives the patch from git height.

Verified on Linux (net8.0)

Native build ✅, full managed build ✅ (0 errors), tests: Runtime 145 pass / 1 pre-existing skip, CodeGen 199, Core 19, Schema 12, IdlImporter 28, Compiler.Common 8 — all green. NuGet pack produces runtimes/linux-x64/native/libddsc.so + tools/idlc alongside the win-x64 assets.

The two FeatureDemo example-test failures are pre-existing and environmental (Spectre.Console needs a TTY; the StockPublisher timing test the upstream author already ignores in CI) and are not run by the pack/CI pipeline.

Windows CI in this PR runs the same DiscoveryTests, which cross-validates the by-value ABI fix on Windows too.

🤖 Generated with Claude Code

https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11


Generated by Claude Code

claude added 7 commits July 12, 2026 06:22
Add cross-platform native build and packaging so the bindings run on
Linux x64 alongside the existing Windows x64 support, without changing
the target framework (stays net8.0).

Native build & CI
- build/native-linux.sh: compile the CycloneDDS submodule + idlc on
  Linux, stage libddsc/libcycloneddsidl* .so and idlc into
  artifacts/native/linux-x64, and rewrite RPATH to $ORIGIN via patchelf
  so the flat NuGet tools/ layout resolves its .so dependencies.
- CI: add a linux-build job that builds the native libs, then builds the
  managed solution (exercising build-time idlc codegen on Linux) and runs
  the runtime tests; the Windows job downloads the linux-x64 native
  artifacts and packs a cross-platform NuGet via the existing pack.ps1.
- .gitignore: ignore build/native-linux/.

Cross-platform code generator
- IdlcRunner.FindIdlc/RunIdlc are now platform-aware: try idlc/idlc.exe
  and the win-x64/linux-x64 RID folders, set LD_LIBRARY_PATH to the idlc
  directory on Linux, and restore the Unix execute bit (NuGet does not
  preserve it) before launching idlc.

Packaging & project files
- Runtime.csproj: ship libddsc.so under runtimes/linux-x64/native and
  idlc + its .so deps under tools/ (Exists-guarded so a Windows-only pack
  still succeeds); update the package description.
- Core.csproj and the native-consuming test projects + FeatureDemo:
  OS-guard the win-x64 native copy and add a linux-x64 copy via
  MSBuild IsOSPlatform conditions.

Tests
- ErrorHandlingTests: resolve the idlc path by RID/OS at runtime.
- IdlcRunnerTests: use the platform idlc name and skip the Windows
  batch-file plumbing test on non-Windows.

Native fork must be built from the pjanec/cyclonedds submodule; DllImport
("ddsc") resolves to libddsc.so on Linux with no source change.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
Found by building and running the full test suite on Linux with the .NET
SDK (net8.0 target, .NET 10 SDK toolchain).

Case-sensitive project references
- Four ProjectReference paths used "..\..\Src\..." (capital S). Windows'
  case-insensitive filesystem hid this; on Linux the references failed and
  CycloneDDS.CodeGen/Schema.Tests could not resolve their dependencies.
  Corrected to lowercase "src".

Listener callback ABI (the real fix)
- DdsOnPublicationMatched / DdsOnSubscriptionMatched passed the 24-byte
  status struct by `ref`. That matches the Windows x64 ABI (>16-byte
  structs passed by implicit pointer) but not the Linux System V ABI
  (passed by value on the stack), which shifted `arg` into the wrong
  register — GCHandle.FromIntPtr then threw and the callback was silently
  dropped. As a result PublicationMatched/SubscriptionMatched events and
  WaitForReaderAsync never fired on Linux. Declaring the status by value
  lets the .NET marshaller emit the correct per-platform ABI; the matching
  handler signatures in DdsWriter/DdsReader are updated to match.

CI toolchain
- The code uses C# 13 features (ref structs in async methods) that require
  the .NET 10 SDK compiler, so CI now installs both the 8.0 and 10.0 SDKs
  (10.0 builds, 8.0 runs the net8.0 tests) instead of pinning 8.0.x.

Cleanup
- Guard the idlc chmod with OperatingSystem.IsWindows() to silence CA1416.

Verified on Linux: native build, full managed build, and tests all green —
Runtime 145/1 skip, CodeGen 199, Core 19, Schema 12, IdlImporter 28,
Compiler.Common 8. The two FeatureDemo example-test failures are
pre-existing and environmental (Spectre.Console needs a TTY; the
StockPublisher timing test the upstream author already ignores in CI) and
are not run by the pack/CI pipeline.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
Adding a new supported platform (Linux x64) is a feature, and the
listener-callback ABI fix changes a public interop delegate signature, so
under SemVer (pre-1.0) this is a minor bump. Nerdbank.GitVersioning derives
the patch component from git height automatically.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
Matches the Nerdbank.GitVersioning output on main after a squash merge
(single 0.3 commit -> git height 1 -> 0.3.1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
The DdsMonitor tool bundled its native ddsc library only via the transitive
Content copy from CycloneDDS.Core, which is OS-guarded — so a tool packed on
Windows (as CI does) carried only ddsc.dll and threw DllNotFoundException when
installed on Linux (and vice-versa).

Bundle BOTH platforms' runtime native into the tool payload: the transitive
copy still provides the pack host's native, and new Exists/OS-guarded Content
items add the other platform's (linux-x64 libddsc.so when packing on Windows,
win-x64 ddsc.dll when packing on Linux). Native libs sit flat in the tool's
app-base dir, so DllImport("ddsc") resolves the right file per OS. Pack="false"
keeps them in the tool payload (via CopyToOutputDirectory) without duplicating
them into content/ and contentFiles/.

Verified by packing on Linux with a win-x64 stub: the tool payload contains
both tools/net8.0/any/ddsc.dll and tools/net8.0/any/libddsc.so(.0), and a normal
Linux build (no other-platform native present) is unaffected. IdlImporter was
already cross-platform via the package's tools/ idlc; confirmed by importing IDL
to C# end-to-end on Linux.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
The ddsmonitor global tool gained a new supported platform (Linux) — a
feature — so bump its independent version line (tools/DdsMonitor/version.json)
from 1.0 to 1.1. Nerdbank.GitVersioning derives the patch from git height, so
after a squash merge to main the tool publishes as 1.1.1 (the main
CycloneDDS.NET package remains 0.3.1).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
- Installation: note native assets ship for both Windows x64 and Linux x64,
  and add a Supported platforms table (VC++ redist on Windows; glibc on Linux).
- Build from source: add the Linux native build path (build/native-linux.sh)
  alongside native-win.ps1, and a cross-platform build/test snippet.
- Requirements: clarify the target is net8.0 but building needs the .NET 10 SDK
  (C# 13 features), plus the Linux toolchain (cmake/build-essential/patchelf).
- Dependencies: list the Linux native asset names next to the Windows ones.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N7RoW2a2tBB2T8tDbNHB11
@pjanec pjanec merged commit 5e78508 into main Jul 12, 2026
2 checks passed
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.

2 participants