15
15
REPO_DIR = os .path .normpath (os .path .join (SCRIPT_DIR , ".." , ".." , ".." , ".." ))
16
16
BUILD_PY = os .path .join (REPO_DIR , "tools" , "ci_build" , "build.py" )
17
17
18
- # We by default will build below 2 archs
19
- DEFAULT_BUILD_OSX_ARCHS = [
20
- { 'sysroot' : ' iphoneos', 'arch' : 'arm64' } ,
21
- { 'sysroot ' : 'iphonesimulator ' , 'arch' : ' x86_64'} ,
22
- ]
18
+ # We by default will build below 3 archs
19
+ DEFAULT_BUILD_OSX_ARCHS = {
20
+ ' iphoneos': [ 'arm64' ] ,
21
+ 'iphonesimulator ' : [ 'arm64 ' , 'x86_64' ] ,
22
+ }
23
23
24
24
25
25
def _parse_build_settings (args ):
@@ -40,40 +40,30 @@ def _parse_build_settings(args):
40
40
return build_settings
41
41
42
42
43
- def _build_package (args ):
44
- build_settings = _parse_build_settings (args )
45
- build_dir = os .path .abspath (args .build_dir )
46
-
47
- # Temp dirs to hold building results
48
- intermediates_dir = os .path .join (build_dir , 'intermediates' )
49
- build_config = args .config
50
- base_build_command = [sys .executable , BUILD_PY , '--config=' + build_config ] + build_settings ['build_params' ]
51
-
43
+ # Build fat framework for all archs of a single sysroot
44
+ # For example, arm64 and x86_64 for iphonesimulator
45
+ def _build_for_ios_sysroot (build_config , intermediates_dir , base_build_command ,
46
+ sysroot , archs , build_dynamic_framework ):
52
47
# paths of the onnxruntime libraries for different archs
53
48
ort_libs = []
54
49
info_plist_path = ''
55
50
56
51
# Build binary for each arch, one by one
57
- for osx_arch in build_settings ['build_osx_archs' ]:
58
- sysroot = osx_arch ['sysroot' ]
59
- current_arch = osx_arch ['arch' ]
52
+ for current_arch in archs :
60
53
build_dir_current_arch = os .path .join (intermediates_dir , sysroot + "_" + current_arch )
61
54
build_command = base_build_command + [
62
55
'--ios_sysroot=' + sysroot ,
63
56
'--osx_arch=' + current_arch ,
64
57
'--build_dir=' + build_dir_current_arch
65
58
]
66
59
67
- if args .include_ops_by_config is not None :
68
- build_command += ['--include_ops_by_config=' + str (args .include_ops_by_config .resolve ())]
69
-
70
60
# the actual build process for current arch
71
61
subprocess .run (build_command , shell = False , check = True , cwd = REPO_DIR )
72
62
73
63
# get the compiled lib path
74
64
framework_dir = os .path .join (
75
65
build_dir_current_arch , build_config , build_config + "-" + sysroot ,
76
- 'onnxruntime.framework' if args . build_dynamic_framework
66
+ 'onnxruntime.framework' if build_dynamic_framework
77
67
else os .path .join ('static_framework' , 'onnxruntime.framework' ))
78
68
ort_libs .append (os .path .join (framework_dir , 'onnxruntime' ))
79
69
@@ -84,12 +74,15 @@ def _build_package(args):
84
74
headers = glob .glob (os .path .join (framework_dir , 'Headers' , '*.h' ))
85
75
86
76
# manually create the fat framework
87
- framework_dir = os .path .join (build_dir , 'framework_out' , 'onnxruntime.framework' )
77
+ framework_dir = os .path .join (intermediates_dir , 'frameworks' , sysroot , 'onnxruntime.framework' )
78
+ # remove the existing framework if any
79
+ if os .path .exists (framework_dir ):
80
+ shutil .rmtree (framework_dir )
88
81
pathlib .Path (framework_dir ).mkdir (parents = True , exist_ok = True )
89
82
90
83
# copy the Info.plist, framework_info.json, and header files
91
84
shutil .copy (info_plist_path , framework_dir )
92
- shutil .copy (framework_info_path , build_dir )
85
+ shutil .copy (framework_info_path , os . path . dirname ( framework_dir ) )
93
86
header_dir = os .path .join (framework_dir , 'Headers' )
94
87
pathlib .Path (header_dir ).mkdir (parents = True , exist_ok = True )
95
88
for _header in headers :
@@ -101,14 +94,62 @@ def _build_package(args):
101
94
lipo_command += ['-output' , os .path .join (framework_dir , 'onnxruntime' )]
102
95
subprocess .run (lipo_command , shell = False , check = True )
103
96
97
+ return framework_dir
98
+
99
+
100
+ def _build_package (args ):
101
+ build_settings = _parse_build_settings (args )
102
+ build_dir = os .path .abspath (args .build_dir )
103
+
104
+ # Temp dirs to hold building results
105
+ intermediates_dir = os .path .join (build_dir , 'intermediates' )
106
+ build_config = args .config
107
+ base_build_command = [sys .executable , BUILD_PY , '--config=' + build_config ] + build_settings ['build_params' ]
108
+ if args .include_ops_by_config is not None :
109
+ base_build_command += ['--include_ops_by_config=' + str (args .include_ops_by_config .resolve ())]
110
+
111
+ # build framework for individual sysroot
112
+ framework_dirs = []
113
+ framework_info_path = ''
114
+ public_headers_path = ''
115
+ for sysroot in build_settings ['build_osx_archs' ]:
116
+ framework_dir = _build_for_ios_sysroot (
117
+ build_config , intermediates_dir , base_build_command , sysroot ,
118
+ build_settings ['build_osx_archs' ][sysroot ], args .build_dynamic_framework )
119
+ framework_dirs .append (framework_dir )
120
+ # podspec and headers for each sysroot are the same, pick one of them
121
+ if not framework_info_path :
122
+ framework_info_path = os .path .join (os .path .dirname (framework_dir ), 'framework_info.json' )
123
+ public_headers_path = os .path .join (os .path .dirname (framework_dir ), 'onnxruntime.framework' , 'Headers' )
124
+
125
+ # create the folder for xcframework and copy the LICENSE and podspec file
126
+ xcframework_dir = os .path .join (build_dir , 'framework_out' )
127
+ pathlib .Path (xcframework_dir ).mkdir (parents = True , exist_ok = True )
128
+ shutil .copy (os .path .join (REPO_DIR , 'LICENSE' ), xcframework_dir )
129
+ shutil .copytree (public_headers_path , os .path .join (xcframework_dir , 'Headers' ), dirs_exist_ok = True )
130
+ shutil .copy (framework_info_path , build_dir )
131
+
132
+ # remove existing xcframework if any
133
+ xcframework_path = os .path .join (xcframework_dir , 'onnxruntime.xcframework' )
134
+ if os .path .exists (xcframework_path ):
135
+ shutil .rmtree (xcframework_path )
136
+
137
+ # Assemble the final xcframework
138
+ build_xcframework_cmd = ['xcrun' , 'xcodebuild' , '-create-xcframework' ,
139
+ '-output' , xcframework_path ]
140
+ for framework_dir in framework_dirs :
141
+ build_xcframework_cmd .extend (['-framework' , framework_dir ])
142
+
143
+ subprocess .run (build_xcframework_cmd , shell = False , check = True , cwd = REPO_DIR )
144
+
104
145
105
146
def parse_args ():
106
147
parser = argparse .ArgumentParser (
107
148
os .path .basename (__file__ ),
108
- description = '''Create iOS framework and podspec for one or more osx_archs (fat framework )
149
+ description = '''Create iOS framework and podspec for one or more osx_archs (xcframework )
109
150
and building properties specified in the given build config file, see
110
151
tools/ci_build/github/apple/default_mobile_ios_framework_build_settings.json for details.
111
- The output of the final framework and podspec can be found under [build_dir]/framework_out.
152
+ The output of the final xcframework and podspec can be found under [build_dir]/framework_out.
112
153
Please note, this building script will only work on macOS.
113
154
'''
114
155
)
@@ -124,12 +165,12 @@ def parse_args():
124
165
choices = ["Debug" , "MinSizeRel" , "Release" , "RelWithDebInfo" ],
125
166
help = "Configuration to build." )
126
167
127
- parser .add_argument ('build_settings_file' , type = pathlib .Path ,
128
- help = 'Provide the file contains settings for building iOS framework' )
129
-
130
168
parser .add_argument ("--build_dynamic_framework" , action = 'store_true' ,
131
169
help = "Build Dynamic Framework (default is build static framework)." )
132
170
171
+ parser .add_argument ('build_settings_file' , type = pathlib .Path ,
172
+ help = 'Provide the file contains settings for building iOS framework' )
173
+
133
174
args = parser .parse_args ()
134
175
135
176
if not args .build_settings_file .resolve ().is_file ():
0 commit comments