diff --git a/.github/workflows/samples-integration-test.yml b/.github/workflows/samples-integration-test.yml index ebac905e7..9d45d3fd0 100644 --- a/.github/workflows/samples-integration-test.yml +++ b/.github/workflows/samples-integration-test.yml @@ -235,6 +235,74 @@ jobs: exit 1 } + # ── C++ Samples (Windows only — SDK is Windows-only) ─────────────── + cpp-samples: + runs-on: windows-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + with: + clean: true + + - name: Setup MSVC developer environment + uses: ilammy/msvc-dev-cmd@v1 + with: + arch: x64 + + - name: Export VCPKG_ROOT (use runner's preinstalled vcpkg) + shell: pwsh + run: | + if (-not $env:VCPKG_INSTALLATION_ROOT) { + Write-Error "VCPKG_INSTALLATION_ROOT is not set on this runner." + exit 1 + } + "VCPKG_ROOT=$env:VCPKG_INSTALLATION_ROOT" | Out-File -FilePath $env:GITHUB_ENV -Append -Encoding utf8 + Write-Host "VCPKG_ROOT -> $env:VCPKG_INSTALLATION_ROOT" + + - name: Verify build tools + shell: pwsh + run: | + Write-Host "cmake: $((Get-Command cmake).Source)" + cmake --version | Select-Object -First 1 + Write-Host "ninja: $((Get-Command ninja).Source)" + ninja --version + Write-Host "cl.exe: $((Get-Command cl.exe).Source)" + Write-Host "nuget: $((Get-Command nuget -ErrorAction SilentlyContinue).Source)" + + - name: Build C++ samples + shell: pwsh + run: | + $failed = @() + $samples = Get-ChildItem samples/cpp/*/CMakeLists.txt -ErrorAction SilentlyContinue + foreach ($sample in $samples) { + $dir = $sample.Directory + $name = $dir.Name + Write-Host "`n=== Building: $name ===" + Push-Location $dir.FullName + try { + cmake --preset x64-debug 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host "CONFIGURE FAILED: $name" + $failed += $name + continue + } + cmake --build --preset x64-debug 2>&1 + if ($LASTEXITCODE -ne 0) { + Write-Host "BUILD FAILED: $name" + $failed += $name + } else { + Write-Host "BUILD PASSED: $name" + } + } finally { + Pop-Location + } + } + if ($failed.Count -gt 0) { + Write-Error "Failed builds: $($failed -join ', ')" + exit 1 + } + # ── Rust Samples ──────────────────────────────────────────────────── rust-samples: runs-on: ${{ matrix.platform }}-latest diff --git a/samples/README.md b/samples/README.md index ebd1afb8c..6e4cd1be7 100644 --- a/samples/README.md +++ b/samples/README.md @@ -9,7 +9,7 @@ Explore complete working examples that demonstrate how to use Foundry Local — | Language | Samples | Description | |----------|---------|-------------| | [**C#**](cs/) | 13 | .NET SDK samples including native chat, embeddings, audio transcription, tool calling, model management, web server, tutorials, and WinML EP verification. Uses WinML on Windows for hardware acceleration. | +| [**C++**](cpp/) | 3 | Native C++17 SDK samples: SDK quickstart, live audio transcription, and a Responses API vision sample. | | [**JavaScript**](js/) | 15 | Node.js SDK samples including native chat, embeddings, audio transcription, Electron desktop app, Copilot SDK integration, LangChain, tool calling, web server, tutorials, and WinML EP verification. | | [**Python**](python/) | 14 | Python samples using the OpenAI-compatible API, including chat, embeddings, audio transcription, LangChain integration, tool calling, web server, Responses API, tutorials, and WinML EP verification. | | [**Rust**](rust/) | 11 | Rust SDK samples including native chat, embeddings, audio transcription, tool calling, web server, tutorials, and WinML EP verification. | -| [**C++**](cpp/) | 1 | C++ sample for live audio transcription. | diff --git a/samples/cpp/sdk-quickstart/CMakeLists.txt b/samples/cpp/sdk-quickstart/CMakeLists.txt new file mode 100644 index 000000000..ce364b917 --- /dev/null +++ b/samples/cpp/sdk-quickstart/CMakeLists.txt @@ -0,0 +1,36 @@ +cmake_minimum_required(VERSION 3.20) +project(sdk-quickstart LANGUAGES CXX) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +# Match the SDK's vcpkg triplet +set(VCPKG_TARGET_TRIPLET "x64-windows-static-md" CACHE STRING "") + +# Try find_package first (prebuilt SDK zip), fall back to add_subdirectory (repo) +find_package(FoundryLocal QUIET) +if(NOT FoundryLocal_FOUND) + message(STATUS "Prebuilt FoundryLocal not found — building SDK from source") + set(FOUNDRY_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../sdk/cpp") + set(BUILD_TESTING OFF CACHE BOOL "" FORCE) + add_subdirectory(${FOUNDRY_SDK_DIR} ${CMAKE_CURRENT_BINARY_DIR}/CppSdk) + set(FL_SDK_TARGET CppSdk) +else() + message(STATUS "Using prebuilt FoundryLocal SDK") + set(FL_SDK_TARGET FoundryLocal::FoundryLocal) +endif() + +add_executable(CppSdkSample main.cpp) + +target_link_libraries(CppSdkSample PRIVATE ${FL_SDK_TARGET}) + +# Copy Foundry Local + ONNX Runtime DLLs next to the executable (Windows only). +# The fl_copy_runtime_dlls helper from sdk/cpp/CMakeLists.txt expands FL_*_DLL_DIR +# in the caller's scope; re-derive them here so the post-build commands have real paths. +if(WIN32 AND COMMAND fl_copy_runtime_dlls) + set(FL_NATIVE_DEPS_DIR "${CMAKE_CURRENT_BINARY_DIR}/CppSdk/_native_deps") + set(FL_CORE_DLL_DIR "${FL_NATIVE_DEPS_DIR}/Microsoft.AI.Foundry.Local.Core.${FL_CORE_VERSION}/runtimes/win-x64/native") + set(FL_ORT_DLL_DIR "${FL_NATIVE_DEPS_DIR}/Microsoft.ML.OnnxRuntime.Foundry.${FL_ORT_VERSION}/runtimes/win-x64/native") + set(FL_ORTGENAI_DLL_DIR "${FL_NATIVE_DEPS_DIR}/Microsoft.ML.OnnxRuntimeGenAI.Foundry.${FL_ORTGENAI_VERSION}/runtimes/win-x64/native") + fl_copy_runtime_dlls(CppSdkSample) +endif() diff --git a/sdk/cpp/sample/web-server-responses-vision/CMakePresets.json b/samples/cpp/sdk-quickstart/CMakePresets.json similarity index 90% rename from sdk/cpp/sample/web-server-responses-vision/CMakePresets.json rename to samples/cpp/sdk-quickstart/CMakePresets.json index 1ec183fe2..deadd17ac 100644 --- a/sdk/cpp/sample/web-server-responses-vision/CMakePresets.json +++ b/samples/cpp/sdk-quickstart/CMakePresets.json @@ -13,7 +13,7 @@ "CMAKE_CXX_COMPILER": "cl.exe", "CMAKE_BUILD_TYPE": "Debug", "VCPKG_TARGET_TRIPLET": "x64-windows-static-md", - "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/../../triplets" + "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/../../../sdk/cpp/triplets" }, "condition": { "type": "equals", diff --git a/sdk/cpp/sample/main.cpp b/samples/cpp/sdk-quickstart/main.cpp similarity index 100% rename from sdk/cpp/sample/main.cpp rename to samples/cpp/sdk-quickstart/main.cpp diff --git a/sdk/cpp/sample/web-server-responses-vision/vcpkg-configuration.json b/samples/cpp/sdk-quickstart/vcpkg-configuration.json similarity index 100% rename from sdk/cpp/sample/web-server-responses-vision/vcpkg-configuration.json rename to samples/cpp/sdk-quickstart/vcpkg-configuration.json diff --git a/samples/cpp/sdk-quickstart/vcpkg.json b/samples/cpp/sdk-quickstart/vcpkg.json new file mode 100644 index 000000000..d668bb4d5 --- /dev/null +++ b/samples/cpp/sdk-quickstart/vcpkg.json @@ -0,0 +1,8 @@ +{ + "name": "sdk-quickstart", + "version-string": "0.1.0", + "dependencies": [ + "nlohmann-json", + "ms-gsl" + ] +} diff --git a/sdk/cpp/sample/web-server-responses-vision/CMakeLists.txt b/samples/cpp/web-server-responses-vision/CMakeLists.txt similarity index 52% rename from sdk/cpp/sample/web-server-responses-vision/CMakeLists.txt rename to samples/cpp/web-server-responses-vision/CMakeLists.txt index b6dbcbd04..1120255bd 100644 --- a/sdk/cpp/sample/web-server-responses-vision/CMakeLists.txt +++ b/samples/cpp/web-server-responses-vision/CMakeLists.txt @@ -11,7 +11,7 @@ set(VCPKG_TARGET_TRIPLET "x64-windows-static-md" CACHE STRING "") find_package(FoundryLocal QUIET) if(NOT FoundryLocal_FOUND) message(STATUS "Prebuilt FoundryLocal not found — building SDK from source") - set(FOUNDRY_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../..") + set(FOUNDRY_SDK_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../../sdk/cpp") set(BUILD_TESTING OFF CACHE BOOL "" FORCE) add_subdirectory(${FOUNDRY_SDK_DIR} ${CMAKE_CURRENT_BINARY_DIR}/CppSdk) set(FL_SDK_TARGET CppSdk) @@ -29,5 +29,13 @@ target_link_libraries(web-server-responses-vision PRIVATE CURL::libcurl ) -# Copy runtime DLLs next to the executable -fl_copy_runtime_dlls(web-server-responses-vision) +# Copy runtime DLLs next to the executable. +# fl_copy_runtime_dlls expands FL_*_DLL_DIR in the caller's scope, so re-derive +# them here (the SDK sets them as non-CACHE vars, invisible to sub-projects). +if(WIN32 AND COMMAND fl_copy_runtime_dlls) + set(FL_NATIVE_DEPS_DIR "${CMAKE_CURRENT_BINARY_DIR}/CppSdk/_native_deps") + set(FL_CORE_DLL_DIR "${FL_NATIVE_DEPS_DIR}/Microsoft.AI.Foundry.Local.Core.${FL_CORE_VERSION}/runtimes/win-x64/native") + set(FL_ORT_DLL_DIR "${FL_NATIVE_DEPS_DIR}/Microsoft.ML.OnnxRuntime.Foundry.${FL_ORT_VERSION}/runtimes/win-x64/native") + set(FL_ORTGENAI_DLL_DIR "${FL_NATIVE_DEPS_DIR}/Microsoft.ML.OnnxRuntimeGenAI.Foundry.${FL_ORTGENAI_VERSION}/runtimes/win-x64/native") + fl_copy_runtime_dlls(web-server-responses-vision) +endif() diff --git a/samples/cpp/web-server-responses-vision/CMakePresets.json b/samples/cpp/web-server-responses-vision/CMakePresets.json new file mode 100644 index 000000000..deadd17ac --- /dev/null +++ b/samples/cpp/web-server-responses-vision/CMakePresets.json @@ -0,0 +1,31 @@ +{ + "version": 6, + "configurePresets": [ + { + "name": "x64-debug", + "displayName": "MSVC x64 Debug", + "inherits": [], + "generator": "Ninja", + "binaryDir": "${sourceDir}/out/build/${presetName}", + "toolchainFile": "$env{VCPKG_ROOT}/scripts/buildsystems/vcpkg.cmake", + "cacheVariables": { + "CMAKE_C_COMPILER": "cl.exe", + "CMAKE_CXX_COMPILER": "cl.exe", + "CMAKE_BUILD_TYPE": "Debug", + "VCPKG_TARGET_TRIPLET": "x64-windows-static-md", + "VCPKG_OVERLAY_TRIPLETS": "${sourceDir}/../../../sdk/cpp/triplets" + }, + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + } + } + ], + "buildPresets": [ + { + "name": "x64-debug", + "configurePreset": "x64-debug" + } + ] +} diff --git a/sdk/cpp/sample/web-server-responses-vision/README.md b/samples/cpp/web-server-responses-vision/README.md similarity index 98% rename from sdk/cpp/sample/web-server-responses-vision/README.md rename to samples/cpp/web-server-responses-vision/README.md index cf9f343d8..0ba01f5fb 100644 --- a/sdk/cpp/sample/web-server-responses-vision/README.md +++ b/samples/cpp/web-server-responses-vision/README.md @@ -28,7 +28,7 @@ The sample downloads the specified model the first time it runs (skips if alread Open an **x64 Native Tools Command Prompt for VS 2022** (or run `vcvars64.bat`), then navigate to the sample directory: ```bash -cd sdk/cpp/sample/web-server-responses-vision +cd samples/cpp/web-server-responses-vision ``` ### Configure (CMake + vcpkg) diff --git a/sdk/cpp/sample/web-server-responses-vision/main.cpp b/samples/cpp/web-server-responses-vision/main.cpp similarity index 100% rename from sdk/cpp/sample/web-server-responses-vision/main.cpp rename to samples/cpp/web-server-responses-vision/main.cpp diff --git a/sdk/cpp/sample/web-server-responses-vision/stb_impl.cpp b/samples/cpp/web-server-responses-vision/stb_impl.cpp similarity index 100% rename from sdk/cpp/sample/web-server-responses-vision/stb_impl.cpp rename to samples/cpp/web-server-responses-vision/stb_impl.cpp diff --git a/sdk/cpp/sample/web-server-responses-vision/test_image.jpg b/samples/cpp/web-server-responses-vision/test_image.jpg similarity index 100% rename from sdk/cpp/sample/web-server-responses-vision/test_image.jpg rename to samples/cpp/web-server-responses-vision/test_image.jpg diff --git a/samples/cpp/web-server-responses-vision/vcpkg-configuration.json b/samples/cpp/web-server-responses-vision/vcpkg-configuration.json new file mode 100644 index 000000000..a5253fb7a --- /dev/null +++ b/samples/cpp/web-server-responses-vision/vcpkg-configuration.json @@ -0,0 +1,6 @@ +{ + "default-registry": { + "kind": "builtin", + "baseline": "a9f0cd0345fb29cd227d802f1fd1917c28f8e5a3" + } +} diff --git a/sdk/cpp/sample/web-server-responses-vision/vcpkg.json b/samples/cpp/web-server-responses-vision/vcpkg.json similarity index 100% rename from sdk/cpp/sample/web-server-responses-vision/vcpkg.json rename to samples/cpp/web-server-responses-vision/vcpkg.json diff --git a/sdk/cpp/CMakeLists.txt b/sdk/cpp/CMakeLists.txt index 6b7977a42..bddc82fdf 100644 --- a/sdk/cpp/CMakeLists.txt +++ b/sdk/cpp/CMakeLists.txt @@ -137,41 +137,6 @@ endfunction() endif() # WIN32 -# ----------------------------- -# Sample executable -# ----------------------------- -add_executable(CppSdkSample - sample/main.cpp -) - -target_link_libraries(CppSdkSample PRIVATE CppSdk) - -# Copy DLLs for the SDK sample -fl_copy_runtime_dlls(CppSdkSample) - -# ----------------------------- -# Vision sample (Responses API) — built if present -# ----------------------------- -set(VISION_SAMPLE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/sample/web-server-responses-vision") - -if(EXISTS "${VISION_SAMPLE_DIR}/main.cpp") - find_package(CURL QUIET) - if(CURL_FOUND) - add_executable(WebServerResponsesVision - ${VISION_SAMPLE_DIR}/main.cpp - ${VISION_SAMPLE_DIR}/stb_impl.cpp - ) - - target_link_libraries(WebServerResponsesVision PRIVATE CppSdk CURL::libcurl) - fl_copy_runtime_dlls(WebServerResponsesVision) - message(STATUS "Vision sample: enabled") - else() - message(STATUS "Vision sample: disabled (curl not found — add 'curl' to vcpkg.json)") - endif() -else() - message(STATUS "Vision sample: not found") -endif() - # ----------------------------- # Unit tests # ----------------------------- @@ -239,10 +204,6 @@ if (BUILD_TESTING) ) endif() -# Make Visual Studio start/debug this target by default -set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - PROPERTY VS_STARTUP_PROJECT CppSdkSample) - # ----------------------------- # Install — produces the redistributable SDK zip layout: # lib/CppSdk.lib @@ -269,7 +230,8 @@ endif() # Install CMake config file install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/cmake/FoundryLocalConfig.cmake DESTINATION cmake) -# Install vision sample into the zip +# Install vision sample into the zip (sample lives under samples/cpp/) +set(VISION_SAMPLE_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../samples/cpp/web-server-responses-vision") if(EXISTS "${VISION_SAMPLE_DIR}/main.cpp") install(DIRECTORY "${VISION_SAMPLE_DIR}/" DESTINATION sample/web-server-responses-vision diff --git a/sdk/cpp/README.md b/sdk/cpp/README.md index 8d96083de..bf5b85dc2 100644 --- a/sdk/cpp/README.md +++ b/sdk/cpp/README.md @@ -122,26 +122,18 @@ int main() { ### Vision Sample (Responses API) -A complete vision sample is included at `sample/web-server-responses-vision/`. It demonstrates image understanding using the Responses API with streaming via cURL. +A complete vision sample lives at [`samples/cpp/web-server-responses-vision/`](../../samples/cpp/web-server-responses-vision/). It demonstrates image understanding using the Responses API with streaming via cURL. -Build and run from the SDK root: +Build and run from the sample directory: ```bash -cmake --preset x64-debug -cmake --build --preset x64-debug --target WebServerResponsesVision -.\out\build\x64-debug\WebServerResponsesVision.exe qwen3.5-0.8b -``` - -Or build standalone from the sample directory: - -```bash -cd sample/web-server-responses-vision +cd samples/cpp/web-server-responses-vision cmake --preset x64-debug cmake --build --preset x64-debug .\out\build\x64-debug\web-server-responses-vision.exe qwen3.5-0.8b ``` -See [sample/web-server-responses-vision/README.md](sample/web-server-responses-vision/README.md) for full details. +See [samples/cpp/web-server-responses-vision/README.md](../../samples/cpp/web-server-responses-vision/README.md) for full details. ## Usage @@ -483,16 +475,15 @@ sdk/cpp/ │ ├── audio_client.h # Audio transcription client │ └── tool_types.h # Tool calling types ├── src/ # Private implementation -├── sample/ -│ ├── main.cpp # Sample application -│ └── web-server-responses-vision/ # Vision sample (Responses API) -├── test/ # Unit & E2E tests (GTest) +├── test/ # Unit & E2E tests (GTest) ├── CMakeLists.txt ├── CMakePresets.json -├── vcpkg.json # vcpkg dependencies +├── vcpkg.json # vcpkg dependencies └── vcpkg-configuration.json ``` +> Samples live under the repo's [`samples/cpp/`](../../samples/cpp/) directory — see [`sdk-quickstart`](../../samples/cpp/sdk-quickstart/) and [`web-server-responses-vision`](../../samples/cpp/web-server-responses-vision/). + ## Troubleshooting | Error | Cause | Fix |