From 55278056ad222a025ffde91d37bf49d202c7fec7 Mon Sep 17 00:00:00 2001 From: Guoyu Wang <62914304+gwang-msft@users.noreply.github.com> Date: Wed, 4 Aug 2021 19:07:27 -0700 Subject: [PATCH] Cherry picks for release - 1.8.2, 2nd attempt (#8620) * Add test for iOS package (#7816) * Add test for iOS package * Add readme * fix pep8 warning * Addressed CR comments, fixed CI failure * Address CR comments * Update readme.md * Update package name and readme, added comments to the podspec * Add podspec template for ios package, update build settings (#7907) * Add podspec template for ios package * minor formatting update * Add spec.source_files for header files * Update spec.public_header_files to spec.source_files * minor update * Add iOS packaging pipeline (#8264) Create a pipeline to produce the iOS package artifacts. * [iOS] Packaging pipeline improvements. (#8324) Updates to the iOS packaging pipeline: - Make it harder to overwrite package archives accidentally when uploading (fails if the archive already exists) - Only upload package archives for release builds - Some clean up * Add metadata_props to ORT model (#8340) * Add metadata_props to ORT model * Minor update * Update python binding, and increase the minimal pipeline size threshold * Fixed a small bug in serializing ir_version * Remove temp ort.py.fbs and add it to .gitignore * Add iOS/macOS static framework (#8357) * Add ability to generate ios static framework * Fix typos * Add pod cache clean, update some comments of previous commit * Fix CI failure with newly added cpuinfo library * Update test model (CoreML requires node has a name) * Addressed CR comments * Fix iOS packaging pipeline failure (#8433) * Fix optimizer crash (#8274) * Update iOS packaging script to default build static framework, disable bitcode (#8533) * default package build to static, disable bitcode * fix pipeline failure * Address CR comments * Add HardSigmoid to mobile packages. Used by PyTorch MobileNet v3 (#8552) * bump the version number to 1.8.2 * Change Windows GPU machine pool to onnxruntime-win-cuda11-0 * [Objective-C API] Fix ORTIsCoreMLExecutionProviderAvailable link error when used from Swift. (#8350) Co-authored-by: Edward Chen <18449977+edgchen1@users.noreply.github.com> Co-authored-by: RandySheriffH <48490400+RandySheriffH@users.noreply.github.com> Co-authored-by: Scott McKay Co-authored-by: Changming Sun --- VERSION_NUMBER | 2 +- cmake/onnxruntime.cmake | 120 +++-- cmake/onnxruntime_providers.cmake | 14 +- .../include/ort_coreml_execution_provider.h | 10 +- .../experimental/fbs/Model.py | 29 +- .../experimental/fbs/StringStringEntry.py | 44 ++ .../core/flatbuffers/schema/.gitignore | 1 + onnxruntime/core/flatbuffers/schema/ort.fbs | 10 +- onnxruntime/core/flatbuffers/schema/ort.fbs.h | 92 +++- onnxruntime/core/graph/model.cc | 39 +- .../core/optimizer/conv_activation_fusion.cc | 18 +- .../test/framework/ort_model_only_test.cc | 38 +- .../test/optimizer/graph_transform_test.cc | 46 +- .../platform/ios/ios_package_test/.gitignore | 26 + .../platform/ios/ios_package_test/Podfile | 12 + .../platform/ios/ios_package_test/README.md | 47 ++ .../project.pbxproj | 470 ++++++++++++++++++ .../contents.xcworkspacedata | 7 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../ios_package_test/AppDelegate.h | 14 + .../ios_package_test/AppDelegate.m | 40 ++ .../Base.lproj/LaunchScreen.storyboard | 25 + .../Base.lproj/Main.storyboard | 24 + .../ios_package_test/Info.plist | 66 +++ .../ios_package_test/ios_package_test/main.m | 18 + .../ios_package_testTests/Info.plist | 22 + .../ios_package_test_cpp_api.mm | 97 ++++ .../ios/ios_package_test/models/sigmoid.ort | Bin 0 -> 1240 bytes .../test/testdata/model_with_metadata.onnx | Bin 0 -> 233 bytes .../test/testdata/model_with_metadata.py | 38 ++ .../testdata/transform/fusion/conv_add.onnx | 44 ++ .../fusion/conv_add_relu_identity.onnx | 46 ++ .../util/include/inference_session_wrapper.h | 4 + tools/ci_build/build.py | 36 +- .../mobile_package.required_operators.config | 5 +- ...bile_package.required_operators.readme.txt | 4 +- .../apple/assemble_ios_packaging_artifacts.sh | 63 +++ .../github/apple/build_ios_framework.py | 13 +- .../github/apple/c/assemble_c_pod_package.py | 73 +++ .../c/onnxruntime-mobile-c.podspec.template | 24 + ...t_mobile_ios_framework_build_settings.json | 3 +- .../github/apple/framework_info.json.template | 4 + .../apple/ios_packaging.requirements.txt | 1 + ...ackage.py => assemble_objc_pod_package.py} | 69 +-- .../onnxruntime-mobile-objc.podspec.template | 9 +- .../github/apple/package_assembly_utils.py | 69 +++ .../github/apple/test_ios_packages.py | 124 +++++ .../azure-pipelines/mac-ios-ci-pipeline.yml | 2 + .../mac-ios-packaging-pipeline.yml | 109 ++++ .../orttraining-win-gpu-ci-pipeline.yml | 2 +- .../azure-pipelines/win-gpu-ci-pipeline.yml | 2 +- .../win-gpu-reduce-op-ci-pipeline.yml | 2 +- ...rt_android_baseline_and_report_bin_size.sh | 2 +- 53 files changed, 1947 insertions(+), 140 deletions(-) create mode 100644 onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/StringStringEntry.py create mode 100644 onnxruntime/core/flatbuffers/schema/.gitignore create mode 100644 onnxruntime/test/platform/ios/ios_package_test/.gitignore create mode 100644 onnxruntime/test/platform/ios/ios_package_test/Podfile create mode 100644 onnxruntime/test/platform/ios/ios_package_test/README.md create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.pbxproj create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.h create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.m create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/LaunchScreen.storyboard create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/Main.storyboard create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Info.plist create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_test/main.m create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/Info.plist create mode 100644 onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/ios_package_test_cpp_api.mm create mode 100644 onnxruntime/test/platform/ios/ios_package_test/models/sigmoid.ort create mode 100644 onnxruntime/test/testdata/model_with_metadata.onnx create mode 100644 onnxruntime/test/testdata/model_with_metadata.py create mode 100644 onnxruntime/test/testdata/transform/fusion/conv_add.onnx create mode 100644 onnxruntime/test/testdata/transform/fusion/conv_add_relu_identity.onnx create mode 100755 tools/ci_build/github/apple/assemble_ios_packaging_artifacts.sh create mode 100644 tools/ci_build/github/apple/c/assemble_c_pod_package.py create mode 100644 tools/ci_build/github/apple/c/onnxruntime-mobile-c.podspec.template create mode 100644 tools/ci_build/github/apple/framework_info.json.template create mode 100644 tools/ci_build/github/apple/ios_packaging.requirements.txt rename tools/ci_build/github/apple/objectivec/{assemble_pod_package.py => assemble_objc_pod_package.py} (58%) create mode 100644 tools/ci_build/github/apple/package_assembly_utils.py create mode 100644 tools/ci_build/github/apple/test_ios_packages.py create mode 100644 tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml diff --git a/VERSION_NUMBER b/VERSION_NUMBER index a8fdfda1c7824..53adb84c8220e 100644 --- a/VERSION_NUMBER +++ b/VERSION_NUMBER @@ -1 +1 @@ -1.8.1 +1.8.2 diff --git a/cmake/onnxruntime.cmake b/cmake/onnxruntime.cmake index ec7f2b06de984..524a91664b4de 100644 --- a/cmake/onnxruntime.cmake +++ b/cmake/onnxruntime.cmake @@ -66,10 +66,18 @@ elseif(onnxruntime_BUILD_APPLE_FRAMEWORK) "${CMAKE_CURRENT_BINARY_DIR}/generated_source.c" ) + # create Info.plist for the framework and podspec for CocoaPods (optional) set(MACOSX_FRAMEWORK_NAME "onnxruntime") set(MACOSX_FRAMEWORK_IDENTIFIER "com.microsoft.onnxruntime") - configure_file(${REPO_ROOT}/cmake/Info.plist.in ${CMAKE_CURRENT_BINARY_DIR}/Info.plist) - + # Need to include CoreML as a weaklink for CocoaPods package if the EP is enabled + if(onnxruntime_USE_COREML) + set(APPLE_WEAK_FRAMEWORK "\\\"CoreML\\\"") + endif() + set(INFO_PLIST_PATH "${CMAKE_CURRENT_BINARY_DIR}/Info.plist") + configure_file(${REPO_ROOT}/cmake/Info.plist.in ${INFO_PLIST_PATH}) + configure_file( + ${REPO_ROOT}/tools/ci_build/github/apple/framework_info.json.template + ${CMAKE_CURRENT_BINARY_DIR}/framework_info.json) set_target_properties(onnxruntime PROPERTIES FRAMEWORK TRUE FRAMEWORK_VERSION A @@ -145,40 +153,50 @@ if(CMAKE_SYSTEM_NAME STREQUAL "Android" AND onnxruntime_BUILD_JAVA) # copy the header files one by one foreach(h_ ${ANDROID_AAR_HEADERS}) get_filename_component(HEADER_NAME_ ${h_} NAME) - configure_file(${h_} ${ANDROID_HEADERS_DIR}/${HEADER_NAME_} COPYONLY) + add_custom_command(TARGET onnxruntime POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${h_} ${ANDROID_HEADERS_DIR}/${HEADER_NAME_}) endforeach() endif() -target_link_libraries(onnxruntime PRIVATE - onnxruntime_session - ${onnxruntime_libs} - ${PROVIDERS_ACL} - ${PROVIDERS_ARMNN} - ${PROVIDERS_COREML} - ${PROVIDERS_DML} - ${PROVIDERS_MIGRAPHX} - ${PROVIDERS_NNAPI} - ${PROVIDERS_NUPHAR} - ${PROVIDERS_RKNPU} - ${PROVIDERS_ROCM} - ${PROVIDERS_VITISAI} - ${PROVIDERS_INTERNAL_TESTING} - ${onnxruntime_winml} - onnxruntime_optimizer - onnxruntime_providers - onnxruntime_util - ${onnxruntime_tvm_libs} - onnxruntime_framework - onnxruntime_graph - onnxruntime_common - onnxruntime_mlas - onnxruntime_flatbuffers - ${onnxruntime_EXTERNAL_LIBRARIES}) +set(onnxruntime_INTERNAL_LIBRARIES + onnxruntime_session + ${onnxruntime_libs} + ${PROVIDERS_ACL} + ${PROVIDERS_ARMNN} + ${PROVIDERS_COREML} + ${PROVIDERS_DML} + ${PROVIDERS_MIGRAPHX} + ${PROVIDERS_NNAPI} + ${PROVIDERS_NUPHAR} + ${PROVIDERS_RKNPU} + ${PROVIDERS_ROCM} + ${PROVIDERS_VITISAI} + ${PROVIDERS_INTERNAL_TESTING} + ${onnxruntime_winml} + onnxruntime_optimizer + onnxruntime_providers + onnxruntime_util + ${onnxruntime_tvm_libs} + onnxruntime_framework + onnxruntime_graph + onnxruntime_common + onnxruntime_mlas + onnxruntime_flatbuffers +) if (onnxruntime_ENABLE_LANGUAGE_INTEROP_OPS) - target_link_libraries(onnxruntime PRIVATE onnxruntime_language_interop onnxruntime_pyop) + list(APPEND onnxruntime_INTERNAL_LIBRARIES + onnxruntime_language_interop + onnxruntime_pyop + ) endif() +# If you are linking a new library, please add it to the list onnxruntime_INTERNAL_LIBRARIES or onnxruntime_EXTERNAL_LIBRARIES, +# Please do not add a library directly to the target_link_libraries command +target_link_libraries(onnxruntime PRIVATE + ${onnxruntime_INTERNAL_LIBRARIES} + ${onnxruntime_EXTERNAL_LIBRARIES} +) + set_property(TARGET onnxruntime APPEND_STRING PROPERTY LINK_FLAGS ${ONNXRUNTIME_SO_LINK_FLAG} ${onnxruntime_DELAYLOAD_FLAGS}) set_target_properties(onnxruntime PROPERTIES LINK_DEPENDS ${SYMBOL_FILE}) @@ -196,3 +214,47 @@ set_target_properties(onnxruntime PROPERTIES FOLDER "ONNXRuntime") if (WINDOWS_STORE) target_link_options(onnxruntime PRIVATE /DELAYLOAD:api-ms-win-core-libraryloader-l1-2-1.dll) endif() + +# Assemble the Apple static framework (iOS and macOS) +if(onnxruntime_BUILD_APPLE_FRAMEWORK) + set(STATIC_LIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/static_libraries) + file(MAKE_DIRECTORY ${STATIC_LIB_DIR}) + + # Remove the existing files in the STATIC_LIB_DIR folder + file(GLOB _OLD_STATIC_LIBS ${STATIC_LIB_DIR}/*.a) + file(REMOVE "${_OLD_STATIC_LIBS}") + + # Go through all the static libraries, and create symbolic links + foreach(_LIB ${onnxruntime_INTERNAL_LIBRARIES} ${onnxruntime_EXTERNAL_LIBRARIES}) + GET_TARGET_PROPERTY(_LIB_TYPE ${_LIB} TYPE) + if(_LIB_TYPE STREQUAL "STATIC_LIBRARY") + add_custom_command(TARGET onnxruntime POST_BUILD COMMAND ${CMAKE_COMMAND} -E create_symlink $ ${STATIC_LIB_DIR}/$) + endif() + endforeach() + + if(${CMAKE_SYSTEM_NAME} STREQUAL "iOS") + set(STATIC_FRAMEWORK_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}-${CMAKE_OSX_SYSROOT}) + else() # macOS + set(STATIC_FRAMEWORK_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}) + endif() + + # Assemble the static framework + set(STATIC_FRAMEWORK_DIR ${STATIC_FRAMEWORK_OUTPUT_DIR}/static_framework/onnxruntime.framework) + set(STATIC_FRAMEWORK_HEADER_DIR ${STATIC_FRAMEWORK_DIR}/Headers) + file(MAKE_DIRECTORY ${STATIC_FRAMEWORK_DIR}) + # Remove all files under STATIC_FRAMEWORK_DIR (if any) + file(GLOB_RECURSE _OLD_STATIC_FRAMEWORK ${STATIC_FRAMEWORK_DIR}/*.*) + file(REMOVE "${_OLD_STATIC_FRAMEWORK}") + + file(MAKE_DIRECTORY ${STATIC_FRAMEWORK_HEADER_DIR}) + + # copy the header files one by one, and the Info.plist + foreach(h_ ${APPLE_FRAMEWORK_HEADERS}) + get_filename_component(HEADER_NAME_ ${h_} NAME) + add_custom_command(TARGET onnxruntime POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${h_} ${STATIC_FRAMEWORK_HEADER_DIR}/${HEADER_NAME_}) + endforeach() + add_custom_command(TARGET onnxruntime POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${INFO_PLIST_PATH} ${STATIC_FRAMEWORK_DIR}/Info.plist) + + # link the static library + add_custom_command(TARGET onnxruntime POST_BUILD COMMAND libtool -static -o ${STATIC_FRAMEWORK_DIR}/onnxruntime *.a WORKING_DIRECTORY ${STATIC_LIB_DIR}) +endif() diff --git a/cmake/onnxruntime_providers.cmake b/cmake/onnxruntime_providers.cmake index 6ba1be3d9978f..c8b0ae6b654cc 100644 --- a/cmake/onnxruntime_providers.cmake +++ b/cmake/onnxruntime_providers.cmake @@ -57,16 +57,16 @@ file(GLOB onnxruntime_providers_common_srcs CONFIGURE_DEPENDS ) if(onnxruntime_USE_NUPHAR) - set(PROVIDERS_NUPHAR onnxruntime_providers_nuphar) + set(PROVIDERS_NUPHAR onnxruntime_providers_nuphar) endif() if(onnxruntime_USE_VITISAI) - set(PROVIDERS_VITISAI onnxruntime_providers_vitisai) + set(PROVIDERS_VITISAI onnxruntime_providers_vitisai) endif() if(onnxruntime_USE_CUDA) set(PROVIDERS_CUDA onnxruntime_providers_cuda) endif() if(onnxruntime_USE_COREML) - set(PROVIDERS_COREML onnxruntime_providers_coreml) + set(PROVIDERS_COREML onnxruntime_providers_coreml onnxruntime_coreml_proto) endif() if(onnxruntime_USE_NNAPI_BUILTIN) set(PROVIDERS_NNAPI onnxruntime_providers_nnapi) @@ -75,7 +75,7 @@ if(onnxruntime_USE_RKNPU) set(PROVIDERS_RKNPU onnxruntime_providers_rknpu) endif() if(onnxruntime_USE_DML) - set(PROVIDERS_DML onnxruntime_providers_dml) + set(PROVIDERS_DML onnxruntime_providers_dml) endif() if(onnxruntime_USE_MIGRAPHX) set(PROVIDERS_MIGRAPHX onnxruntime_providers_migraphx) @@ -84,13 +84,13 @@ if(onnxruntime_USE_WINML) set(PROVIDERS_WINML onnxruntime_providers_winml) endif() if(onnxruntime_USE_ACL) - set(PROVIDERS_ACL onnxruntime_providers_acl) + set(PROVIDERS_ACL onnxruntime_providers_acl) endif() if(onnxruntime_USE_ARMNN) - set(PROVIDERS_ARMNN onnxruntime_providers_armnn) + set(PROVIDERS_ARMNN onnxruntime_providers_armnn) endif() if(onnxruntime_USE_ROCM) - set(PROVIDERS_ROCM onnxruntime_providers_rocm) + set(PROVIDERS_ROCM onnxruntime_providers_rocm) endif() source_group(TREE ${ONNXRUNTIME_ROOT}/core FILES ${onnxruntime_providers_common_srcs} ${onnxruntime_providers_srcs}) diff --git a/objectivec/include/ort_coreml_execution_provider.h b/objectivec/include/ort_coreml_execution_provider.h index 94043f92fa21d..a015b6fd60c8f 100644 --- a/objectivec/include/ort_coreml_execution_provider.h +++ b/objectivec/include/ort_coreml_execution_provider.h @@ -5,13 +5,21 @@ #import "ort_session.h" -NS_ASSUME_NONNULL_BEGIN +#ifdef __cplusplus +extern "C" { +#endif /** * Gets whether the CoreML execution provider is available. */ BOOL ORTIsCoreMLExecutionProviderAvailable(void); +#ifdef __cplusplus +} +#endif + +NS_ASSUME_NONNULL_BEGIN + /** * Options for configuring the CoreML execution provider. */ diff --git a/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/Model.py b/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/Model.py index 879db8415be76..814abed71a2b4 100644 --- a/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/Model.py +++ b/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/Model.py @@ -109,7 +109,32 @@ def GraphDocString(self): return self._tab.String(o + self._tab.Pos) return None -def ModelStart(builder): builder.StartObject(9) + # Model + def MetadataProps(self, j): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(22)) + if o != 0: + x = self._tab.Vector(o) + x += flatbuffers.number_types.UOffsetTFlags.py_type(j) * 4 + x = self._tab.Indirect(x) + from ort_flatbuffers_py.experimental.fbs.StringStringEntry import StringStringEntry + obj = StringStringEntry() + obj.Init(self._tab.Bytes, x) + return obj + return None + + # Model + def MetadataPropsLength(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(22)) + if o != 0: + return self._tab.VectorLen(o) + return 0 + + # Model + def MetadataPropsIsNone(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(22)) + return o == 0 + +def ModelStart(builder): builder.StartObject(10) def ModelAddIrVersion(builder, irVersion): builder.PrependInt64Slot(0, irVersion, 0) def ModelAddOpsetImport(builder, opsetImport): builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(opsetImport), 0) def ModelStartOpsetImportVector(builder, numElems): return builder.StartVector(4, numElems, 4) @@ -120,4 +145,6 @@ def ModelAddModelVersion(builder, modelVersion): builder.PrependInt64Slot(5, mod def ModelAddDocString(builder, docString): builder.PrependUOffsetTRelativeSlot(6, flatbuffers.number_types.UOffsetTFlags.py_type(docString), 0) def ModelAddGraph(builder, graph): builder.PrependUOffsetTRelativeSlot(7, flatbuffers.number_types.UOffsetTFlags.py_type(graph), 0) def ModelAddGraphDocString(builder, graphDocString): builder.PrependUOffsetTRelativeSlot(8, flatbuffers.number_types.UOffsetTFlags.py_type(graphDocString), 0) +def ModelAddMetadataProps(builder, metadataProps): builder.PrependUOffsetTRelativeSlot(9, flatbuffers.number_types.UOffsetTFlags.py_type(metadataProps), 0) +def ModelStartMetadataPropsVector(builder, numElems): return builder.StartVector(4, numElems, 4) def ModelEnd(builder): return builder.EndObject() diff --git a/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/StringStringEntry.py b/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/StringStringEntry.py new file mode 100644 index 0000000000000..4363d07588718 --- /dev/null +++ b/onnxruntime/core/flatbuffers/ort_flatbuffers_py/experimental/fbs/StringStringEntry.py @@ -0,0 +1,44 @@ +# automatically generated by the FlatBuffers compiler, do not modify + +# namespace: fbs + +import flatbuffers +from flatbuffers.compat import import_numpy +np = import_numpy() + +class StringStringEntry(object): + __slots__ = ['_tab'] + + @classmethod + def GetRootAsStringStringEntry(cls, buf, offset): + n = flatbuffers.encode.Get(flatbuffers.packer.uoffset, buf, offset) + x = StringStringEntry() + x.Init(buf, n + offset) + return x + + @classmethod + def StringStringEntryBufferHasIdentifier(cls, buf, offset, size_prefixed=False): + return flatbuffers.util.BufferHasIdentifier(buf, offset, b"\x4F\x52\x54\x4D", size_prefixed=size_prefixed) + + # StringStringEntry + def Init(self, buf, pos): + self._tab = flatbuffers.table.Table(buf, pos) + + # StringStringEntry + def Key(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(4)) + if o != 0: + return self._tab.String(o + self._tab.Pos) + return None + + # StringStringEntry + def Value(self): + o = flatbuffers.number_types.UOffsetTFlags.py_type(self._tab.Offset(6)) + if o != 0: + return self._tab.String(o + self._tab.Pos) + return None + +def StringStringEntryStart(builder): builder.StartObject(2) +def StringStringEntryAddKey(builder, key): builder.PrependUOffsetTRelativeSlot(0, flatbuffers.number_types.UOffsetTFlags.py_type(key), 0) +def StringStringEntryAddValue(builder, value): builder.PrependUOffsetTRelativeSlot(1, flatbuffers.number_types.UOffsetTFlags.py_type(value), 0) +def StringStringEntryEnd(builder): return builder.EndObject() diff --git a/onnxruntime/core/flatbuffers/schema/.gitignore b/onnxruntime/core/flatbuffers/schema/.gitignore new file mode 100644 index 0000000000000..51484e9aa29f2 --- /dev/null +++ b/onnxruntime/core/flatbuffers/schema/.gitignore @@ -0,0 +1 @@ +ort.py.fbs diff --git a/onnxruntime/core/flatbuffers/schema/ort.fbs b/onnxruntime/core/flatbuffers/schema/ort.fbs index fa4e216cda4c5..12ac33604b647 100644 --- a/onnxruntime/core/flatbuffers/schema/ort.fbs +++ b/onnxruntime/core/flatbuffers/schema/ort.fbs @@ -194,6 +194,11 @@ table Graph{ sparse_initializers:[SparseTensor]; } +table StringStringEntry { + key:string; + value:string; +} + table Model { ir_version:int64; opset_import:[OperatorSetId]; @@ -202,10 +207,11 @@ table Model { domain:string; model_version:int64; doc_string:string; - + graph:Graph; - + graph_doc_string:string; + metadata_props:[StringStringEntry]; } table KernelCreateInfos { diff --git a/onnxruntime/core/flatbuffers/schema/ort.fbs.h b/onnxruntime/core/flatbuffers/schema/ort.fbs.h index a56461d594858..d0f9083c89540 100644 --- a/onnxruntime/core/flatbuffers/schema/ort.fbs.h +++ b/onnxruntime/core/flatbuffers/schema/ort.fbs.h @@ -57,6 +57,9 @@ struct AttributeBuilder; struct Graph; struct GraphBuilder; +struct StringStringEntry; +struct StringStringEntryBuilder; + struct Model; struct ModelBuilder; @@ -1805,6 +1808,72 @@ inline flatbuffers::Offset CreateGraphDirect( sparse_initializers__); } +struct StringStringEntry FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { + typedef StringStringEntryBuilder Builder; + enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { + VT_KEY = 4, + VT_VALUE = 6 + }; + const flatbuffers::String *key() const { + return GetPointer(VT_KEY); + } + const flatbuffers::String *value() const { + return GetPointer(VT_VALUE); + } + bool Verify(flatbuffers::Verifier &verifier) const { + return VerifyTableStart(verifier) && + VerifyOffset(verifier, VT_KEY) && + verifier.VerifyString(key()) && + VerifyOffset(verifier, VT_VALUE) && + verifier.VerifyString(value()) && + verifier.EndTable(); + } +}; + +struct StringStringEntryBuilder { + typedef StringStringEntry Table; + flatbuffers::FlatBufferBuilder &fbb_; + flatbuffers::uoffset_t start_; + void add_key(flatbuffers::Offset key) { + fbb_.AddOffset(StringStringEntry::VT_KEY, key); + } + void add_value(flatbuffers::Offset value) { + fbb_.AddOffset(StringStringEntry::VT_VALUE, value); + } + explicit StringStringEntryBuilder(flatbuffers::FlatBufferBuilder &_fbb) + : fbb_(_fbb) { + start_ = fbb_.StartTable(); + } + StringStringEntryBuilder &operator=(const StringStringEntryBuilder &); + flatbuffers::Offset Finish() { + const auto end = fbb_.EndTable(start_); + auto o = flatbuffers::Offset(end); + return o; + } +}; + +inline flatbuffers::Offset CreateStringStringEntry( + flatbuffers::FlatBufferBuilder &_fbb, + flatbuffers::Offset key = 0, + flatbuffers::Offset value = 0) { + StringStringEntryBuilder builder_(_fbb); + builder_.add_value(value); + builder_.add_key(key); + return builder_.Finish(); +} + +inline flatbuffers::Offset CreateStringStringEntryDirect( + flatbuffers::FlatBufferBuilder &_fbb, + const char *key = nullptr, + const char *value = nullptr) { + auto key__ = key ? _fbb.CreateString(key) : 0; + auto value__ = value ? _fbb.CreateString(value) : 0; + return onnxruntime::experimental::fbs::CreateStringStringEntry( + _fbb, + key__, + value__); +} + struct Model FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { typedef ModelBuilder Builder; enum FlatBuffersVTableOffset FLATBUFFERS_VTABLE_UNDERLYING_TYPE { @@ -1816,7 +1885,8 @@ struct Model FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { VT_MODEL_VERSION = 14, VT_DOC_STRING = 16, VT_GRAPH = 18, - VT_GRAPH_DOC_STRING = 20 + VT_GRAPH_DOC_STRING = 20, + VT_METADATA_PROPS = 22 }; int64_t ir_version() const { return GetField(VT_IR_VERSION, 0); @@ -1845,6 +1915,9 @@ struct Model FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { const flatbuffers::String *graph_doc_string() const { return GetPointer(VT_GRAPH_DOC_STRING); } + const flatbuffers::Vector> *metadata_props() const { + return GetPointer> *>(VT_METADATA_PROPS); + } bool Verify(flatbuffers::Verifier &verifier) const { return VerifyTableStart(verifier) && VerifyField(verifier, VT_IR_VERSION) && @@ -1864,6 +1937,9 @@ struct Model FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { verifier.VerifyTable(graph()) && VerifyOffset(verifier, VT_GRAPH_DOC_STRING) && verifier.VerifyString(graph_doc_string()) && + VerifyOffset(verifier, VT_METADATA_PROPS) && + verifier.VerifyVector(metadata_props()) && + verifier.VerifyVectorOfTables(metadata_props()) && verifier.EndTable(); } }; @@ -1899,6 +1975,9 @@ struct ModelBuilder { void add_graph_doc_string(flatbuffers::Offset graph_doc_string) { fbb_.AddOffset(Model::VT_GRAPH_DOC_STRING, graph_doc_string); } + void add_metadata_props(flatbuffers::Offset>> metadata_props) { + fbb_.AddOffset(Model::VT_METADATA_PROPS, metadata_props); + } explicit ModelBuilder(flatbuffers::FlatBufferBuilder &_fbb) : fbb_(_fbb) { start_ = fbb_.StartTable(); @@ -1921,10 +2000,12 @@ inline flatbuffers::Offset CreateModel( int64_t model_version = 0, flatbuffers::Offset doc_string = 0, flatbuffers::Offset graph = 0, - flatbuffers::Offset graph_doc_string = 0) { + flatbuffers::Offset graph_doc_string = 0, + flatbuffers::Offset>> metadata_props = 0) { ModelBuilder builder_(_fbb); builder_.add_model_version(model_version); builder_.add_ir_version(ir_version); + builder_.add_metadata_props(metadata_props); builder_.add_graph_doc_string(graph_doc_string); builder_.add_graph(graph); builder_.add_doc_string(doc_string); @@ -1945,13 +2026,15 @@ inline flatbuffers::Offset CreateModelDirect( int64_t model_version = 0, const char *doc_string = nullptr, flatbuffers::Offset graph = 0, - const char *graph_doc_string = nullptr) { + const char *graph_doc_string = nullptr, + const std::vector> *metadata_props = nullptr) { auto opset_import__ = opset_import ? _fbb.CreateVector>(*opset_import) : 0; auto producer_name__ = producer_name ? _fbb.CreateString(producer_name) : 0; auto producer_version__ = producer_version ? _fbb.CreateString(producer_version) : 0; auto domain__ = domain ? _fbb.CreateString(domain) : 0; auto doc_string__ = doc_string ? _fbb.CreateString(doc_string) : 0; auto graph_doc_string__ = graph_doc_string ? _fbb.CreateString(graph_doc_string) : 0; + auto metadata_props__ = metadata_props ? _fbb.CreateVector>(*metadata_props) : 0; return onnxruntime::experimental::fbs::CreateModel( _fbb, ir_version, @@ -1962,7 +2045,8 @@ inline flatbuffers::Offset CreateModelDirect( model_version, doc_string__, graph, - graph_doc_string__); + graph_doc_string__, + metadata_props__); } struct KernelCreateInfos FLATBUFFERS_FINAL_CLASS : private flatbuffers::Table { diff --git a/onnxruntime/core/graph/model.cc b/onnxruntime/core/graph/model.cc index 302b37bac4af0..0aa43ddcf4ed2 100644 --- a/onnxruntime/core/graph/model.cc +++ b/onnxruntime/core/graph/model.cc @@ -652,18 +652,34 @@ common::Status Model::SaveToOrtFormat(flatbuffers::FlatBufferBuilder& builder, } auto op_set_ids = builder.CreateVector(op_set_ids_vec); + flatbuffers::Offset>> + metadata_props{0}; + + // We will not serialize an empty metadata_props + if (!model_metadata_.empty()) { + std::vector> metadata_props_vec; + metadata_props_vec.reserve(model_metadata_.size()); + for (const auto& prop : model_metadata_) { + metadata_props_vec.push_back( + fbs::CreateStringStringEntryDirect(builder, prop.first.c_str(), prop.second.c_str())); + } + metadata_props = builder.CreateVector(metadata_props_vec); + } + flatbuffers::Offset fbs_graph; ORT_RETURN_IF_ERROR(graph_->SaveToOrtFormat(builder, fbs_graph)); fbs::ModelBuilder mb(builder); - mb.add_ir_version(model_proto_.ir_version()); + mb.add_ir_version(IrVersion()); mb.add_opset_import(op_set_ids); mb.add_producer_name(producer_name); mb.add_producer_version(producer_version); mb.add_domain(domain); - mb.add_model_version(model_proto_.model_version()); + mb.add_model_version(ModelVersion()); mb.add_doc_string(doc_string); mb.add_graph_doc_string(graph_doc_string); + mb.add_metadata_props(metadata_props); mb.add_graph(fbs_graph); // add graph @@ -686,6 +702,18 @@ common::Status Model::LoadFromOrtFormat(const fbs::Model& fbs_model, std::unique_ptr& model) { model.reset(new Model()); + // Load the model metadata + if (const auto* fbs_metadata_props = fbs_model.metadata_props()) { + model->model_metadata_.reserve(fbs_metadata_props->size()); + for (const auto* prop : *fbs_metadata_props) { + ORT_RETURN_IF(nullptr == prop, "Null entry in meta_props. Invalid ORT format model."); + std::string key, value; + experimental::utils::LoadStringFromOrtFormat(key, prop->key()); + experimental::utils::LoadStringFromOrtFormat(value, prop->value()); + model->model_metadata_.insert({key, value}); + } + } + #if !defined(ORT_MINIMAL_BUILD) LOAD_STR_FROM_ORT_FORMAT(model->model_proto_, producer_name, fbs_model.producer_name()); LOAD_STR_FROM_ORT_FORMAT(model->model_proto_, producer_version, fbs_model.producer_version()); @@ -703,6 +731,13 @@ common::Status Model::LoadFromOrtFormat(const fbs::Model& fbs_model, schema_registry->RegisterRegistry(schema_collection); } } + + // Populate the metadata to model_proto + for (auto& metadata : model->model_metadata_) { + const gsl::not_null prop{model->model_proto_.add_metadata_props()}; + prop->set_key(metadata.first); + prop->set_value(metadata.second); + } #else experimental::utils::LoadStringFromOrtFormat(model->producer_name_, fbs_model.producer_name()); experimental::utils::LoadStringFromOrtFormat(model->producer_version_, fbs_model.producer_version()); diff --git a/onnxruntime/core/optimizer/conv_activation_fusion.cc b/onnxruntime/core/optimizer/conv_activation_fusion.cc index 52351ebabcfd4..40361e9f5f881 100644 --- a/onnxruntime/core/optimizer/conv_activation_fusion.cc +++ b/onnxruntime/core/optimizer/conv_activation_fusion.cc @@ -101,7 +101,7 @@ Status ConvActivationFusion::ApplyImpl(Graph& graph, bool& modified, int graph_l } if (node->GetExecutionProviderType() == onnxruntime::kCudaExecutionProvider) { - if (node->InputDefs()[0]->TypeAsProto()->tensor_type().elem_type() != + if (node->InputDefs()[0]->TypeAsProto()->tensor_type().elem_type() != ONNX_NAMESPACE::TensorProto_DataType_FLOAT) { continue; } @@ -121,24 +121,32 @@ Status ConvActivationFusion::ApplyImpl(Graph& graph, bool& modified, int graph_l graph_utils::FinalizeNodeFusion(graph, {conv_node, act_node}, fused_conv); modified = true; } else if (graph_utils::IsSupportedOptypeVersionAndDomain(next_node, "Add", {6, 7, 13, 14})) { + if (next_node.GetOutputEdgesCount() != 1) { + continue; + } const auto& last_node = *(next_node.OutputNodesBegin()); if (last_node.GetExecutionProviderType() != node->GetExecutionProviderType()) { continue; } - if (graph_utils::IsSupportedOptypeVersionAndDomain(last_node, "Relu", {6, 13, 14}) && - next_node.GetOutputEdgesCount() == 1) { + if (graph_utils::IsSupportedOptypeVersionAndDomain(last_node, "Relu", {6, 13, 14})) { Node& conv_node = *node; Node& add_node = *graph.GetNode(next_node.Index()); Node& act_node = *graph.GetNode(last_node.Index()); auto conv_inputs = conv_node.MutableInputDefs(); auto conv_outputs = conv_node.MutableOutputDefs(); auto add_inputs = add_node.MutableInputDefs(); + int32_t dependent = 0, independent = 0; for (auto add_input : add_inputs) { - if (add_input->Name() != conv_outputs[0]->Name()) { + if (add_input->Name() == conv_outputs[0]->Name()) { + dependent++; + } else { conv_inputs.push_back(add_input); - break; + independent++; } } + if (dependent != 1 || independent != 1) { + continue; + } auto node_name = graph.GenerateNodeName(conv_node.Name() + "_" + add_node.Name() + "_" + act_node.Name()); diff --git a/onnxruntime/test/framework/ort_model_only_test.cc b/onnxruntime/test/framework/ort_model_only_test.cc index 6a60e9e9c3675..f79a38afb52ab 100644 --- a/onnxruntime/test/framework/ort_model_only_test.cc +++ b/onnxruntime/test/framework/ort_model_only_test.cc @@ -193,6 +193,31 @@ static void CompareGraphAndSessionState(const InferenceSessionWrapper& session_o } } +static void CompareSessionMetadata(const InferenceSessionWrapper& session_object_1, + const InferenceSessionWrapper& session_object_2) { + const auto pair_1 = session_object_1.GetModelMetadata(); + ASSERT_STATUS_OK(pair_1.first); + const auto& metadata_1 = *pair_1.second; + const auto& model_1 = session_object_1.GetModel(); + + const auto pair_2 = session_object_2.GetModelMetadata(); + ASSERT_STATUS_OK(pair_2.first); + const auto& metadata_2 = *pair_2.second; + const auto& model_2 = session_object_2.GetModel(); + + ASSERT_EQ(metadata_1.producer_name, metadata_2.producer_name); + // ORT format does not have graph name + // ASSERT_EQ(metadata_1.graph_name, metadata_2.graph_name); + ASSERT_EQ(metadata_1.domain, metadata_2.domain); + ASSERT_EQ(metadata_1.description, metadata_2.description); + ASSERT_EQ(metadata_1.graph_description, metadata_2.graph_description); + ASSERT_EQ(metadata_1.version, metadata_2.version); + ASSERT_EQ(metadata_1.custom_metadata_map, metadata_2.custom_metadata_map); + + ASSERT_EQ(model_1.IrVersion(), model_2.IrVersion()); + ASSERT_EQ(model_1.ProducerVersion(), model_2.ProducerVersion()); +} + static void SaveAndCompareModels(const std::string& onnx_file, const std::basic_string& ort_file) { SessionOptions so; so.session_logid = "SerializeToOrtFormat"; @@ -216,6 +241,7 @@ static void SaveAndCompareModels(const std::string& onnx_file, const std::basic_ ASSERT_STATUS_OK(session_object2.Load(ort_file)); ASSERT_STATUS_OK(session_object2.Initialize()); + CompareSessionMetadata(session_object, session_object2); CompareGraphAndSessionState(session_object, session_object2); } @@ -240,10 +266,10 @@ static void DumpOrtModelAsJson(const std::string& model_uri) { } */ -/* +/* Validate we don't run optimizers on an ORT format model in a full build. The optimizers will remove nodes, -which will create a mismatch with the saved kernel information and result in a runtime error. -We could take steps to handle this scenario in a full build, but for consistency we choose to not run optimizers +which will create a mismatch with the saved kernel information and result in a runtime error. +We could take steps to handle this scenario in a full build, but for consistency we choose to not run optimizers on any ORT format model. */ TEST(OrtModelOnlyTests, ValidateOrtFormatModelDoesNotRunOptimizersInFullBuild) { @@ -325,6 +351,12 @@ TEST(OrtModelOnlyTests, TensorAttributeSerialization) { SaveAndCompareModels("testdata/ort_minimal_test_models/tensor_attribute.onnx", ort_file); } +TEST(OrtModelOnlyTests, MetadataSerialization) { + const std::basic_string ort_file = + ORT_TSTR("testdata/model_with_metadata.onnx.test_output.ort"); + SaveAndCompareModels("testdata/model_with_metadata.onnx", ort_file); +} + #if !defined(DISABLE_ML_OPS) TEST(OrtModelOnlyTests, SerializeToOrtFormatMLOps) { const std::basic_string ort_file = diff --git a/onnxruntime/test/optimizer/graph_transform_test.cc b/onnxruntime/test/optimizer/graph_transform_test.cc index 934ddf0982cc0..4b4fb9ba5e655 100644 --- a/onnxruntime/test/optimizer/graph_transform_test.cc +++ b/onnxruntime/test/optimizer/graph_transform_test.cc @@ -658,6 +658,7 @@ TEST_F(GraphTransformationTests, NotWhereFusion) { } #if defined(USE_CUDA) && !defined(DISABLE_CONTRIB_OPS) +// Conv->Add->Relu will be transformed to FusedConv TEST_F(GraphTransformationTests, FuseCudaConvAddRelu) { auto model_uri = MODEL_FOLDER "fusion/conv_add_relu.onnx"; std::shared_ptr p_model; @@ -673,9 +674,50 @@ TEST_F(GraphTransformationTests, FuseCudaConvAddRelu) { graph_transformation_mgr.Register(std::make_unique(), TransformerLevel::Level2); ASSERT_STATUS_OK(graph_transformation_mgr.ApplyTransformers(graph, TransformerLevel::Level2, *logger_)); op_to_count = CountOpsInGraph(graph); - ASSERT_TRUE(op_to_count["Add"] == 0); - ASSERT_TRUE(op_to_count["Relu"] == 0); + ASSERT_TRUE(op_to_count["Add"] == 0); //Add removed from graph + ASSERT_TRUE(op_to_count["Relu"] == 0); //Relu removed from graph } + +//Conv->Add->Relu will be left intact since there is Identity depend on Add +TEST_F(GraphTransformationTests, FuseCudaConvAddReluIdentity) { + auto model_uri = MODEL_FOLDER "fusion/conv_add_relu_identity.onnx"; + std::shared_ptr p_model; + ASSERT_STATUS_OK(Model::Load(model_uri, p_model, nullptr, *logger_)); + Graph& graph = p_model->MainGraph(); + for (auto& node : p_model->MainGraph().Nodes()) { + node.SetExecutionProviderType(kCudaExecutionProvider); + } + std::map op_to_count = CountOpsInGraph(graph); + ASSERT_TRUE(op_to_count["Add"] == 1); + ASSERT_TRUE(op_to_count["Relu"] == 1); + ASSERT_TRUE(op_to_count["Identity"] == 1); + onnxruntime::GraphTransformerManager graph_transformation_mgr{5}; + graph_transformation_mgr.Register(std::make_unique(), TransformerLevel::Level2); + ASSERT_STATUS_OK(graph_transformation_mgr.ApplyTransformers(graph, TransformerLevel::Level2, *logger_)); + op_to_count = CountOpsInGraph(graph); + ASSERT_TRUE(op_to_count["Add"] == 1); //Add remains + ASSERT_TRUE(op_to_count["Relu"] == 1); //Relu remains + ASSERT_TRUE(op_to_count["Identity"] == 1); //Identity remains +} + +//Conv->Add will be left intact since there is no Relu follows +TEST_F(GraphTransformationTests, FuseCudaConvAdd) { + auto model_uri = MODEL_FOLDER "fusion/conv_add.onnx"; + std::shared_ptr p_model; + ASSERT_STATUS_OK(Model::Load(model_uri, p_model, nullptr, *logger_)); + Graph& graph = p_model->MainGraph(); + for (auto& node : p_model->MainGraph().Nodes()) { + node.SetExecutionProviderType(kCudaExecutionProvider); + } + std::map op_to_count = CountOpsInGraph(graph); + ASSERT_TRUE(op_to_count["Add"] == 1); + onnxruntime::GraphTransformerManager graph_transformation_mgr{5}; + graph_transformation_mgr.Register(std::make_unique(), TransformerLevel::Level2); + ASSERT_STATUS_OK(graph_transformation_mgr.ApplyTransformers(graph, TransformerLevel::Level2, *logger_)); + op_to_count = CountOpsInGraph(graph); + ASSERT_TRUE(op_to_count["Add"] == 1); //Add remains, no transform applied to the graph +} + #endif #ifndef DISABLE_CONTRIB_OPS diff --git a/onnxruntime/test/platform/ios/ios_package_test/.gitignore b/onnxruntime/test/platform/ios/ios_package_test/.gitignore new file mode 100644 index 0000000000000..910554681ae36 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/.gitignore @@ -0,0 +1,26 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## User settings +xcuserdata/ + +## compatibility with Xcode 8 and earlier (ignoring not required starting Xcode 9) +*.xcscmblueprint +*.xccheckout + +## compatibility with Xcode 3 and earlier (ignoring not required starting Xcode 4) +build/ +DerivedData/ +*.moved-aside +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 + +## Gcc Patch +/*.gcno diff --git a/onnxruntime/test/platform/ios/ios_package_test/Podfile b/onnxruntime/test/platform/ios/ios_package_test/Podfile new file mode 100644 index 0000000000000..eac73cbb84a4d --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/Podfile @@ -0,0 +1,12 @@ +platform :ios, '13.0' + +target 'ios_package_test' do + # Comment the next line if you don't want to use dynamic frameworks + use_frameworks! + + target 'ios_package_testTests' do + inherit! :search_paths + pod 'onnxruntime-mobile-c', :podspec => './onnxruntime-mobile-c.podspec' + end + +end diff --git a/onnxruntime/test/platform/ios/ios_package_test/README.md b/onnxruntime/test/platform/ios/ios_package_test/README.md new file mode 100644 index 0000000000000..980804b708b46 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/README.md @@ -0,0 +1,47 @@ +# iOS End-to-End Test App for ORT-Mobile + +This End-to-End test app for iOS will test ORT Mobile C/C++ API framework using XCode and CocoaPods + +## Requirements + +- [Prerequisites for building ORT-Mobile for iOS](http://www.onnxruntime.ai/docs/how-to/build/android-ios.html#prerequisites-1) +- [CocoaPods](https://cocoapods.org/) + +## iOS End-to-End Test App Overview + +The iOS End-to-End Test App will use CocoaPods to install the Onnx Runtime C/C++ framework, and run basic End-to-End tests of Onnx Runtime C and C++ API. + +### Model used +- [sigmoid ONNX model](https://github.com/onnx/onnx/blob/f9b0cc99344869c246b8f4011b8586a39841284c/onnx/backend/test/data/node/test_sigmoid/model.onnx) converted to ORT format + + Here's the [document](http://www.onnxruntime.ai/docs/how-to/deploy-on-mobile.html#1-create-ort-format-model-and-configuration-file-with-required-operators) about how you can convert an ONNX model into ORT format. + +### Tests +- [Tests for C API ](./ios_package_testTests/ios_package_test_c_api.m) +- [Tests for C++ API ](./ios_package_testTests/ios_package_test_cpp_api.mm) + +## Build and Test iOS Framework using [build.py](../../../../../tools/ci_build/build.py) + +Use the [build for iOS simulator](http://www.onnxruntime.ai/docs/how-to/build/android-ios.html#cross-build-for-ios-simulator) with `--build_apple_framework` + +## Run the iOS End-to-End Test App standalone + +### Requirements + +- A pre-built ORT Mobile iOS framework, which can be built using the [instruction](#build-and-test-ios-framework-using-buildpy) above. The framework can be found as `/iOS//-iphonesimulator/onnxruntime.framework` + +### Steps + +1. Go to this folder +2. Copy the [onnxruntime-mobile.podspec.template](./onnxruntime-mobile.podspec.template) to `onnxruntime-mobile.podspec` +3. Update the `onnxruntime-mobile.podspec`, replace `${ORT_BASE_FRAMEWORK_ARCHIVE}` with the path of a zip archive contains the pre-built ORT Mobile iOS framework +4. Run `pod install` to install the pre-built ORT Mobile iOS framework +5. Run the following command to perform the test + +``` + xcrun xcodebuild \ + -workspace ./ios_package_test.xcworkspace \ + -destination '' \ + -scheme ios_package_test \ + test +``` \ No newline at end of file diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.pbxproj b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.pbxproj new file mode 100644 index 0000000000000..c0fd060a05612 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.pbxproj @@ -0,0 +1,470 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 229E5921265869BF006E41AE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 229E5920265869BF006E41AE /* AppDelegate.m */; }; + 229E592A265869BF006E41AE /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 229E5928265869BF006E41AE /* Main.storyboard */; }; + 229E592F265869C2006E41AE /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 229E592D265869C2006E41AE /* LaunchScreen.storyboard */; }; + 229E5932265869C2006E41AE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 229E5931265869C2006E41AE /* main.m */; }; + 229E593C265869C2006E41AE /* ios_package_test_cpp_api.mm in Sources */ = {isa = PBXBuildFile; fileRef = 229E593B265869C2006E41AE /* ios_package_test_cpp_api.mm */; }; + 229E595926586B4A006E41AE /* sigmoid.ort in Resources */ = {isa = PBXBuildFile; fileRef = 229E595826586B4A006E41AE /* sigmoid.ort */; }; + 229E595A26586B4A006E41AE /* sigmoid.ort in Resources */ = {isa = PBXBuildFile; fileRef = 229E595826586B4A006E41AE /* sigmoid.ort */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 229E5938265869C2006E41AE /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 229E5914265869BF006E41AE /* Project object */; + proxyType = 1; + remoteGlobalIDString = 229E591B265869BF006E41AE; + remoteInfo = ios_package_test; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 229E591C265869BF006E41AE /* ios_package_test.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ios_package_test.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 229E591F265869BF006E41AE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 229E5920265869BF006E41AE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 229E5929265869BF006E41AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 229E592E265869C2006E41AE /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 229E5930265869C2006E41AE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 229E5931265869C2006E41AE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 229E5937265869C2006E41AE /* ios_package_testTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ios_package_testTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 229E593B265869C2006E41AE /* ios_package_test_cpp_api.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ios_package_test_cpp_api.mm; sourceTree = ""; }; + 229E593D265869C2006E41AE /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 229E595826586B4A006E41AE /* sigmoid.ort */ = {isa = PBXFileReference; lastKnownFileType = file; path = sigmoid.ort; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 229E5919265869BF006E41AE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 229E5934265869C2006E41AE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 229E5913265869BF006E41AE = { + isa = PBXGroup; + children = ( + 229E595426586A77006E41AE /* models */, + 229E591E265869BF006E41AE /* ios_package_test */, + 229E593A265869C2006E41AE /* ios_package_testTests */, + 229E591D265869BF006E41AE /* Products */, + ); + sourceTree = ""; + }; + 229E591D265869BF006E41AE /* Products */ = { + isa = PBXGroup; + children = ( + 229E591C265869BF006E41AE /* ios_package_test.app */, + 229E5937265869C2006E41AE /* ios_package_testTests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 229E591E265869BF006E41AE /* ios_package_test */ = { + isa = PBXGroup; + children = ( + 229E591F265869BF006E41AE /* AppDelegate.h */, + 229E5920265869BF006E41AE /* AppDelegate.m */, + 229E5928265869BF006E41AE /* Main.storyboard */, + 229E592D265869C2006E41AE /* LaunchScreen.storyboard */, + 229E5930265869C2006E41AE /* Info.plist */, + 229E5931265869C2006E41AE /* main.m */, + ); + path = ios_package_test; + sourceTree = ""; + }; + 229E593A265869C2006E41AE /* ios_package_testTests */ = { + isa = PBXGroup; + children = ( + 229E593B265869C2006E41AE /* ios_package_test_cpp_api.mm */, + 229E593D265869C2006E41AE /* Info.plist */, + ); + path = ios_package_testTests; + sourceTree = ""; + }; + 229E595426586A77006E41AE /* models */ = { + isa = PBXGroup; + children = ( + 229E595826586B4A006E41AE /* sigmoid.ort */, + ); + path = models; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 229E591B265869BF006E41AE /* ios_package_test */ = { + isa = PBXNativeTarget; + buildConfigurationList = 229E594B265869C2006E41AE /* Build configuration list for PBXNativeTarget "ios_package_test" */; + buildPhases = ( + 229E5918265869BF006E41AE /* Sources */, + 229E5919265869BF006E41AE /* Frameworks */, + 229E591A265869BF006E41AE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ios_package_test; + productName = ios_package_test; + productReference = 229E591C265869BF006E41AE /* ios_package_test.app */; + productType = "com.apple.product-type.application"; + }; + 229E5936265869C2006E41AE /* ios_package_testTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 229E594E265869C2006E41AE /* Build configuration list for PBXNativeTarget "ios_package_testTests" */; + buildPhases = ( + 229E5933265869C2006E41AE /* Sources */, + 229E5934265869C2006E41AE /* Frameworks */, + 229E5935265869C2006E41AE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 229E5939265869C2006E41AE /* PBXTargetDependency */, + ); + name = ios_package_testTests; + productName = ios_package_testTests; + productReference = 229E5937265869C2006E41AE /* ios_package_testTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 229E5914265869BF006E41AE /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1250; + TargetAttributes = { + 229E591B265869BF006E41AE = { + CreatedOnToolsVersion = 12.5; + }; + 229E5936265869C2006E41AE = { + CreatedOnToolsVersion = 12.5; + TestTargetID = 229E591B265869BF006E41AE; + }; + }; + }; + buildConfigurationList = 229E5917265869BF006E41AE /* Build configuration list for PBXProject "ios_package_test" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 229E5913265869BF006E41AE; + productRefGroup = 229E591D265869BF006E41AE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 229E591B265869BF006E41AE /* ios_package_test */, + 229E5936265869C2006E41AE /* ios_package_testTests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 229E591A265869BF006E41AE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 229E592F265869C2006E41AE /* LaunchScreen.storyboard in Resources */, + 229E595926586B4A006E41AE /* sigmoid.ort in Resources */, + 229E592A265869BF006E41AE /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 229E5935265869C2006E41AE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 229E595A26586B4A006E41AE /* sigmoid.ort in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 229E5918265869BF006E41AE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 229E5921265869BF006E41AE /* AppDelegate.m in Sources */, + 229E5932265869C2006E41AE /* main.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 229E5933265869C2006E41AE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 229E593C265869C2006E41AE /* ios_package_test_cpp_api.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 229E5939265869C2006E41AE /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 229E591B265869BF006E41AE /* ios_package_test */; + targetProxy = 229E5938265869C2006E41AE /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 229E5928265869BF006E41AE /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 229E5929265869BF006E41AE /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; + 229E592D265869C2006E41AE /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 229E592E265869C2006E41AE /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 229E5949265869C2006E41AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + }; + name = Debug; + }; + 229E594A265869C2006E41AE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 229E594C265869C2006E41AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ios_package_test/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ai.onnxruntime.tests.ios-package-test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 229E594D265869C2006E41AE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ios_package_test/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ai.onnxruntime.tests.ios-package-test"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 229E594F265869C2006E41AE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ios_package_testTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ai.onnxruntime.tests.ios-package-testTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ios_package_test.app/ios_package_test"; + }; + name = Debug; + }; + 229E5950265869C2006E41AE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = ios_package_testTests/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 13.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "ai.onnxruntime.tests.ios-package-testTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ios_package_test.app/ios_package_test"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 229E5917265869BF006E41AE /* Build configuration list for PBXProject "ios_package_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 229E5949265869C2006E41AE /* Debug */, + 229E594A265869C2006E41AE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 229E594B265869C2006E41AE /* Build configuration list for PBXNativeTarget "ios_package_test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 229E594C265869C2006E41AE /* Debug */, + 229E594D265869C2006E41AE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 229E594E265869C2006E41AE /* Build configuration list for PBXNativeTarget "ios_package_testTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 229E594F265869C2006E41AE /* Debug */, + 229E5950265869C2006E41AE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 229E5914265869BF006E41AE /* Project object */; +} diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000000000..919434a6254f0 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000000000..18d981003d68d --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.h b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.h new file mode 100644 index 0000000000000..63c992ff83700 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.h @@ -0,0 +1,14 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// AppDelegate.h +// ios_package_test +// + +#import + +@interface AppDelegate : UIResponder + + +@end + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.m b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.m new file mode 100644 index 0000000000000..c06026092759b --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/AppDelegate.m @@ -0,0 +1,40 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// AppDelegate.m +// ios_package_test +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +@implementation AppDelegate + + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { + // Override point for customization after application launch. + return YES; +} + + +#pragma mark - UISceneSession lifecycle + + +- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; +} + + +- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. +} + + +@end diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/LaunchScreen.storyboard b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 0000000000000..865e9329f3767 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/Main.storyboard b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/Main.storyboard new file mode 100644 index 0000000000000..808a21ce779ba --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Base.lproj/Main.storyboard @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Info.plist b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Info.plist new file mode 100644 index 0000000000000..72bf2c4f5985f --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/Info.plist @@ -0,0 +1,66 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIMainStoryboardFile + Main + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/main.m b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/main.m new file mode 100644 index 0000000000000..a4fe174a60d2a --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_test/main.m @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// main.m +// ios_package_test +// + +#import +#import "AppDelegate.h" + +int main(int argc, char * argv[]) { + NSString * appDelegateClassName; + @autoreleasepool { + // Setup code that might create autoreleased objects goes here. + appDelegateClassName = NSStringFromClass([AppDelegate class]); + } + return UIApplicationMain(argc, argv, nil, appDelegateClassName); +} diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/Info.plist b/onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/Info.plist new file mode 100644 index 0000000000000..64d65ca495770 --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/ios_package_test_cpp_api.mm b/onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/ios_package_test_cpp_api.mm new file mode 100644 index 0000000000000..f44e9f673b82b --- /dev/null +++ b/onnxruntime/test/platform/ios/ios_package_test/ios_package_testTests/ios_package_test_cpp_api.mm @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +// +// ios_package_test_cpp_api.mm +// ios_package_testTests +// +// This file hosts the tests of ORT C++ API, for tests of ORT C API, please see ios_package_test_c_api.mm +// + +#import +#include +#include + +#if __has_include() +#define COREML_EP_AVAILABLE 1 +#else +#define COREML_EP_AVAILABLE 0 +#endif + +#if COREML_EP_AVAILABLE +#include +#endif + +void testSigmoid(bool useCoreML) { + // This is an e2e test for ORT C++ API + Ort::Env env(ORT_LOGGING_LEVEL_WARNING, "testCppAPI"); + + // initialize session options if needed + Ort::SessionOptions session_options; + session_options.SetIntraOpNumThreads(1); + +#if COREML_EP_AVAILABLE + if (useCoreML) { + const uint32_t flags = COREML_FLAG_USE_CPU_ONLY; + Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CoreML(session_options, flags)); + } +#else + (void)useCoreML; +#endif + + NSString* ns_model_path = [[NSBundle mainBundle] pathForResource:@"sigmoid" ofType:@"ort"]; + Ort::Session session(env, ns_model_path.UTF8String, session_options); + + size_t input_tensor_size = 3 * 4 * 5; + float input_tensor_values[input_tensor_size]; + float expected_output_values[input_tensor_size]; + const char* input_node_names[] = {"x"}; + const char* output_node_names[] = {"y"}; + const int64_t input_node_dims[] = {3, 4, 5}; + + for (size_t i = 0; i < input_tensor_size; i++) { + input_tensor_values[i] = (float)i - 30; + expected_output_values[i] = 1.0f / (1 + exp(-input_tensor_values[i])); + } + + auto memory_info = Ort::MemoryInfo::CreateCpu(OrtArenaAllocator, OrtMemTypeDefault); + Ort::Value input_tensor = + Ort::Value::CreateTensor(memory_info, input_tensor_values, input_tensor_size, input_node_dims, 3); + XCTAssert(input_tensor.IsTensor()); + + auto output_tensors = session.Run(Ort::RunOptions{nullptr}, input_node_names, + &input_tensor, 1, output_node_names, 1); + XCTAssertEqual(output_tensors.size(), 1); + XCTAssert(output_tensors.front().IsTensor()); + + // Get pointer to output tensor float values + float* output_values = output_tensors.front().GetTensorMutableData(); + for (size_t i = 0; i < input_tensor_size; i++) { + XCTAssertEqualWithAccuracy(expected_output_values[i], output_values[i], 1e-6); + } +} + +@interface ios_package_test_cpp_api : XCTestCase + +@end + +@implementation ios_package_test_cpp_api + +- (void)setUp { + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. +} + +- (void)testCppAPI_Basic { + testSigmoid(false /* useCoreML */); +} + +#if COREML_EP_AVAILABLE +- (void)testCppAPI_Basic_CoreML { + testSigmoid(true /* useCoreML */); +} +#endif + +@end diff --git a/onnxruntime/test/platform/ios/ios_package_test/models/sigmoid.ort b/onnxruntime/test/platform/ios/ios_package_test/models/sigmoid.ort new file mode 100644 index 0000000000000000000000000000000000000000..355bef8d929272970de70757f47703d19cd83031 GIT binary patch literal 1240 zcmZWpv2GJV5S;@92ZYEX7P26UQbfoF94S(yh-iQkVL_1Gq04!bEadL2^CmH=D5SOQC= z3w&;feE3tA^kG)=1%Au<0d(j6r#sJozIjKR(v}sVg?u)TJsDz6WDOXf5m`sF4d4X! zS*&?!;q0<3y7QI~YYh-*fL8#0)}sCa^sq*s`Kj*%?8kBrC;fQ-)wV3js@#%JRZmcn z=gHaDT!O#qu;MhiuLGRRdBA`;?M=W7>wAo0(Ef%Gx!&tOeK1nVzR`KsE%F21R|U^q z0G{ZbG}nE)m0ZMkXxE;R2U?V4pb6OCLTGzev<9fLRhA`fNYI1L+U)t)-?F6UoHu}F zfc`vUsJqZm!8v>=H{JTKTW1XAE_}{3lxyG|;;)im`u@mmzS^O&b9-P!`DguX05*G{e7~3wYKm1^Vayjd3UytRxPgH2ZA7;6ZOj( zf5ZR?=g>pIBJdmb7lq%rHy=M8cZ@i`Q3~_&e~6DuF^JQdJlC;b;})sWKovSwnTZh}R=-5s7k>*B=JhfM UV=GQxsH}h6sG;FH=d}j>1K;(j=>Px# literal 0 HcmV?d00001 diff --git a/onnxruntime/test/testdata/model_with_metadata.onnx b/onnxruntime/test/testdata/model_with_metadata.onnx new file mode 100644 index 0000000000000000000000000000000000000000..dbd32a36888a6852f676717d90404858bc562f19 GIT binary patch literal 233 zcmd;J7vc^{%`46?3dt`@&QRh?%+$-z%d61K&C%GIU?dchky)$&L@D{n@x>)YnR)3} z@mvyIj1fYNky7l%nd!OtnJG%_!4QU!q@SN-fM>jCUP)?EUSdvsX=YxDMSMtVaY>Xg zNE06y4+otu0RxIGmTa}}~vD-{eCg?Pc7vc#OyR3NuV jf{Q^&Bn-?;%P&$$%`GUY1Sti{h6onW7&$m3+ literal 0 HcmV?d00001 diff --git a/onnxruntime/test/testdata/model_with_metadata.py b/onnxruntime/test/testdata/model_with_metadata.py new file mode 100644 index 0000000000000..0d085d6deb34d --- /dev/null +++ b/onnxruntime/test/testdata/model_with_metadata.py @@ -0,0 +1,38 @@ +import onnx +from onnx import helper +from onnx import TensorProto + + +# Create a model with metadata to test ORT conversion +def GenerateModel(model_name): + nodes = [ + helper.make_node("Sigmoid", ["X"], ["Y"], "sigmoid"), + ] + + graph = helper.make_graph( + nodes, + "NNAPI_Internal_uint8_Test", + [helper.make_tensor_value_info('X', TensorProto.FLOAT, [1, 3])], + [helper.make_tensor_value_info('Y', TensorProto.FLOAT, [1, 3])], + ) + + model = helper.make_model(graph) + + # Add meta data + model.doc_string = 'This is doc_string' + model.producer_name = 'TensorTorch' + model.model_version = 12345 + model.domain = 'ai.onnx.ml' + helper.set_model_props( + model, + { + 'I am key 1!': 'I am value 1!', + '': 'Value for empty key!', + 'Key for empty value!': '', + } + ) + onnx.save(model, model_name) + + +if __name__ == "__main__": + GenerateModel('model_with_metadata.onnx') diff --git a/onnxruntime/test/testdata/transform/fusion/conv_add.onnx b/onnxruntime/test/testdata/transform/fusion/conv_add.onnx new file mode 100644 index 0000000000000..d3fa4dcf03c39 --- /dev/null +++ b/onnxruntime/test/testdata/transform/fusion/conv_add.onnx @@ -0,0 +1,44 @@ +:° + +X +W +BC"Conv + +C +AY"AddgraphZ +X + + + + +Z +W + + + + +Z +B + + +Z +A + + + + +b +Y + + + + +BB +com.microsoft.experimentalB +ai.onnx.preview.trainingB +com.microsoft.nchwcB +com.microsoft.mlfeaturizersB + +ai.onnx.mlB + com.microsoftB +ai.onnx.training \ No newline at end of file diff --git a/onnxruntime/test/testdata/transform/fusion/conv_add_relu_identity.onnx b/onnxruntime/test/testdata/transform/fusion/conv_add_relu_identity.onnx new file mode 100644 index 0000000000000..45f09a22af2a4 --- /dev/null +++ b/onnxruntime/test/testdata/transform/fusion/conv_add_relu_identity.onnx @@ -0,0 +1,46 @@ +:í + +X +W +BC"Conv + +C +AS"Add + +SY"Relu + +SI"IdentitygraphZ +X + + + + +Z +W + + + + +Z +B + + +Z +A + + + + +b +Y + + + + +b +I + + + + +B \ No newline at end of file diff --git a/onnxruntime/test/util/include/inference_session_wrapper.h b/onnxruntime/test/util/include/inference_session_wrapper.h index c7cf979380a14..eab83c26b681f 100644 --- a/onnxruntime/test/util/include/inference_session_wrapper.h +++ b/onnxruntime/test/util/include/inference_session_wrapper.h @@ -27,6 +27,10 @@ class InferenceSessionWrapper : public InferenceSession { const SessionState& GetSessionState() const { return InferenceSession::GetSessionState(); } + + const Model& GetModel() const { + return *model_; + } }; } // namespace test diff --git a/tools/ci_build/build.py b/tools/ci_build/build.py index b36d745583288..aec4aadf3bfed 100644 --- a/tools/ci_build/build.py +++ b/tools/ci_build/build.py @@ -331,8 +331,6 @@ def parse_arguments(): help="Specify the minimum version of the target platform " "(e.g. macOS or iOS)" "This is only supported on MacOS") - parser.add_argument("--apple_disable_bitcode", action='store_true', - help="Disable bitcode for iOS, bitcode is by default enabled for iOS.") # WebAssembly build parser.add_argument("--build_wasm", action='store_true', help="Build for WebAssembly") @@ -878,7 +876,6 @@ def generate_build_tree(cmake_path, source_dir, build_dir, cuda_home, cudnn_home "-DCMAKE_OSX_DEPLOYMENT_TARGET=" + args.apple_deploy_target, # we do not need protoc binary for ios cross build "-Dprotobuf_BUILD_PROTOC_BINARIES=OFF", - "-Donnxruntime_ENABLE_BITCODE=" + ("OFF" if args.apple_disable_bitcode else "ON"), "-DCMAKE_TOOLCHAIN_FILE=" + ( args.ios_toolchain_file if args.ios_toolchain_file else "../cmake/onnxruntime_ios.toolchain.cmake") @@ -1255,16 +1252,29 @@ def run_adb_shell(cmd): def run_ios_tests(args, source_dir, config, cwd): - cpr = run_subprocess(["xcodebuild", "test-without-building", "-project", "./onnxruntime.xcodeproj", - "-configuration", config, - "-scheme", "onnxruntime_test_all_xc", "-destination", - "platform=iOS Simulator,OS=latest,name=iPhone SE (2nd generation)"], cwd=cwd) - if cpr.returncode == 0: - cpr = run_subprocess(["xcodebuild", "test-without-building", "-project", "./onnxruntime.xcodeproj", - "-configuration", config, - "-scheme", "onnxruntime_shared_lib_test_xc", "-destination", - "platform=iOS Simulator,OS=latest,name=iPhone SE (2nd generation)"], cwd=cwd) - cpr.check_returncode() + run_subprocess(["xcodebuild", "test-without-building", "-project", "./onnxruntime.xcodeproj", + "-configuration", config, + "-scheme", "onnxruntime_test_all_xc", "-destination", + "platform=iOS Simulator,OS=latest,name=iPhone SE (2nd generation)"], cwd=cwd) + + run_subprocess(["xcodebuild", "test-without-building", "-project", "./onnxruntime.xcodeproj", + "-configuration", config, + "-scheme", "onnxruntime_shared_lib_test_xc", "-destination", + "platform=iOS Simulator,OS=latest,name=iPhone SE (2nd generation)"], cwd=cwd) + + if args.build_apple_framework: + package_test_py = os.path.join(source_dir, 'tools', 'ci_build', 'github', 'apple', 'test_ios_packages.py') + framework_info_file = os.path.join(cwd, 'framework_info.json') + dynamic_framework_dir = os.path.join(cwd, config + '-' + args.ios_sysroot) + static_framework_dir = os.path.join(cwd, config + '-' + args.ios_sysroot, 'static_framework') + # test dynamic framework + run_subprocess([sys.executable, package_test_py, + '--c_framework_dir', dynamic_framework_dir, + '--framework_info_file', framework_info_file], cwd=cwd) + # test static framework + run_subprocess([sys.executable, package_test_py, + '--c_framework_dir', static_framework_dir, + '--framework_info_file', framework_info_file], cwd=cwd) def run_orttraining_test_orttrainer_frontend_separately(cwd): diff --git a/tools/ci_build/github/android/mobile_package.required_operators.config b/tools/ci_build/github/android/mobile_package.required_operators.config index 255f631ea5851..5ce34a7ba0eea 100644 --- a/tools/ci_build/github/android/mobile_package.required_operators.config +++ b/tools/ci_build/github/android/mobile_package.required_operators.config @@ -14,8 +14,8 @@ ai.onnx;12;Abs,Add,And,ArgMax,ArgMin,AveragePool,Cast,Ceil,Clip,Concat,ConstantO ai.onnx;13;Abs,Add,And,ArgMax,ArgMin,AveragePool,Cast,Ceil,Clip,Concat,ConstantOfShape,Conv,ConvTranspose,Cos,CumSum,DepthToSpace,DequantizeLinear,Div,DynamicQuantizeLinear,Elu,Equal,Exp,Expand,Flatten,Floor,Gather,GatherND,Gemm,Greater,GreaterOrEqual,Identity,If,LRN,LeakyRelu,Less,LessOrEqual,Log,LogSoftmax,Loop,MatMul,Max,MaxPool,Mean,Min,Mul,Neg,NonMaxSuppression,NonZero,Not,Or,PRelu,Pad,Pow,QuantizeLinear,Range,Reciprocal,ReduceMax,ReduceMean,ReduceMin,ReduceProd,ReduceSum,Relu,Reshape,Resize,ReverseSequence,Round,ScatterND,Shape,Sigmoid,Sin,Size,Slice,Softmax,SpaceToDepth,Split,Sqrt,Squeeze,Sub,Sum,Tanh,ThresholdedRelu,Tile,TopK,Transpose,Unique,Unsqueeze,Where # other ops found in test models -ai.onnx;12;Erf,GlobalAveragePool,InstanceNormalization,MatMulInteger,QLinearConv,QLinearMatMul -ai.onnx;13;Erf,GlobalAveragePool,InstanceNormalization,MatMulInteger,QLinearConv,QLinearMatMul +ai.onnx;12;Erf,GlobalAveragePool,InstanceNormalization,HardSigmoid,MatMulInteger,QLinearConv,QLinearMatMul +ai.onnx;13;Erf,GlobalAveragePool,InstanceNormalization,HardSigmoid,MatMulInteger,QLinearConv,QLinearMatMul # Control flow ops # - If and Loop are covered by the tflite converter list @@ -27,6 +27,7 @@ ai.onnx;13;Scan # Note: LayerNormalization is an internal op even though it is (incorrectly) registered in the ONNX domain. ai.onnx;1;LayerNormalization com.microsoft;1;DynamicQuantizeMatMul,FusedConv,FusedGemm,FusedMatMul,Gelu,MatMulIntegerToFloat,NhwcMaxPool,QLinearAdd,QLinearAveragePool,QLinearConv,QLinearGlobalAveragePool,QLinearMul,QLinearSigmoid + # NHWC transformer also uses this, so assuming it's valuable enough to include com.microsoft;1;QLinearLeakyRelu diff --git a/tools/ci_build/github/android/mobile_package.required_operators.readme.txt b/tools/ci_build/github/android/mobile_package.required_operators.readme.txt index 141e336e29c7f..9e60cba4a42f1 100644 --- a/tools/ci_build/github/android/mobile_package.required_operators.readme.txt +++ b/tools/ci_build/github/android/mobile_package.required_operators.readme.txt @@ -68,9 +68,11 @@ Models from MLPerf Mobile - ssd_mobilenet_v2_300-qdq.onnx Other - Mobilenet v2 from pytorch + Mobilenet v2 and v3 from pytorch + - https://pytorch.org/vision/stable/models.html - pytorch.mobilenet_v2_float.onnx - pytorch.mobilenet_v2_uint8.onnx + - pytorch.mobilenet_v3_small.onnx Other assorted pytorch models - Huggingface mobilebert-uncased (https://huggingface.co/transformers/serialization.html, https://huggingface.co/google/mobilebert-uncased) - SuperResolution (https://pytorch.org/tutorials/advanced/super_resolution_with_onnxruntime.html) diff --git a/tools/ci_build/github/apple/assemble_ios_packaging_artifacts.sh b/tools/ci_build/github/apple/assemble_ios_packaging_artifacts.sh new file mode 100755 index 0000000000000..3e9e114e7991e --- /dev/null +++ b/tools/ci_build/github/apple/assemble_ios_packaging_artifacts.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +# Note: This script is intended to be called from the iOS packaging build or a similar context +# See tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml + +set -e +set -x + +USAGE_TEXT="Usage: ${0} " + +abspath() { + local INPUT_PATH=${1:?"Expected path as the first argument."} + echo "$(cd "$(dirname "${INPUT_PATH}")" && pwd)/$(basename "${INPUT_PATH}")" +} + +# staging directory for binaries (source) +BINARIES_STAGING_DIR=$(abspath "${1:?${USAGE_TEXT}}") +# staging directory for build artifacts (destination) +ARTIFACTS_STAGING_DIR=$(abspath "${2:?${USAGE_TEXT}}") +ORT_POD_VERSION=${3:?${USAGE_TEXT}} +SHOULD_UPLOAD_ARCHIVES=${4:?${USAGE_TEXT}} + +STORAGE_ACCOUNT_NAME="onnxruntimepackages" +STORAGE_ACCOUNT_CONTAINER_NAME="ortmobilestore" +STORAGE_URL_PREFIX="https://${STORAGE_ACCOUNT_NAME}.blob.core.windows.net/${STORAGE_ACCOUNT_CONTAINER_NAME}" + +assemble_and_upload_pod() { + local POD_NAME=${1:?"Expected pod name as first argument."} + local POD_ARCHIVE_BASENAME="${POD_NAME}-${ORT_POD_VERSION}.zip" + local PODSPEC_BASENAME="${POD_NAME}.podspec" + + pushd ${BINARIES_STAGING_DIR}/${POD_NAME} + + # assemble the files in the artifacts staging directory + zip -r ${ARTIFACTS_STAGING_DIR}/${POD_ARCHIVE_BASENAME} * --exclude ${PODSPEC_BASENAME} + cp ${PODSPEC_BASENAME} ${ARTIFACTS_STAGING_DIR}/${PODSPEC_BASENAME} + + if [[ "${SHOULD_UPLOAD_ARCHIVES}" == "true" ]]; then + # upload the pod archive and set the podspec source to the pod archive URL + az storage blob upload \ + --account-name ${STORAGE_ACCOUNT_NAME} --container-name ${STORAGE_ACCOUNT_CONTAINER_NAME} \ + --file ${ARTIFACTS_STAGING_DIR}/${POD_ARCHIVE_BASENAME} --name ${POD_ARCHIVE_BASENAME} \ + --if-none-match "*" + + sed -i "" -e "s|file:///http_source_placeholder|${STORAGE_URL_PREFIX}/${POD_ARCHIVE_BASENAME}|" \ + ${ARTIFACTS_STAGING_DIR}/${PODSPEC_BASENAME} + fi + + popd +} + +assemble_and_upload_pod "onnxruntime-mobile-c" + +assemble_and_upload_pod "onnxruntime-mobile-objc" + +cd ${BINARIES_STAGING_DIR}/objc_api_docs +zip -r ${ARTIFACTS_STAGING_DIR}/objc_api_docs.zip * + +cat > ${ARTIFACTS_STAGING_DIR}/readme.txt <<'EOM' +Release TODO: +- publish the podspecs +- publish the Objective-C API documentation +EOM diff --git a/tools/ci_build/github/apple/build_ios_framework.py b/tools/ci_build/github/apple/build_ios_framework.py index cf0c8a1196840..cba3c5bd645e6 100644 --- a/tools/ci_build/github/apple/build_ios_framework.py +++ b/tools/ci_build/github/apple/build_ios_framework.py @@ -72,20 +72,24 @@ def _build_package(args): # get the compiled lib path framework_dir = os.path.join( - build_dir_current_arch, build_config, build_config + "-" + sysroot, 'onnxruntime.framework') + build_dir_current_arch, build_config, build_config + "-" + sysroot, + 'onnxruntime.framework' if args.build_dynamic_framework + else os.path.join('static_framework', 'onnxruntime.framework')) ort_libs.append(os.path.join(framework_dir, 'onnxruntime')) - # We actually only need to define the Info.plist and headers once since they are all the same + # We only need to copy Info.plist, framework_info.json, and headers once since they are the same if not info_plist_path: info_plist_path = os.path.join(build_dir_current_arch, build_config, 'Info.plist') + framework_info_path = os.path.join(build_dir_current_arch, build_config, 'framework_info.json') headers = glob.glob(os.path.join(framework_dir, 'Headers', '*.h')) # manually create the fat framework framework_dir = os.path.join(build_dir, 'framework_out', 'onnxruntime.framework') pathlib.Path(framework_dir).mkdir(parents=True, exist_ok=True) - # copy the header files and Info.plist + # copy the Info.plist, framework_info.json, and header files shutil.copy(info_plist_path, framework_dir) + shutil.copy(framework_info_path, build_dir) header_dir = os.path.join(framework_dir, 'Headers') pathlib.Path(header_dir).mkdir(parents=True, exist_ok=True) for _header in headers: @@ -123,6 +127,9 @@ def parse_args(): parser.add_argument('build_settings_file', type=pathlib.Path, help='Provide the file contains settings for building iOS framework') + parser.add_argument("--build_dynamic_framework", action='store_true', + help="Build Dynamic Framework (default is build static framework).") + args = parser.parse_args() if not args.build_settings_file.resolve().is_file(): diff --git a/tools/ci_build/github/apple/c/assemble_c_pod_package.py b/tools/ci_build/github/apple/c/assemble_c_pod_package.py new file mode 100644 index 0000000000000..18dc8a19d23ce --- /dev/null +++ b/tools/ci_build/github/apple/c/assemble_c_pod_package.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python3 + +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import argparse +import pathlib +import shutil +import sys + + +_script_dir = pathlib.Path(__file__).parent.resolve(strict=True) +sys.path.append(str(_script_dir.parent)) + + +from package_assembly_utils import ( # noqa: E402 + copy_repo_relative_to_dir, gen_file_from_template, load_framework_info) + + +def parse_args(): + parser = argparse.ArgumentParser(description=""" + Assembles the files for the C/C++ pod package in a staging directory. + This directory can be validated (e.g., with `pod lib lint`) and then zipped to create a package for release. + """) + + parser.add_argument("--staging-dir", type=pathlib.Path, + default=pathlib.Path("./onnxruntime-mobile-c-staging"), + help="Path to the staging directory for the C/C++ pod files.") + parser.add_argument("--pod-version", required=True, + help="C/C++ pod version.") + parser.add_argument("--framework-info-file", type=pathlib.Path, required=True, + help="Path to the framework_info.json file containing additional values for the podspec. " + "This file should be generated by CMake in the build directory.") + parser.add_argument("--framework-dir", type=pathlib.Path, required=True, + help="Path to the onnxruntime.framework directory to include in the pod.") + + return parser.parse_args() + + +def main(): + args = parse_args() + + framework_info = load_framework_info(args.framework_info_file.resolve()) + + staging_dir = args.staging_dir.resolve() + print(f"Assembling files in staging directory: {staging_dir}") + if staging_dir.exists(): + print("Warning: staging directory already exists", file=sys.stderr) + + # copy the necessary files to the staging directory + framework_dir = args.framework_dir.resolve() + shutil.copytree(framework_dir, staging_dir / framework_dir.name, dirs_exist_ok=True) + copy_repo_relative_to_dir(["LICENSE"], staging_dir) + + # generate the podspec file from the template + + variable_substitutions = { + "VERSION": args.pod_version, + "IOS_DEPLOYMENT_TARGET": framework_info["IOS_DEPLOYMENT_TARGET"], + "WEAK_FRAMEWORK": framework_info["WEAK_FRAMEWORK"], + "LICENSE_FILE": '"LICENSE"', + } + + podspec_template = _script_dir / "onnxruntime-mobile-c.podspec.template" + podspec = staging_dir / "onnxruntime-mobile-c.podspec" + + gen_file_from_template(podspec_template, podspec, variable_substitutions) + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/tools/ci_build/github/apple/c/onnxruntime-mobile-c.podspec.template b/tools/ci_build/github/apple/c/onnxruntime-mobile-c.podspec.template new file mode 100644 index 0000000000000..1d2a736b7e5dd --- /dev/null +++ b/tools/ci_build/github/apple/c/onnxruntime-mobile-c.podspec.template @@ -0,0 +1,24 @@ +Pod::Spec.new do |spec| + spec.name = "onnxruntime-mobile-c" + spec.version = "@VERSION@" + spec.authors = { "ONNX Runtime" => "onnxruntime@microsoft.com" } + spec.license = { :type => "MIT", :file => @LICENSE_FILE@ } + spec.homepage = "https://github.com/microsoft/onnxruntime" + spec.source = { :http => "file:///http_source_placeholder" } + spec.summary = "ONNX Runtime Mobile C/C++ Pod" + spec.platform = :ios, "@IOS_DEPLOYMENT_TARGET@" + spec.vendored_frameworks = "onnxruntime.framework" + spec.static_framework = true + spec.weak_framework = [ @WEAK_FRAMEWORK@ ] + spec.source_files = "onnxruntime.framework/Headers/*.h" + spec.preserve_paths = [ @LICENSE_FILE@ ] + spec.description = <<-DESC + A pod for the ONNX Runtime Mobile C/C++ library. + DESC + spec.library = "c++" + spec.user_target_xcconfig = { + "OTHER_CPLUSPLUSFLAGS" => "-fvisibility=hidden -fvisibility-inlines-hidden", + # TODO workaround - support arm64 iphonesimulator later + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" => "arm64" + } +end diff --git a/tools/ci_build/github/apple/default_mobile_ios_framework_build_settings.json b/tools/ci_build/github/apple/default_mobile_ios_framework_build_settings.json index 51ca753509af7..da97470d642ee 100644 --- a/tools/ci_build/github/apple/default_mobile_ios_framework_build_settings.json +++ b/tools/ci_build/github/apple/default_mobile_ios_framework_build_settings.json @@ -14,11 +14,12 @@ "--parallel", "--use_xcode", "--build_apple_framework", - "--minimal_build", + "--minimal_build=extended", "--disable_rtti", "--disable_ml_ops", "--disable_exceptions", "--enable_reduced_operator_type_support", + "--use_coreml", "--skip_tests", "--apple_deploy_target=11.0" ] diff --git a/tools/ci_build/github/apple/framework_info.json.template b/tools/ci_build/github/apple/framework_info.json.template new file mode 100644 index 0000000000000..788e52302b3f1 --- /dev/null +++ b/tools/ci_build/github/apple/framework_info.json.template @@ -0,0 +1,4 @@ +{ + "IOS_DEPLOYMENT_TARGET": "@CMAKE_OSX_DEPLOYMENT_TARGET@", + "WEAK_FRAMEWORK": "@APPLE_WEAK_FRAMEWORK@" +} \ No newline at end of file diff --git a/tools/ci_build/github/apple/ios_packaging.requirements.txt b/tools/ci_build/github/apple/ios_packaging.requirements.txt new file mode 100644 index 0000000000000..adf11d699fe6f --- /dev/null +++ b/tools/ci_build/github/apple/ios_packaging.requirements.txt @@ -0,0 +1 @@ +flatbuffers diff --git a/tools/ci_build/github/apple/objectivec/assemble_pod_package.py b/tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py similarity index 58% rename from tools/ci_build/github/apple/objectivec/assemble_pod_package.py rename to tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py index 7543df0b82367..4b4ffcc63803d 100755 --- a/tools/ci_build/github/apple/objectivec/assemble_pod_package.py +++ b/tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py @@ -4,15 +4,17 @@ # Licensed under the MIT License. import argparse -import os import pathlib -import re -import shutil import sys _script_dir = pathlib.Path(__file__).parent.resolve(strict=True) -_repo_root = _script_dir.parents[4] +sys.path.append(str(_script_dir.parent)) + + +from package_assembly_utils import ( # noqa: E402 + copy_repo_relative_to_dir, gen_file_from_template, load_framework_info) + # these variables contain paths or path patterns that are relative to the repo root @@ -61,71 +63,28 @@ def parse_args(): parser.add_argument("--staging-dir", type=pathlib.Path, default=pathlib.Path("./onnxruntime-mobile-objc-staging"), - help="Staging directory for the Objective-C pod files.") + help="Path to the staging directory for the Objective-C pod files.") parser.add_argument("--pod-version", required=True, help="Objective-C pod version.") - parser.add_argument("--pod-source-http", required=True, - help="Objective-C pod source http value (e.g., the package download URL).") + parser.add_argument("--framework-info-file", type=pathlib.Path, required=True, + help="Path to the framework_info.json file containing additional values for the podspec. " + "This file should be generated by CMake in the build directory.") return parser.parse_args() -_template_variable_pattern = re.compile(r"@(\w+)@") # match "@var@" - - -def gen_file_from_template(template_file: pathlib.Path, output_file: pathlib.Path, - variable_substitutions: dict[str, str]): - ''' - Generates a file from a template file. - The template file may contain template variables that will be substituted - with the provided values in the generated output file. - In the template file, template variable names are delimited by "@"'s, - e.g., "@var@". - - :param template_file The template file path. - :param output_file The generated output file path. - :variable_substitutions The mapping from template variable name to value. - ''' - with open(template_file, mode="r") as template: - content = template.read() - - def replace_template_variable(match): - variable_name = match.group(1) - return variable_substitutions.get(variable_name, match.group(0)) - - content = _template_variable_pattern.sub(replace_template_variable, content) - - with open(output_file, mode="w") as output: - output.write(content) - - -def copy_to_staging_dir(patterns: list[str], staging_dir: pathlib.Path): - ''' - Copies files to a staging directory. - The files are relative to the repo root, and the repo root-relative - intermediate directory structure is maintained. - - :param patterns The paths or path patterns relative to the repo root. - :param staging_dir The staging directory. - ''' - paths = [path for pattern in patterns for path in _repo_root.glob(pattern)] - for path in paths: - repo_relative_path = path.relative_to(_repo_root) - dst_path = staging_dir / repo_relative_path - os.makedirs(dst_path.parent, exist_ok=True) - shutil.copy(path, dst_path) - - def main(): args = parse_args() + framework_info = load_framework_info(args.framework_info_file.resolve()) + staging_dir = args.staging_dir.resolve() print(f"Assembling files in staging directory: {staging_dir}") if staging_dir.exists(): print("Warning: staging directory already exists", file=sys.stderr) # copy the necessary files to the staging directory - copy_to_staging_dir( + copy_repo_relative_to_dir( [license_file] + source_files + test_source_files + test_resource_files, staging_dir) @@ -136,7 +95,7 @@ def path_patterns_as_variable_value(patterns: list[str]): variable_substitutions = { "VERSION": args.pod_version, - "SOURCE_HTTP": args.pod_source_http, + "IOS_DEPLOYMENT_TARGET": framework_info["IOS_DEPLOYMENT_TARGET"], "LICENSE_FILE": path_patterns_as_variable_value([license_file]), "INCLUDE_DIR_LIST": path_patterns_as_variable_value(include_dirs), "PUBLIC_HEADER_FILE_LIST": path_patterns_as_variable_value(public_header_files), diff --git a/tools/ci_build/github/apple/objectivec/onnxruntime-mobile-objc.podspec.template b/tools/ci_build/github/apple/objectivec/onnxruntime-mobile-objc.podspec.template index a0221292531c3..8694ca77f621f 100644 --- a/tools/ci_build/github/apple/objectivec/onnxruntime-mobile-objc.podspec.template +++ b/tools/ci_build/github/apple/objectivec/onnxruntime-mobile-objc.podspec.template @@ -1,19 +1,20 @@ Pod::Spec.new do |s| s.name = 'onnxruntime-mobile-objc' s.version = '@VERSION@' - s.summary = 'ONNX Runtime Objective-C Pod' + s.summary = 'ONNX Runtime Mobile Objective-C Pod' s.description = <<-DESC - Preview pod for the ONNX Runtime Objective-C API. + A pod for the ONNX Runtime Mobile Objective-C API. DESC s.homepage = 'https://github.com/microsoft/onnxruntime' s.license = { :type => 'MIT', :file => @LICENSE_FILE@ } s.author = { "ONNX Runtime" => "onnxruntime@microsoft.com" } - s.source = { :http => "@SOURCE_HTTP@" } - s.ios.deployment_target = '11.0' + s.source = { :http => "file:///http_source_placeholder" } + s.ios.deployment_target = '@IOS_DEPLOYMENT_TARGET@' s.preserve_paths = [ @LICENSE_FILE@ ] s.default_subspec = "Core" + s.static_framework = true s.subspec "Core" do |core| core.dependency "onnxruntime-mobile-c", "#{s.version}" diff --git a/tools/ci_build/github/apple/package_assembly_utils.py b/tools/ci_build/github/apple/package_assembly_utils.py new file mode 100644 index 0000000000000..1478ae0958525 --- /dev/null +++ b/tools/ci_build/github/apple/package_assembly_utils.py @@ -0,0 +1,69 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import json +import os +import pathlib +import re +import shutil + +from typing import Dict, List + +_script_dir = pathlib.Path(__file__).parent.resolve(strict=True) +repo_root = _script_dir.parents[3] + +_template_variable_pattern = re.compile(r"@(\w+)@") # match "@var@" + + +def gen_file_from_template(template_file: pathlib.Path, output_file: pathlib.Path, + variable_substitutions: Dict[str, str]): + ''' + Generates a file from a template file. + The template file may contain template variables that will be substituted + with the provided values in the generated output file. + In the template file, template variable names are delimited by "@"'s, + e.g., "@var@". + + :param template_file The template file path. + :param output_file The generated output file path. + :param variable_substitutions The mapping from template variable name to value. + ''' + with open(template_file, mode="r") as template: + content = template.read() + + def replace_template_variable(match): + variable_name = match.group(1) + return variable_substitutions.get(variable_name, match.group(0)) + + content = _template_variable_pattern.sub(replace_template_variable, content) + + with open(output_file, mode="w") as output: + output.write(content) + + +def copy_repo_relative_to_dir(patterns: List[str], dest_dir: pathlib.Path): + ''' + Copies file paths relative to the repo root to a directory. + The given paths or path patterns are relative to the repo root, and the + repo root-relative intermediate directory structure is maintained. + + :param patterns The paths or path patterns relative to the repo root. + :param dest_dir The destination directory. + ''' + paths = [path for pattern in patterns for path in repo_root.glob(pattern)] + for path in paths: + repo_relative_path = path.relative_to(repo_root) + dst_path = dest_dir / repo_relative_path + os.makedirs(dst_path.parent, exist_ok=True) + shutil.copy(path, dst_path) + + +def load_framework_info(framework_info_file: pathlib.Path): + ''' + Loads framework info from a file. + + :param framework_info_file The framework info file path. + :return The framework info values. + ''' + with open(framework_info_file, mode="r") as framework_info: + return json.load(framework_info) diff --git a/tools/ci_build/github/apple/test_ios_packages.py b/tools/ci_build/github/apple/test_ios_packages.py new file mode 100644 index 0000000000000..03d1c60529111 --- /dev/null +++ b/tools/ci_build/github/apple/test_ios_packages.py @@ -0,0 +1,124 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import argparse +import os +import pathlib +import shutil +import subprocess + +SCRIPT_DIR = os.path.dirname(os.path.realpath(__file__)) +REPO_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "..", "..")) + + +from package_assembly_utils import ( # noqa: E402 + gen_file_from_template, load_framework_info) + + +def _test_ios_packages(args): + # check if CocoaPods is installed + if shutil.which('pod') is None: + if args.fail_if_cocoapods_missing: + raise ValueError('CocoaPods is required for this test') + else: + print('CocoaPods is not installed, ignore this test') + return + + # Now we need to create a zip file contains the framework and the podspec file, both of these 2 files + # should be under the c_framework_dir + c_framework_dir = args.c_framework_dir.resolve() + if not c_framework_dir.is_dir(): + raise FileNotFoundError('c_framework_dir {} is not a folder.'.format(c_framework_dir)) + + framework_path = os.path.join(c_framework_dir, 'onnxruntime.framework') + if not pathlib.Path(framework_path).exists(): + raise FileNotFoundError('{} does not have onnxruntime.framework'.format(c_framework_dir)) + + # create a temp folder + import tempfile + with tempfile.TemporaryDirectory() as temp_dir: + # This is for debugging only + # temp_dir = + # shutil.rmtree(temp_dir) + + # create a zip file contains the framework + # TODO, move this into a util function + local_pods_dir = os.path.join(temp_dir, 'local_pods') + os.makedirs(local_pods_dir, exist_ok=True) + # shutil.make_archive require target file as full path without extension + zip_base_filename = os.path.join(local_pods_dir, 'onnxruntime-mobile-c') + zip_file_path = zip_base_filename + '.zip' + shutil.make_archive(zip_base_filename, 'zip', root_dir=c_framework_dir, base_dir='onnxruntime.framework') + + # copy the test project to the temp_dir + test_proj_path = os.path.join(REPO_DIR, 'onnxruntime', 'test', 'platform', 'ios', 'ios_package_test') + target_proj_path = os.path.join(temp_dir, 'ios_package_test') + shutil.copytree(test_proj_path, target_proj_path) + + # generate the podspec file from the template + framework_info = load_framework_info(args.framework_info_file.resolve()) + + with open(os.path.join(REPO_DIR, 'VERSION_NUMBER')) as version_file: + ORT_VERSION = version_file.readline().strip() + + variable_substitutions = { + "VERSION": ORT_VERSION, + "IOS_DEPLOYMENT_TARGET": framework_info["IOS_DEPLOYMENT_TARGET"], + "WEAK_FRAMEWORK": framework_info["WEAK_FRAMEWORK"], + "LICENSE_FILE": '"LICENSE"', + } + + podspec_template = os.path.join(SCRIPT_DIR, "c", "onnxruntime-mobile-c.podspec.template") + podspec = os.path.join(target_proj_path, "onnxruntime-mobile-c.podspec") + + gen_file_from_template(podspec_template, podspec, variable_substitutions) + + # update the podspec to point to the local framework zip file + with open(podspec, 'r') as file: + file_data = file.read() + file_data = file_data.replace('file:///http_source_placeholder', 'file:' + zip_file_path) + with open(podspec, 'w') as file: + file.write(file_data) + + # clean the Cocoapods cache first, in case the same pod was cached in previous runs + subprocess.run(['pod', 'cache', 'clean', '--all'], shell=False, check=True, cwd=target_proj_path) + + # install pods + subprocess.run(['pod', 'install'], shell=False, check=True, cwd=target_proj_path) + + # run the tests + subprocess.run(['xcrun', 'xcodebuild', 'test', + '-workspace', './ios_package_test.xcworkspace', + '-scheme', 'ios_package_test', + '-destination', 'platform=iOS Simulator,OS=latest,name=iPhone SE (2nd generation)'], + shell=False, check=True, cwd=target_proj_path) + + +def parse_args(): + parser = argparse.ArgumentParser( + os.path.basename(__file__), + description='Test iOS framework using CocoaPods package.' + ) + + parser.add_argument('--fail_if_cocoapods_missing', action='store_true', + help='This script will fail if CocoaPods is not installed, ' + 'will not throw error unless fail_if_cocoapod_missing is set.') + + parser.add_argument("--framework_info_file", type=pathlib.Path, required=True, + help="Path to the framework_info.json file containing additional values for the podspec. " + "This file should be generated by CMake in the build directory.") + + parser.add_argument('--c_framework_dir', type=pathlib.Path, required=True, + help='Provide the parent directory for C/C++ framework') + + return parser.parse_args() + + +def main(): + args = parse_args() + _test_ios_packages(args) + + +if __name__ == '__main__': + main() diff --git a/tools/ci_build/github/azure-pipelines/mac-ios-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/mac-ios-ci-pipeline.yml index 750aa525acc5c..b31ab975fa29e 100644 --- a/tools/ci_build/github/azure-pipelines/mac-ios-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/mac-ios-ci-pipeline.yml @@ -13,6 +13,7 @@ jobs: --apple_deploy_target 12.1 \ --use_xcode \ --config RelWithDebInfo \ + --build_apple_framework \ --parallel displayName: (CPU EP) Build onnxruntime for iOS x86_64 and run tests using simulator - script: | @@ -25,5 +26,6 @@ jobs: --apple_deploy_target 12.1 \ --use_xcode \ --config RelWithDebInfo \ + --build_apple_framework \ --parallel displayName: (CoreML EP) Build onnxruntime for iOS x86_64 and run tests using simulator 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 new file mode 100644 index 0000000000000..7db861520c76a --- /dev/null +++ b/tools/ci_build/github/azure-pipelines/mac-ios-packaging-pipeline.yml @@ -0,0 +1,109 @@ +parameters: +- name: IsReleaseBuild + displayName: Is this a release build? Set this parameter to true if you are doing an Onnx Runtime release. + type: boolean + default: false + +jobs: +- job: IosPackaging + displayName: "iOS Packaging" + + pool: + vmImage: "macOS-10.15" + + steps: + - task: UsePythonVersion@0 + inputs: + versionSpec: "3.9" + addToPath: true + architecture: "x64" + + - script: | + pip install -r tools/ci_build/github/apple/ios_packaging.requirements.txt + displayName: "Install Python requirements" + + - bash: | + BASE_VERSION=$(cat VERSION_NUMBER) + IS_RELEASE_BUILD=$(echo "${{ parameters.IsReleaseBuild }}" | tr "[:upper:]" "[:lower:]") + + if [[ "${IS_RELEASE_BUILD}" == "true" ]]; then + VERSION=${BASE_VERSION} + else + VERSION="${BASE_VERSION}-dev" + fi + + 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" "${IS_RELEASE_BUILD}" + displayName: "Set variables" + + - script: | + python tools/ci_build/github/apple/build_ios_framework.py \ + --build_dir "$(Build.BinariesDirectory)/ios_framework" \ + --include_ops_by_config tools/ci_build/github/android/mobile_package.required_operators.config \ + tools/ci_build/github/apple/default_mobile_ios_framework_build_settings.json + displayName: "Build iOS framework" + + - script: | + python tools/ci_build/github/apple/test_ios_packages.py \ + --fail_if_cocoapods_missing \ + --framework_info_file "$(Build.BinariesDirectory)/ios_framework/framework_info.json" \ + --c_framework_dir "$(Build.BinariesDirectory)/ios_framework/framework_out" + displayName: "Test iOS framework" + + - script: | + python tools/ci_build/github/apple/c/assemble_c_pod_package.py \ + --staging-dir "$(Build.BinariesDirectory)/staging/onnxruntime-mobile-c" \ + --pod-version ${ORT_POD_VERSION} \ + --framework-info-file "$(Build.BinariesDirectory)/ios_framework/framework_info.json" \ + --framework-dir "$(Build.BinariesDirectory)/ios_framework/framework_out/onnxruntime.framework" + displayName: "Assemble onnxruntime-mobile-c pod files" + + - script: | + pod lib lint --verbose + workingDirectory: "$(Build.BinariesDirectory)/staging/onnxruntime-mobile-c" + displayName: "Check onnxruntime-mobile-c pod" + + - script: | + python tools/ci_build/github/apple/objectivec/assemble_objc_pod_package.py \ + --staging-dir "$(Build.BinariesDirectory)/staging/onnxruntime-mobile-objc" \ + --pod-version ${ORT_POD_VERSION} \ + --framework-info-file "$(Build.BinariesDirectory)/ios_framework/framework_info.json" + displayName: "Assemble onnxruntime-mobile-objc pod files" + + - script: | + pod lib lint --verbose \ + --include-podspecs="$(Build.BinariesDirectory)/staging/onnxruntime-mobile-c/onnxruntime-mobile-c.podspec" + workingDirectory: "$(Build.BinariesDirectory)/staging/onnxruntime-mobile-objc" + displayName: "Check onnxruntime-mobile-objc pod" + + - bash: | + set -e + gem install jazzy + jazzy --config objectivec/docs/jazzy_config.yaml \ + --output "$(Build.BinariesDirectory)/staging/objc_api_docs" \ + --module-version ${ORT_POD_VERSION} + displayName: "Generate Objective-C API docs" + + - 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" + + - publish: "$(Build.ArtifactStagingDirectory)" + artifact: ios_packaging_artifacts + displayName: "Publish artifacts" diff --git a/tools/ci_build/github/azure-pipelines/orttraining-win-gpu-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/orttraining-win-gpu-ci-pipeline.yml index 306ceb126be86..a27347e6868ca 100644 --- a/tools/ci_build/github/azure-pipelines/orttraining-win-gpu-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/orttraining-win-gpu-ci-pipeline.yml @@ -3,7 +3,7 @@ trigger: none jobs: - template: templates/win-ci-2019.yml parameters: - AgentPool : 'Win-GPU-2019' + AgentPool : 'onnxruntime-win-cuda11-0' JobName: 'Win_GPU_Training' # TODO fix test failures and remove --skip_onnx_tests BuildCommand: >- diff --git a/tools/ci_build/github/azure-pipelines/win-gpu-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/win-gpu-ci-pipeline.yml index 2150ac121c6f3..fb5e3f6a8af2a 100644 --- a/tools/ci_build/github/azure-pipelines/win-gpu-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/win-gpu-ci-pipeline.yml @@ -6,7 +6,7 @@ parameters: jobs: - job: 'build' - pool: 'Win-GPU-2019' + pool: 'onnxruntime-win-cuda11-0' variables: OrtPackageId: 'Microsoft.ML.OnnxRuntime.Gpu' MsbuildArguments: '-maxcpucount' diff --git a/tools/ci_build/github/azure-pipelines/win-gpu-reduce-op-ci-pipeline.yml b/tools/ci_build/github/azure-pipelines/win-gpu-reduce-op-ci-pipeline.yml index 0b08865b80f35..f30810b1a727b 100644 --- a/tools/ci_build/github/azure-pipelines/win-gpu-reduce-op-ci-pipeline.yml +++ b/tools/ci_build/github/azure-pipelines/win-gpu-reduce-op-ci-pipeline.yml @@ -1,6 +1,6 @@ jobs: - job: 'build' - pool: 'Win-GPU-2019' + pool: 'onnxruntime-win-cuda11-0' strategy: maxParallel: 2 matrix: diff --git a/tools/ci_build/github/linux/ort_minimal/build_minimal_ort_android_baseline_and_report_bin_size.sh b/tools/ci_build/github/linux/ort_minimal/build_minimal_ort_android_baseline_and_report_bin_size.sh index f6daf5ec04245..758c126c91e6c 100644 --- a/tools/ci_build/github/linux/ort_minimal/build_minimal_ort_android_baseline_and_report_bin_size.sh +++ b/tools/ci_build/github/linux/ort_minimal/build_minimal_ort_android_baseline_and_report_bin_size.sh @@ -30,7 +30,7 @@ python3 /onnxruntime_src/tools/ci_build/build.py \ # set current size limit to 1165KB. python3 /onnxruntime_src/tools/ci_build/github/linux/ort_minimal/check_build_binary_size.py \ - --threshold=1165000 \ + --threshold=1175000 \ /build/MinSizeRel/libonnxruntime.so # Post the binary size info to ort mysql DB