Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DON'T MERGE] Build Apple xcframework #892

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
124 changes: 123 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Copyright (c) Microsoft Corporation. All rights reserved.
# Licensed under the MIT License.

cmake_minimum_required(VERSION 3.26)
cmake_minimum_required(VERSION 3.28)
include(FetchContent)
include(CMakeDependentOption)
project(Generators LANGUAGES C CXX)
Expand All @@ -13,6 +13,12 @@ if("${CMAKE_C_COMPILER_ID}" STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_
message(FATAL_ERROR "GCC version must be greater than or equal to 11")
endif()

# Avoid warning of Calling FetchContent_Populate(Lib) is deprecated temporarily
# TODO: find a better way to handle the header-only 3rd party deps
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0")
cmake_policy(SET CMP0169 OLD)
endif()

if(MSVC)
# DLL initialization errors due to old conda msvcp140.dll dll are a result of the new MSVC compiler
# See https://developercommunity.visualstudio.com/t/Access-violation-with-std::mutex::lock-a/10664660#T-N10668856
Expand Down Expand Up @@ -90,10 +96,19 @@ else()
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})

set(ONNXRUNTIME_GENAI_PUBLIC_HEADERS
${SRC_ROOT}/ort_genai.h
${SRC_ROOT}/ort_genai_c.h
)

if(USE_CUDA AND CMAKE_CUDA_COMPILER)
set_target_properties(onnxruntime-genai PROPERTIES LINKER_LANGUAGE CUDA)
target_link_libraries(onnxruntime-genai PRIVATE cublasLt cublas curand cufft cudart)
Expand Down Expand Up @@ -153,6 +168,36 @@ if(USE_DML)
add_dependencies(onnxruntime-genai-static RESTORE_PACKAGES)
endif()

if (BUILD_APPLE_FRAMEWORK)
# create Info.plist for the framework and podspec for CocoaPods (optional)
set(MACOSX_FRAMEWORK_NAME "onnxruntime-genai")
set(MACOSX_FRAMEWORK_IDENTIFIER "com.microsoft.onnxruntime-genai")
# Need to include CoreML as a weaklink for CocoaPods package if the EP is enabled
if(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-genai PROPERTIES
FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_INFO_PLIST ${INFO_PLIST_PATH}
PUBLIC_HEADER "${ONNXRUNTIME_GENAI_PUBLIC_HEADERS}"
)

if (${CMAKE_SYSTEM_NAME} STREQUAL "iOS")
set_target_properties(onnxruntime-genai PROPERTIES
MACOSX_RPATH TRUE
)
else()
set_target_properties(onnxruntime-genai PROPERTIES INSTALL_RPATH "@loader_path")
endif()
endif()

if(ANDROID)
# strip the binary if it's not a build with debug info
set_target_properties(onnxruntime-genai PROPERTIES LINK_FLAGS_RELEASE -s)
Expand Down Expand Up @@ -188,6 +233,83 @@ foreach(DLL_FILE ${onnxruntime_libs})
)
endforeach()

# Assemble the Apple static framework (iOS and macOS)
if(BUILD_APPLE_FRAMEWORK)
# when building for mac catalyst, the CMAKE_OSX_SYSROOT is set to MacOSX as well, to avoid duplication,
# we specify as `-macabi` in the name of the output static apple framework directory.
if (PLATFORM_NAME STREQUAL "macabi")
set(STATIC_FRAMEWORK_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}-macabi)
else()
set(STATIC_FRAMEWORK_OUTPUT_DIR ${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_BUILD_TYPE}-${CMAKE_OSX_SYSROOT})
endif()

# Setup the various directories required. Remove any existing ones so we start with a clean directory.
set(STATIC_LIB_DIR ${CMAKE_CURRENT_BINARY_DIR}/static_libraries)
set(STATIC_LIB_TEMP_DIR ${STATIC_LIB_DIR}/temp)
add_custom_command(TARGET onnxruntime-genai PRE_BUILD COMMAND ${CMAKE_COMMAND} -E rm -rf ${STATIC_LIB_DIR})
add_custom_command(TARGET onnxruntime-genai PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${STATIC_LIB_DIR})
add_custom_command(TARGET onnxruntime-genai PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${STATIC_LIB_TEMP_DIR})

