diff --git a/.agents/docs/2026-10-11-add-onnxruntime-plan.md b/.agents/docs/2026-10-11-add-onnxruntime-plan.md new file mode 100644 index 00000000..d4f3a211 --- /dev/null +++ b/.agents/docs/2026-10-11-add-onnxruntime-plan.md @@ -0,0 +1,24 @@ +# ONNX Runtime 1.31.0 compat package + +- Branch: `add-onnxruntime`. +- Source: [microsoft/onnxruntime](https://github.com/microsoft/onnxruntime), + release `v1.31.0`, MIT licensed. +- Shape: Form B with an external CMake build hook. ONNX Runtime cannot be + represented by a source glob: the upstream build generates protobuf and + flatbuffers sources and fetches its pinned dependency graph from + `cmake/external/deps.txt`. +- Build profile: CPU execution provider only. CUDA, TensorRT, OpenVINO, + CoreML, DNNL, XNNPACK, mimalloc, language bindings, tests, and optional + provider plugins are disabled. The upstream shared library is installed + into the package prefix, and the mcpp descriptor links it through a small C + anchor target. +- Platforms: Linux, macOS, and Windows use the same source archive and the + upstream CMake platform selection. `xim:cmake` is declared as the build + dependency on each platform. +- Test: `tests/examples/onnxruntime` includes the installed public C API and + calls `OrtGetApiBase()` as a cross-platform runtime smoke test. The C API + header is installed at `include/onnxruntime/onnxruntime_c_api.h`. +- CN mirror: not declared because no mcpp-res upload is available; the + descriptor uses the permitted upstream URL fallback. The release archive + SHA-256 is + `a7e84230b5d509e48fbd6b73c2453a56345ccb22da34a46dac1f0cb4a2d6507b`. diff --git a/mcpp.toml b/mcpp.toml index a96c9edc..99521ce1 100644 --- a/mcpp.toml +++ b/mcpp.toml @@ -106,6 +106,7 @@ members = [ "tests/examples/glm", "tests/examples/libaio", "tests/examples/libserial", + "tests/examples/onnxruntime", "tests/examples/apriltag", "tests/examples/libassert", "tests/examples/libcoro", @@ -179,6 +180,7 @@ members = [ "tests/examples/httplib-zstd", "tests/examples/msdfgen", "tests/examples/gtl", + "tests/examples/etl", "tests/examples/galay", "tests/examples/miniaudio", "tests/examples/nanosvg", diff --git a/pkgs/c/compat.etl.lua b/pkgs/c/compat.etl.lua new file mode 100644 index 00000000..e0a5d1ea --- /dev/null +++ b/pkgs/c/compat.etl.lua @@ -0,0 +1,56 @@ +-- Form B inline descriptor for the Embedded Template Library (ETL), a +-- header-only collection of fixed-capacity containers and utilities for +-- resource-constrained systems. +-- +-- The upstream CMake target is INTERFACE-only and exposes `include/`, so a +-- small C anchor gives mcpp a buildable library target while consumers use +-- headers such as ``. +-- +-- ETL supports older language modes, but this package is built by mcpp, whose +-- supported C++ standard starts at C++20. The package therefore uses C++23 +-- for its anchor and remains compatible with C++20 consumers. +package = { + spec = "1", + namespace = "compat", + name = "etl", + description = "Embedded Template Library — fixed-capacity containers and utilities (header-only)", + licenses = {"MIT"}, + repo = "https://github.com/ETLCPP/etl", + type = "package", + + xpm = { + linux = { + ["20.49.0"] = { + url = "https://github.com/ETLCPP/etl/archive/refs/tags/20.49.0.tar.gz", + sha256 = "b3e7a5544795ec8f4a1b2ab46f6e88fdb78be5a07738f008ce11dd37ed14b6e4", + }, + }, + macosx = { + ["20.49.0"] = { + url = "https://github.com/ETLCPP/etl/archive/refs/tags/20.49.0.tar.gz", + sha256 = "b3e7a5544795ec8f4a1b2ab46f6e88fdb78be5a07738f008ce11dd37ed14b6e4", + }, + }, + windows = { + ["20.49.0"] = { + url = "https://github.com/ETLCPP/etl/archive/refs/tags/20.49.0.tar.gz", + sha256 = "b3e7a5544795ec8f4a1b2ab46f6e88fdb78be5a07738f008ce11dd37ed14b6e4", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + c_standard = "c11", + include_dirs = { "*/include" }, + generated_files = { + ["mcpp_generated/etl_anchor.c"] = [==[ +int mcpp_compat_etl_headers_anchor(void) { return 0; } +]==], + }, + sources = { "mcpp_generated/etl_anchor.c" }, + targets = { ["etl"] = { kind = "lib" } }, + deps = {}, + }, +} diff --git a/pkgs/c/compat.onnxruntime.lua b/pkgs/c/compat.onnxruntime.lua new file mode 100644 index 00000000..308994f8 --- /dev/null +++ b/pkgs/c/compat.onnxruntime.lua @@ -0,0 +1,285 @@ +-- compat.onnxruntime — ONNX Runtime 1.31.0, built by the upstream CMake +-- project with the CPU execution provider. +-- +-- ONNX Runtime is not a source-glob compat package: its build generates +-- protobuf/flatbuffers sources and fetches a fixed dependency graph from +-- cmake/external/deps.txt. install() therefore runs the upstream CMake build, +-- then the mcpp target links the installed shared library. +package = { + spec = "1", + namespace = "compat", + name = "onnxruntime", + description = "ONNX Runtime CPU inference runtime", + licenses = {"MIT"}, + repo = "https://github.com/microsoft/onnxruntime", + type = "package", + + xpm = { + linux = { + deps = { "xim:cmake@latest", "xim:linux-headers@5.11.1" }, + ["1.31.0"] = { + url = "https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.31.0.tar.gz", + sha256 = "a7e84230b5d509e48fbd6b73c2453a56345ccb22da34a46dac1f0cb4a2d6507b", + }, + }, + macosx = { + deps = { "xim:cmake@latest" }, + ["1.31.0"] = { + url = "https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.31.0.tar.gz", + sha256 = "a7e84230b5d509e48fbd6b73c2453a56345ccb22da34a46dac1f0cb4a2d6507b", + }, + }, + windows = { + deps = { "xim:cmake@latest" }, + ["1.31.0"] = { + url = "https://github.com/microsoft/onnxruntime/archive/refs/tags/v1.31.0.tar.gz", + sha256 = "a7e84230b5d509e48fbd6b73c2453a56345ccb22da34a46dac1f0cb4a2d6507b", + }, + }, + }, + + mcpp = { + language = "c++23", + import_std = false, + include_dirs = { "include" }, + generated_files = { + ["mcpp_generated/onnxruntime_anchor.c"] = + "int mcpp_compat_onnxruntime_anchor(void) { return 0; }\n", + }, + sources = { "mcpp_generated/onnxruntime_anchor.c" }, + targets = { ["onnxruntime"] = { kind = "lib" } }, + deps = { ["compat.protobuf"] = {version = "35.1", features = {"protoc", "upb"} } }, + linux = { + requires = { "mcpp:c++-abi=libc++" }, + ldflags = { "-Llib", "-lonnxruntime", + "-Wl,-Bdynamic,-lc++,-lc++abi,-lunwind" }, + }, + macosx = { + requires = { "mcpp:c++-abi=libc++" }, + ldflags = { "-Llib", "-lonnxruntime" }, + }, + windows = { + ldflags = { "-Llib", "-lonnxruntime" }, + }, + runtime = { + library_dirs = { "lib" }, + }, + }, +} + +import("xim.libxpkg.pkginfo") +import("xim.libxpkg.log") + +local function quote(value) + return '"' .. tostring(value):gsub('"', '\\"') .. '"' +end + +local function resolve_cmake() + local dep = pkginfo.build_dep("xim:cmake") + if dep then + local candidates = {} + if dep.bin then + candidates[#candidates + 1] = path.join(dep.bin, "cmake") + candidates[#candidates + 1] = path.join(dep.bin, "cmake.exe") + end + if dep.path then + candidates[#candidates + 1] = + path.join(dep.path, "CMake.app", "Contents", "bin", "cmake") + end + for _, candidate in ipairs(candidates) do + if os.isfile(candidate) then return candidate end + end + end + return nil +end + +local function resolve_source(address, version, directory) + local root = pkginfo.install_dir(address, version) + if not root then return nil end + local direct = path.join(root, "CMakeLists.txt") + if os.isfile(direct) then return root end + local nested = path.join(root, directory, "CMakeLists.txt") + if os.isfile(nested) then return path.join(root, directory) end + return nil +end + +function install() + local wrap = "onnxruntime-" .. pkginfo.version() + local source = path.join(wrap, "cmake") + if not os.isfile(path.join(source, "CMakeLists.txt")) then + log.error("compat.onnxruntime: expected %s/CMakeLists.txt", source) + return false + end + + local cmake = resolve_cmake() + if not cmake then + log.error("compat.onnxruntime: xim:cmake executable not found") + return false + end + + local protobuf = resolve_source("compat:protobuf", "35.1", + "protobuf-35.1") + local abseil = resolve_source("compat:abseil", "20250512.1", + "abseil-cpp-20250512.1") + if not protobuf or not abseil then + log.error("compat.onnxruntime: mcpp protobuf/abseil sources not found") + return false + end + + local prefix = pkginfo.install_dir() + local build = path.join(wrap, "mcpp-build") + local protoc_build = path.join(wrap, "mcpp-protoc-build") + os.tryrm(prefix) + os.mkdir(prefix) + os.tryrm(build) + os.tryrm(protoc_build) + + local jobs = (os.default_njob and os.default_njob()) or 4 + local function run(command) + if os.host() == "windows" then + local bat = path.join(prefix, "mcpp_onnxruntime_command.bat") + io.writefile(bat, "@echo off\r\n" .. command + .. "\r\nif errorlevel 1 exit /b 1\r\n") + command = "cmd /c " .. quote(bat) + end + return pcall(os.exec, command) + end + + local protoc_configure = table.concat({ + quote(cmake), "-S", quote(protobuf), "-B", quote(protoc_build), + "-DCMAKE_BUILD_TYPE=Release", + "-Dprotobuf_BUILD_PROTOBUF_BINARIES=ON", + "-Dprotobuf_BUILD_LIBPROTOBUF=ON", + "-Dprotobuf_BUILD_PROTOC_BINARIES=ON", + "-Dprotobuf_BUILD_LIBPROTOC=ON", + "-Dprotobuf_BUILD_TESTS=OFF", + "-Dprotobuf_WITH_ZLIB=OFF", + "-Dprotobuf_INSTALL=OFF", + "-DFETCHCONTENT_SOURCE_DIR_ABSEIL_CPP=" .. quote(abseil), + }, " ") + local invoked, result = run(protoc_configure) + if not invoked or not result then + log.error("compat.onnxruntime: Protobuf protoc configure failed") + return false + end + local protoc_build_cmd = string.format("%s --build %s --target protoc --parallel %d", + quote(cmake), quote(protoc_build), jobs) + invoked, result = run(protoc_build_cmd) + if not invoked or not result then + log.error("compat.onnxruntime: Protobuf protoc build failed") + return false + end + local protoc_name = os.host() == "windows" and "protoc.exe" or "protoc" + local protoc_candidates = { + path.join(protoc_build, protoc_name), + path.join(protoc_build, "Release", protoc_name), + path.join(protoc_build, "RelWithDebInfo", protoc_name), + path.join(protoc_build, "Debug", protoc_name), + path.join(protoc_build, "bin", protoc_name), + } + local protoc + for _, candidate in ipairs(protoc_candidates) do + if os.isfile(candidate) then + protoc = candidate + break + end + end + if not protoc then + log.error("compat.onnxruntime: matching Protobuf protoc executable not found") + return false + end + + local configure = table.concat({ + quote(cmake), "-S", quote(source), "-B", quote(build), + "-DCMAKE_BUILD_TYPE=Release", + "-DCMAKE_INSTALL_PREFIX=" .. quote(prefix), + "-DCMAKE_SKIP_INSTALL_RULES=OFF", + "-DFETCHCONTENT_TRY_FIND_PACKAGE_MODE=NEVER", + "-DFETCHCONTENT_SOURCE_DIR_PROTOBUF=" .. quote(protobuf), + "-DFETCHCONTENT_SOURCE_DIR_ABSEIL_CPP=" .. quote(abseil), + "-Donnxruntime_BUILD_SHARED_LIB=ON", + "-Donnxruntime_BUILD_UNIT_TESTS=OFF", + "-Donnxruntime_ENABLE_PYTHON=OFF", + "-Donnxruntime_BUILD_CSHARP=OFF", + "-Donnxruntime_BUILD_JAVA=OFF", + "-Donnxruntime_BUILD_NODEJS=OFF", + "-Donnxruntime_BUILD_OBJC=OFF", + "-Donnxruntime_BUILD_APPLE_FRAMEWORK=OFF", + "-Donnxruntime_USE_CUDA=OFF", + "-Donnxruntime_USE_TENSORRT=OFF", + "-Donnxruntime_USE_OPENVINO=OFF", + "-Donnxruntime_USE_COREML=OFF", + "-Donnxruntime_USE_DNNL=OFF", + "-Donnxruntime_USE_XNNPACK=OFF", + "-Donnxruntime_USE_MIMALLOC=OFF", + "-Donnxruntime_USE_VCPKG=OFF", + "-Dprotobuf_WITH_ZLIB=OFF", + "-Dprotobuf_BUILD_PROTOBUF_BINARIES=ON", + "-Dprotobuf_BUILD_LIBPROTOBUF=ON", + "-Dprotobuf_BUILD_PROTOC_BINARIES=ON", + "-Dprotobuf_BUILD_LIBPROTOC=ON", + "-Dprotobuf_BUILD_TESTS=OFF", + "-DONNX_CUSTOM_PROTOC_EXECUTABLE=" .. quote(protoc), + }, " ") + local include_flag = "-I" + local warning_flags = "" + if os.host() == "windows" then + include_flag = "/I" + warning_flags = " /wd4101 /wd4189" + else + warning_flags = " -Wno-error=unused-variable -Wno-error=unused-result" + end + local extra_include_dirs = { path.join(protobuf, "third_party", "utf8_range") } + if os.host() == "linux" then + local headers = pkginfo.build_dep("xim:linux-headers") + or pkginfo.build_dep("linux-headers") + if not headers or not headers.path then + log.error("compat.onnxruntime: xim:linux-headers include path not found") + return false + end + extra_include_dirs[#extra_include_dirs + 1] = path.join(headers.path, "include") + end + local extra_includes = "" + for _, directory in ipairs(extra_include_dirs) do + extra_includes = extra_includes .. " " .. include_flag .. directory + end + configure = configure .. " -DCMAKE_C_FLAGS=" + .. quote(extra_includes .. warning_flags) + .. " -DCMAKE_CXX_FLAGS=" + .. quote(extra_includes + .. warning_flags) + if os.host() == "linux" then + configure = configure .. " -DCMAKE_SHARED_LINKER_FLAGS=" + .. quote("-Wl,-Bsymbolic") + end + invoked, result = run(configure) + if not invoked or not result then + log.error("compat.onnxruntime: CMake configure failed") + return false + end + + local build_cmd = string.format("%s --build %s --parallel %d", + quote(cmake), quote(build), jobs) + invoked, result = run(build_cmd) + if not invoked or not result then + log.error("compat.onnxruntime: CMake build failed") + return false + end + + local install_cmd = string.format("%s --install %s", + quote(cmake), quote(build)) + invoked, result = run(install_cmd) + if not invoked or not result then + log.error("compat.onnxruntime: CMake install failed") + return false + end + + if not os.isfile(path.join(prefix, "include", "onnxruntime", + "onnxruntime_c_api.h")) then + log.error("compat.onnxruntime: installed C API header not found") + return false + end + return os.isfile(path.join(prefix, "lib", "libonnxruntime.so")) + or os.isfile(path.join(prefix, "lib", "libonnxruntime.dylib")) + or os.isfile(path.join(prefix, "lib", "onnxruntime.lib")) +end diff --git a/tests/examples/etl/mcpp.toml b/tests/examples/etl/mcpp.toml new file mode 100644 index 00000000..be6872d7 --- /dev/null +++ b/tests/examples/etl/mcpp.toml @@ -0,0 +1,8 @@ +# ETL test project: exercise a fixed-capacity vector and a queue without +# requiring dynamic allocation from the library. +[package] +name = "etl-tests" +version = "0.1.0" + +[dependencies.compat] +etl = "20.49.0" diff --git a/tests/examples/etl/tests/containers.cpp b/tests/examples/etl/tests/containers.cpp new file mode 100644 index 00000000..67a6800d --- /dev/null +++ b/tests/examples/etl/tests/containers.cpp @@ -0,0 +1,29 @@ +// compat.etl — exercise ETL's fixed-capacity containers and their observable +// capacity/ordering behaviour. +#include +#include + +int main() { + etl::vector values; + values.push_back(3); + values.push_back(1); + values.push_back(4); + + if (values.size() != 3 || values.capacity() != 4 || + values[0] != 3 || values[2] != 4) { + return 1; + } + + etl::queue queue; + queue.push(10); + queue.push(20); + if (queue.front() != 10 || queue.back() != 20) { + return 2; + } + queue.pop(); + if (queue.front() != 20 || queue.size() != 1) { + return 3; + } + + return 0; +} diff --git a/tests/examples/onnxruntime/mcpp.toml b/tests/examples/onnxruntime/mcpp.toml new file mode 100644 index 00000000..8a5b07dd --- /dev/null +++ b/tests/examples/onnxruntime/mcpp.toml @@ -0,0 +1,6 @@ +[package] +name = "onnxruntime-tests" +version = "0.1.0" + +[dependencies.compat] +onnxruntime = "1.31.0" diff --git a/tests/examples/onnxruntime/tests/version.cpp b/tests/examples/onnxruntime/tests/version.cpp new file mode 100644 index 00000000..77e87bf8 --- /dev/null +++ b/tests/examples/onnxruntime/tests/version.cpp @@ -0,0 +1,8 @@ +#include +#include + +int main() { + const OrtApiBase* base = OrtGetApiBase(); + assert(base != nullptr); + return 0; +}