Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
4689b51
save work
edgchen1 Feb 10, 2026
8f66c20
save work
edgchen1 Feb 11, 2026
88ee8a7
save work
edgchen1 Feb 11, 2026
1fc9fb2
revert tools/ci_build/github/azure-pipelines/qnn-ep-nuget-packaging-p…
edgchen1 Feb 11, 2026
616efc3
update build-win-arm64x-steps.yml to get all other build params from …
edgchen1 Feb 11, 2026
56890f2
update qnn-ep-win for updated template
edgchen1 Feb 11, 2026
a707e93
save work
edgchen1 Feb 11, 2026
c729bb7
fix quoting
edgchen1 Feb 11, 2026
3f00648
remove BuildConfig parameter from arm64x build template
edgchen1 Feb 11, 2026
8194aa5
update diplayname
edgchen1 Feb 11, 2026
2b354c3
add set-variable.yml
edgchen1 Feb 11, 2026
1ab1e29
Merge remote-tracking branch 'origin/main' into edgchen1/arm64x_binaries
edgchen1 Feb 11, 2026
fc1d429
build arm64x in py-win-cpu.yml
edgchen1 Feb 11, 2026
025193f
rename parameters, make quoting consistent
edgchen1 Feb 11, 2026
99f3d46
make variable setting message consistent with other steps
edgchen1 Feb 11, 2026
b000c8c
revert changes to py-win-cpu.yml
edgchen1 Feb 11, 2026
30807cd
Merge remote-tracking branch 'origin/main' into edgchen1/arm64x_binaries
edgchen1 Feb 12, 2026
602c996
Add samples/cxx example. Thanks AI!
edgchen1 Feb 19, 2026
bd0ef50
update readme to not specify location for archive
edgchen1 Feb 19, 2026
4c6f7ba
add binary archive tests to package test pipeline
edgchen1 Feb 20, 2026
a0fbcda
try to fix stage name by using underscore instead of hyphen
edgchen1 Feb 20, 2026
ed28bb1
remove indentation to avoid newlines
edgchen1 Feb 20, 2026
bfd5d47
use arm64 windows pool
edgchen1 Feb 20, 2026
8d679f6
try another pool
edgchen1 Feb 20, 2026
3bf406d
use separate build directory for win-arm64x x64 build
edgchen1 Feb 20, 2026
6e356cc
change win-arm64x test to build/run arm64ec instead of x64
edgchen1 Feb 20, 2026
bc15455
TEST - see if win-arm64 fails for arm64ec build
edgchen1 Feb 20, 2026
45cda25
Revert "TEST - see if win-arm64 fails for arm64ec build"
edgchen1 Feb 20, 2026
2106825
use job variables, reduce platform-specific stuff
edgchen1 Feb 20, 2026
002f3d6
make previousStageName param optional, make agentPool param an object…
edgchen1 Feb 20, 2026
b44f8be
add agent setup steps parameter and use it to set up build tools on m…
edgchen1 Feb 20, 2026
a6adfda
fix path to template
edgchen1 Feb 20, 2026
1801cd8
remove extra indentation
edgchen1 Feb 20, 2026
d7bf27d
Merge remote-tracking branch 'origin/main' into edgchen1/arm64x_binaries
edgchen1 Feb 20, 2026
b8e9904
fix lint issues
edgchen1 Feb 20, 2026
e7290e4
update generate_model.py imports
edgchen1 Feb 20, 2026
4ae443b
Merge remote-tracking branch 'origin/main' into edgchen1/arm64x_binaries
edgchen1 Feb 25, 2026
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
50 changes: 50 additions & 0 deletions samples/cxx/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

cmake_minimum_required(VERSION 3.28)

project(onnxruntime_sample CXX)

set(CMAKE_CXX_STANDARD 20)

foreach(VAR IN ITEMS ORT_LIBRARY_DIR ORT_HEADER_DIR)
if (NOT DEFINED ${VAR})
message(FATAL_ERROR "Required variable ${VAR} is not set. "
"Set ORT_LIBRARY_DIR to the ONNX Runtime lib directory and "
"ORT_HEADER_DIR to the ONNX Runtime include directory.")
endif()
endforeach()