set(STATIC_FRAMEWORK_DIR ${STATIC_FRAMEWORK_OUTPUT_DIR}/static_framework/onnxruntime-genai.framework)
add_custom_command(TARGET onnxruntime-genai PRE_BUILD COMMAND ${CMAKE_COMMAND} -E rm -rf ${STATIC_FRAMEWORK_DIR})
add_custom_command(TARGET onnxruntime-genai PRE_BUILD COMMAND ${CMAKE_COMMAND} -E make_directory ${STATIC_FRAMEWORK_DIR})

set(INTERNAL_LIBRARIES)
list(APPEND INTERNAL_LIBRARIES onnxruntime-genai-static)

# If it's an onnxruntime library, extract .o files from the original cmake build path to a separate directory for
# each library to avoid any clashes with filenames (e.g. utils.o)
foreach(_LIB ${INTERNAL_LIBRARIES} )
GET_TARGET_PROPERTY(_LIB_TYPE ${_LIB} TYPE)
if(_LIB_TYPE STREQUAL "STATIC_LIBRARY")
set(CUR_STATIC_LIB_OBJ_DIR ${STATIC_LIB_TEMP_DIR}/$<TARGET_LINKER_FILE_BASE_NAME:${_LIB}>)
add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND ${CMAKE_COMMAND} -E make_directory ${CUR_STATIC_LIB_OBJ_DIR})

add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND ar ARGS -x $<TARGET_FILE:${_LIB}>
WORKING_DIRECTORY ${CUR_STATIC_LIB_OBJ_DIR})
endif()
endforeach()

# for external libraries we create a symlink to the .a file
foreach(_LIB ${EXTERNAL_LIBRARIES})
GET_TARGET_PROPERTY(_LIB_TYPE ${_LIB} TYPE)
if(_LIB_TYPE STREQUAL "STATIC_LIBRARY")
add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND ${CMAKE_COMMAND} -E create_symlink
$<TARGET_FILE:${_LIB}> ${STATIC_LIB_DIR}/$<TARGET_LINKER_FILE_NAME:${_LIB}>)
endif()
endforeach()

