Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 30 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ jobs:
10.0.x

- name: Install native build prerequisites
run: sudo apt-get update && sudo apt-get install -y cmake build-essential patchelf
run: sudo apt-get update && sudo apt-get install -y cmake build-essential patchelf libssl-dev

- name: Display Tools Versions
run: |
Expand Down Expand Up @@ -93,6 +93,14 @@ jobs:
dotnet-version: |
8.0.x
10.0.x

# NOTE: there is deliberately no OpenSSL install step. The windows-latest image
# already ships OpenSSL 3.x under "C:\Program Files\OpenSSL", which is what
# CMake's FindOpenSSL resolves to (build/native-win.ps1 now pins it via
# -DOPENSSL_ROOT_DIR and fails fast if it is absent). The previous
# "vcpkg install openssl:x64-windows-static-md" step built OpenSSL from source
# for ~6 minutes on every run and its output was then ignored, because no vcpkg
# toolchain file was ever passed to CMake.

- name: Display Tools Versions
run: |
Expand All @@ -105,9 +113,29 @@ jobs:
name: native-linux-x64
path: artifacts/native/linux-x64/

# The native build (CycloneDDS + idlc, ~2 min) only depends on the pinned
# submodule revision, the build script, and the image's toolchain — none of which
# change between managed-code commits, so it is cached rather than repeated.
- name: Resolve native cache key
id: native-key
shell: pwsh
run: |
# Gitlink SHA of the cyclonedds submodule: changes exactly when the native
# sources do, which is exactly when the cache must miss.
"submodule=$(git rev-parse HEAD:cyclonedds)" | Out-File -FilePath $env:GITHUB_OUTPUT -Append

- name: Cache native win-x64 build
id: native-cache
uses: actions/cache@v4
with:
path: artifacts/native/win-x64
# ImageVersion is set by the hosted runner; including it invalidates the
# cache when the image's MSVC/OpenSSL move under us.
key: native-win-x64-${{ env.ImageVersion }}-${{ steps.native-key.outputs.submodule }}-${{ hashFiles('build/native-win.ps1') }}

- name: Build, Test, and Pack
shell: pwsh
run: .\build\pack.ps1
run: .\build\pack.ps1 ${{ steps.native-cache.outputs.cache-hit == 'true' && '-SkipNativeBuild' || '' }}

- name: Smoke-test the packed package (Windows)
shell: pwsh
Expand Down
19 changes: 15 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,19 @@ This single package includes:

| Platform | Native runtime | Target requirement |
| :--- | :--- | :--- |
| Windows x64 | `ddsc.dll` | [Visual C++ Redistributable for Visual Studio 2022](https://aka.ms/vs/17/release/vc_redist.x64.exe) installed on the target machine. |
| Linux x64 | `libddsc.so` | A glibc-based distribution (e.g. Ubuntu/Debian). No extra runtime install needed. |
| Windows x64 | `ddsc.dll` | [Visual C++ Redistributable for Visual Studio 2022](https://aka.ms/vs/17/release/vc_redist.x64.exe) installed on the target machine. OpenSSL is bundled. |
| Linux x64 | `libddsc.so` | A glibc-based distribution (e.g. Ubuntu/Debian) with OpenSSL 3.x (`libssl3`), which is preinstalled on all current mainstream distributions. |

> **On OpenSSL.** The native runtime is built with DDS Security and TCP+TLS enabled, so
> `ddsc.dll` / `libddsc.so` import `libssl` and `libcrypto` directly and will not load
> without them. Windows has no system-provided OpenSSL, so the package bundles
> `libcrypto-3-x64.dll` and `libssl-3-x64.dll` (Apache-2.0 — see `ThirdPartyNotices.txt`)
> and places them next to your application automatically. On Linux they come from the
> distribution and nothing is bundled.
>
> The DDS Security plugins (`dds_security_auth`, `dds_security_ac`,
> `dds_security_crypto`) ship alongside them, and are loaded only when your domain
> configuration contains a `<Security>` section.

### Working with Source Code

Expand All @@ -54,8 +65,8 @@ If you want to build the project from source or contribute:
3. **Requirements:**
- **.NET SDK:** the solution targets `net8.0`, but building requires the **.NET 10 SDK** (the code uses C# 13 language features). Produced binaries still run on .NET 8+.
- **CMake 3.16+** on your `PATH`.
- **Windows:** Visual Studio 2022 with the *C++ Desktop Development* workload (for native compilation).
- **Linux:** a C toolchain and `patchelf` — e.g. `sudo apt-get install -y cmake build-essential patchelf`.
- **Windows:** Visual Studio 2022 with the *C++ Desktop Development* workload (for native compilation), plus the OpenSSL 3.x development files — `winget install ShiningLight.OpenSSL.Prod`. `build\native-win.ps1` looks in `C:\Program Files\OpenSSL`, `C:\Program Files\OpenSSL-Win64` and `C:\OpenSSL-Win64`; set `OPENSSL_ROOT_DIR` to override.
- **Linux:** a C toolchain, `patchelf` and the OpenSSL headers — e.g. `sudo apt-get install -y cmake build-essential patchelf libssl-dev`.

## Key Features

Expand Down
19 changes: 19 additions & 0 deletions ThirdPartyNotices.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,22 @@ The full license text is available in the cyclonedds subdirectory of this
repository or at the URLs above.

================================================================================

OpenSSL
https://www.openssl.org/

Redistributed in binary form on Windows only, as libcrypto-3-x64.dll and
libssl-3-x64.dll under runtimes/win-x64/native/ and tools/. The Cyclone DDS
runtime links OpenSSL for DDS Security and TCP+TLS support. On Linux the
equivalent libraries are provided by the operating system and are not
redistributed here.

Licensed under the Apache License, Version 2.0:
http://www.apache.org/licenses/LICENSE-2.0

SPDX-License-Identifier: Apache-2.0

Copyright (c) 1998-2024 The OpenSSL Project Authors
Copyright (c) 1995-1998 Eric A. Young, Tim J. Hudson

================================================================================
21 changes: 19 additions & 2 deletions build/native-linux.sh
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,9 @@ cmake -S "$SOURCE_DIR" -B "$BUILD_DIR" \
-DBUILD_IDLC=ON \
-DBUILD_TESTING=OFF \
-DBUILD_EXAMPLES=OFF \
-DENABLE_SSL=OFF \
-DENABLE_SSL=ON \
-DENABLE_SHM=OFF \
-DENABLE_SECURITY=OFF
-DENABLE_SECURITY=ON

# ----------------------------------------------------------------
# [2/3] Build & Install
Expand Down Expand Up @@ -116,6 +116,23 @@ copy_lib libcycloneddsidl
copy_lib libcycloneddsidlc
copy_lib libcycloneddsidljson

# DDS Security plugins (ENABLE_SECURITY=ON). Unlike the libraries above these are
# unversioned, and ddsc dlopen()s them by bare soname ("libdds_security_auth.so")
# when a <Security> section is present in the domain configuration. glibc resolves
# a dlopen from within libddsc.so against libddsc.so's own DT_RUNPATH, which the
# RPATH fix below sets to $ORIGIN - so staging them flat next to libddsc.so here is
# what makes them findable at runtime. Without them every secure participant fails
# with "Could not load authentication library".
for plugin in libdds_security_auth libdds_security_ac libdds_security_crypto; do
if [ -f "$LIB_DIR/${plugin}.so" ]; then
cp -f "$LIB_DIR/${plugin}.so" "$ARTIFACTS_DIR/${plugin}.so"
echo " [+] ${plugin}.so"
else
echo " [-] Missing ${plugin}.so in $LIB_DIR" >&2
exit 1
fi
done

# IDL compiler executable.
if [ -f "$BIN_DIR/idlc" ]; then
cp -f "$BIN_DIR/idlc" "$ARTIFACTS_DIR/"
Expand Down
86 changes: 83 additions & 3 deletions build/native-win.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,52 @@ if (!(Get-Command cmake -ErrorAction SilentlyContinue)) {
exit 1
}

# ===================================================================================
# Locate OpenSSL (required: ENABLE_SSL=ON / ENABLE_SECURITY=ON below).
#
# The GitHub windows-latest image ships OpenSSL 3.x preinstalled under
# "C:\Program Files\OpenSSL", which is what CMake's FindOpenSSL was already
# picking up. We resolve it here and pass -DOPENSSL_ROOT_DIR explicitly so that a
# missing or relocated install fails HERE, with an actionable message, instead of
# surfacing as an obscure compile error several minutes into the build.
#
# Do NOT reintroduce a "vcpkg install openssl" step to satisfy this. Without a
# vcpkg toolchain file CMake never looks inside the vcpkg tree, so that step built
# OpenSSL from source for ~6 minutes every CI run and then had its output ignored
# in favour of the preinstalled copy.
# ===================================================================================
$opensslCandidates = @()
if ($env:OPENSSL_ROOT_DIR) { $opensslCandidates += $env:OPENSSL_ROOT_DIR }
$opensslCandidates += @(
"C:\Program Files\OpenSSL",
"C:\Program Files\OpenSSL-Win64",
"C:\OpenSSL-Win64"
)

$OpenSslRoot = $null
foreach ($candidate in $opensslCandidates) {
if (Test-Path (Join-Path $candidate "include\openssl\ssl.h")) {
$OpenSslRoot = (Resolve-Path $candidate).Path
break
}
}

if (-not $OpenSslRoot) {
throw @"
OpenSSL was not found, but this build requires it (ENABLE_SSL=ON, ENABLE_SECURITY=ON).

Searched:
$($opensslCandidates -join "`n ")

Install the OpenSSL 3.x development files with one of:
winget install ShiningLight.OpenSSL.Prod
choco install openssl
...or point OPENSSL_ROOT_DIR at an existing installation.
"@
}

Write-Host "Using OpenSSL at: $OpenSslRoot" -ForegroundColor Green

# Configure CMake
Write-Host "`n[1/3] Configuring CMake..." -ForegroundColor Yellow
Push-Location $BuildDir
Expand All @@ -62,9 +108,11 @@ try {
"-DBUILD_IDLC=ON",
"-DBUILD_TESTING=OFF",
"-DBUILD_EXAMPLES=OFF",
"-DENABLE_SSL=OFF",
"-DENABLE_SSL=ON",
"-DENABLE_SHM=OFF",
"-DENABLE_SECURITY=OFF"
"-DENABLE_SECURITY=ON",
# Forward slashes: CMake treats backslashes in -D values as escapes.
"-DOPENSSL_ROOT_DIR=$($OpenSslRoot -replace '\\', '/')"
)

foreach ($gen in $cmakeGenerators) {
Expand Down Expand Up @@ -128,7 +176,15 @@ $RequiredFiles = @(
"idlc.exe",
"cycloneddsidl.dll",
"cycloneddsidlc.dll",
"cycloneddsidljson.dll"
"cycloneddsidljson.dll",

# DDS Security plugins (ENABLE_SECURITY=ON). ddsc dlopen()s these by bare
# name when a <Security> section is present in the domain configuration, so
# they have to ship with the package - without them every secure participant
# fails with "Could not load authentication library".
"dds_security_auth.dll",
"dds_security_ac.dll",
"dds_security_crypto.dll"
)

# Copy required files from bin/
Expand All @@ -142,6 +198,30 @@ foreach ($file in $RequiredFiles) {
}
}

# ===================================================================================
# OpenSSL runtime DLLs.
#
# ENABLE_SSL=ON links OpenSSL into ddsc ITSELF (TCP+TLS), not merely into the
# security plugins, so ddsc.dll carries hard import-table entries for
# libcrypto-3-x64.dll / libssl-3-x64.dll and will not LOAD at all without them.
# Windows ships no system OpenSSL, so the package has to carry these.
#
# Globbed rather than hard-coded: the "-3" infix tracks the OpenSSL major version
# and would change on an eventual OpenSSL 4.
# ===================================================================================
$OpenSslBin = Join-Path $OpenSslRoot "bin"

foreach ($pattern in @("libcrypto-*.dll", "libssl-*.dll")) {
$found = @(Get-ChildItem -Path $OpenSslBin -Filter $pattern -File -ErrorAction SilentlyContinue)
if ($found.Count -eq 0) {
throw "No file matching '$pattern' in $OpenSslBin. ddsc.dll imports the OpenSSL runtime DLLs and cannot load without them."
}
foreach ($dll in $found) {
Copy-Item -Path $dll.FullName -Destination $ArtifactsDir -Force
Write-Host " [+] Copied $($dll.Name) (OpenSSL runtime)" -ForegroundColor Green
}
}

# Can we locate VC++ runtime?
# Usually in $env:SystemRoot\System32 but dependent on VS installation.
# However, redistributables might not be legally redistributable by simple copy
Expand Down
47 changes: 42 additions & 5 deletions build/pack.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,17 @@
# This is what runs in CI to produce release artifacts.
#
# USAGE:
# .\build\pack.ps1
# .\build\pack.ps1 [-SkipNativeBuild]
# ===================================================================================

param (
# Reuse the native assets already staged in artifacts/native/win-x64 instead of
# rebuilding them. CI passes this when the native build cache hits; the contents
# are validated below so a partial or stale cache fails loudly rather than
# producing a package with pieces missing.
[switch]$SkipNativeBuild
)

$ErrorActionPreference = "Stop"

$RepoRoot = $PSScriptRoot | Split-Path -Parent
Expand All @@ -34,10 +42,39 @@ Write-Host " Unified Build & Pack" -ForegroundColor Cyan
Write-Host "============================================================" -ForegroundColor Cyan

# 1. Native Build
Write-Host "`n[1/6] Building Native Assets..." -ForegroundColor Yellow
$NativeScript = Join-Path $PSScriptRoot "native-win.ps1"
& $NativeScript -Configuration Release
if ($LASTEXITCODE -ne 0) { throw "Native build failed." }
if ($SkipNativeBuild) {
Write-Host "`n[1/6] Reusing prebuilt native assets (-SkipNativeBuild)..." -ForegroundColor Yellow

$NativeDir = Join-Path $ArtifactsDir "native\win-x64"
$Expected = @(
"ddsc.dll",
"idlc.exe",
"cycloneddsidl.dll",
"cycloneddsidlc.dll",
"cycloneddsidljson.dll",
"dds_security_auth.dll",
"dds_security_ac.dll",
"dds_security_crypto.dll"
)
$Missing = $Expected | Where-Object { -not (Test-Path (Join-Path $NativeDir $_)) }

# The OpenSSL runtime DLLs carry a major-version infix (libcrypto-3-x64.dll),
# so they are matched by pattern rather than by exact name.
$Missing += @("libcrypto-*.dll", "libssl-*.dll") |
Where-Object { -not (Get-ChildItem -Path $NativeDir -Filter $_ -File -ErrorAction SilentlyContinue) }

if ($Missing) {
throw "-SkipNativeBuild was requested but $NativeDir is missing: $($Missing -join ', '). Re-run without -SkipNativeBuild."
}

Write-Host " [+] Native assets present in $NativeDir" -ForegroundColor Green
}
else {
Write-Host "`n[1/6] Building Native Assets..." -ForegroundColor Yellow
$NativeScript = Join-Path $PSScriptRoot "native-win.ps1"
& $NativeScript -Configuration Release
if ($LASTEXITCODE -ne 0) { throw "Native build failed." }
}

# 2. Restore
Write-Host "`n[2/6] Restoring (core - examples excluded)..." -ForegroundColor Yellow
Expand Down
50 changes: 49 additions & 1 deletion build/targets/CycloneDDS.targets
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@
The in-repo shim sets it for the bindings' own projects. -->
<CycloneDdsCodeGenActive>false</CycloneDdsCodeGenActive>
<CycloneDdsCodeGenActive Condition="'$(CycloneDdsCodeGenEnabled)' == 'true' AND '$(CycloneDdsDisableCodeGen)' != 'true'">true</CycloneDdsCodeGenActive>

<!-- Where the win-x64 native files live. Default = packaged location
(buildTransitive/ -> ../runtimes/win-x64/native/). The in-repo shim points
this at the staged native artifacts instead. See target 6. -->
<CycloneDdsWinNativeDir Condition="'$(CycloneDdsWinNativeDir)' == ''">$(MSBuildThisFileDirectory)..\runtimes\win-x64\native\</CycloneDdsWinNativeDir>
</PropertyGroup>

<!-- Conditioned so the **\*.cs glob is not even evaluated for projects that don't run codegen. -->
Expand Down Expand Up @@ -229,7 +234,50 @@
<Copy SourceFiles="@(GeneratedIdlFiles)" DestinationFolder="$(OutputPath)" SkipUnchangedFiles="true" />
</Target>

<!-- 6. Clean target — removes the generated dir AND the stamp inside it. -->
<!-- 6. Stage the Windows native files that the OS loader can only find in the
APPLICATION directory. Two problems, one copy:

a) The DDS Security plugins. ddsc loads these with
LoadLibrary("dds_security_auth.dll") — a BARE name, so Windows searches the
executable's own directory. NuGet only lays native assets out under
$(OutDir)runtimes\win-x64\native\, which LoadLibrary does not consult, so
without this copy every participant created against a <Security>
configuration fails with "Could not load authentication library".

b) The OpenSSL runtime DLLs those plugins import. A plain LoadLibrary resolves
a module's imports relative to the EXECUTABLE's directory, not the module's
own, so the plugins cannot reach the copy in runtimes\win-x64\native\.
(That copy is still required — it is the one ddsc.dll itself binds against,
since the .NET host loads ddsc with LOAD_WITH_ALTERED_SEARCH_PATH, which
searches ddsc.dll's own directory first. Hence both copies.)

Linux needs no counterpart: dlopen() resolves the bare soname through the
DT_RUNPATH of the caller (libddsc.so), which build/native-linux.sh patches to
$ORIGIN, and libssl/libcrypto come from the distribution.

Modelled on target 5 rather than a plain AfterTargets="Build" copy: routing the
files through @(Content) means Publish honours them too, not just Build. -->
<Target Name="IncludeCycloneDdsWindowsNativeInOutput"
BeforeTargets="GetCopyToOutputDirectoryItems;AssignTargetPaths"
Condition="'$(CycloneDdsStageWinNative)' != 'false' AND
($(RuntimeIdentifier.StartsWith('win')) OR
('$(RuntimeIdentifier)' == '' AND '$(OS)' == 'Windows_NT'))">
<ItemGroup>
<CycloneDdsWinNativeFiles Include="$(CycloneDdsWinNativeDir)dds_security_*.dll" />
<CycloneDdsWinNativeFiles Include="$(CycloneDdsWinNativeDir)libcrypto-*.dll" />
<CycloneDdsWinNativeFiles Include="$(CycloneDdsWinNativeDir)libssl-*.dll" />

<!-- Flat Link: these must land directly in the output dir, not in a subfolder
mirroring their source path. -->
<Content Include="@(CycloneDdsWinNativeFiles)">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Visible>false</Visible>
<Link>%(Filename)%(Extension)</Link>
</Content>
</ItemGroup>
</Target>

<!-- 7. Clean target — removes the generated dir AND the stamp inside it. -->
<Target Name="CycloneDDSClean" AfterTargets="Clean">
<RemoveDir Directories="$(CycloneDdsGeneratedDir)" />
</Target>
Expand Down
Loading