diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4fd4047..e904351 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -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: | @@ -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: | @@ -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 diff --git a/README.md b/README.md index 4ac0a8f..8e0ae2b 100644 --- a/README.md +++ b/README.md @@ -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 `` section. ### Working with Source Code @@ -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 diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index c300f30..40730e4 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -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 + +================================================================================ diff --git a/build/native-linux.sh b/build/native-linux.sh index b86fac3..bd91e77 100755 --- a/build/native-linux.sh +++ b/build/native-linux.sh @@ -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 @@ -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 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/" diff --git a/build/native-win.ps1 b/build/native-win.ps1 index 7d3e6ba..0de1d5e 100644 --- a/build/native-win.ps1 +++ b/build/native-win.ps1 @@ -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 @@ -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) { @@ -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 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/ @@ -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 diff --git a/build/pack.ps1 b/build/pack.ps1 index ba3f2af..3c6b0b9 100644 --- a/build/pack.ps1 +++ b/build/pack.ps1 @@ -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 @@ -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 diff --git a/build/targets/CycloneDDS.targets b/build/targets/CycloneDDS.targets index abc605f..61cba4c 100644 --- a/build/targets/CycloneDDS.targets +++ b/build/targets/CycloneDDS.targets @@ -63,6 +63,11 @@ The in-repo shim sets it for the bindings' own projects. --> false true + + + $(MSBuildThisFileDirectory)..\runtimes\win-x64\native\ @@ -229,7 +234,50 @@ - + + + + + + + + + + PreserveNewest + false + %(Filename)%(Extension) + + + + + diff --git a/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj b/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj index 7c8cce5..a5cb318 100644 --- a/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj +++ b/src/CycloneDDS.Runtime/CycloneDDS.Runtime.csproj @@ -24,6 +24,36 @@ + + + + + + + + + + - + diff --git a/tools/CycloneDDS.CodeGen/CycloneDDS.targets b/tools/CycloneDDS.CodeGen/CycloneDDS.targets index 978390c..7f89bd9 100644 --- a/tools/CycloneDDS.CodeGen/CycloneDDS.targets +++ b/tools/CycloneDDS.CodeGen/CycloneDDS.targets @@ -26,6 +26,10 @@ $(MSBuildThisFileDirectory)CycloneDDS.CodeGen.csproj $(MSBuildThisFileDirectory)bin\Release\net8.0\CycloneDDS.CodeGen.dll $(MSBuildThisFileDirectory)bin\Debug\net8.0\CycloneDDS.CodeGen.dll + + + $(MSBuildThisFileDirectory)..\..\artifacts\native\win-x64\