# Resolve to absolute paths
get_filename_component(ORT_LIBRARY_DIR "${ORT_LIBRARY_DIR}" ABSOLUTE)
get_filename_component(ORT_HEADER_DIR "${ORT_HEADER_DIR}" ABSOLUTE)

#
# onnxruntime_sample_program
#
block()
add_executable(onnxruntime_sample_program)

target_sources(onnxruntime_sample_program PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/main.cc)

target_include_directories(onnxruntime_sample_program PRIVATE ${ORT_HEADER_DIR})

target_link_directories(onnxruntime_sample_program PRIVATE ${ORT_LIBRARY_DIR})
target_link_libraries(onnxruntime_sample_program PRIVATE onnxruntime)

# Copy ONNX Runtime shared libraries next to the executable.
# Collect shared library files from the ORT library directory based on platform.
if (WIN32)
file(GLOB ORT_SHARED_LIBS "${ORT_LIBRARY_DIR}/*.dll")
elseif (APPLE)
file(GLOB ORT_SHARED_LIBS "${ORT_LIBRARY_DIR}/*.dylib")
else()
file(GLOB ORT_SHARED_LIBS "${ORT_LIBRARY_DIR}/*.so" "${ORT_LIBRARY_DIR}/*.so.*")
endif()

add_custom_command(TARGET onnxruntime_sample_program POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
${ORT_SHARED_LIBS}
$<TARGET_FILE_DIR:onnxruntime_sample_program>
)
endblock()
92 changes: 92 additions & 0 deletions samples/cxx/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# ONNX Runtime C++ Sample

A minimal C++ program demonstrating basic ONNX Runtime inference. It loads an ONNX model that adds two float tensors (`C = A + B`), runs inference, and verifies the result.

## Prerequisites

- CMake 3.28 or later
- C++20 compatible compiler (e.g., Visual Studio 2022)
- An ONNX Runtime release package (download from [GitHub releases](https://github.com/microsoft/onnxruntime/releases))
- For model generation:
- Python with the `onnx` package

## Directory Structure

```
samples/cxx/
├── CMakeLists.txt # Build configuration
├── main.cc # Sample program source
├── add_model.onnx # ONNX model (C = A + B)
├── generate_model.py # Script to generate the ONNX model
└── README.md # This file
```

## Steps

### 1. Extract the ONNX Runtime package

Download and extract an ONNX Runtime release archive. For example:

```
tar -xf onnxruntime-win-x64-1.25.0.zip
```

This creates a directory like `onnxruntime-win-x64-1.25.0/` containing `include/` and `lib/` subdirectories.

### 2. [Optional] Generate the ONNX model

```
cd samples/cxx
pip install onnx
python generate_model.py
```

This creates `add_model.onnx` in the current directory.

### 3. Configure and build

From the `samples/cxx` directory:

**Windows:**
```
cmake -S . -B build ^
-DORT_HEADER_DIR:PATH=path\to\onnxruntime-win-x64-1.25.0\include ^
-DORT_LIBRARY_DIR:PATH=path\to\onnxruntime-win-x64-1.25.0\lib
cmake --build build --config Release
```

**Linux / macOS:**
```
cmake -S . -B build \
-DORT_HEADER_DIR:PATH=path/to/onnxruntime-linux-x64-1.25.0/include \
-DORT_LIBRARY_DIR:PATH=path/to/onnxruntime-linux-x64-1.25.0/lib
cmake --build build --config Release
```

Adjust the paths to match your extracted package name and location.

The build automatically copies the ONNX Runtime shared libraries next to the executable.

#### CMake Variables

| Variable | Description |
|---|---|
| `ORT_HEADER_DIR` | Path to the ONNX Runtime `include` directory |
| `ORT_LIBRARY_DIR` | Path to the ONNX Runtime `lib` directory |

### 4. Run

**Windows:**
```
build\Release\onnxruntime_sample_program.exe
```

**Linux / macOS:**
```
./build/onnxruntime_sample_program
```

You can also pass a model path as an argument:
```
onnxruntime_sample_program path/to/add_model.onnx
```
Binary file added samples/cxx/add_model.onnx
Binary file not shown.
42 changes: 42 additions & 0 deletions samples/cxx/generate_model.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

"""Generate a simple ONNX model that computes C = A + B.

Inputs:
A : float tensor of shape [1, 3]
B : float tensor of shape [1, 3]

Output:
C : float tensor of shape [1, 3]

Usage:
pip install onnx
python generate_model.py
"""

from onnx import TensorProto, helper, save_model
from onnx.checker import check_model


def main():
# Define inputs and output
a = helper.make_tensor_value_info("A", TensorProto.FLOAT, [1, 3])
b = helper.make_tensor_value_info("B", TensorProto.FLOAT, [1, 3])
c = helper.make_tensor_value_info("C", TensorProto.FLOAT, [1, 3])

# Create the Add node
add_node = helper.make_node("Add", inputs=["A", "B"], outputs=["C"])

# Build the graph and model
graph = helper.make_graph([add_node], "add_graph", [a, b], [c])
model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 13)])