# do the pre-link with `ld -r` to create a single relocatable object with correct symbol visibility
add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND ld ARGS -r -o ${STATIC_LIB_DIR}/prelinked_objects.o */*.o ../*.a
WORKING_DIRECTORY ${STATIC_LIB_TEMP_DIR})

# create the static library
add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND libtool -static -o ${STATIC_FRAMEWORK_DIR}/onnxruntime-genai prelinked_objects.o
WORKING_DIRECTORY ${STATIC_LIB_DIR})

# Assemble the other pieces of the static framework
add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND ${CMAKE_COMMAND} -E
copy_if_different ${INFO_PLIST_PATH} ${STATIC_FRAMEWORK_DIR}/Info.plist)

# add the framework header files
set(STATIC_FRAMEWORK_HEADER_DIR ${STATIC_FRAMEWORK_DIR}/Headers)
file(MAKE_DIRECTORY ${STATIC_FRAMEWORK_HEADER_DIR})

foreach(h_ ${ONNXRUNTIME_GENAI_PUBLIC_HEADERS})
get_filename_component(HEADER_NAME_ ${h_} NAME)
add_custom_command(TARGET onnxruntime-genai POST_BUILD
COMMAND ${CMAKE_COMMAND} -E
copy_if_different ${h_} ${STATIC_FRAMEWORK_HEADER_DIR}/${HEADER_NAME_})
endforeach()

endif()

# Have visual studio put all files into one single folder vs the default split of header files into a separate folder
source_group(TREE ${GENERATORS_ROOT} FILES ${generator_srcs})

Expand Down
76 changes: 53 additions & 23 deletions build.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,12 @@
# The following options are mutually exclusive (cross compiling options such as android, ios, etc.)
platform_group = parser.add_mutually_exclusive_group()
platform_group.add_argument("--android", action="store_true", help="Build for Android")
platform_group.add_argument("--ios", action="store_true", help="Build for ios")
platform_group.add_argument("--ios", action="store_true", help="Build for iOS")
platform_group.add_argument(
"--macos",
choices=["MacOSX", "Catalyst"],
help="Specify the target platform for macOS build. Only specify this argument when --build_apple_framework is present.",
)

# Android options
parser.add_argument(
Expand All @@ -157,23 +162,27 @@

# iOS build options
parser.add_argument(
"--ios_sysroot",
"--apple_sysroot",
default="",
help="Specify the location name of the macOS platform SDK to be used",
)
parser.add_argument(
"--ios_arch",
"--osx_arch",
type=str,
help="Specify the Target specific architectures for iOS "
"This is only supported on MacOS host",
)
parser.add_argument(
"--ios_deployment_target",
"--apple_deploy_target",
type=str,
help="Specify the minimum version of the target platform "
"This is only supported on MacOS host",
)

parser.add_argument(
"--build_apple_framework", action="store_true", help="Build a macOS/iOS framework for the ONNXRuntime."
)

parser.add_argument(
"--arm64",
action="store_true",
Expand Down Expand Up @@ -285,14 +294,14 @@
raise ValueError("A Mac host is required to build for iOS")

needed_args = [
args.ios_sysroot,
args.ios_arch,
args.ios_deployment_target,
args.apple_sysroot,
args.osx_arch,
args.apple_deploy_target,
]
arg_names = [
"--ios_sysroot <the location or name of the macOS platform SDK>",
"--ios_arch <the Target specific architectures for iOS>",
"--ios_deployment_target <the minimum version of the target platform>",
"--apple_sysroot <the location or name of the macOS platform SDK>",
"--osx_arch <the Target specific architectures for iOS>",
"--apple_deploy_target <the minimum version of the target platform>",
]
have_required_args = all(_ is not None for _ in needed_args)
if not have_required_args:
Expand Down Expand Up @@ -487,9 +496,26 @@
"-DENABLE_TESTS=OFF",
]

if args.ios or args.macos:
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,
]

if args.macos:
command += [
f"-DCMAKE_OSX_SYSROOT={args.apple_sysroot}",
f"-DCMAKE_OSX_ARCHITECTURES={args.osx_arch}",
f"-DCMAKE_OSX_DEPLOYMENT_TARGET={args.apple_deploy_target}",
]

if args.ios:
def _get_opencv_toolchain_file():
if args.ios_sysroot == "iphoneos":
if args.apple_sysroot == "iphoneos":
return (
REPO_ROOT / "cmake" / "external" / "opencv" / "platforms" / "iOS" / "cmake" /
"Toolchains" / "Toolchain-iPhoneOS_Xcode.cmake"
Expand All @@ -500,30 +526,34 @@
"Toolchains" / "Toolchain-iPhoneSimulator_Xcode.cmake"
)


command += [
"-DCMAKE_SYSTEM_NAME=iOS",
f"-DCMAKE_OSX_SYSROOT={args.ios_sysroot}",
f"-DCMAKE_OSX_ARCHITECTURES={args.ios_arch}",
f"-DCMAKE_OSX_DEPLOYMENT_TARGET={args.ios_deployment_target}",
"-DENABLE_PYTHON=OFF",
f"-DIOS_ARCH={args.osx_arch}",
f"-DIPHONEOS_DEPLOYMENT_TARGET={args.apple_deploy_target}",
# The following arguments are specific to the OpenCV toolchain file
f"-DIOS_ARCH={args.ios_arch}",
f"-DIPHONEOS_DEPLOYMENT_TARGET={args.ios_deployment_target}",
f"-DCMAKE_TOOLCHAIN_FILE={_get_opencv_toolchain_file()}",
]

if args.macos == "Catalyst":
macabi_target = f"{args.osx_arch}-apple-ios{args.apple_deploy_target}-macabi"
cmake_args += [
Fixed Show fixed Hide fixed
"-DCMAKE_CXX_COMPILER_TARGET=" + macabi_target,
"-DCMAKE_C_COMPILER_TARGET=" + macabi_target,
"-DCMAKE_CC_COMPILER_TARGET=" + macabi_target,
f"-DCMAKE_CXX_FLAGS=--target={macabi_target}",
f"-DCMAKE_CXX_FLAGS_RELEASE=-O3 -DNDEBUG --target={macabi_target}",
f"-DCMAKE_C_FLAGS=--target={macabi_target}",
f"-DCMAKE_C_FLAGS_RELEASE=-O3 -DNDEBUG --target={macabi_target}",
f"-DCMAKE_CC_FLAGS=--target={macabi_target}",
f"-DCMAKE_CC_FLAGS_RELEASE=-O3 -DNDEBUG --target={macabi_target}",
]

if args.arm64:
command += ["-A", "ARM64"]
elif args.arm64ec:
command += ["-A", "ARM64EC"]

if args.arm64 or args.arm64ec:
# Build zlib from source. Otherwise zlib from Python might be used.
# And architecture mismatch will happen.
command += ["-D", "BUILD_ZLIB=ON"]
command += ["-DOPENCV_SKIP_SYSTEM_PROCESSOR_DETECTION=ON"]

if args.test:
log.warning(
"Cannot test on host build machine for cross-compiled "
Expand Down
20 changes: 20 additions & 0 deletions cmake/Info.plist.in
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>${MACOSX_FRAMEWORK_NAME}</string>
<key>CFBundleName</key>
<string>${MACOSX_FRAMEWORK_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_FRAMEWORK_IDENTIFIER}</string>
<key>CFBundleVersion</key>
<string>${ORT_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${ORT_VERSION}</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundlePackageType</key>
<string>FMWK</string>
</dict>
</plist>
6 changes: 6 additions & 0 deletions cmake/external/onnxruntime_external_deps.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -87,3 +87,9 @@ FetchContent_Declare(
)
set(OCOS_BUILD_PRESET ort_genai)
onnxruntime_fetchcontent_makeavailable(onnxruntime_extensions)

list(APPEND EXTERNAL_LIBRARIES
onnxruntime_extensions
ocos_operators
noexcep_operators
)
25 changes: 19 additions & 6 deletions cmake/global_variables.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +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")
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")
if(BUILD_APPLE_FRAMEWORK)
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()
else()
set(ONNXRUNTIME_LIB "libonnxruntime.so")
set(ONNXRUNTIME_PROVIDERS_CUDA_LIB "libonnxruntime_providers_cuda.so")
Expand All @@ -74,9 +80,16 @@ 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.")
if (BUILD_APPLE_FRAMEWORK)
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()

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.")
endif()
Expand Down
2 changes: 2 additions & 0 deletions cmake/package.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ install(TARGETS
RUNTIME DESTINATION lib
ARCHIVE DESTINATION lib
PUBLIC_HEADER DESTINATION include
FRAMEWORK DESTINATION ${CMAKE_INSTALL_BINDIR}
)

if (WIN32)
install(FILES $<TARGET_PDB_FILE:onnxruntime-genai> DESTINATION lib CONFIGURATIONS RelWithDebInfo Debug)
endif()
Expand Down
6 changes: 0 additions & 6 deletions src/python/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,6 @@ if(USE_CUDA AND CMAKE_CUDA_COMPILER)
target_link_libraries(python PRIVATE cublas curand cudart)
endif()

# Avoid warning of Calling FetchContent_Populate(Lib) is deprecated temporarily
# TODO: find a better way to handle the header-only 3rd party deps
if(CMAKE_VERSION VERSION_GREATER_EQUAL "3.30.0")
cmake_policy(SET CMP0169 OLD)
endif()

set_target_properties(python PROPERTIES FOLDER "Sources")
source_group(TREE ${PROJECT_SOURCE_DIR} FILES ${python_srcs})

Expand Down
Loading
Loading