From 8e52b603f0abd40f611c761746f31fb84733cc8e Mon Sep 17 00:00:00 2001 From: Dustin Gooding Date: Sun, 25 Jan 2026 18:59:44 -0700 Subject: [PATCH] :heavy_plus_sign: add simpleble dependency --- .github/workflows/ci.yml | 35 ++++-- .github/workflows/codeql-analysis.yml | 37 ++++-- CMakePresets.json | 5 + Dependencies.cmake | 42 +++++++ docs/getting-started/installation.md | 122 +++++++++++++++++++ lib/transport/CMakeLists.txt | 3 +- test/CMakeLists.txt | 11 ++ test/simpleble_bluez_integration_test.cpp | 137 ++++++++++++++++++++++ 8 files changed, 373 insertions(+), 19 deletions(-) create mode 100644 test/simpleble_bluez_integration_test.cpp diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 145060d7..3a3d3b09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -174,13 +174,22 @@ jobs: rustc --version cargo --version - - name: Install Protocol Buffers (Linux) - if: runner.os == 'Linux' - run: sudo apt-get update && sudo apt-get install -y protobuf-compiler libprotobuf-dev + - name: Setup vcpkg environment (Linux/macOS) + if: runner.os != 'Windows' + run: | + echo "VCPKG_ROOT=$HOME/vcpkg" >> $GITHUB_ENV + + - name: Install vcpkg dependencies (Linux/macOS) + if: runner.os != 'Windows' + run: | + cd $VCPKG_ROOT + ./vcpkg install protobuf + echo "$VCPKG_ROOT/installed/${{ runner.os == 'macOS' && 'arm64-osx' || 'x64-linux' }}/tools/protobuf" >> $GITHUB_PATH + shell: bash - - name: Install Protocol Buffers (macOS) - if: runner.os == 'macOS' - run: brew install protobuf + - name: Install D-Bus (Linux) + if: runner.os == 'Linux' + run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev - name: Install Dependencies (Windows) if: runner.os == 'Windows' @@ -195,9 +204,6 @@ jobs: env: VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}\vcpkg_cache,readwrite - - name: Verify Protobuf installation - run: protoc --version - - name: Determine CMake preset id: preset run: | @@ -232,7 +238,7 @@ jobs: - name: Configure CMake (Windows) if: runner.os == 'Windows' - run: cmake --preset=${{ steps.preset.outputs.preset }} -DCMAKE_TOOLCHAIN_FILE="$env:USERPROFILE\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x64-windows-static-md -D${{ env.PROJECT_NAME }}_ENABLE_IPO=${{matrix.enable_ipo }} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} + run: cmake --preset=${{ steps.preset.outputs.preset }} -D${{ env.PROJECT_NAME }}_ENABLE_IPO=${{matrix.enable_ipo }} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} env: VCPKG_BINARY_SOURCES: clear;files,${{ github.workspace }}\vcpkg_cache,readwrite @@ -240,9 +246,18 @@ jobs: if: runner.os != 'Windows' run: cmake --preset=${{ steps.preset.outputs.preset }} -D${{ env.PROJECT_NAME }}_ENABLE_IPO=${{matrix.enable_ipo }} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} -DGIT_SHA:STRING=${{ github.sha }} + - name: Verify protoc installation + run: | + PROTOC_PATH="${{ runner.os == 'Windows' && format('{0}\vcpkg_installed\x64-windows-static-md\tools\protobuf\protoc.exe', github.workspace) || format('{0}/out/build/{1}/vcpkg_installed/{2}/tools/protobuf/protoc', github.workspace, steps.preset.outputs.preset, runner.os == 'macOS' && 'arm64-osx' || 'x64-linux') }}" + echo "Checking for protoc at: $PROTOC_PATH" + ls -la "$PROTOC_PATH" || find ${{ github.workspace }}/out/build -name protoc -type f + shell: bash + - name: Build run: | cmake --build --preset=${{ steps.preset.outputs.preset }} + env: + PROTOC: ${{ runner.os == 'Windows' && format('{0}\vcpkg_installed\x64-windows-static-md\tools\protobuf\protoc.exe', github.workspace) || format('{0}/out/build/{1}/vcpkg_installed/{2}/tools/protobuf/protoc', github.workspace, steps.preset.outputs.preset, runner.os == 'macOS' && 'arm64-osx' || 'x64-linux') }} - name: Rust Quality Checks if: matrix.packaging_maintainer_mode == 'ON' diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 42e6c0c8..fd5f2d77 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -61,8 +61,12 @@ jobs: echo "Disk space after cleanup:" df -h - - name: Install Protocol Buffers - run: sudo apt-get update && sudo apt-get install -y protobuf-compiler libprotobuf-dev + - name: Install D-Bus + run: sudo apt-get update && sudo apt-get install -y libdbus-1-dev + + - name: Setup vcpkg environment + run: | + echo "VCPKG_ROOT=$HOME/vcpkg" >> $GITHUB_ENV - name: Setup Cache uses: ./.github/actions/setup_cache @@ -86,7 +90,7 @@ jobs: cmake: true ninja: true - vcpkg: false + vcpkg: true ccache: true clangtidy: false @@ -107,11 +111,28 @@ jobs: rustc --version cargo --version + - name: Determine CMake preset + id: preset + run: | + if [ "${{ matrix.compiler }}" = "gcc-14" ]; then + if [ "${{ matrix.build_type }}" = "Debug" ]; then + echo "preset=unixlike-gcc-debug" >> $GITHUB_OUTPUT + else + echo "preset=unixlike-gcc-release" >> $GITHUB_OUTPUT + fi + else + if [ "${{ matrix.build_type }}" = "Debug" ]; then + echo "preset=unixlike-clang-debug" >> $GITHUB_OUTPUT + else + echo "preset=unixlike-clang-release" >> $GITHUB_OUTPUT + fi + fi + shell: bash + # make sure coverage is only enabled for Debug builds, since it sets -O0 to make sure coverage # has meaningful results - name: Configure CMake - run: | - cmake -S . -B ./build -G "${{matrix.generator}}" -DCMAKE_BUILD_TYPE:STRING=${{matrix.build_type}} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} + run: cmake --preset=${{ steps.preset.outputs.preset }} -D${{ env.PROJECT_NAME }}_PACKAGING_MAINTAINER_MODE:BOOL=${{matrix.packaging_maintainer_mode}} -D${{ env.PROJECT_NAME }}_ENABLE_COVERAGE:BOOL=${{ matrix.build_type == 'Debug' }} # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL @@ -125,9 +146,9 @@ jobs: - name: Build - # Execute the build. You can specify a specific target with "--target " - run: | - cmake --build ./build --config ${{matrix.build_type}} + run: cmake --build --preset=${{ steps.preset.outputs.preset }} + env: + PROTOC: ${{ github.workspace }}/out/build/${{ steps.preset.outputs.preset }}/vcpkg_installed/x64-linux/tools/protobuf/protoc - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v4 diff --git a/CMakePresets.json b/CMakePresets.json index 9016ecb1..7fcdb3e5 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -33,6 +33,8 @@ "strategy": "external" }, "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "VCPKG_TARGET_TRIPLET": "x64-windows-static-md", "ENABLE_CPPCHECK_DEFAULT": "FALSE", "ENABLE_CLANG_TIDY_DEFAULT": "FALSE" } @@ -50,6 +52,9 @@ "Darwin" ] }, + "cacheVariables": { + "CMAKE_TOOLCHAIN_FILE": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake" + }, "vendor": { "microsoft.com/VisualStudioRemoteSettings/CMake/1.0": { "sourceDir": "$env{HOME}/.vs/$ms{projectDirName}" diff --git a/Dependencies.cmake b/Dependencies.cmake index e0ea4023..cbe61894 100644 --- a/Dependencies.cmake +++ b/Dependencies.cmake @@ -252,4 +252,46 @@ function(radix_relay_setup_dependencies) endif() endif() + if(NOT TARGET simpleble::simpleble) + cpmaddpackage( + NAME SimpleBLE + GITHUB_REPOSITORY simpleble/simpleble + GIT_TAG v0.6.1 + SOURCE_SUBDIR simpleble + OPTIONS + "SIMPLEBLE_PLAIN OFF" + "SIMPLEBLE_USE_SESSION_DBUS OFF" + "SIMPLEBLE_LOG_LEVEL INFO" + "SIMPLEBLUEZ_LOG_LEVEL FATAL" + "SIMPLEDBUS_LOG_LEVEL FATAL" + SYSTEM YES + ) + if(SimpleBLE_ADDED) + message(STATUS "System SimpleBLE not found, built from source via CPM") + if(TARGET simpleble AND NOT TARGET simpleble::simpleble) + add_library(simpleble::simpleble ALIAS simpleble) + message(STATUS "Created simpleble::simpleble alias for simpleble target") + endif() + # Suppress specific deprecation warnings on MSVC for SimpleBLE + if(TARGET simpleble) + target_compile_options(simpleble PRIVATE + $<$:/wd4996> + ) + target_compile_definitions(simpleble PRIVATE + $<$:_CRT_SECURE_NO_WARNINGS> + ) + endif() + if(TARGET simpleble-c) + target_compile_options(simpleble-c PRIVATE + $<$:/wd4996> + ) + target_compile_definitions(simpleble-c PRIVATE + $<$:_CRT_SECURE_NO_WARNINGS> + ) + endif() + else() + message(STATUS "Found system SimpleBLE ${SimpleBLE_VERSION}") + endif() + endif() + endfunction() diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md index 59f1e7f3..8a2242f5 100644 --- a/docs/getting-started/installation.md +++ b/docs/getting-started/installation.md @@ -32,6 +32,57 @@ curl -LJO "https://github.com/aminya/setup-cpp/releases/latest/download/setup_cp RefreshEnv.cmd ``` +## Dependency Management Strategy + +Radix Relay uses a multi-layered dependency management approach: + +### Build Tools & Compilers (Spack - Local Development) + +For local development with precise version control: + +- **cmake** @3.27 - Build system +- **ninja** @1.12 - Build generator +- **llvm** @19.1 - Clang compiler toolchain +- **gcc** @13.3 - GCC compiler toolchain +- **doxygen** @1.9 - Documentation generator +- **graphviz** @2.42 - Documentation diagrams +- **pkgconf** @2.3 - Package config tool (required for SimpleBLE on Linux) + +**Installation:** See spack.yaml in the project root. Spack automatically installs these when you activate the environment. + +### C++ Library Dependencies (vcpkg) + +Managed via vcpkg.json, automatically installed during CMake configuration: + +- **protobuf** - All platforms (Protocol Buffers) +- **openssl** - Windows only (Linux/macOS use system OpenSSL) +- **sqlcipher** - Windows only (Linux/macOS use system packages) + +**Installation:** Automatically handled during CMake configuration. + +### Additional C++ Libraries (CPM - Automatic) + +Downloaded and built automatically via CMake Package Manager (CPM): + +- **SimpleBLE** (v0.6.1) - Cross-platform BLE library +- **Boost** (asio, beast, system) - Async I/O and HTTP +- **Slint** - GUI framework +- **Replxx** - TUI readline library +- **Catch2** - Testing framework +- **libsignal** (Rust) - Signal Protocol via Corrosion + +**Installation:** No action required - handled by Dependencies.cmake. + +### System Libraries (Platform Package Managers) + +Platform-specific system dependencies: + +- **OpenSSL** - Linux/macOS only (vcpkg on Windows) +- **SQLCipher** - Linux/macOS only (vcpkg on Windows) +- **D-Bus** - Linux only (for SimpleBLE BlueZ backend) + +**Installation:** Use your system package manager (apt/dnf/pacman/brew). + ## Manual Installation ### Required Dependencies @@ -138,6 +189,53 @@ brew install protobuf Protocol Buffers is installed automatically via vcpkg during the build process. +#### 6. BLE Transport Dependencies + +Required for Bluetooth Low Energy transport functionality. + +**System D-Bus (Linux only):** + +SimpleBLE on Linux uses the BlueZ backend which requires D-Bus development headers. On Windows and macOS, SimpleBLE uses native Bluetooth APIs (no D-Bus needed). + +```bash +# Ubuntu/Debian +sudo apt install libdbus-1-dev + +# Fedora/RHEL +sudo dnf install dbus-devel + +# Arch +sudo pacman -S dbus +``` + +**SimpleBLE (all platforms):** + +SimpleBLE is automatically downloaded and built via CPM (CMake Package Manager) during configuration. No manual installation needed. + +- **Version**: v0.6.1 +- **Linux**: Uses BlueZ backend (requires system D-Bus installed above) +- **Windows**: Uses native Windows Bluetooth API +- **macOS**: Uses CoreBluetooth framework + +**vcpkg Setup (for Protobuf):** + +Install vcpkg if you haven't already: + +```bash +git clone https://github.com/microsoft/vcpkg.git /data/git/vcpkg +/data/git/vcpkg/bootstrap-vcpkg.sh # Linux/macOS +# or +git clone https://github.com/microsoft/vcpkg.git %USERPROFILE%\vcpkg +%USERPROFILE%\vcpkg\bootstrap-vcpkg.bat # Windows + +# Set environment variable (add to your shell profile or .envrc) +export VCPKG_ROOT=/data/git/vcpkg # Linux/macOS +# or +set VCPKG_ROOT=%USERPROFILE%\vcpkg # Windows +``` + +The project's CMake presets automatically use the vcpkg toolchain for Protobuf and other dependencies. + ### Optional Dependencies #### Documentation Tools @@ -211,6 +309,8 @@ brew install llvm@19 | C++ Compiler | C++20 support | Clang 19.1.1, GCC 14, or MSVC 2022 | | Rust | stable | Latest stable | | Protocol Buffers | 3.x | Latest | +| pkg-config (Linux BLE) | Any | Latest | +| D-Bus (Linux BLE) | 1.x | Latest | | Python (for docs) | 3.8 | 3.12+ | ## Verification @@ -230,6 +330,10 @@ clang++ --version # 19.1.1+ (Linux/macOS) g++ --version # 14+ (Linux) # or cl # MSVC 2022 (Windows) + +# BLE dependencies (Linux only) +pkg-config --version # Any version +pkg-config --exists dbus-1 && echo "D-Bus found" || echo "D-Bus not found" ``` ## Next Steps @@ -274,4 +378,22 @@ which protoc # Linux/macOS where protoc # Windows ``` +### BLE dependencies missing (Linux) + +If CMake cannot find DBus1 or pkg-config: + +```bash +# Verify pkg-config is installed +pkg-config --version + +# Verify D-Bus development libraries are installed +pkg-config --exists dbus-1 && echo "Found" || echo "Not found" + +# If not found, install them: +sudo apt install pkg-config libdbus-1-dev # Ubuntu/Debian +sudo dnf install pkgconfig dbus-devel # Fedora/RHEL +sudo pacman -S pkgconf dbus # Arch +spack install pkgconf dbus # Spack +``` + If issues persist, see the [GitHub Issues](https://github.com/dustingooding/radix-relay/issues) or join discussions. diff --git a/lib/transport/CMakeLists.txt b/lib/transport/CMakeLists.txt index c6883c65..1ac94e3c 100644 --- a/lib/transport/CMakeLists.txt +++ b/lib/transport/CMakeLists.txt @@ -17,7 +17,8 @@ target_link_system_libraries( Boost::beast Boost::system OpenSSL::SSL - OpenSSL::Crypto) + OpenSSL::Crypto + simpleble::simpleble) target_include_directories(radix_relay_transport ${WARNING_GUARD} PUBLIC $ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index f61aa55a..173f08f7 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -108,3 +108,14 @@ target_link_libraries( radix_relay::radix_relay_options radix_relay::transport Catch2::Catch2WithMain) + +# Integration test for SimpleBLE with BlueZ (not in ALL target, requires BLE hardware or virtual HCI) +# Build with: cmake --build --target simpleble_bluez_integration_test +# Run with: ./test/simpleble_bluez_integration_test +add_executable(simpleble_bluez_integration_test EXCLUDE_FROM_ALL simpleble_bluez_integration_test.cpp) +target_link_libraries( + simpleble_bluez_integration_test + PRIVATE radix_relay::radix_relay_warnings + radix_relay::radix_relay_options + radix_relay::transport + Catch2::Catch2WithMain) diff --git a/test/simpleble_bluez_integration_test.cpp b/test/simpleble_bluez_integration_test.cpp new file mode 100644 index 00000000..50032374 --- /dev/null +++ b/test/simpleble_bluez_integration_test.cpp @@ -0,0 +1,137 @@ +// Integration test for SimpleBLE library with BlueZ stack +// Not included in default test suite - build manually when needed +// +// Usage: +// cmake --build out/build/unixlike-clang-debug --target simpleble_bluez_integration_test +// ./out/build/unixlike-clang-debug/test/simpleble_bluez_integration_test +// +// Linux Setup (BlueZ virtual HCI): +// # Install tools +// sudo apt-get install bluez-tools +// +// # Create virtual HCI device +// sudo modprobe vhci +// sudo hciconfig hci0 up # or hci1, etc. +// +// # Verify device exists +// hciconfig +// +// This test will scan for nearby BLE devices to validate SimpleBLE integration + +#include + +#include + +#include +#include +#include +#include + +namespace { +constexpr int scan_duration_ms = 5000; +constexpr int scan_lifecycle_delay_ms = 500; +constexpr int max_adapter_enum_attempts = 5; +constexpr int adapter_enum_delay_increment_ms = 100; +}// namespace + +TEST_CASE("SimpleBLE can enumerate adapters", "[integration][ble][linux]") +{ + std::vector adapters; + + // Try multiple times with increasing delays + for (int attempt = 0; attempt < max_adapter_enum_attempts && adapters.empty(); ++attempt) { + if (attempt > 0) { + std::cout << "Attempt " << (attempt + 1) << " after " << (attempt * adapter_enum_delay_increment_ms) + << "ms delay\n"; + std::this_thread::sleep_for(std::chrono::milliseconds(attempt * adapter_enum_delay_increment_ms)); + } + + std::cout << "Bluetooth enabled: " << SimpleBLE::Adapter::bluetooth_enabled() << '\n'; + + try { + adapters = SimpleBLE::Adapter::get_adapters(); + std::cout << "Found " << adapters.size() << " adapters\n"; + } catch (const std::exception &e) { + std::cout << "Exception in get_adapters: " << e.what() << '\n'; + throw; + } + } + + INFO("Found " << adapters.size() << " BLE adapter(s)"); + + if (adapters.empty()) { + WARN("No BLE adapters found - test skipped"); + WARN("On Linux, ensure BlueZ is installed and hci device is up"); + return; + } + + REQUIRE(!adapters.empty()); + + for (size_t i = 0; i < adapters.size(); i++) { + std::cout << "Adapter " << i << ": " << adapters[i].identifier() << " [" << adapters[i].address() << "]" << '\n'; + } +} + +TEST_CASE("SimpleBLE can scan for BLE devices", "[integration][ble][linux]") +{ + auto adapters = SimpleBLE::Adapter::get_adapters(); + + if (adapters.empty()) { + WARN("No BLE adapters found - test skipped"); + return; + } + + auto &adapter = adapters[0]; + INFO("Using adapter: " << adapter.identifier()); + + std::vector peripherals; + + adapter.set_callback_on_scan_found([&peripherals](SimpleBLE::Peripheral peripheral) { + std::cout << "Found device: " << peripheral.identifier() << " [" << peripheral.address() << "]" << '\n'; + peripherals.push_back(peripheral); + }); + + adapter.scan_for(scan_duration_ms); + + INFO("Found " << peripherals.size() << " BLE device(s) during scan"); + + if (peripherals.empty()) { + WARN("No BLE devices found during scan - this is not necessarily a failure"); + WARN("Ensure there is a BLE device advertising nearby, or use virtual HCI peripheral"); + } + + // Test passes if scan completes without error, even if no devices found + REQUIRE(true); +} + +TEST_CASE("SimpleBLE adapter can start and stop scanning", "[integration][ble][linux]") +{ + auto adapters = SimpleBLE::Adapter::get_adapters(); + + if (adapters.empty()) { + WARN("No BLE adapters found - test skipped"); + return; + } + + auto &adapter = adapters[0]; + + bool scan_started = false; + bool scan_stopped = false; + + adapter.set_callback_on_scan_start([&scan_started]() { + scan_started = true; + std::cout << "Scan started" << '\n'; + }); + + adapter.set_callback_on_scan_stop([&scan_stopped]() { + scan_stopped = true; + std::cout << "Scan stopped" << '\n'; + }); + + adapter.scan_start(); + std::this_thread::sleep_for(std::chrono::milliseconds(scan_lifecycle_delay_ms)); + adapter.scan_stop(); + + REQUIRE(scan_started); + REQUIRE(scan_stopped); +}