diff --git a/.gitignore b/.gitignore index 116f8db60a..5947fdfae2 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ cmake/external/FeaturizersLibrary/ # Java specific ignores java/.gradle java/hs_*.log +/java/bin onnxruntime/python/version_info.py /orttraining/orttraining/eager/ort_aten.g.cpp /orttraining/orttraining/eager/ort_customops.g.cpp @@ -188,3 +189,10 @@ dmypy.json # Cython debug symbols cython_debug/ + +# Swift Package Manager +Packages/ +Package.pins +Package.resolved +.build/ +.swiftpm/ diff --git a/Package.swift b/Package.swift new file mode 100644 index 0000000000..7f8bfe0c3c --- /dev/null +++ b/Package.swift @@ -0,0 +1,102 @@ +// swift-tools-version: 5.6 +// The swift-tools-version declares the minimum version of Swift required to build this package and MUST be the first +// line of this file. 5.6 is required to support zip files for the pod archive binaryTarget. +// +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// A user of the Swift Package Manager (SPM) package will consume this file directly from the ORT github repository. +// For context, the end user's config will look something like: +// +// dependencies: [ +// .package(url: "https://github.com/microsoft/onnxruntime", branch: "rel-1.15.0"), +// ... +// ], +// +// NOTE: The direct consumption creates a somewhat complicated setup to 'release' a new version of the ORT SPM package. +// TBD: how to manage the release process + +import PackageDescription +import class Foundation.ProcessInfo + +let package = Package( + name: "onnxruntime", + platforms: [.iOS(.v11)], + products: [ + .library(name: "onnxruntime", + type: .static, + targets: ["OnnxRuntimeBindings"]), + ], + dependencies: [], + targets: [ + .target(name: "OnnxRuntimeBindings", + dependencies: ["onnxruntime"], + path: "objectivec", + exclude: ["test", "docs", "ReadMe.md", "format_objc.sh"], + cxxSettings: [ + .define("SPM_BUILD"), + .unsafeFlags(["-std=c++17", + "-fobjc-arc-exceptions" + ]), + ], linkerSettings: [ + .unsafeFlags(["-ObjC"]), + ]), + .testTarget(name: "OnnxRuntimeBindingsTests", + dependencies: ["OnnxRuntimeBindings"], + path: "swift/OnnxRuntimeBindingsTests", + resources: [ + .copy("Resources/single_add.basic.ort") + ]), + ] +) + +// Add the ORT iOS Pod archive as a binary target. +// +// There are 2 scenarios: +// +// Release branch of ORT github repo: +// Target will be set to the released pod archive and its checksum. +// +// Any other branch/tag of ORT github repo: +// Invalid by default. We do not have a pod archive that is guaranteed to work +// as the objective-c bindings may have changed since the pod archive was released. + +// CI or local testing where you have built/obtained the iOS Pod archive matching the current source code. +// Requires the ORT_IOS_POD_LOCAL_PATH environment variable to be set to specify the location of the pod. +if let pod_archive_path = ProcessInfo.processInfo.environment["ORT_IOS_POD_LOCAL_PATH"] { + // ORT_IOS_POD_LOCAL_PATH MUST be a path that is relative to Package.swift. + // + // To build locally, tools/ci_build/github/apple/build_and_assemble_ios_pods.py can be used + // See https://onnxruntime.ai/docs/build/custom.html#ios + // Example command: + // python3 tools/ci_build/github/apple/build_and_assemble_ios_pods.py \ + // --variant Full \ + // --build-settings-file tools/ci_build/github/apple/default_full_ios_framework_build_settings.json + // + // This should produce the pod archive in build/ios_pod_staging, and ORT_IOS_POD_LOCAL_PATH can be set to + // "build/ios_pod_staging/pod-archive-onnxruntime-c-???.zip" where '???' is replaced by the version info in the + // actual filename. + package.targets.append(Target.binaryTarget(name: "onnxruntime", path: pod_archive_path)) + +} else { + // When creating the release version: + // - remove the fatalError + // - uncomment the package.targets.append call + // - update the major/minor/patch version info in the url + // - insert the checksum info from the onnxruntime-ios-packaging-pipeline CI's 'Print ORT iOS Pod checksum' + // stage output (or download the pod archive artifact from the CI and run `shasum -a 256 ` + // to manually calculate it). + // The checksum length and chars should look something like + // "c89cd106ff02eb3892243acd7c4f2bd8e68c2c94f2751b5e35f98722e10c042b" + // + // package.targets.append( + // Target.binaryTarget(name: "onnxruntime", + // url: "https://onnxruntimepackages.z14.web.core.windows.net/pod-archive-onnxruntime-c-.zip", + // checksum: "Insert checksum here") + // ) + + fatalError("It is not valid to use a non-release branch from https://github.com/microsoft/onnxruntime.\n" + + "Please use a release branch (e.g. rel-1.15.0), or build the ONNX Runtime iOS pod archive locally " + + "and set the ORT_IOS_POD_LOCAL_PATH environment variable.\n" + + "See Package.swift for more information on using a local pod archive.") +} diff --git a/cmake/onnxruntime_objectivec.cmake b/cmake/onnxruntime_objectivec.cmake index ff17813d0d..1051607731 100644 --- a/cmake/onnxruntime_objectivec.cmake +++ b/cmake/onnxruntime_objectivec.cmake @@ -36,9 +36,9 @@ file(GLOB onnxruntime_objc_headers CONFIGURE_DEPENDS "${OBJC_ROOT}/include/*.h") file(GLOB onnxruntime_objc_srcs CONFIGURE_DEPENDS - "${OBJC_ROOT}/src/*.h" - "${OBJC_ROOT}/src/*.m" - "${OBJC_ROOT}/src/*.mm") + "${OBJC_ROOT}/*.h" + "${OBJC_ROOT}/*.m" + "${OBJC_ROOT}/*.mm") source_group(TREE "${OBJC_ROOT}" FILES ${onnxruntime_objc_headers} diff --git a/objectivec/ReadMe.md b/objectivec/ReadMe.md new file mode 100644 index 0000000000..9b89726641 --- /dev/null +++ b/objectivec/ReadMe.md @@ -0,0 +1,2 @@ +NOTE: Flat directory structure to work with both the Objective-C build and the Swift Package Manager build which is done +via ../Package.swift diff --git a/objectivec/src/assert_arc_enabled.mm b/objectivec/assert_arc_enabled.mm similarity index 100% rename from objectivec/src/assert_arc_enabled.mm rename to objectivec/assert_arc_enabled.mm diff --git a/objectivec/src/cxx_api.h b/objectivec/cxx_api.h similarity index 62% rename from objectivec/src/cxx_api.h rename to objectivec/cxx_api.h index 02ccf95058..3e4821c24a 100644 --- a/objectivec/src/cxx_api.h +++ b/objectivec/cxx_api.h @@ -10,17 +10,30 @@ #pragma clang diagnostic ignored "-Wdocumentation" #endif // defined(__clang__) +// paths are different when building the Swift Package Manager package as the headers come from the iOS pod archive +#ifdef SPM_BUILD +#include "onnxruntime/onnxruntime_c_api.h" +#include "onnxruntime/onnxruntime_cxx_api.h" + +#if __has_include("onnxruntime/coreml_provider_factory.h") +#define ORT_OBJC_API_COREML_EP_AVAILABLE 1 +#include "onnxruntime/coreml_provider_factory.h" +#else +#define ORT_OBJC_API_COREML_EP_AVAILABLE 0 +#endif + +#else #include "onnxruntime_c_api.h" #include "onnxruntime_cxx_api.h" #if __has_include("coreml_provider_factory.h") #define ORT_OBJC_API_COREML_EP_AVAILABLE 1 +#include "coreml_provider_factory.h" #else #define ORT_OBJC_API_COREML_EP_AVAILABLE 0 + #endif -#if ORT_OBJC_API_COREML_EP_AVAILABLE -#include "coreml_provider_factory.h" #endif #if defined(__clang__) diff --git a/objectivec/src/error_utils.h b/objectivec/error_utils.h similarity index 98% rename from objectivec/src/error_utils.h rename to objectivec/error_utils.h index 0a32f944c5..274e74aec1 100644 --- a/objectivec/src/error_utils.h +++ b/objectivec/error_utils.h @@ -5,7 +5,7 @@ #include -#import "src/cxx_api.h" +#import "cxx_api.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/src/error_utils.mm b/objectivec/error_utils.mm similarity index 97% rename from objectivec/src/error_utils.mm rename to objectivec/error_utils.mm index 6ab7f0234e..91863262ca 100644 --- a/objectivec/src/error_utils.mm +++ b/objectivec/error_utils.mm @@ -1,7 +1,7 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#import "src/error_utils.h" +#import "error_utils.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/include/ort_env.h b/objectivec/include/ort_env.h index e97253689e..8c4184c26e 100644 --- a/objectivec/include/ort_env.h +++ b/objectivec/include/ort_env.h @@ -7,6 +7,21 @@ NS_ASSUME_NONNULL_BEGIN +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Gets the ORT version string in format major.minor.patch. + * + * Available since 1.15. + */ +NSString* ORTVersion(void); + +#ifdef __cplusplus +} +#endif + /** * The ORT environment. */ diff --git a/objectivec/src/ort_coreml_execution_provider.mm b/objectivec/ort_coreml_execution_provider.mm similarity index 93% rename from objectivec/src/ort_coreml_execution_provider.mm rename to objectivec/ort_coreml_execution_provider.mm index 18b58db9d8..6340fdea1c 100644 --- a/objectivec/src/ort_coreml_execution_provider.mm +++ b/objectivec/ort_coreml_execution_provider.mm @@ -3,9 +3,9 @@ #import "ort_coreml_execution_provider.h" -#import "src/cxx_api.h" -#import "src/error_utils.h" -#import "src/ort_session_internal.h" +#import "cxx_api.h" +#import "error_utils.h" +#import "ort_session_internal.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/src/ort_enums.mm b/objectivec/ort_enums.mm similarity index 99% rename from objectivec/src/ort_enums.mm rename to objectivec/ort_enums.mm index 82191b7801..0144a333d1 100644 --- a/objectivec/src/ort_enums.mm +++ b/objectivec/ort_enums.mm @@ -5,7 +5,7 @@ #include -#import "src/cxx_api.h" +#import "cxx_api.h" namespace { diff --git a/objectivec/src/ort_enums_internal.h b/objectivec/ort_enums_internal.h similarity index 96% rename from objectivec/src/ort_enums_internal.h rename to objectivec/ort_enums_internal.h index b8a51f779b..f1470d7ae6 100644 --- a/objectivec/src/ort_enums_internal.h +++ b/objectivec/ort_enums_internal.h @@ -3,7 +3,7 @@ #import "ort_enums.h" -#import "src/cxx_api.h" +#import "cxx_api.h" OrtLoggingLevel PublicToCAPILoggingLevel(ORTLoggingLevel logging_level); diff --git a/objectivec/src/ort_env.mm b/objectivec/ort_env.mm similarity index 72% rename from objectivec/src/ort_env.mm rename to objectivec/ort_env.mm index c880d76560..cd83ac5a85 100644 --- a/objectivec/src/ort_env.mm +++ b/objectivec/ort_env.mm @@ -1,17 +1,22 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#import "src/ort_env_internal.h" +#import "ort_env_internal.h" #include -#import "src/cxx_api.h" +#import "cxx_api.h" -#import "src/error_utils.h" -#import "src/ort_enums_internal.h" +#import "error_utils.h" +#import "ort_enums_internal.h" NS_ASSUME_NONNULL_BEGIN +NSString* ORTVersion(void) { + std::string result = OrtGetApiBase()->GetVersionString(); + return [NSString stringWithUTF8String:result.c_str()]; +} + @implementation ORTEnv { std::optional _env; } diff --git a/objectivec/src/ort_env_internal.h b/objectivec/ort_env_internal.h similarity index 90% rename from objectivec/src/ort_env_internal.h rename to objectivec/ort_env_internal.h index ff127f8b6a..32df9bb654 100644 --- a/objectivec/src/ort_env_internal.h +++ b/objectivec/ort_env_internal.h @@ -3,7 +3,7 @@ #import "ort_env.h" -#import "src/cxx_api.h" +#import "cxx_api.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/src/ort_session.mm b/objectivec/ort_session.mm similarity index 98% rename from objectivec/src/ort_session.mm rename to objectivec/ort_session.mm index a3ccdaf1fc..c4f57b0a6f 100644 --- a/objectivec/src/ort_session.mm +++ b/objectivec/ort_session.mm @@ -1,16 +1,16 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#import "src/ort_session_internal.h" +#import "ort_session_internal.h" #include #include -#import "src/cxx_api.h" -#import "src/error_utils.h" -#import "src/ort_enums_internal.h" -#import "src/ort_env_internal.h" -#import "src/ort_value_internal.h" +#import "cxx_api.h" +#import "error_utils.h" +#import "ort_enums_internal.h" +#import "ort_env_internal.h" +#import "ort_value_internal.h" namespace { enum class NamedValueType { diff --git a/objectivec/src/ort_session_internal.h b/objectivec/ort_session_internal.h similarity index 93% rename from objectivec/src/ort_session_internal.h rename to objectivec/ort_session_internal.h index 3adc2f96f4..c250264ee1 100644 --- a/objectivec/src/ort_session_internal.h +++ b/objectivec/ort_session_internal.h @@ -3,7 +3,7 @@ #import "ort_session.h" -#import "src/cxx_api.h" +#import "cxx_api.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/src/ort_value.mm b/objectivec/ort_value.mm similarity index 97% rename from objectivec/src/ort_value.mm rename to objectivec/ort_value.mm index 29f542a8d0..ce77ca752c 100644 --- a/objectivec/src/ort_value.mm +++ b/objectivec/ort_value.mm @@ -1,13 +1,13 @@ // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the MIT License. -#import "src/ort_value_internal.h" +#import "ort_value_internal.h" #include -#import "src/cxx_api.h" -#import "src/error_utils.h" -#import "src/ort_enums_internal.h" +#import "cxx_api.h" +#import "error_utils.h" +#import "ort_enums_internal.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/src/ort_value_internal.h b/objectivec/ort_value_internal.h similarity index 95% rename from objectivec/src/ort_value_internal.h rename to objectivec/ort_value_internal.h index 3b1e74f598..345c0fcaf8 100644 --- a/objectivec/src/ort_value_internal.h +++ b/objectivec/ort_value_internal.h @@ -3,7 +3,7 @@ #import "ort_value.h" -#import "src/cxx_api.h" +#import "cxx_api.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/src/ort_xnnpack_execution_provider.mm b/objectivec/ort_xnnpack_execution_provider.mm similarity index 89% rename from objectivec/src/ort_xnnpack_execution_provider.mm rename to objectivec/ort_xnnpack_execution_provider.mm index 34927055b1..324974a1cf 100644 --- a/objectivec/src/ort_xnnpack_execution_provider.mm +++ b/objectivec/ort_xnnpack_execution_provider.mm @@ -3,9 +3,9 @@ #import "ort_xnnpack_execution_provider.h" -#import "src/cxx_api.h" -#import "src/error_utils.h" -#import "src/ort_session_internal.h" +#import "cxx_api.h" +#import "error_utils.h" +#import "ort_session_internal.h" NS_ASSUME_NONNULL_BEGIN diff --git a/objectivec/test/ort_env_test.mm b/objectivec/test/ort_env_test.mm index 041402c5a0..420f55feb6 100644 --- a/objectivec/test/ort_env_test.mm +++ b/objectivec/test/ort_env_test.mm @@ -13,6 +13,10 @@ NS_ASSUME_NONNULL_BEGIN @end @implementation ORTEnvTest +- (void)testGetOrtVersion { + NSString* ver = ORTVersion(); + XCTAssertNotNil(ver); +} - (void)testInitOk { NSError* err = nil; diff --git a/swift/OnnxRuntimeBindingsTests/Resources/single_add.basic.ort b/swift/OnnxRuntimeBindingsTests/Resources/single_add.basic.ort new file mode 100644 index 0000000000..f622784b35 Binary files /dev/null and b/swift/OnnxRuntimeBindingsTests/Resources/single_add.basic.ort differ diff --git a/swift/OnnxRuntimeBindingsTests/SwiftOnnxRuntimeBindingsTests.swift b/swift/OnnxRuntimeBindingsTests/SwiftOnnxRuntimeBindingsTests.swift new file mode 100644 index 0000000000..48e276487e --- /dev/null +++ b/swift/OnnxRuntimeBindingsTests/SwiftOnnxRuntimeBindingsTests.swift @@ -0,0 +1,62 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +import XCTest +import Foundation +@testable import OnnxRuntimeBindings + +final class SwiftOnnxRuntimeBindingsTests: XCTestCase { + let modelPath: String = Bundle.module.url(forResource: "single_add.basic", withExtension: "ort")!.path + + func testGetVersionString() throws { + do { + let version = ORTVersion() + XCTAssertNotNil(version) + } catch let error { + XCTFail(error.localizedDescription) + } + } + + func testCreateSession() throws { + do { + let env = try ORTEnv(loggingLevel: ORTLoggingLevel.verbose) + let options = try ORTSessionOptions() + try options.setLogSeverityLevel(ORTLoggingLevel.verbose) + try options.setIntraOpNumThreads(1) + // Create the ORTSession + _ = try ORTSession(env: env, modelPath: modelPath, sessionOptions: options) + } catch let error { + XCTFail(error.localizedDescription) + } + } + + func testAppendCoreMLEP() throws { + do { + let env = try ORTEnv(loggingLevel: ORTLoggingLevel.verbose) + let sessionOptions: ORTSessionOptions = try ORTSessionOptions() + let coreMLOptions: ORTCoreMLExecutionProviderOptions = ORTCoreMLExecutionProviderOptions() + coreMLOptions.enableOnSubgraphs = true + try sessionOptions.appendCoreMLExecutionProvider(with: coreMLOptions) + + XCTAssertTrue(ORTIsCoreMLExecutionProviderAvailable()) + _ = try ORTSession(env: env, modelPath: modelPath, sessionOptions: sessionOptions) + } catch let error { + XCTFail(error.localizedDescription) + } + } + + func testAppendXnnpackEP() throws { + do { + let env = try ORTEnv(loggingLevel: ORTLoggingLevel.verbose) + let sessionOptions: ORTSessionOptions = try ORTSessionOptions() + let XnnpackOptions: ORTXnnpackExecutionProviderOptions = ORTXnnpackExecutionProviderOptions() + XnnpackOptions.intra_op_num_threads = 2 + try sessionOptions.appendXnnpackExecutionProvider(with: XnnpackOptions) + + XCTAssertTrue(ORTIsCoreMLExecutionProviderAvailable()) + _ = try ORTSession(env: env, modelPath: modelPath, sessionOptions: sessionOptions) + } catch let error { + XCTFail(error.localizedDescription) + } + } +} diff --git a/tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py b/tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py index f83ea12950..b80c408e98 100755 --- a/tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py +++ b/tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py @@ -32,9 +32,9 @@ include_dirs = [ # pod source files source_files = [ "objectivec/include/*.h", - "objectivec/src/*.h", - "objectivec/src/*.m", - "objectivec/src/*.mm", + "objectivec/*.h", + "objectivec/*.m", + "objectivec/*.mm", ] # pod public header files diff --git a/tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml b/tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml index 0904047f37..1e1037f1d9 100644 --- a/tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml @@ -188,6 +188,28 @@ jobs: "$(ORT_SHOULD_UPLOAD_ARCHIVES)" displayName: "Assemble artifacts" + - script: | + set -e -x + ls -R "$(Build.ArtifactStagingDirectory)" + displayName: "List staged artifacts" + + - script: | + set -e -x + shasum -a 256 "$(Build.ArtifactStagingDirectory)/pod-archive-onnxruntime-c-${ORT_POD_VERSION}.zip" + displayName: "Print ORT iOS Pod checksum" + + + # copy the pod archive to a path relative to Package.swift and set the env var required by Package.swift to use that. + # xcodebuild will implicitly use Package.swift and build/run the .testTarget (tests in swift/onnxTests). + # once that's done cleanup the copy of the pod zip file + - script: | + set -e -x + cp "$(Build.ArtifactStagingDirectory)/pod-archive-onnxruntime-c-${ORT_POD_VERSION}.zip" swift/ + export ORT_IOS_POD_LOCAL_PATH="swift/pod-archive-onnxruntime-c-${ORT_POD_VERSION}.zip" + xcodebuild test -scheme onnxruntime -destination 'platform=iOS Simulator' + rm swift/pod-archive-onnxruntime-c-*.zip + displayName: "Test Package.swift usage" + - publish: "$(Build.ArtifactStagingDirectory)" artifact: ios_packaging_artifacts displayName: "Publish artifacts" diff --git a/tools/ci_build/github/azure-pipelines/mac-objc-static-analysis-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/mac-objc-static-analysis-ci-pipeline.yml index 37580329f0..3fffec6159 100644 --- a/tools/ci_build/github/azure-pipelines/mac-objc-static-analysis-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/mac-objc-static-analysis-ci-pipeline.yml @@ -36,8 +36,8 @@ jobs: "$(brew --prefix llvm@15)/bin/clang-tidy" \ -p="$(Build.BinariesDirectory)/Debug" \ --checks="-*,clang-analyzer-*" \ - --header-filter="objectivec/include|objectivec/src|onnxruntime/core" \ - ./objectivec/src/*.mm \ + --header-filter="objectivec/include|objectivec|onnxruntime/core" \ + ./objectivec/*.mm \ ./onnxruntime/core/platform/apple/logging/apple_log_sink.mm \ ./onnxruntime/core/providers/coreml/model/*.mm displayName: Analyze Objective-C/C++ source code