# Validate and save
check_model(model)
save_model(model, "add_model.onnx")
print("Saved add_model.onnx")


if __name__ == "__main__":
main()
170 changes: 170 additions & 0 deletions samples/cxx/main.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

// Sample program demonstrating basic ONNX Runtime C++ API usage.
// Loads a simple ONNX model (C = A + B), runs inference, and prints the result.
//
// Generate the model first: python generate_model.py

#include <array>
#include <cstdint>
#include <cstdlib>
#include <filesystem>

Check warning on line 12 in samples/cxx/main.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 <filesystem> is an unapproved C++17 header. [build/c++17] [5] Raw Output: samples/cxx/main.cc:12: <filesystem> is an unapproved C++17 header. [build/c++17] [5]
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

#include "onnxruntime_cxx_api.h"

Check warning on line 18 in samples/cxx/main.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Include the directory when naming header files [build/include_subdir] [4] Raw Output: samples/cxx/main.cc:18: Include the directory when naming header files [build/include_subdir] [4]

// Throw std::runtime_error if `condition` is false. Includes file and line info.
#define THROW_IF_NOT(condition) \
do { \
if (!(condition)) { \
throw std::runtime_error(std::string(__FILE__) + ":" + \
std::to_string(__LINE__) + ": " + \
"check failed: " #condition); \
} \
} while (0)

int main(int argc, char* argv[]) {
try {
// -----------------------------------------------------------------------
// 1. Initialize the ONNX Runtime environment
// -----------------------------------------------------------------------
Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "onnxruntime_sample");
std::cout << "ONNX Runtime version: " << Ort::GetVersionString() << "\n\n";

// -----------------------------------------------------------------------
// 2. Create session options (could add execution providers here)
// -----------------------------------------------------------------------
Ort::SessionOptions session_options;
session_options.SetIntraOpNumThreads(1);
session_options.SetGraphOptimizationLevel(GraphOptimizationLevel::ORT_ENABLE_BASIC);

// -----------------------------------------------------------------------
// 3. Load the ONNX model from a file
// Generate with: python generate_model.py
// -----------------------------------------------------------------------
const std::filesystem::path model_path = (argc > 1) ? argv[1] : "add_model.onnx";
std::cout << "Loading model: " << model_path.string() << "\n";

Ort::Session session(env, model_path.native().c_str(), session_options);

// -----------------------------------------------------------------------
// 4. Query model metadata: input/output names and shapes
// -----------------------------------------------------------------------
Ort::AllocatorWithDefaultOptions allocator;

const size_t num_inputs = session.GetInputCount();
const size_t num_outputs = session.GetOutputCount();
std::cout << "Model inputs: " << num_inputs << "\n";
std::cout << "Model outputs: " << num_outputs << "\n";

// Collect input/output names
std::vector<std::string> input_names;
std::vector<std::string> output_names;

for (size_t i = 0; i < num_inputs; ++i) {
auto name = session.GetInputNameAllocated(i, allocator);
std::cout << " Input " << i << ": " << name.get() << "\n";
input_names.emplace_back(name.get());
}
for (size_t i = 0; i < num_outputs; ++i) {
auto name = session.GetOutputNameAllocated(i, allocator);
std::cout << " Output " << i << ": " << name.get() << "\n";
output_names.emplace_back(name.get());
}
std::cout << "\n";

// -----------------------------------------------------------------------
// 5. Prepare input tensors
// -----------------------------------------------------------------------
// Our model expects two float tensors of shape [1, 3].
constexpr int64_t batch_size = 1;
constexpr int64_t num_elements = 3;
const std::array<int64_t, 2> input_shape = {batch_size, num_elements};

std::array<float, num_elements> input_a = {1.0f, 2.0f, 3.0f};
std::array<float, num_elements> input_b = {4.0f, 5.0f, 6.0f};

auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault);

auto tensor_a = Ort::Value::CreateTensor<float>(
memory_info, input_a.data(), input_a.size(),
input_shape.data(), input_shape.size());

auto tensor_b = Ort::Value::CreateTensor<float>(
memory_info, input_b.data(), input_b.size(),
input_shape.data(), input_shape.size());

THROW_IF_NOT(tensor_a.IsTensor());
THROW_IF_NOT(tensor_b.IsTensor());

// The Run() API expects arrays of C strings for input/output names.
std::vector<const char*> input_name_ptrs;
std::vector<const char*> output_name_ptrs;
for (const auto& n : input_names) input_name_ptrs.push_back(n.c_str());
for (const auto& n : output_names) output_name_ptrs.push_back(n.c_str());

std::array<Ort::Value, 2> input_tensors{std::move(tensor_a), std::move(tensor_b)};

Check warning on line 110 in samples/cxx/main.cc

View workflow job for this annotation

GitHub Actions / Optional Lint C++

[cpplint] reported by reviewdog 🐶 Add #include <utility> for move [build/include_what_you_use] [4] Raw Output: samples/cxx/main.cc:110: Add #include <utility> for move [build/include_what_you_use] [4]

// -----------------------------------------------------------------------
// 6. Run inference
// -----------------------------------------------------------------------
std::cout << "Running inference...\n";

Ort::RunOptions run_options;
auto output_tensors = session.Run(
run_options,
input_name_ptrs.data(), input_tensors.data(), input_tensors.size(),
output_name_ptrs.data(), output_name_ptrs.size());

// -----------------------------------------------------------------------
// 7. Process output
// -----------------------------------------------------------------------
THROW_IF_NOT(!output_tensors.empty() && output_tensors[0].IsTensor());

const float* output_data = output_tensors[0].GetTensorData<float>();
auto type_info = output_tensors[0].GetTensorTypeAndShapeInfo();
size_t output_count = type_info.GetElementCount();

std::cout << "\nInputs:\n";
std::cout << " A = [";
for (size_t i = 0; i < input_a.size(); ++i) {
std::cout << (i ? ", " : "") << input_a[i];
}
std::cout << "]\n";

std::cout << " B = [";
for (size_t i = 0; i < input_b.size(); ++i) {
std::cout << (i ? ", " : "") << input_b[i];
}
std::cout << "]\n";

std::cout << "\nOutput (A + B):\n";
std::cout << " C = [";
for (size_t i = 0; i < output_count; ++i) {
std::cout << (i ? ", " : "") << output_data[i];
}
std::cout << "]\n";

// Verify correctness
bool correct = true;
for (size_t i = 0; i < num_elements; ++i) {
if (output_data[i] != input_a[i] + input_b[i]) {
correct = false;
break;
}
}
std::cout << "\nResult: " << (correct ? "PASS" : "FAIL") << "\n";

return correct ? EXIT_SUCCESS : EXIT_FAILURE;
} catch (const Ort::Exception& e) {
std::cerr << "ONNX Runtime error: " << e.what() << "\n";
return EXIT_FAILURE;
} catch (const std::exception& e) {
std::cerr << "Error: " << e.what() << "\n";
return EXIT_FAILURE;
}
}
Loading
Loading