From 3a7826e671e28908ce49f43c01d52d6e52391589 Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:19:24 +0800 Subject: [PATCH 01/10] Enable ios testing --- build.py | 43 ++++- test/CMakeLists.txt | 61 +++++++- test/main.cpp | 12 +- test/xctest/ortgenaitestmain.mm | 90 +++++++++++ test/xctest/ortgenaixctest.m | 22 +++ test/xctest/xcgtest.mm | 270 ++++++++++++++++++++++++++++++++ 6 files changed, 488 insertions(+), 10 deletions(-) create mode 100644 test/xctest/ortgenaitestmain.mm create mode 100644 test/xctest/ortgenaixctest.m create mode 100644 test/xctest/xcgtest.mm diff --git a/build.py b/build.py index c03a06c45..83899869d 100644 --- a/build.py +++ b/build.py @@ -10,6 +10,8 @@ import shlex import shutil import sys +import json +import subprocess import textwrap from pathlib import Path @@ -387,7 +389,6 @@ def _run_android_tests(args: argparse.Namespace): # the test app loads and runs a test model using the GenAI Java bindings gradle_executable = str(REPO_ROOT / "src" / "java" / ("gradlew.bat" if util.is_windows() else "gradlew")) android_test_path = args.build_dir / "src" / "java" / "androidtest" - import subprocess exception = None try: util.run([gradle_executable, "--no-daemon", @@ -412,6 +413,39 @@ def _run_android_tests(args: argparse.Namespace): raise exception +def _run_ios_tests(args: argparse.Namespace): + simulator_device_info = subprocess.check_output( + [ + sys.executable, + os.path.join(REPO_ROOT, "tools", "ci_build", "github", "apple", "get_simulator_device_info.py"), + ], + text=True, + ).strip() + log.debug(f"Simulator device info:\n{simulator_device_info}") + + simulator_device_info = json.loads(simulator_device_info) + + xc_test_schemes = [ + "unit_tests_xc", + ] + + for xc_test_scheme in xc_test_schemes: + util.run( + [ + "xcodebuild", + "test-without-building", + "-project", + "./Generators.xcodeproj", + "-configuration", + args.config, + "-scheme", + xc_test_scheme, + "-destination", + f"platform=iOS Simulator,id={simulator_device_info['device_udid']}", + ], + cwd=args.build_dir, + ) + def update(args: argparse.Namespace, env: dict[str, str]): """ Update the cmake build files. @@ -533,8 +567,8 @@ def test(args: argparse.Namespace, env: dict[str, str]): """ Run the tests. """ - ctest_cmd = [str(args.ctest_path), "--build-config", args.config, "--verbose", "--timeout", "10800"] - util.run(ctest_cmd, cwd=str(args.build_dir)) + #ctest_cmd = [str(args.ctest_path), "--build-config", args.config, "--verbose", "--timeout", "10800"] + #util.run(ctest_cmd, cwd=str(args.build_dir)) if util.is_windows() and not args.skip_csharp: dotnet = str(_resolve_executable_path("dotnet")) @@ -545,6 +579,9 @@ def test(args: argparse.Namespace, env: dict[str, str]): if args.android: _run_android_tests(args) + if args.ios: + _run_ios_tests(args) + def clean(args: argparse.Namespace, env: dict[str, str]): """ diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index da3502bb4..a69dc7b59 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,3 +1,13 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +if (IOS) + find_package(XCTest REQUIRED) +endif() + +set(TEST_SRC_DIR ${CMAKE_SOURCE_DIR}/test) +set(TEST_INC_DIR ${ORT_HEADER_DIR} ${CMAKE_SOURCE_DIR}/src) + include(${CMAKE_SOURCE_DIR}/cmake/cxx_standard.cmake) set(TESTS_ROOT ${CMAKE_CURRENT_SOURCE_DIR} PARENT_SCOPE) @@ -6,8 +16,7 @@ file(GLOB test_srcs CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/*.cpp" ) -# google unit test -add_executable(unit_tests +set(_UT_SOURCES main.cpp c_api_tests.cpp model_tests.cpp @@ -15,10 +24,14 @@ add_executable(unit_tests sampling_benchmark.cpp ) -target_include_directories(unit_tests PRIVATE - ${ORT_HEADER_DIR} - ${CMAKE_SOURCE_DIR}/src -) +if (IOS) + add_executable(unit_tests ${_UT_SOURCES} ${TEST_SRC_DIR}/xctest/ortgenaitestmain.mm) +else() + # google unit test + add_executable(unit_tests ${_UT_SOURCES}) +endif() + +target_include_directories(unit_tests PRIVATE ${TEST_INC_DIR}) target_link_directories(unit_tests PRIVATE ${ORT_LIB_DIR}) target_link_libraries(unit_tests PRIVATE @@ -52,6 +65,42 @@ set_target_properties(unit_tests PROPERTIES FOLDER "Tests") source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${test_srcs}) set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT unit_tests) +if (IOS) + set_target_properties(unit_tests PROPERTIES FOLDER "ONNXRuntimeGenAITest" + MACOSX_BUNDLE_BUNDLE_NAME unit_tests + MACOSX_BUNDLE_GUI_IDENTIFIER com.onnxruntimegenai.utest + MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} + MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} + XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") + + xctest_add_bundle(unit_tests_xc unit_tests + ${TEST_SRC_DIR}/xctest/ortgenaixctest.m + ${TEST_SRC_DIR}/xctest/xcgtest.mm + ${_UT_SOURCES}) + + get_target_property(srcs unit_tests_xc SOURCES) + set(objective_c_cc_srcs ${srcs}) + list(FILTER objective_c_cc_srcs INCLUDE REGEX "\\.mm?$") + set_property(SOURCE ${objective_c_cc_srcs} APPEND PROPERTY COMPILE_OPTIONS "-fobjc-arc") + + target_include_directories(unit_tests_xc PRIVATE ${TEST_INC_DIR}) + target_link_directories(unit_tests_xc PRIVATE ${ORT_LIB_DIR}) + target_link_libraries(unit_tests_xc PRIVATE onnxruntime-genai-static GTest::gtest) + target_link_libraries(unit_tests_xc PRIVATE ${ONNXRUNTIME_LIB}) + set_target_properties(unit_tests_xc PROPERTIES FOLDER "ONNXRuntimeGenAIXCTest" + MACOSX_BUNDLE_BUNDLE_NAME unit_tests_xc + MACOSX_BUNDLE_GUI_IDENTIFIER com.onnxruntime.utest.unit_tests_xc + MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} + MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") + + xctest_add_test(xctest.unit_tests unit_tests_xc) +endif() + include(GoogleTest) diff --git a/test/main.cpp b/test/main.cpp index ee21918c4..f778c4c3f 100644 --- a/test/main.cpp +++ b/test/main.cpp @@ -5,7 +5,17 @@ #include #include -int main(int argc, char** argv) { +#define TEST_MAIN main + +#if defined(__APPLE__) +#include +#if TARGET_OS_SIMULATOR || TARGET_OS_IOS +#undef TEST_MAIN +#define TEST_MAIN main_no_link_ // there is a UI test app for iOS. +#endif +#endif + +int TEST_MAIN(int argc, char** argv) { std::cout << "Generators Utility Library" << std::endl; std::cout << "Initializing OnnxRuntime... "; std::cout.flush(); diff --git a/test/xctest/ortgenaitestmain.mm b/test/xctest/ortgenaitestmain.mm new file mode 100644 index 000000000..bfa46d939 --- /dev/null +++ b/test/xctest/ortgenaitestmain.mm @@ -0,0 +1,90 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +#import + +static const size_t kMaxStrLen = 2048; + +static void set_test_rootdir(const char* image_path) { + size_t n = strnlen(image_path, kMaxStrLen); + for (; n >= 0; n--) { + if (image_path[n] == '/') { + break; + } + } + + char* bundle_dir = (char*)malloc(n + 1); + if (bundle_dir != NULL) { + strncpy(bundle_dir, image_path, n); + bundle_dir[n] = 0; + chdir(bundle_dir); + free(bundle_dir); + } +} + +@interface ViewController : UIViewController + +@end + +@implementation ViewController + +- (void)viewDidLoad { + [super viewDidLoad]; + + self.view.backgroundColor = [UIColor whiteColor]; +} + +- (void)didReceiveMemoryWarning { + [super didReceiveMemoryWarning]; +} + +@end + +@interface AppDelegate : UIResponder + +@property(strong, nonatomic) UIWindow* window; + +@property(nonatomic, strong) UIViewController* rootViewController; + +@end + +@implementation AppDelegate + +- (BOOL)application:(UIApplication*)application didFinishLaunchingWithOptions:(NSDictionary*)launchOptions { + self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; + self.window.tintAdjustmentMode = UIViewTintAdjustmentModeNormal; + self.window.rootViewController = [[ViewController alloc] init]; + + self.window.backgroundColor = [UIColor whiteColor]; + self.window.clipsToBounds = NO; + [self.window makeKeyAndVisible]; + + return YES; +} + +- (void)applicationWillResignActive:(UIApplication*)application { +} + +- (void)applicationDidEnterBackground:(UIApplication*)application { +} + +- (void)applicationWillEnterForeground:(UIApplication*)application { +} + +- (void)applicationDidBecomeActive:(UIApplication*)application { +} + +- (void)applicationWillTerminate:(UIApplication*)application { +} + +@end + +int main(int argc, char* argv[]) { + set_test_rootdir(argv[0]); + int ret = 0; + @autoreleasepool { + ret = UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); + } + + return ret; +} diff --git a/test/xctest/ortgenaixctest.m b/test/xctest/ortgenaixctest.m new file mode 100644 index 000000000..eb6ecbf46 --- /dev/null +++ b/test/xctest/ortgenaixctest.m @@ -0,0 +1,22 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License + +#import +#import + +// This is the stub test case which will let the xcode command line tool start testing on Simulator +@interface ONNXRuntimeGenAITestXCWrapper : XCTestCase + +@end + +@implementation ONNXRuntimeGenAITestXCWrapper + +- (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. +} + +@end diff --git a/test/xctest/xcgtest.mm b/test/xctest/xcgtest.mm new file mode 100644 index 000000000..3a39b224c --- /dev/null +++ b/test/xctest/xcgtest.mm @@ -0,0 +1,270 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +/* + * Copyright (c) 2013 Matthew Stevens + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#import +#import +#import +#import + +using testing::TestCase; +using testing::TestInfo; +using testing::TestPartResult; +using testing::UnitTest; + + +static NSString* const GoogleTestDisabledPrefix = @"DISABLED_"; + +/** + * Class prefix used for generated Objective-C class names. + * + * If a class name generated for a Google Test case conflicts with an existing + * class the value of this variable can be changed to add a class prefix. + */ +static NSString* const GeneratedClassPrefix = @""; + +/** + * Map of test keys to Google Test filter strings. + * + * Some names allowed by Google Test would result in illegal Objective-C + * identifiers and in such cases the generated class and method names are + * adjusted to handle this. This map is used to obtain the original Google Test + * filter string associated with a generated Objective-C test method. + */ +static NSDictionary* GoogleTestFilterMap; + +/** + * A Google Test listener that reports failures to XCTest. + */ +class XCTestListener : public testing::EmptyTestEventListener { + public: + XCTestListener(XCTestCase* testCase) : _testCase(testCase) {} + + void OnTestPartResult(const TestPartResult& test_part_result) { + if (test_part_result.passed() || test_part_result.skipped()) + return; + + int lineNumber = test_part_result.line_number(); + const char* fileName = test_part_result.file_name(); + NSString* path = fileName ? [@(fileName) stringByStandardizingPath] : nil; + NSString* description = @(test_part_result.message()); + [_testCase recordFailureWithDescription:description + inFile:path + atLine:(lineNumber >= 0 ? (NSUInteger)lineNumber : 0) + expected:YES]; + } + + private: + XCTestCase* _testCase; +}; + +/** + * Registers an XCTestCase subclass for each Google Test case. + * + * Generating these classes allows Google Test cases to be represented as peers + * of standard XCTest suites and supports filtering of test runs to specific + * Google Test cases or individual tests via Xcode. + */ +@interface GoogleTestLoader : NSObject +@end + +/** + * Base class for the generated classes for Google Test cases. + */ +@interface GoogleTestCase : XCTestCase +@end + +@implementation GoogleTestCase + +/** + * Associates generated Google Test classes with the test bundle. + * + * This affects how the generated test cases are represented in reports. By + * associating the generated classes with a test bundle the Google Test cases + * appear to be part of the same test bundle that this source file is compiled + * into. Without this association they appear to be part of a bundle + * representing the directory of an internal Xcode tool that runs the tests. + */ ++ (NSBundle*)bundleForClass { + return [NSBundle bundleForClass:[GoogleTestLoader class]]; +} + +/** + * Implementation of +[XCTestCase testInvocations] that returns an array of test + * invocations for each test method in the class. + * + * This differs from the standard implementation of testInvocations, which only + * adds methods with a prefix of "test". + */ ++ (NSArray*)testInvocations { + NSMutableArray* invocations = [NSMutableArray array]; + + unsigned int methodCount = 0; + Method* methods = class_copyMethodList([self class], &methodCount); + + for (unsigned int i = 0; i < methodCount; i++) { + SEL sel = method_getName(methods[i]); + NSMethodSignature* sig = [self instanceMethodSignatureForSelector:sel]; + NSInvocation* invocation = [NSInvocation invocationWithMethodSignature:sig]; + [invocation setSelector:sel]; + [invocations addObject:invocation]; + } + + free(methods); + + return invocations; +} + +@end + +/** + * Runs a single test. + */ +static void RunTest(id self, SEL _cmd) { + XCTestListener* listener = new XCTestListener(self); + UnitTest* googleTest = UnitTest::GetInstance(); + googleTest->listeners().Append(listener); + + NSString* testKey = [NSString stringWithFormat:@"%@.%@", [self class], NSStringFromSelector(_cmd)]; + NSString* testFilter = GoogleTestFilterMap[testKey]; + XCTAssertNotNil(testFilter, @"No test filter found for test %@", testKey); + + testing::GTEST_FLAG(filter) = [testFilter UTF8String]; + (void)RUN_ALL_TESTS(); + + delete googleTest->listeners().Release(listener); + + int totalTestsRun = googleTest->successful_test_count() + + googleTest->failed_test_count() + googleTest->skipped_test_count(); + XCTAssertEqual(totalTestsRun, 1, @"Expected to run a single test for filter \"%@\"", testFilter); +} + +@implementation GoogleTestLoader + +/** + * Performs registration of classes for Google Test cases after our bundle has + * finished loading. + * + * This registration needs to occur before XCTest queries the runtime for test + * subclasses, but after C++ static initializers have run so that all Google + * Test cases have been registered. This is accomplished by synchronously + * observing the NSBundleDidLoadNotification for our own bundle. + */ ++ (void)load { + NSBundle* bundle = [NSBundle bundleForClass:self]; + [[NSNotificationCenter defaultCenter] addObserverForName:NSBundleDidLoadNotification + object:bundle + queue:nil + usingBlock:^(NSNotification* notification) { + [self registerTestClasses]; + }]; +} + ++ (void)registerTestClasses { + // Pass the command-line arguments to Google Test to support the --gtest options + NSArray* arguments = [[NSProcessInfo processInfo] arguments]; + + int i = 0; + int argc = (int)[arguments count]; + const char** argv = (const char**)calloc((unsigned int)argc + 1, sizeof(const char*)); + for (NSString* arg in arguments) { + argv[i++] = [arg UTF8String]; + } + + testing::InitGoogleTest(&argc, (char**)argv); + UnitTest* googleTest = UnitTest::GetInstance(); + testing::TestEventListeners& listeners = googleTest->listeners(); + delete listeners.Release(listeners.default_result_printer()); + free(argv); + + BOOL runDisabledTests = GTEST_FLAG_GET(also_run_disabled_tests); + NSMutableDictionary* testFilterMap = [NSMutableDictionary dictionary]; + NSCharacterSet* decimalDigitCharacterSet = [NSCharacterSet decimalDigitCharacterSet]; + + for (int testCaseIndex = 0; testCaseIndex < googleTest->total_test_case_count(); testCaseIndex++) { + const TestCase* testCase = googleTest->GetTestCase(testCaseIndex); + NSString* testCaseName = @(testCase->name()); + + // For typed tests '/' is used to separate the parts of the test case name. + NSArray* testCaseNameComponents = [testCaseName componentsSeparatedByString:@"/"]; + + if (runDisabledTests == NO) { + BOOL testCaseDisabled = NO; + + for (NSString* component in testCaseNameComponents) { + if ([component hasPrefix:GoogleTestDisabledPrefix]) { + testCaseDisabled = YES; + break; + } + } + + if (testCaseDisabled) { + continue; + } + } + + // Join the test case name components with '_' rather than '/' to create + // a valid class name. + NSString* className = [GeneratedClassPrefix stringByAppendingString:[testCaseNameComponents componentsJoinedByString:@"_"]]; + + Class testClass = objc_allocateClassPair([GoogleTestCase class], [className UTF8String], 0); + NSAssert1(testClass, @"Failed to register Google Test class \"%@\", this class may already exist. The value of GeneratedClassPrefix can be changed to avoid this.", className); + BOOL hasMethods = NO; + + for (int testIndex = 0; testIndex < testCase->total_test_count(); testIndex++) { + const TestInfo* testInfo = testCase->GetTestInfo(testIndex); + NSString* testName = @(testInfo->name()); + if (runDisabledTests == NO && [testName hasPrefix:GoogleTestDisabledPrefix]) { + continue; + } + + // Google Test allows test names starting with a digit, prefix these with an + // underscore to create a valid method name. + NSString* methodName = testName; + if ([methodName length] > 0 && [decimalDigitCharacterSet characterIsMember:[methodName characterAtIndex:0]]) { + methodName = [@"_" stringByAppendingString:methodName]; + } + + NSString* testKey = [NSString stringWithFormat:@"%@.%@", className, methodName]; + NSString* testFilter = [NSString stringWithFormat:@"%@.%@", testCaseName, testName]; + testFilterMap[testKey] = testFilter; + + SEL selector = sel_registerName([methodName UTF8String]); + BOOL added = class_addMethod(testClass, selector, (IMP)RunTest, "v@:"); + NSAssert1(added, @"Failed to add Goole Test method \"%@\", this method may already exist in the class.", methodName); + hasMethods = YES; + } + + if (hasMethods) { + objc_registerClassPair(testClass); + } else { + objc_disposeClassPair(testClass); + } + } + + GoogleTestFilterMap = testFilterMap; +} + +@end From 1e39e7d029ad3980ac5b418cf4d5432c4277bad5 Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Tue, 10 Sep 2024 17:38:26 +0800 Subject: [PATCH 02/10] cmake --- CMakeLists.txt | 4 ++++ benchmark/c/CMakeLists.txt | 4 ++++ cmake/global_variables.cmake | 24 ++++++++++++++++++------ 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 06d196d7b..6fcfa3dd8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -88,6 +88,10 @@ if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "Linu target_link_libraries(onnxruntime-genai PRIVATE ${ONNXRUNTIME_LIB}) endif() +if(APPLE) +target_link_libraries(onnxruntime-genai PRIVATE "-framework Foundation" "-framework CoreML") +endif() + set_target_properties(onnxruntime-genai PROPERTIES FOLDER "Sources") set_target_properties(onnxruntime-genai-static PROPERTIES FOLDER "Sources") source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${generator_srcs}) diff --git a/benchmark/c/CMakeLists.txt b/benchmark/c/CMakeLists.txt index 98501f207..f382058c8 100644 --- a/benchmark/c/CMakeLists.txt +++ b/benchmark/c/CMakeLists.txt @@ -30,6 +30,10 @@ target_link_libraries(model_benchmark PRIVATE onnxruntime-genai-static ${ONNXRUN target_link_directories(model_benchmark PRIVATE ${ORT_LIB_DIR}) +if(APPLE) +target_link_libraries(model_benchmark PRIVATE "-framework Foundation" "-framework CoreML") +endif() + add_custom_command(TARGET model_benchmark POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_if_different ${onnxruntime_libs} $ ) diff --git a/cmake/global_variables.cmake b/cmake/global_variables.cmake index 2953ee287..821a031f5 100644 --- a/cmake/global_variables.cmake +++ b/cmake/global_variables.cmake @@ -52,7 +52,16 @@ if(WIN32) set(ONNXRUNTIME_EXTENSIONS_LIB "tfmtok_c.lib") set(ONNXRUNTIME_EXTENSIONS_FILES "tfmtok_c.dll") elseif(APPLE) - set(ONNXRUNTIME_LIB "libonnxruntime.dylib") + if(IOS) + add_library(onnxruntime IMPORTED STATIC) + set_property(TARGET onnxruntime PROPERTY IMPORTED_LOCATION ${ORT_LIB_DIR}/onnxruntime.xcframework) + set(ONNXRUNTIME_LIB onnxruntime) + else() + set(ONNXRUNTIME_LIB "libonnxruntime.dylib") + set(ONNXRUNTIME_PROVIDERS_CUDA_LIB "libonnxruntime_providers_cuda.dylib") + set(ONNXRUNTIME_PROVIDERS_ROCM_LIB "libonnxruntime_providers_rocm.dylib") + set(ONNXRUNTIME_ALL_SHARED_LIBS "libonnxruntime*.dylib") + endif() set(ONNXRUNTIME_PROVIDERS_CUDA_LIB "libonnxruntime_providers_cuda.dylib") set(ONNXRUNTIME_PROVIDERS_ROCM_LIB "libonnxruntime_providers_rocm.dylib") set(ONNXRUNTIME_ALL_SHARED_LIBS "libonnxruntime*.dylib") @@ -74,11 +83,14 @@ file(GLOB generator_srcs CONFIGURE_DEPENDS file(GLOB onnxruntime_libs "${ORT_LIB_DIR}/${ONNXRUNTIME_ALL_SHARED_LIBS}") set(ortgenai_embed_libs "") # shared libs that will be embedded inside the onnxruntime-genai package -if(NOT EXISTS "${ORT_LIB_DIR}/${ONNXRUNTIME_LIB}") - message(FATAL_ERROR "Expected the ONNX Runtime library to be found at ${ORT_LIB_DIR}/${ONNXRUNTIME_LIB}. Actual: Not found.") -endif() -if(NOT EXISTS "${ORT_HEADER_DIR}/onnxruntime_c_api.h") - message(FATAL_ERROR "Expected the ONNX Runtime C API header to be found at \"${ORT_HEADER_DIR}/onnxruntime_c_api.h\". Actual: Not found.") +if (IOS) + if (NOT EXISTS "${ORT_LIB_DIR}/onnxruntime.xcframework") + message(FATAL_ERROR "Expected the ONNX Runtime XCFramework to be found at ${ORT_LIB_DIR}/onnxruntime.xcframework. Actual: Not found.") + endif() +else() + if(NOT EXISTS "${ORT_LIB_DIR}/${ONNXRUNTIME_LIB}") + message(FATAL_ERROR "Expected the ONNX Runtime library to be found at ${ORT_LIB_DIR}/${ONNXRUNTIME_LIB}. Actual: Not found.") + endif() endif() From 2d724941d68301dda3609c494af085c8db23464d Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Wed, 11 Sep 2024 15:11:47 +0800 Subject: [PATCH 03/10] cmake fix --- benchmark/c/CMakeLists.txt | 11 +++++++---- test/CMakeLists.txt | 8 +++++++- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/benchmark/c/CMakeLists.txt b/benchmark/c/CMakeLists.txt index f382058c8..c5f59813e 100644 --- a/benchmark/c/CMakeLists.txt +++ b/benchmark/c/CMakeLists.txt @@ -31,11 +31,14 @@ target_link_libraries(model_benchmark PRIVATE onnxruntime-genai-static ${ONNXRUN target_link_directories(model_benchmark PRIVATE ${ORT_LIB_DIR}) if(APPLE) -target_link_libraries(model_benchmark PRIVATE "-framework Foundation" "-framework CoreML") + target_link_libraries(model_benchmark PRIVATE "-framework Foundation" "-framework CoreML") endif() -add_custom_command(TARGET model_benchmark POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${onnxruntime_libs} $ -) +if(NOT IOS) + # On iOS static xcframework is used + add_custom_command(TARGET model_benchmark POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different ${onnxruntime_libs} $ + ) +endif() source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${model_benchmark_srcs}) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a69dc7b59..b5312c515 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -57,9 +57,15 @@ set(TEST_MODEL_SRC_DIR "${CMAKE_CURRENT_SOURCE_DIR}/test_models/") set(TEST_MODEL_DES_DIR "$/test_models/") add_custom_command(TARGET unit_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${TEST_MODEL_SRC_DIR} ${TEST_MODEL_DES_DIR} - COMMAND ${CMAKE_COMMAND} -E copy ${onnxruntime_libs} $ ) +if (NOT IOS) + # On iOS static xcframework is used + add_custom_command(TARGET unit_tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${onnxruntime_libs} $ + ) +endif() + add_compile_definitions(MODEL_PATH="${TEST_MODEL_DES_DIR}") set_target_properties(unit_tests PROPERTIES FOLDER "Tests") source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${test_srcs}) From 2ed72e673f4f2e97ad8a5e05dbf7616d943accc3 Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Tue, 8 Oct 2024 15:24:49 +0800 Subject: [PATCH 04/10] fix --- build.py | 1 - test/CMakeLists.txt | 47 ++++++++++++++++++++++----------------------- 2 files changed, 23 insertions(+), 25 deletions(-) diff --git a/build.py b/build.py index 35b446dde..8d41d0936 100644 --- a/build.py +++ b/build.py @@ -532,7 +532,6 @@ def update(args: argparse.Namespace, env: dict[str, str]): platform_name = "macabi" if args.macos == "Catalyst" else args.apple_sysroot command += [ "-DENABLE_PYTHON=OFF", - "-DENABLE_TESTS=OFF", "-DENABLE_MODEL_BENCHMARK=OFF", f"-DBUILD_APPLE_FRAMEWORK={'ON' if args.build_apple_framework else 'OFF'}", "-DPLATFORM_NAME=" + platform_name, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index dbe18d2a1..982c02bc9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -59,13 +59,6 @@ add_custom_command(TARGET unit_tests POST_BUILD COMMAND ${CMAKE_COMMAND} -E copy_directory ${TEST_MODEL_SRC_DIR} ${TEST_MODEL_DES_DIR} ) -if (NOT IOS) - # On iOS static xcframework is used - add_custom_command(TARGET unit_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${onnxruntime_libs} $ - ) -endif() - add_compile_definitions(MODEL_PATH="${TEST_MODEL_DES_DIR}") set_target_properties(unit_tests PROPERTIES FOLDER "Tests") source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${test_srcs}) @@ -73,19 +66,19 @@ set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT unit_test if (IOS) set_target_properties(unit_tests PROPERTIES FOLDER "ONNXRuntimeGenAITest" - MACOSX_BUNDLE_BUNDLE_NAME unit_tests - MACOSX_BUNDLE_GUI_IDENTIFIER com.onnxruntimegenai.utest - MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} - MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} - XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" - XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" - XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") + MACOSX_BUNDLE_BUNDLE_NAME unit_tests + MACOSX_BUNDLE_GUI_IDENTIFIER com.onnxruntimegenai.utest.unit_tests + MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} + MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} + XCODE_ATTRIBUTE_CLANG_ENABLE_MODULES "YES" + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO" + XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED "NO") xctest_add_bundle(unit_tests_xc unit_tests - ${TEST_SRC_DIR}/xctest/ortgenaixctest.m - ${TEST_SRC_DIR}/xctest/xcgtest.mm - ${_UT_SOURCES}) + ${TEST_SRC_DIR}/xctest/ortgenaixctest.m + ${TEST_SRC_DIR}/xctest/xcgtest.mm + ${_UT_SOURCES}) get_target_property(srcs unit_tests_xc SOURCES) set(objective_c_cc_srcs ${srcs}) @@ -97,14 +90,20 @@ if (IOS) target_link_libraries(unit_tests_xc PRIVATE onnxruntime-genai-static GTest::gtest) target_link_libraries(unit_tests_xc PRIVATE ${ONNXRUNTIME_LIB}) set_target_properties(unit_tests_xc PROPERTIES FOLDER "ONNXRuntimeGenAIXCTest" - MACOSX_BUNDLE_BUNDLE_NAME unit_tests_xc - MACOSX_BUNDLE_GUI_IDENTIFIER com.onnxruntime.utest.unit_tests_xc - MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} - MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} - MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} - XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") + MACOSX_BUNDLE_BUNDLE_NAME unit_tests_xc + MACOSX_BUNDLE_GUI_IDENTIFIER com.onnxruntimegenai.utest.unit_tests + MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} + MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} + MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} + XCODE_PRODUCT_TYPE com.apple.product-type.bundle.ui-testing + XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") xctest_add_test(xctest.unit_tests unit_tests_xc) +else() + # On iOS static xcframework is used. No need to copy. + add_custom_command(TARGET unit_tests POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${onnxruntime_libs} $ +) endif() include(GoogleTest) From fbe7c5e801a3a819532b8757f8b958a4ffa8695b Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Tue, 8 Oct 2024 23:27:16 +0800 Subject: [PATCH 05/10] ok --- build.py | 9 +- test/CMakeLists.txt | 7 +- .../github/apple/get_simulator_device_info.py | 152 ++++++++++++++++++ 3 files changed, 162 insertions(+), 6 deletions(-) create mode 100644 tools/ci_build/github/apple/get_simulator_device_info.py diff --git a/build.py b/build.py index 8d41d0936..b0cc8c526 100644 --- a/build.py +++ b/build.py @@ -634,6 +634,11 @@ def test(args: argparse.Namespace, env: dict[str, str]): """ Run the tests. """ + if args.ios: + _run_ios_tests(args) + # CTest won't work on iOS + return + lib_dir = args.build_dir / "test" if not args.ort_home: _ = util.download_dependencies(args.use_cuda, args.use_rocm, args.use_dml, lib_dir) @@ -652,10 +657,6 @@ def test(args: argparse.Namespace, env: dict[str, str]): if args.android: _run_android_tests(args) - if args.ios: - _run_ios_tests(args) - - def clean(args: argparse.Namespace, env: dict[str, str]): """ Clean the build output. diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 982c02bc9..d95a89d44 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -40,7 +40,11 @@ target_link_libraries(unit_tests PRIVATE ) if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "Linux" OR CMAKE_SYSTEM_NAME STREQUAL "Darwin")) -target_link_libraries(unit_tests PRIVATE ${ONNXRUNTIME_LIB}) + target_link_libraries(unit_tests PRIVATE ${ONNXRUNTIME_LIB}) +endif() + +if(APPLE) + target_link_libraries(unit_tests PRIVATE "-framework Foundation" "-framework CoreML" "-framework UIKit") endif() if(USE_CUDA AND CMAKE_CUDA_COMPILER) @@ -95,7 +99,6 @@ if (IOS) MACOSX_BUNDLE_LONG_VERSION_STRING ${VERSION_STR} MACOSX_BUNDLE_BUNDLE_VERSION ${VERSION_STR} MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} - XCODE_PRODUCT_TYPE com.apple.product-type.bundle.ui-testing XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") xctest_add_test(xctest.unit_tests unit_tests_xc) diff --git a/tools/ci_build/github/apple/get_simulator_device_info.py b/tools/ci_build/github/apple/get_simulator_device_info.py new file mode 100644 index 000000000..7de9aa139 --- /dev/null +++ b/tools/ci_build/github/apple/get_simulator_device_info.py @@ -0,0 +1,152 @@ +#!/usr/bin/env python3 +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +from __future__ import annotations + +import argparse +import functools +import itertools +import json +import subprocess + + +@functools.total_ordering +class Version: + """ + A simple Version class. + We opt to use this instead of `packaging.version.Version` to avoid depending on the external `packaging` package. + It only supports integer version components. + """ + + def __init__(self, version_string: str): + self._components = tuple(int(component) for component in version_string.split(".")) + + def __eq__(self, other: Version) -> bool: + component_pairs = itertools.zip_longest(self._components, other._components, fillvalue=0) + return all(pair[0] == pair[1] for pair in component_pairs) + + def __lt__(self, other: Version) -> bool: + component_pairs = itertools.zip_longest(self._components, other._components, fillvalue=0) + for self_component, other_component in component_pairs: + if self_component != other_component: + return self_component < other_component + return False + + +def get_simulator_device_info( + requested_runtime_platform: str = "iOS", + requested_device_type_product_family: str = "iPhone", + max_runtime_version_str: str | None = None, +) -> dict[str, str]: + """ + Retrieves simulator device information from Xcode. + This simulator device should be appropriate for running tests on this machine. + + :param requested_runtime_platform: The runtime platform to select. + :param requested_device_type_product_family: The device type product family to select. + :param max_runtime_version_str: The maximum runtime version to allow. + + :return: A dictionary containing information about the selected simulator device. + """ + max_runtime_version = Version(max_runtime_version_str) if max_runtime_version_str is not None else None + + simctl_proc = subprocess.run( + ["xcrun", "simctl", "list", "--json", "--no-escape-slashes"], + text=True, + capture_output=True, + check=True, + ) + + simctl_json = json.loads(simctl_proc.stdout) + + # device type id -> device type structure + device_type_map = {device_type["identifier"]: device_type for device_type in simctl_json["devicetypes"]} + + # runtime id -> runtime structure + runtime_map = {runtime["identifier"]: runtime for runtime in simctl_json["runtimes"]} + + def runtime_filter(runtime) -> bool: + if not runtime["isAvailable"]: + return False + + if runtime["platform"] != requested_runtime_platform: + return False + + if max_runtime_version is not None and Version(runtime["version"]) > max_runtime_version: + return False + + return True + + def runtime_id_filter(runtime_id: str) -> bool: + runtime = runtime_map.get(runtime_id) + if runtime is None: + return False + return runtime_filter(runtime) + + def device_type_filter(device_type) -> bool: + if device_type["productFamily"] != requested_device_type_product_family: + return False + + return True + + def device_filter(device) -> bool: + if not device["isAvailable"]: + return False + + if not device_type_filter(device_type_map[device["deviceTypeIdentifier"]]): + return False + + return True + + # simctl_json["devices"] is a map of runtime id -> list of device structures + # expand this into a list of (runtime id, device structure) and filter out invalid entries + runtime_id_and_device_pairs = [] + for runtime_id, device_list in filter( + lambda runtime_id_and_device_list: runtime_id_filter(runtime_id_and_device_list[0]), + simctl_json["devices"].items(), + ): + runtime_id_and_device_pairs.extend((runtime_id, device) for device in filter(device_filter, device_list)) + + # sort key - tuple of (runtime version, device type min runtime version) + # the secondary device type min runtime version value is to treat more recent device types as greater + def runtime_id_and_device_pair_key(runtime_id_and_device_pair): + runtime_id, device = runtime_id_and_device_pair + + runtime = runtime_map[runtime_id] + device_type = device_type_map[device["deviceTypeIdentifier"]] + + return (Version(runtime["version"]), device_type["minRuntimeVersion"]) + + selected_runtime_id, selected_device = max(runtime_id_and_device_pairs, key=runtime_id_and_device_pair_key) + selected_runtime = runtime_map[selected_runtime_id] + selected_device_type = device_type_map[selected_device["deviceTypeIdentifier"]] + + result = { + "device_name": selected_device["name"], + "device_udid": selected_device["udid"], + "device_type_identifier": selected_device_type["identifier"], + "device_type_name": selected_device_type["name"], + "device_type_product_family": selected_device_type["productFamily"], + "runtime_identifier": selected_runtime["identifier"], + "runtime_platform": selected_runtime["platform"], + "runtime_version": selected_runtime["version"], + } + + return result + + +def main(): + parser = argparse.ArgumentParser(description="Gets simulator info from Xcode and prints it in JSON format.") + parser.add_argument("--max-runtime-version", help="The maximum runtime version to allow.") + args = parser.parse_args() + + info = get_simulator_device_info( + max_runtime_version_str=args.max_runtime_version, + ) + + print(json.dumps(info, indent=2)) + + +if __name__ == "__main__": + main() From b1b96c3f19fe07467eaf5708d1c0c705127a12aa Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:29:13 +0800 Subject: [PATCH 06/10] GHA job --- .github/workflows/ios-cpu-arm64-build.yml | 43 +++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 .github/workflows/ios-cpu-arm64-build.yml diff --git a/.github/workflows/ios-cpu-arm64-build.yml b/.github/workflows/ios-cpu-arm64-build.yml new file mode 100644 index 000000000..95267a0ed --- /dev/null +++ b/.github/workflows/ios-cpu-arm64-build.yml @@ -0,0 +1,43 @@ +name: "iOS CPU ARM64 Build" +on: + workflow_dispatch: + push: + branches: + - main + - rel-* + pull_request: +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true +env: + ORT_NIGHTLY_REST_API: "https://feeds.dev.azure.com/aiinfra/PublicPackages/_apis/packaging/Feeds/ORT-Nightly/packages?packageNameQuery=Microsoft.ML.OnnxRuntime&api-version=6.0-preview.1" + ORT_PACKAGE_NAME: "Microsoft.ML.OnnxRuntime" +jobs: + mac-cpu-arm64-build: + runs-on: macos-latest # arm64 + env: + xcode_version: 14 + steps: + - name: Checkout OnnxRuntime GenAI repo + uses: actions/checkout@v4 + with: + submodules: true + + - name: Get the Latest OnnxRuntime Nightly Version + run: | + ORT_NIGHTLY_VERSION=$(curl -s "${{ env.ORT_NIGHTLY_REST_API }}" | jq -r '.value[0].versions[0].normalizedVersion') + echo "$ORT_NIGHTLY_VERSION" + echo "ORT_NIGHTLY_VERSION=$ORT_NIGHTLY_VERSION" >> $GITHUB_ENV + - name: Download OnnxRuntime Nightly + run: | + nuget install ${{ env.ORT_PACKAGE_NAME }} -version ${{ env.ORT_NIGHTLY_VERSION }} -x + + - name: Extract OnnxRuntime library and header files + run: | + mkdir -p ort/lib + mv ${{ env.ORT_PACKAGE_NAME }}/build/native/include ort/ + mv ${{ env.ORT_PACKAGE_NAME }}/runtimes/ios/native/* ort/lib/ + + - name: Build and Run tests + run: | + python3 build.py --ios --parallel --apple_sysroot iphonesimulator --osx_arch arm64 --apple_deploy_target 15.4 --cmake_generator 'Xcode' From d93d2d3d45a44bb0c7097a29cd22b18868d24f6e Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:46:26 +0800 Subject: [PATCH 07/10] Fix --- .github/workflows/ios-cpu-arm64-build.yml | 2 +- test/CMakeLists.txt | 10 ++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ios-cpu-arm64-build.yml b/.github/workflows/ios-cpu-arm64-build.yml index 95267a0ed..8daf4dc5d 100644 --- a/.github/workflows/ios-cpu-arm64-build.yml +++ b/.github/workflows/ios-cpu-arm64-build.yml @@ -13,7 +13,7 @@ env: ORT_NIGHTLY_REST_API: "https://feeds.dev.azure.com/aiinfra/PublicPackages/_apis/packaging/Feeds/ORT-Nightly/packages?packageNameQuery=Microsoft.ML.OnnxRuntime&api-version=6.0-preview.1" ORT_PACKAGE_NAME: "Microsoft.ML.OnnxRuntime" jobs: - mac-cpu-arm64-build: + ios-cpu-arm64-build: runs-on: macos-latest # arm64 env: xcode_version: 14 diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index d95a89d44..58c562b69 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -44,7 +44,10 @@ if(NOT (CMAKE_SYSTEM_NAME STREQUAL "Android" OR CMAKE_SYSTEM_NAME STREQUAL "Linu endif() if(APPLE) - target_link_libraries(unit_tests PRIVATE "-framework Foundation" "-framework CoreML" "-framework UIKit") + target_link_libraries(unit_tests PRIVATE "-framework Foundation" "-framework CoreML") + if(IOS) + target_link_libraries(unit_tests PRIVATE "-framework UIKit") + endif() endif() if(USE_CUDA AND CMAKE_CUDA_COMPILER) @@ -102,11 +105,6 @@ if (IOS) XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") xctest_add_test(xctest.unit_tests unit_tests_xc) -else() - # On iOS static xcframework is used. No need to copy. - add_custom_command(TARGET unit_tests POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy ${onnxruntime_libs} $ -) endif() include(GoogleTest) From 617256fc036da47d27810cc5babb9fd685af3ed4 Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Wed, 9 Oct 2024 11:52:11 +0800 Subject: [PATCH 08/10] pip install requests --- .github/workflows/ios-cpu-arm64-build.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/ios-cpu-arm64-build.yml b/.github/workflows/ios-cpu-arm64-build.yml index 8daf4dc5d..2763bea51 100644 --- a/.github/workflows/ios-cpu-arm64-build.yml +++ b/.github/workflows/ios-cpu-arm64-build.yml @@ -26,7 +26,7 @@ jobs: - name: Get the Latest OnnxRuntime Nightly Version run: | ORT_NIGHTLY_VERSION=$(curl -s "${{ env.ORT_NIGHTLY_REST_API }}" | jq -r '.value[0].versions[0].normalizedVersion') - echo "$ORT_NIGHTLY_VERSION" + echo "$ORT_NIGHTLY_VERSION" echo "ORT_NIGHTLY_VERSION=$ORT_NIGHTLY_VERSION" >> $GITHUB_ENV - name: Download OnnxRuntime Nightly run: | @@ -40,4 +40,5 @@ jobs: - name: Build and Run tests run: | + python3 -m pip install requests python3 build.py --ios --parallel --apple_sysroot iphonesimulator --osx_arch arm64 --apple_deploy_target 15.4 --cmake_generator 'Xcode' From 58427f0c4c9ce0134dfd1e22bf19611c2cf41135 Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Wed, 9 Oct 2024 12:04:07 +0800 Subject: [PATCH 09/10] Fxi --- .github/workflows/ios-cpu-arm64-build.yml | 2 ++ benchmark/c/CMakeLists.txt | 7 ------- 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ios-cpu-arm64-build.yml b/.github/workflows/ios-cpu-arm64-build.yml index 2763bea51..06c313d1e 100644 --- a/.github/workflows/ios-cpu-arm64-build.yml +++ b/.github/workflows/ios-cpu-arm64-build.yml @@ -40,5 +40,7 @@ jobs: - name: Build and Run tests run: | + python3 -m venv genai-ios-venv + source genai-ios-venv/bin/activate python3 -m pip install requests python3 build.py --ios --parallel --apple_sysroot iphonesimulator --osx_arch arm64 --apple_deploy_target 15.4 --cmake_generator 'Xcode' diff --git a/benchmark/c/CMakeLists.txt b/benchmark/c/CMakeLists.txt index c5f59813e..6c52a66c4 100644 --- a/benchmark/c/CMakeLists.txt +++ b/benchmark/c/CMakeLists.txt @@ -34,11 +34,4 @@ if(APPLE) target_link_libraries(model_benchmark PRIVATE "-framework Foundation" "-framework CoreML") endif() -if(NOT IOS) - # On iOS static xcframework is used - add_custom_command(TARGET model_benchmark POST_BUILD - COMMAND ${CMAKE_COMMAND} -E copy_if_different ${onnxruntime_libs} $ - ) -endif() - source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${model_benchmark_srcs}) From 536bdc8f9213719097102ac481edb007dceb3cfa Mon Sep 17 00:00:00 2001 From: Chester Liu <4710575+skyline75489@users.noreply.github.com> Date: Tue, 15 Oct 2024 11:36:39 +0800 Subject: [PATCH 10/10] xcode16 --- test/CMakeLists.txt | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 58c562b69..f25411951 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -104,7 +104,25 @@ if (IOS) MACOSX_BUNDLE_SHORT_VERSION_STRING ${VERSION_STR} XCODE_ATTRIBUTE_ENABLE_BITCODE "NO") - xctest_add_test(xctest.unit_tests unit_tests_xc) + # This is a workaround for an Xcode 16 / CMake issue: + # error: Multiple commands produce '/Debug/Debug-iphonesimulator/onnxruntime_test_all.app/PlugIns' + # note: CreateBuildDirectory /Debug/Debug-iphonesimulator/onnxruntime_test_all.app/PlugIns + # note: Target 'onnxruntime_test_all' (project 'onnxruntime') has create directory command with output + # '/Debug/Debug-iphonesimulator/onnxruntime_test_all.app/PlugIns' + # + # It seems related to the test target (e.g., unit_tests_xc) LIBRARY_OUTPUT_DIRECTORY property getting set + # to "$/PlugIns" in xctest_add_bundle(): + # https://github.com/Kitware/CMake/blob/9c4a0a9ff09735b847bbbc38caf6da7f6c7238f2/Modules/FindXCTest.cmake#L159-L168 + # + # This is the related CMake issue: https://gitlab.kitware.com/cmake/cmake/-/issues/26301 + # + # Unsetting LIBRARY_OUTPUT_DIRECTORY avoids the build error. + set_property(TARGET unit_tests_xc PROPERTY LIBRARY_OUTPUT_DIRECTORY) + + # Don't bother calling xctest_add_test() because we don't use CTest to run tests on iOS. + # Instead, we can call 'xcodebuild test-without-building' and specify a '-destination' referring to an iOS + # simulator or device. + # xctest_add_test(xctest.unit_tests unit_tests_xc) endif() include(GoogleTest)