diff --git a/.gitignore b/.gitignore index 2d7278a..e4772f9 100644 --- a/.gitignore +++ b/.gitignore @@ -398,4 +398,6 @@ FodyWeavers.xsd *.sln.iml .DS_Store -*.DS_Store \ No newline at end of file +*.DS_Store + +.build/ \ No newline at end of file diff --git a/Package.swift b/Package.swift index e7f008a..54609b1 100644 --- a/Package.swift +++ b/Package.swift @@ -32,14 +32,7 @@ let package = Package( .target(name: "OnnxRuntimeBindings", dependencies: ["onnxruntime"], path: "objectivec", - // exclude: ["test", "docs", "ReadMe.md", "format_objc.sh", - // "ort_checkpoint.mm", - // "ort_checkpoint_internal.h", - // "ort_training_session_internal.h", - // "ort_training_session.mm", - // "include/ort_checkpoint.h", - // "include/ort_training_session.h", - // "include/onnxruntime_training.h"], + exclude: ["test", "docs", "ReadMe.md", "format_objc.sh"], cxxSettings: [ .define("SPM_BUILD"), .unsafeFlags(["-std=c++17", @@ -86,24 +79,10 @@ if let pod_archive_path = ProcessInfo.processInfo.environment["ORT_IOS_POD_LOCAL 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.") + // ORT 1.15.0 release + package.targets.append( + Target.binaryTarget(name: "onnxruntime", + url: "https://onnxruntimepackages.z14.web.core.windows.net/pod-archive-onnxruntime-c-1.15.0.zip", + checksum: "9b41412329a73d7d298b1d94ab40ae9adb65cb84f132054073bc82515b4f5f82") + ) } diff --git a/objectivec/.DS_Store b/objectivec/.DS_Store deleted file mode 100644 index 67f406e..0000000 Binary files a/objectivec/.DS_Store and /dev/null differ diff --git a/objectivec/cxx_api.h b/objectivec/cxx_api.h index b57c865..3e4821c 100644 --- a/objectivec/cxx_api.h +++ b/objectivec/cxx_api.h @@ -11,30 +11,31 @@ #endif // defined(__clang__) // paths are different when building the Swift Package Manager package as the headers come from the iOS pod archive -// clang-format off -#define STRINGIFY(x) #x #ifdef SPM_BUILD -#define ORT_C_CXX_HEADER_FILE_PATH(x) STRINGIFY(onnxruntime/x) -#else -#define ORT_C_CXX_HEADER_FILE_PATH(x) STRINGIFY(x) -#endif -// clang-format on +#include "onnxruntime/onnxruntime_c_api.h" +#include "onnxruntime/onnxruntime_cxx_api.h" -#if __has_include(ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_training_c_api.h)) -#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_training_c_api.h) -#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_training_cxx_api.h) -#else -#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_c_api.h) -#include ORT_C_CXX_HEADER_FILE_PATH(onnxruntime_cxx_api.h) -#endif - -#if __has_include(ORT_C_CXX_HEADER_FILE_PATH(coreml_provider_factory.h)) +#if __has_include("onnxruntime/coreml_provider_factory.h") #define ORT_OBJC_API_COREML_EP_AVAILABLE 1 -#include ORT_C_CXX_HEADER_FILE_PATH(coreml_provider_factory.h) +#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 + +#endif + #if defined(__clang__) #pragma clang diagnostic pop #endif // defined(__clang__) diff --git a/objectivec/cxx_utils.h b/objectivec/cxx_utils.h deleted file mode 100644 index 0b3666d..0000000 --- a/objectivec/cxx_utils.h +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#import - -#include -#include -#include - -#import "cxx_api.h" - -NS_ASSUME_NONNULL_BEGIN -@class ORTValue; - -namespace utils { - -NSString* toNSString(const std::string& str); -NSString* _Nullable toNullableNSString(const std::optional& str); - -std::string toStdString(NSString* str); -std::optional toStdOptionalString(NSString* _Nullable str); - -std::vector toStdStringVector(NSArray* strs); -NSArray* toNSStringNSArray(const std::vector& strs); - -NSArray* _Nullable wrapUnownedCAPIOrtValues(const std::vector& values, NSError** error); - -std::vector getWrappedCAPIOrtValues(NSArray* values); - -} // namespace utils - -NS_ASSUME_NONNULL_END diff --git a/objectivec/cxx_utils.mm b/objectivec/cxx_utils.mm deleted file mode 100644 index 01d0955..0000000 --- a/objectivec/cxx_utils.mm +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -#import "cxx_utils.h" - -#include -#include -#include - -#import "error_utils.h" - -#import "ort_value_internal.h" - -NS_ASSUME_NONNULL_BEGIN - -namespace utils { - -NSString* toNSString(const std::string& str) { - NSString* nsStr = [NSString stringWithUTF8String:str.c_str()]; - if (!nsStr) { - ORT_CXX_API_THROW("Failed to convert std::string to NSString", ORT_INVALID_ARGUMENT); - } - - return nsStr; -} - -NSString* _Nullable toNullableNSString(const std::optional& str) { - if (str.has_value()) { - return toNSString(*str); - } - return nil; -} - -std::string toStdString(NSString* str) { - return std::string([str UTF8String]); -} - -std::optional toStdOptionalString(NSString* _Nullable str) { - if (str) { - return std::optional([str UTF8String]); - } - return std::nullopt; -} - -std::vector toStdStringVector(NSArray* strs) { - std::vector result; - result.reserve(strs.count); - for (NSString* str in strs) { - result.push_back([str UTF8String]); - } - return result; -} - -NSArray* toNSStringNSArray(const std::vector& strs) { - NSMutableArray* result = [NSMutableArray arrayWithCapacity:strs.size()]; - for (const std::string& str : strs) { - [result addObject:toNSString(str)]; - } - return result; -} - -NSArray* _Nullable wrapUnownedCAPIOrtValues(const std::vector& CAPIValues, NSError** error) { - NSMutableArray* result = [NSMutableArray arrayWithCapacity:CAPIValues.size()]; - for (size_t i = 0; i < CAPIValues.size(); ++i) { - // Wrap the C OrtValue in a C++ Ort::Value to automatically handle its release. - // Then, transfer that C++ Ort::Value to a new ORTValue. - Ort::Value CXXAPIValue{CAPIValues[i]}; - ORTValue* val = [[ORTValue alloc] initWithCXXAPIOrtValue:std::move(CXXAPIValue) - externalTensorData:nil - error:error]; - if (!val) { - // clean up remaining C OrtValues which haven't been wrapped by a C++ Ort::Value yet - for (size_t j = i + 1; j < CAPIValues.size(); ++j) { - Ort::GetApi().ReleaseValue(CAPIValues[j]); - } - return nil; - } - [result addObject:val]; - } - return result; -} - -std::vector getWrappedCAPIOrtValues(NSArray* values) { - std::vector result; - result.reserve(values.count); - for (ORTValue* val in values) { - result.push_back(static_cast([val CXXAPIOrtValue])); - } - return result; -} - -} // namespace utils - -NS_ASSUME_NONNULL_END diff --git a/objectivec/docs/jazzy_config.yaml b/objectivec/docs/jazzy_config.yaml new file mode 100644 index 0000000..676f89d --- /dev/null +++ b/objectivec/docs/jazzy_config.yaml @@ -0,0 +1,13 @@ +module: ONNX Runtime Objective-C API +author: ONNX Runtime Authors +author_url: https://www.onnxruntime.ai +github_url: https://github.com/microsoft/onnxruntime + +objc: true +umbrella_header: ../include/onnxruntime.h +framework_root: .. + +readme: ./main_page.md + +hide_documentation_coverage: true +undocumented_text: "" diff --git a/objectivec/docs/main_page.md b/objectivec/docs/main_page.md new file mode 100644 index 0000000..fdc7c80 --- /dev/null +++ b/objectivec/docs/main_page.md @@ -0,0 +1,5 @@ +# ONNX Runtime Objective-C API + +ONNX Runtime provides an Objective-C API. + +It can be used from Objective-C/C++ or Swift with a bridging header. diff --git a/objectivec/docs/readme.md b/objectivec/docs/readme.md new file mode 100644 index 0000000..5697480 --- /dev/null +++ b/objectivec/docs/readme.md @@ -0,0 +1,17 @@ +# Objective-C API Documentation + +The API should be documented with comments in the [public header files](../include). + +## Documentation Generation + +The [Jazzy](https://github.com/realm/jazzy) tool is used to generate documentation from the code. + +To generate documentation, from the repo root, run: + +```bash +jazzy --config objectivec/docs/jazzy_config.yaml --output +``` + +The generated documentation website files will be in ``. + +[main_page.md](./main_page.md) contains content for the main page of the generated documentation website. diff --git a/objectivec/error_utils.h b/objectivec/error_utils.h index 4df71ce..274e74a 100644 --- a/objectivec/error_utils.h +++ b/objectivec/error_utils.h @@ -10,7 +10,6 @@ NS_ASSUME_NONNULL_BEGIN void ORTSaveCodeAndDescriptionToError(int code, const char* description, NSError** error); -void ORTSaveCodeAndDescriptionToError(int code, NSString* description, NSError** error); void ORTSaveOrtExceptionToError(const Ort::Exception& e, NSError** error); void ORTSaveExceptionToError(const std::exception& e, NSError** error); diff --git a/objectivec/error_utils.mm b/objectivec/error_utils.mm index 335cf88..9186326 100644 --- a/objectivec/error_utils.mm +++ b/objectivec/error_utils.mm @@ -18,14 +18,6 @@ void ORTSaveCodeAndDescriptionToError(int code, const char* descriptionCstr, NSE userInfo:@{NSLocalizedDescriptionKey : description}]; } -void ORTSaveCodeAndDescriptionToError(int code, NSString* description, NSError** error) { - if (!error) return; - - *error = [NSError errorWithDomain:kOrtErrorDomain - code:code - userInfo:@{NSLocalizedDescriptionKey : description}]; -} - void ORTSaveOrtExceptionToError(const Ort::Exception& e, NSError** error) { ORTSaveCodeAndDescriptionToError(e.GetOrtErrorCode(), e.what(), error); } diff --git a/objectivec/include/onnxruntime.h b/objectivec/include/onnxruntime.h index c7bfe6f..c859f6a 100644 --- a/objectivec/include/onnxruntime.h +++ b/objectivec/include/onnxruntime.h @@ -5,9 +5,8 @@ // the headers below can also be imported individually #import "ort_coreml_execution_provider.h" -#import "ort_custom_op_registration.h" +#import "ort_xnnpack_execution_provider.h" #import "ort_enums.h" #import "ort_env.h" #import "ort_session.h" #import "ort_value.h" -#import "ort_xnnpack_execution_provider.h" diff --git a/objectivec/include/ort_custom_op_registration.h b/objectivec/include/ort_custom_op_registration.h deleted file mode 100644 index 8d50b92..0000000 --- a/objectivec/include/ort_custom_op_registration.h +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright (c) Microsoft Corporation. All rights reserved. -// Licensed under the MIT License. - -/** C API type forward declaration. */ -struct OrtStatus; - -/** C API type forward declaration. */ -struct OrtApiBase; - -/** C API type forward declaration. */ -struct OrtSessionOptions; - -/** - * Pointer to a custom op registration function that uses the ONNX Runtime C API. - * - * The signature is defined in the ONNX Runtime C API: - * https://github.com/microsoft/onnxruntime/blob/67f4cd54fab321d83e4a75a40efeee95a6a17079/include/onnxruntime/core/session/onnxruntime_c_api.h#L697 - * - * This is a low-level type intended for interoperating with libraries which provide such a function for custom op - * registration, such as [ONNX Runtime Extensions](https://github.com/microsoft/onnxruntime-extensions). - */ -typedef struct OrtStatus* (*ORTCAPIRegisterCustomOpsFnPtr)(struct OrtSessionOptions* /*options*/, - const struct OrtApiBase* /*api*/); diff --git a/objectivec/include/ort_env.h b/objectivec/include/ort_env.h index 8456b57..8c4184c 100644 --- a/objectivec/include/ort_env.h +++ b/objectivec/include/ort_env.h @@ -16,7 +16,7 @@ extern "C" { * * Available since 1.15. */ -NSString* _Nullable ORTVersion(void); +NSString* ORTVersion(void); #ifdef __cplusplus } diff --git a/objectivec/include/ort_session.h b/objectivec/include/ort_session.h index cfdb3bf..92269f4 100644 --- a/objectivec/include/ort_session.h +++ b/objectivec/include/ort_session.h @@ -3,7 +3,6 @@ #import -#import "ort_custom_op_registration.h" #import "ort_enums.h" NS_ASSUME_NONNULL_BEGIN @@ -197,19 +196,12 @@ NS_ASSUME_NONNULL_BEGIN * Available since 1.14. * * The registration function must have the signature: - * `OrtStatus* (*fn)(OrtSessionOptions* options, const OrtApiBase* api);` - * - * The signature is defined in the ONNX Runtime C API: - * https://github.com/microsoft/onnxruntime/blob/67f4cd54fab321d83e4a75a40efeee95a6a17079/include/onnxruntime/core/session/onnxruntime_c_api.h#L697 + * OrtStatus* (*fn)(OrtSessionOptions* options, const OrtApiBase* api); * * See https://onnxruntime.ai/docs/reference/operators/add-custom-op.html for more information on custom ops. * See https://github.com/microsoft/onnxruntime/blob/342a5bf2b756d1a1fc6fdc582cfeac15182632fe/onnxruntime/test/testdata/custom_op_library/custom_op_library.cc#L115 * for an example of a custom op library registration function. * - * @note The caller must ensure that `registrationFuncName` names a valid function that is visible to the native ONNX - * Runtime code and has the correct signature. - * They must ensure that the function does what they expect it to do because this method will just call it. - * * @param registrationFuncName The name of the registration function to call. * @param error Optional error information set if an error occurs. * @return Whether the registration function was successfully called. @@ -217,32 +209,6 @@ NS_ASSUME_NONNULL_BEGIN - (BOOL)registerCustomOpsUsingFunction:(NSString*)registrationFuncName error:(NSError**)error; -/** - * Registers custom ops for use with `ORTSession`s using this SessionOptions by calling the specified function - * pointed to by `registerCustomOpsFn`. - * - * Available since 1.16. - * - * The registration function must have the signature: - * `OrtStatus* (*fn)(OrtSessionOptions* options, const OrtApiBase* api);` - * - * The signature is defined in the ONNX Runtime C API: - * https://github.com/microsoft/onnxruntime/blob/67f4cd54fab321d83e4a75a40efeee95a6a17079/include/onnxruntime/core/session/onnxruntime_c_api.h#L697 - * - * See https://onnxruntime.ai/docs/reference/operators/add-custom-op.html for more information on custom ops. - * See https://github.com/microsoft/onnxruntime/blob/342a5bf2b756d1a1fc6fdc582cfeac15182632fe/onnxruntime/test/testdata/custom_op_library/custom_op_library.cc#L115 - * for an example of a custom op library registration function. - * - * @note The caller must ensure that `registerCustomOpsFn` is a valid function pointer and has the correct signature. - * They must ensure that the function does what they expect it to do because this method will just call it. - * - * @param registerCustomOpsFn A pointer to the registration function to call. - * @param error Optional error information set if an error occurs. - * @return Whether the registration function was successfully called. - */ -- (BOOL)registerCustomOpsUsingFunctionPointer:(ORTCAPIRegisterCustomOpsFnPtr)registerCustomOpsFn - error:(NSError**)error; - @end /** diff --git a/objectivec/ort_env.mm b/objectivec/ort_env.mm index 7043785..cd83ac5 100644 --- a/objectivec/ort_env.mm +++ b/objectivec/ort_env.mm @@ -12,8 +12,9 @@ NS_ASSUME_NONNULL_BEGIN -NSString* _Nullable ORTVersion(void) { - return [NSString stringWithUTF8String:OrtGetApiBase()->GetVersionString()]; +NSString* ORTVersion(void) { + std::string result = OrtGetApiBase()->GetVersionString(); + return [NSString stringWithUTF8String:result.c_str()]; } @implementation ORTEnv { diff --git a/objectivec/ort_session.mm b/objectivec/ort_session.mm index 0255e7b..c4f57b0 100644 --- a/objectivec/ort_session.mm +++ b/objectivec/ort_session.mm @@ -66,26 +66,22 @@ NS_ASSUME_NONNULL_BEGIN } std::vector inputNames, outputNames; - std::vector inputCAPIValues; - std::vector outputCAPIValues; + std::vector inputValues; + std::vector outputValues; - inputNames.reserve(inputs.count); - inputCAPIValues.reserve(inputs.count); for (NSString* inputName in inputs) { inputNames.push_back(inputName.UTF8String); - inputCAPIValues.push_back(static_cast([inputs[inputName] CXXAPIOrtValue])); + inputValues.push_back(static_cast([inputs[inputName] CXXAPIOrtValue])); } - outputNames.reserve(outputs.count); - outputCAPIValues.reserve(outputs.count); for (NSString* outputName in outputs) { outputNames.push_back(outputName.UTF8String); - outputCAPIValues.push_back(static_cast([outputs[outputName] CXXAPIOrtValue])); + outputValues.push_back(static_cast([outputs[outputName] CXXAPIOrtValue])); } Ort::ThrowOnError(Ort::GetApi().Run(*_session, [runOptions CXXAPIOrtRunOptions], - inputNames.data(), inputCAPIValues.data(), inputNames.size(), - outputNames.data(), outputNames.size(), outputCAPIValues.data())); + inputNames.data(), inputValues.data(), inputNames.size(), + outputNames.data(), outputNames.size(), outputValues.data())); return YES; } @@ -107,39 +103,30 @@ NS_ASSUME_NONNULL_BEGIN NSArray* outputNameArray = outputNameSet.allObjects; std::vector inputNames, outputNames; - std::vector inputCAPIValues; - std::vector outputCAPIValues; + std::vector inputValues; + std::vector outputValues; - inputNames.reserve(inputs.count); - inputCAPIValues.reserve(inputs.count); for (NSString* inputName in inputs) { inputNames.push_back(inputName.UTF8String); - inputCAPIValues.push_back(static_cast([inputs[inputName] CXXAPIOrtValue])); + inputValues.push_back(static_cast([inputs[inputName] CXXAPIOrtValue])); } - outputNames.reserve(outputNameArray.count); - outputCAPIValues.reserve(outputNameArray.count); for (NSString* outputName in outputNameArray) { outputNames.push_back(outputName.UTF8String); - outputCAPIValues.push_back(nullptr); + outputValues.push_back(nullptr); } Ort::ThrowOnError(Ort::GetApi().Run(*_session, [runOptions CXXAPIOrtRunOptions], - inputNames.data(), inputCAPIValues.data(), inputNames.size(), - outputNames.data(), outputNames.size(), outputCAPIValues.data())); + inputNames.data(), inputValues.data(), inputNames.size(), + outputNames.data(), outputNames.size(), outputValues.data())); NSMutableDictionary* outputs = [[NSMutableDictionary alloc] init]; for (NSUInteger i = 0; i < outputNameArray.count; ++i) { - // Wrap the C OrtValue in a C++ Ort::Value to automatically handle its release. - // Then, transfer that C++ Ort::Value to a new ORTValue. - Ort::Value outputCXXAPIValue{outputCAPIValues[i]}; - ORTValue* outputValue = [[ORTValue alloc] initWithCXXAPIOrtValue:std::move(outputCXXAPIValue) - externalTensorData:nil - error:error]; + ORTValue* outputValue = [[ORTValue alloc] initWithCAPIOrtValue:outputValues[i] externalTensorData:nil error:error]; if (!outputValue) { - // clean up remaining C OrtValues which haven't been wrapped by a C++ Ort::Value yet - for (NSUInteger j = i + 1; j < outputNameArray.count; ++j) { - Ort::GetApi().ReleaseValue(outputCAPIValues[j]); + // clean up remaining C API OrtValues which haven't been wrapped by an ORTValue yet + for (NSUInteger j = i; j < outputNameArray.count; ++j) { + Ort::GetApi().ReleaseValue(outputValues[j]); } return nil; } @@ -309,19 +296,6 @@ NS_ASSUME_NONNULL_BEGIN ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) } -- (BOOL)registerCustomOpsUsingFunctionPointer:(ORTCAPIRegisterCustomOpsFnPtr)registerCustomOpsFn - error:(NSError**)error { - try { - if (!registerCustomOpsFn) { - ORT_CXX_API_THROW("registerCustomOpsFn must not be null", ORT_INVALID_ARGUMENT); - } - Ort::ThrowOnError((*registerCustomOpsFn)(static_cast(*_sessionOptions), - OrtGetApiBase())); - return YES; - } - ORT_OBJC_API_IMPL_CATCH_RETURNING_BOOL(error) -} - #pragma mark - Internal - (Ort::SessionOptions&)CXXAPIOrtSessionOptions { diff --git a/objectivec/ort_value.mm b/objectivec/ort_value.mm index f6ea674..ce77ca7 100644 --- a/objectivec/ort_value.mm +++ b/objectivec/ort_value.mm @@ -85,9 +85,9 @@ bool SafeMultiply(size_t a, size_t b, size_t& out) { memoryInfo, tensorData.mutableBytes, tensorData.length, shapeVector.data(), shapeVector.size(), ONNXElementType); - return [self initWithCXXAPIOrtValue:std::move(ortValue) - externalTensorData:tensorData - error:error]; + return [self initWithCAPIOrtValue:ortValue.release() + externalTensorData:tensorData + error:error]; } ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error) } @@ -129,19 +129,17 @@ bool SafeMultiply(size_t a, size_t b, size_t& out) { #pragma mark - Internal -- (nullable instancetype)initWithCXXAPIOrtValue:(Ort::Value&&)existingCXXAPIOrtValue - externalTensorData:(nullable NSMutableData*)externalTensorData - error:(NSError**)error { +- (nullable instancetype)initWithCAPIOrtValue:(OrtValue*)CAPIOrtValue + externalTensorData:(nullable NSMutableData*)externalTensorData + error:(NSError**)error { if ((self = [super init]) == nil) { return nil; } try { - _typeInfo = existingCXXAPIOrtValue.GetTypeInfo(); + _value = Ort::Value{CAPIOrtValue}; + _typeInfo = _value->GetTypeInfo(); _externalTensorData = externalTensorData; - - // transfer C++ Ort::Value ownership to this instance - _value = std::move(existingCXXAPIOrtValue); return self; } ORT_OBJC_API_IMPL_CATCH_RETURNING_NULLABLE(error); diff --git a/objectivec/ort_value_internal.h b/objectivec/ort_value_internal.h index 6bc2e9b..345c0fc 100644 --- a/objectivec/ort_value_internal.h +++ b/objectivec/ort_value_internal.h @@ -9,18 +9,9 @@ NS_ASSUME_NONNULL_BEGIN @interface ORTValue () -/** - * Creates a value from an existing C++ API Ort::Value and takes ownership from it. - * Note: Ownership is guaranteed to be transferred on success but not otherwise. - * - * @param existingCXXAPIOrtValue The existing C++ API Ort::Value. - * @param externalTensorData Any external tensor data referenced by `existingCXXAPIOrtValue`. - * @param error Optional error information set if an error occurs. - * @return The instance, or nil if an error occurs. - */ -- (nullable instancetype)initWithCXXAPIOrtValue:(Ort::Value&&)existingCXXAPIOrtValue - externalTensorData:(nullable NSMutableData*)externalTensorData - error:(NSError**)error NS_DESIGNATED_INITIALIZER; +- (nullable instancetype)initWithCAPIOrtValue:(OrtValue*)CAPIOrtValue + externalTensorData:(nullable NSMutableData*)externalTensorData + error:(NSError**)error NS_DESIGNATED_INITIALIZER; - (Ort::Value&)CXXAPIOrtValue; diff --git a/objectivec/test/assert_arc_enabled.mm b/objectivec/test/assert_arc_enabled.mm new file mode 100644 index 0000000..9aa0bad --- /dev/null +++ b/objectivec/test/assert_arc_enabled.mm @@ -0,0 +1,4 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +static_assert(__has_feature(objc_arc), "Objective-C ARC must be enabled."); diff --git a/objectivec/test/assertion_utils.h b/objectivec/test/assertion_utils.h new file mode 100644 index 0000000..f2b73e6 --- /dev/null +++ b/objectivec/test/assertion_utils.h @@ -0,0 +1,32 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +NS_ASSUME_NONNULL_BEGIN + +#define ORTAssertNullableResultSuccessful(result, error) \ + do { \ + XCTAssertNotNil(result, @"Expected non-nil result but got nil. Error: %@", error); \ + XCTAssertNil(error); \ + } while (0) + +#define ORTAssertBoolResultSuccessful(result, error) \ + do { \ + XCTAssertTrue(result, @"Expected true result but got false. Error: %@", error); \ + XCTAssertNil(error); \ + } while (0) + +#define ORTAssertNullableResultUnsuccessful(result, error) \ + do { \ + XCTAssertNil(result); \ + XCTAssertNotNil(error); \ + } while (0) + +#define ORTAssertBoolResultUnsuccessful(result, error) \ + do { \ + XCTAssertFalse(result); \ + XCTAssertNotNil(error); \ + } while (0) + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/ort_env_test.mm b/objectivec/test/ort_env_test.mm new file mode 100644 index 0000000..420f55f --- /dev/null +++ b/objectivec/test/ort_env_test.mm @@ -0,0 +1,30 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#import "ort_env.h" + +#import "test/assertion_utils.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ORTEnvTest : XCTestCase +@end + +@implementation ORTEnvTest +- (void)testGetOrtVersion { + NSString* ver = ORTVersion(); + XCTAssertNotNil(ver); +} + +- (void)testInitOk { + NSError* err = nil; + ORTEnv* env = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning + error:&err]; + ORTAssertNullableResultSuccessful(env, err); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/ort_session_test.mm b/objectivec/test/ort_session_test.mm new file mode 100644 index 0000000..790ad1c --- /dev/null +++ b/objectivec/test/ort_session_test.mm @@ -0,0 +1,243 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#import "ort_coreml_execution_provider.h" +#import "ort_xnnpack_execution_provider.h" +#import "ort_env.h" +#import "ort_session.h" +#import "ort_value.h" + +#import "test/assertion_utils.h" + +#include + +NS_ASSUME_NONNULL_BEGIN + +@interface ORTSessionTest : XCTestCase + +@property(readonly, nullable) ORTEnv* ortEnv; + +@end + +@implementation ORTSessionTest + +- (void)setUp { + [super setUp]; + + self.continueAfterFailure = NO; + + NSError* err = nil; + _ortEnv = [[ORTEnv alloc] initWithLoggingLevel:ORTLoggingLevelWarning + error:&err]; + ORTAssertNullableResultSuccessful(_ortEnv, err); +} + +- (void)tearDown { + _ortEnv = nil; + + [super tearDown]; +} + +// model with an Add op +// inputs: A, B +// output: C = A + B ++ (NSString*)getAddModelPath { + NSBundle* bundle = [NSBundle bundleForClass:[ORTSessionTest class]]; + NSString* path = [bundle pathForResource:@"single_add.basic" + ofType:@"ort"]; + return path; +} + ++ (NSMutableData*)dataWithScalarFloat:(float)value { + NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value length:sizeof(value)]; + return data; +} + ++ (ORTValue*)ortValueWithScalarFloatData:(NSMutableData*)data { + NSArray* shape = @[ @1 ]; + NSError* err = nil; + ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data + elementType:ORTTensorElementDataTypeFloat + shape:shape + error:&err]; + ORTAssertNullableResultSuccessful(ortValue, err); + return ortValue; +} + ++ (ORTSessionOptions*)makeSessionOptions { + NSError* err = nil; + ORTSessionOptions* sessionOptions = [[ORTSessionOptions alloc] initWithError:&err]; + ORTAssertNullableResultSuccessful(sessionOptions, err); + return sessionOptions; +} + ++ (ORTRunOptions*)makeRunOptions { + NSError* err = nil; + ORTRunOptions* runOptions = [[ORTRunOptions alloc] initWithError:&err]; + ORTAssertNullableResultSuccessful(runOptions, err); + return runOptions; +} + +- (void)testInitAndRunWithPreallocatedOutputOk { + NSMutableData* aData = [ORTSessionTest dataWithScalarFloat:1.0f]; + NSMutableData* bData = [ORTSessionTest dataWithScalarFloat:2.0f]; + NSMutableData* cData = [ORTSessionTest dataWithScalarFloat:0.0f]; + + ORTValue* a = [ORTSessionTest ortValueWithScalarFloatData:aData]; + ORTValue* b = [ORTSessionTest ortValueWithScalarFloatData:bData]; + ORTValue* c = [ORTSessionTest ortValueWithScalarFloatData:cData]; + + NSError* err = nil; + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultSuccessful(session, err); + + BOOL runResult = [session runWithInputs:@{@"A" : a, @"B" : b} + outputs:@{@"C" : c} + runOptions:[ORTSessionTest makeRunOptions] + error:&err]; + ORTAssertBoolResultSuccessful(runResult, err); + + const float cExpected = 3.0f; + float cActual; + memcpy(&cActual, cData.bytes, sizeof(float)); + XCTAssertEqual(cActual, cExpected); +} + +- (void)testInitAndRunOk { + NSMutableData* aData = [ORTSessionTest dataWithScalarFloat:1.0f]; + NSMutableData* bData = [ORTSessionTest dataWithScalarFloat:2.0f]; + + ORTValue* a = [ORTSessionTest ortValueWithScalarFloatData:aData]; + ORTValue* b = [ORTSessionTest ortValueWithScalarFloatData:bData]; + + NSError* err = nil; + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultSuccessful(session, err); + + NSDictionary* outputs = + [session runWithInputs:@{@"A" : a, @"B" : b} + outputNames:[NSSet setWithArray:@[ @"C" ]] + runOptions:[ORTSessionTest makeRunOptions] + error:&err]; + ORTAssertNullableResultSuccessful(outputs, err); + + ORTValue* cOutput = outputs[@"C"]; + XCTAssertNotNil(cOutput); + + NSData* cData = [cOutput tensorDataWithError:&err]; + ORTAssertNullableResultSuccessful(cData, err); + + const float cExpected = 3.0f; + float cActual; + memcpy(&cActual, cData.bytes, sizeof(float)); + XCTAssertEqual(cActual, cExpected); +} + +- (void)testGetNamesOk { + NSError* err = nil; + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultSuccessful(session, err); + + NSArray* inputNames = [session inputNamesWithError:&err]; + ORTAssertNullableResultSuccessful(inputNames, err); + XCTAssertEqualObjects(inputNames, (@[ @"A", @"B" ])); + + NSArray* overridableInitializerNames = [session overridableInitializerNamesWithError:&err]; + ORTAssertNullableResultSuccessful(overridableInitializerNames, err); + XCTAssertEqualObjects(overridableInitializerNames, (@[])); + + NSArray* outputNames = [session outputNamesWithError:&err]; + ORTAssertNullableResultSuccessful(outputNames, err); + XCTAssertEqualObjects(outputNames, (@[ @"C" ])); +} + +- (void)testInitFailsWithInvalidPath { + NSString* invalidModelPath = @"invalid/path/to/model.ort"; + NSError* err = nil; + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:invalidModelPath + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultUnsuccessful(session, err); +} + +- (void)testRunFailsWithInvalidInput { + NSMutableData* dData = [ORTSessionTest dataWithScalarFloat:1.0f]; + NSMutableData* cData = [ORTSessionTest dataWithScalarFloat:0.0f]; + + ORTValue* d = [ORTSessionTest ortValueWithScalarFloatData:dData]; + ORTValue* c = [ORTSessionTest ortValueWithScalarFloatData:cData]; + + NSError* err = nil; + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:[ORTSessionTest makeSessionOptions] + error:&err]; + ORTAssertNullableResultSuccessful(session, err); + + BOOL runResult = [session runWithInputs:@{@"D" : d} + outputs:@{@"C" : c} + runOptions:[ORTSessionTest makeRunOptions] + error:&err]; + ORTAssertBoolResultUnsuccessful(runResult, err); +} + +- (void)testAppendCoreMLEP { + NSError* err = nil; + ORTSessionOptions* sessionOptions = [ORTSessionTest makeSessionOptions]; + ORTCoreMLExecutionProviderOptions* coreMLOptions = [[ORTCoreMLExecutionProviderOptions alloc] init]; + coreMLOptions.enableOnSubgraphs = YES; // set an arbitrary option + + BOOL appendResult = [sessionOptions appendCoreMLExecutionProviderWithOptions:coreMLOptions + error:&err]; + + if (!ORTIsCoreMLExecutionProviderAvailable()) { + ORTAssertBoolResultUnsuccessful(appendResult, err); + return; + } + + ORTAssertBoolResultSuccessful(appendResult, err); + + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:sessionOptions + error:&err]; + ORTAssertNullableResultSuccessful(session, err); +} + +- (void)testAppendXnnpackEP { + NSError* err = nil; + ORTSessionOptions* sessionOptions = [ORTSessionTest makeSessionOptions]; + ORTXnnpackExecutionProviderOptions* XnnpackOptions = [[ORTXnnpackExecutionProviderOptions alloc] init]; + XnnpackOptions.intra_op_num_threads = 2; + + BOOL appendResult = [sessionOptions appendXnnpackExecutionProviderWithOptions:XnnpackOptions + error:&err]; + // Without xnnpack EP in building also can pass the test + NSString* err_msg = [err localizedDescription]; + if (!appendResult && [err_msg containsString:@"XNNPACK execution provider is not supported in this build. "]) { + return; + } + + ORTAssertBoolResultSuccessful(appendResult, err); + + ORTSession* session = [[ORTSession alloc] initWithEnv:self.ortEnv + modelPath:[ORTSessionTest getAddModelPath] + sessionOptions:sessionOptions + error:&err]; + ORTAssertNullableResultSuccessful(session, err); +} +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/ort_value_test.mm b/objectivec/test/ort_value_test.mm new file mode 100644 index 0000000..734ad39 --- /dev/null +++ b/objectivec/test/ort_value_test.mm @@ -0,0 +1,79 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +#import "ort_value.h" + +#include + +#import "test/assertion_utils.h" + +NS_ASSUME_NONNULL_BEGIN + +@interface ORTValueTest : XCTestCase +@end + +@implementation ORTValueTest + +- (void)setUp { + [super setUp]; + + self.continueAfterFailure = NO; +} + +- (void)testInitTensorOk { + int32_t value = 42; + NSMutableData* data = [[NSMutableData alloc] initWithBytes:&value + length:sizeof(int32_t)]; + NSArray* shape = @[ @1 ]; + + const ORTTensorElementDataType elementType = ORTTensorElementDataTypeInt32; + + NSError* err = nil; + ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data + elementType:elementType + shape:shape + error:&err]; + ORTAssertNullableResultSuccessful(ortValue, err); + + auto checkTensorInfo = [&](ORTTensorTypeAndShapeInfo* tensorInfo) { + XCTAssertEqual(tensorInfo.elementType, elementType); + XCTAssertEqualObjects(tensorInfo.shape, shape); + }; + + ORTValueTypeInfo* typeInfo = [ortValue typeInfoWithError:&err]; + ORTAssertNullableResultSuccessful(typeInfo, err); + XCTAssertEqual(typeInfo.type, ORTValueTypeTensor); + XCTAssertNotNil(typeInfo.tensorTypeAndShapeInfo); + checkTensorInfo(typeInfo.tensorTypeAndShapeInfo); + + ORTTensorTypeAndShapeInfo* tensorInfo = [ortValue tensorTypeAndShapeInfoWithError:&err]; + ORTAssertNullableResultSuccessful(tensorInfo, err); + checkTensorInfo(tensorInfo); + + NSData* actualData = [ortValue tensorDataWithError:&err]; + ORTAssertNullableResultSuccessful(actualData, err); + XCTAssertEqual(actualData.length, sizeof(int32_t)); + int32_t actualValue; + memcpy(&actualValue, actualData.bytes, sizeof(int32_t)); + XCTAssertEqual(actualValue, value); +} + +- (void)testInitTensorFailsWithDataSmallerThanShape { + std::vector values{1, 2, 3, 4}; + NSMutableData* data = [[NSMutableData alloc] initWithBytes:values.data() + length:values.size() * sizeof(int32_t)]; + NSArray* shape = @[ @2, @3 ]; // too large + + NSError* err = nil; + ORTValue* ortValue = [[ORTValue alloc] initWithTensorData:data + elementType:ORTTensorElementDataTypeInt32 + shape:shape + error:&err]; + ORTAssertNullableResultUnsuccessful(ortValue, err); +} + +@end + +NS_ASSUME_NONNULL_END diff --git a/objectivec/test/testdata/gen_models.sh b/objectivec/test/testdata/gen_models.sh new file mode 100755 index 0000000..0a58ca8 --- /dev/null +++ b/objectivec/test/testdata/gen_models.sh @@ -0,0 +1,12 @@ +#!/bin/bash + +set -e + +# Get directory this script is in +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" + +cd ${DIR} + +python3 ./single_add_gen.py + +ORT_CONVERT_ONNX_MODELS_TO_ORT_OPTIMIZATION_LEVEL=basic python3 -m onnxruntime.tools.convert_onnx_models_to_ort --optimization_style=Fixed . diff --git a/objectivec/test/testdata/single_add.basic.ort b/objectivec/test/testdata/single_add.basic.ort new file mode 100644 index 0000000..f622784 Binary files /dev/null and b/objectivec/test/testdata/single_add.basic.ort differ diff --git a/objectivec/test/testdata/single_add.onnx b/objectivec/test/testdata/single_add.onnx new file mode 100644 index 0000000..82e8fe6 Binary files /dev/null and b/objectivec/test/testdata/single_add.onnx differ diff --git a/objectivec/test/testdata/single_add_gen.py b/objectivec/test/testdata/single_add_gen.py new file mode 100644 index 0000000..15b4372 --- /dev/null +++ b/objectivec/test/testdata/single_add_gen.py @@ -0,0 +1,19 @@ +import onnx +from onnx import TensorProto, helper + +graph = helper.make_graph( + [ # nodes + helper.make_node("Add", ["A", "B"], ["C"], "Add"), + ], + "SingleAdd", # name + [ # inputs + helper.make_tensor_value_info("A", TensorProto.FLOAT, [1]), + helper.make_tensor_value_info("B", TensorProto.FLOAT, [1]), + ], + [ # outputs + helper.make_tensor_value_info("C", TensorProto.FLOAT, [1]), + ], +) + +model = helper.make_model(graph, opset_imports=[helper.make_opsetid("", 12)]) +onnx.save(model, r"single_add.onnx") diff --git a/tools/ci_build/github/mac-ios-swift-packaging-pipeline.yml b/tools/ci_build/github/mac-ios-swift-packaging-pipeline.yml index 720d64c..d7658fe 100644 --- a/tools/ci_build/github/mac-ios-swift-packaging-pipeline.yml +++ b/tools/ci_build/github/mac-ios-swift-packaging-pipeline.yml @@ -3,193 +3,111 @@ parameters: displayName: |- Type of build. "normal": A normal build not for publication. - type: string #TODO: add other build types name here + type: string #TODO: add other build types here values: - normal default: normal name: "$(Date:yyyyMMdd)$(Rev:rrr)" # build number format -jobs: -- job: SPMIosPackaging - displayName: "SPM iOS Packaging" +stages: +- stage: SPM iOS local packaging testing + dependsOn: [] + jobs: + - job: SPMIosPackaging + displayName: "SPM iOS Packaging" pool: vmImage: "macOS-13" variables: xcodeVersion: "14.3" - podArchiveName: "" + artifactsNamne: "ios_packaging_artifacts" #TODO: Add `_full` suffix when syncing to latest main timeoutInMinutes: 300 steps: - # - task: InstallAppleCertificate@2 - # inputs: - # certSecureFile: '$(ios_signing_certificate_name)' - # certPwd: '$(ios_signing_certificate_password)' - # keychain: 'temp' - # deleteCert: true - # displayName: 'Install ORT Mobile Test Signing Certificate' - - # - task: InstallAppleProvisioningProfile@1 - # inputs: - # provProfileSecureFile: '$(ios_provision_profile_name)' - # removeProfile: true - # displayName: 'Install ORT Mobile Test Provisioning Profile' - - - task: UsePythonVersion@0 - inputs: - versionSpec: "3.9" - addToPath: true - architecture: "x64" - - template: templates/use-xcode-version.yml parameters: xcodeVersion: ${{ variables.xcodeVersion }} - # - template: templates/install-appcenter.yml - - - script: | - pip install -r tools/ci_build/github/requirements.txt - displayName: "Install Python requirements" - - - bash: | - set -e - - BUILD_TYPE="${{ parameters.BuildType }}" - VERSION_FILE="$(Build.SourcesDirectory)/version.txt" - BASE_VERSION="$(cat "${VERSION_FILE}")" - SHORT_COMMIT_HASH="$(git rev-parse --short HEAD)" - DEV_VERSION="${BASE_VERSION}-dev+$(Build.BuildNumber).${SHORT_COMMIT_HASH}" - - case "${BUILD_TYPE}" in - ("normal") - VERSION="${DEV_VERSION}"; SHOULD_UPLOAD_ARCHIVES="false" ;; - (*) - echo "Invalid build type: ${BUILD_TYPE}"; exit 1 ;; - esac - - # Do not output ##vso[] commands with `set -x` or they may be parsed again and include a trailing quote. - set +x - - set_var() { - local VAR_NAME=${1:?} - local VAR_VALUE=${2:?} - echo "##vso[task.setvariable variable=${VAR_NAME}]${VAR_VALUE}" - echo "${VAR_NAME}: ${VAR_VALUE}" - } - - set_var "ORT_POD_VERSION" "${VERSION}" - set_var "ORT_SHOULD_UPLOAD_ARCHIVES" "${SHOULD_UPLOAD_ARCHIVES}" - displayName: "Set variables" - - # - script: | - # $(Build.SourcesDirectory)/tools/ci_build/github/scripts/install_protobuf.sh -p $(Build.BinariesDirectory)/protobuf_install -d $(Build.SourcesDirectory)/cmake/deps.txt - # displayName: "Build Host Protoc" - - # # create and test full pods - # - script: | - # python tools/ci_build/github/scripts/build_and_assemble_ios_pods.py \ - # --build-dir "$(Build.BinariesDirectory)/ios_framework_full" \ - # --staging-dir "$(Build.BinariesDirectory)/staging" \ - # --pod-version "${ORT_POD_VERSION}" \ - # --test \ - # --variant Full \ - # --build-settings-file tools/ci_build/github/default_full_ios_framework_build_settings.json \ - # -b="--path_to_protoc_exe" -b "$(Build.BinariesDirectory)/protobuf_install/bin/protoc" - # displayName: "[Full] Build iOS framework and assemble pod package files" - - # - script: | - # python tools/ci_build/github/scripts/test_ios_packages.py \ - # --fail_if_cocoapods_missing \ - # --framework_info_file "$(Build.BinariesDirectory)/ios_framework_full/framework_info.json" \ - # --c_framework_dir "$(Build.BinariesDirectory)/ios_framework_full/framework_out" \ - # --variant Full \ - # --test_project_stage_dir "$(Build.BinariesDirectory)/app_center_test_full" \ - # --prepare_test_project_only - # displayName: "[Full] Assemble test project for App Center" - - # - task: Xcode@5 - # inputs: - # actions: 'build-for-testing' - # configuration: 'Debug' - # xcWorkspacePath: '$(Build.BinariesDirectory)/app_center_test_full/ios_package_test/ios_package_test.xcworkspace' - # sdk: 'iphoneos' - # scheme: 'ios_package_test' - # signingOption: 'manual' - # signingIdentity: '$(APPLE_CERTIFICATE_SIGNING_IDENTITY)' - # provisioningProfileName: 'iOS Team Provisioning Profile' - # args: '-derivedDataPath $(Build.BinariesDirectory)/app_center_test_full/ios_package_test/DerivedData' - # workingDirectory: $(Build.BinariesDirectory)/app_center_test_full/ios_package_test/ - # displayName: '[Full] Build iphone arm64 tests' - - # - script: | - # set -e -x - # appcenter test run xcuitest \ - # --app "AI-Frameworks/ORT-Mobile-iOS" \ - # --devices $(app_center_test_devices) \ - # --test-series "master" \ - # --locale "en_US" \ - # --build-dir $(Build.BinariesDirectory)/app_center_test_full/ios_package_test/DerivedData/Build/Products/Debug-iphoneos \ - # --token $(app_center_api_token) - # displayName: "[Full] Run E2E tests on App Center" - - # - task: AzureCLI@2 - # inputs: - # azureSubscription: 'AIInfraBuildOnnxRuntimeOSS' - # scriptType: 'bash' - # scriptLocation: 'scriptPath' - # scriptPath: 'tools/ci_build/github/apple/assemble_ios_packaging_artifacts.sh' - # arguments: >- - # "$(Build.BinariesDirectory)/staging" - # "$(Build.ArtifactStagingDirectory)" - # "$(ORT_POD_VERSION)" - # "$(ORT_SHOULD_UPLOAD_ARCHIVES)" - # displayName: "Assemble artifacts" - - # TODO: Download artifacts from the onnxruntime-ios-packaging pipeline artifacts folder - - # Download artifacts from a specific pipeline. + # Download artifacts from a specific pipeline + # For now, it consumes rel-1.15.0 version ORT iOS Pod which matches the current source code - task: DownloadPipelineArtifact@2 inputs: buildType: 'specific' project: 'Lotus' definition: 995 - buildVersionToDownload: 'latest' + buildVersionToDownload: 'latestFromBranch' + branchName: 'rel-1.15.0' targetPath: '$(Build.ArtifactStagingDirectory)' - script: | set -e -x ls - workingDirectory: '$(Build.ArtifactStagingDirectory)/ios_packaging_artifacts_full' + workingDirectory: '$(Build.ArtifactStagingDirectory)/$(artifactsName)' displayName: "List staged artifacts" - + # 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 - cd "$(Build.ArtifactStagingDirectory)/ios_packaging_artifacts_full" + cd "$(Build.ArtifactStagingDirectory)/$(artifactsName)" ARTIFACTS_LIST=$(ls) - POD_ARCHIVE=$(echo "${ARTIFACTS_LIST}" | sed -n '3p') + POD_ARCHIVE=$(echo "${ARTIFACTS_LIST}" | sed -n '5p') - shasum -a 256 "$(Build.ArtifactStagingDirectory)/ios_packaging_artifacts_full/${POD_ARCHIVE}" + shasum -a 256 "$(Build.ArtifactStagingDirectory)/$(artifactsName)/${POD_ARCHIVE}" cd "$(Build.SourcesDirectory)" - - cp "$(Build.ArtifactStagingDirectory)/ios_packaging_artifacts_full/${POD_ARCHIVE}" swift/ + cp "$(Build.ArtifactStagingDirectory)/$(artifactsName)/${POD_ARCHIVE}" swift/ export ORT_IOS_POD_LOCAL_PATH="swift/${POD_ARCHIVE}" - xcodebuild test -scheme onnxruntime -destination 'platform=iOS Simulator,name=iPhone 14' rm swift/pod-archive-onnxruntime-c-*.zip workingDirectory: "$(Build.SourcesDirectory)" - displayName: "Test Package.swift usage" + displayName: "Print ORT iOS Pod checksum and test Package.swift usage" - # - publish: "$(Build.ArtifactStagingDirectory)" - # artifact: ios_packaging_artifacts - # displayName: "Publish artifacts" + - publish: "$(Build.ArtifactStagingDirectory)/$(artifactName)" + artifact: ios_packaging_artifacts + displayName: "Publish artifacts" - # - template: templates/component-governance-component-detection-steps.yml - # parameters : - # condition : 'succeeded' + - template: templates/component-governance-component-detection-steps.yml + parameters : + condition : 'succeeded' + + - stage: SPM iOS release packaging testing + dependsOn: [] + jobs: + - job: SPMIosPackaging + displayName: "SPM iOS Packaging" + + pool: + vmImage: "macOS-13" + + variables: + xcodeVersion: "14.3" + + timeoutInMinutes: 300 + + steps: + - template: templates/use-xcode-version.yml + parameters: + xcodeVersion: ${{ variables.xcodeVersion }} + + - script: | + set -e -x + VERSION_FILE="$(Build.SourcesDirectory)/version.txt" + SPM_POD_VERSION="$(cat "${VERSION_FILE}")" + shasum -a 256 "https://onnxruntimepackages.z14.web.core.windows.net/pod-archive-onnxruntime-c-${SPM_POD_VERSION}.zip" + workingDirectory: "$(Build.SourcesDirectory)" + displayName: "Print ORT iOS Release Pod checksum" + + - script: | + set -e -x + xcodebuild test -scheme onnxruntime -destination 'platform=iOS Simulator,name=iPhone 14' + workingDirectory: "$(Build.SourcesDirectory)" + displayName: "Test Package.swift usage" + + - template: templates/component-governance-component-detection-steps.yml + parameters : + condition : 'succeeded'