Flutter Engine
The Flutter Engine
create_macos_framework.py
Go to the documentation of this file.
1#!/usr/bin/env python3
2#
3# Copyright 2013 The Flutter Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import argparse
8import platform
9import subprocess
10import shutil
11import sys
12import os
13
14from create_xcframework import create_xcframework # pylint: disable=import-error
15
16buildroot_dir = os.path.abspath(os.path.join(os.path.realpath(__file__), '..', '..', '..', '..'))
17
18ARCH_SUBPATH = 'mac-arm64' if platform.processor() == 'arm' else 'mac-x64'
19DSYMUTIL = os.path.join(
20 os.path.dirname(__file__), '..', '..', 'buildtools', ARCH_SUBPATH, 'clang', 'bin', 'dsymutil'
21)
22
23out_dir = os.path.join(buildroot_dir, 'out')
24
25
26def main():
27 parser = argparse.ArgumentParser(
28 description='Creates FlutterMacOS.framework and FlutterMacOS.xcframework for macOS'
29 )
30
31 parser.add_argument('--dst', type=str, required=True)
32 parser.add_argument('--arm64-out-dir', type=str, required=True)
33 parser.add_argument('--x64-out-dir', type=str, required=True)
34 parser.add_argument('--strip', action='store_true', default=False)
35 parser.add_argument('--dsym', action='store_true', default=False)
36 # TODO(godofredoc): Remove after recipes v2 have landed.
37 parser.add_argument('--zip', action='store_true', default=False)
38
39 args = parser.parse_args()
40
41 dst = (args.dst if os.path.isabs(args.dst) else os.path.join(buildroot_dir, args.dst))
42 arm64_out_dir = (
43 args.arm64_out_dir
44 if os.path.isabs(args.arm64_out_dir) else os.path.join(buildroot_dir, args.arm64_out_dir)
45 )
46 x64_out_dir = (
47 args.x64_out_dir
48 if os.path.isabs(args.x64_out_dir) else os.path.join(buildroot_dir, args.x64_out_dir)
49 )
50
51 fat_framework_bundle = os.path.join(dst, 'FlutterMacOS.framework')
52 arm64_framework = os.path.join(arm64_out_dir, 'FlutterMacOS.framework')
53 x64_framework = os.path.join(x64_out_dir, 'FlutterMacOS.framework')
54
55 arm64_dylib = os.path.join(arm64_framework, 'FlutterMacOS')
56 x64_dylib = os.path.join(x64_framework, 'FlutterMacOS')
57
58 if not os.path.isdir(arm64_framework):
59 print('Cannot find macOS arm64 Framework at %s' % arm64_framework)
60 return 1
61
62 if not os.path.isdir(x64_framework):
63 print('Cannot find macOS x64 Framework at %s' % x64_framework)
64 return 1
65
66 if not os.path.isfile(arm64_dylib):
67 print('Cannot find macOS arm64 dylib at %s' % arm64_dylib)
68 return 1
69
70 if not os.path.isfile(x64_dylib):
71 print('Cannot find macOS x64 dylib at %s' % x64_dylib)
72 return 1
73
74 if not os.path.isfile(DSYMUTIL):
75 print('Cannot find dsymutil at %s' % DSYMUTIL)
76 return 1
77
78 shutil.rmtree(fat_framework_bundle, True)
79 shutil.copytree(arm64_framework, fat_framework_bundle, symlinks=True)
80
81 regenerate_symlinks(fat_framework_bundle)
82
83 fat_framework_binary = os.path.join(fat_framework_bundle, 'Versions', 'A', 'FlutterMacOS')
84
85 # Create the arm64/x64 fat framework.
86 subprocess.check_call([
87 'lipo', arm64_dylib, x64_dylib, '-create', '-output', fat_framework_binary
88 ])
89 # Make the framework readable and executable: u=rwx,go=rx.
90 subprocess.check_call(['chmod', '755', fat_framework_bundle])
91
92 # Add group and other readability to all files.
93 versions_path = os.path.join(fat_framework_bundle, 'Versions')
94 subprocess.check_call(['chmod', '-R', 'og+r', versions_path])
95 # Find all the files below the target dir with owner execute permission
96 find_subprocess = subprocess.Popen(['find', versions_path, '-perm', '-100', '-print0'],
97 stdout=subprocess.PIPE)
98 # Add execute permission for other and group for all files that had it for owner.
99 xargs_subprocess = subprocess.Popen(['xargs', '-0', 'chmod', 'og+x'],
100 stdin=find_subprocess.stdout)
101 find_subprocess.wait()
102 xargs_subprocess.wait()
103
104 process_framework(dst, args, fat_framework_bundle, fat_framework_binary)
105
106 # Create XCFramework from the arm64 and x64 fat framework.
107 xcframeworks = [fat_framework_bundle]
108 create_xcframework(location=dst, name='FlutterMacOS', frameworks=xcframeworks)
109
110 zip_framework(dst, args)
111
112 return 0
113
114
115def regenerate_symlinks(fat_framework_bundle):
116 """Regenerates the symlinks structure.
117
118 Recipes V2 upload artifacts in CAS before integration and CAS follows symlinks.
119 This logic regenerates the symlinks in the expected structure.
120 """
121 if os.path.islink(os.path.join(fat_framework_bundle, 'FlutterMacOS')):
122 return
123 os.remove(os.path.join(fat_framework_bundle, 'FlutterMacOS'))
124 shutil.rmtree(os.path.join(fat_framework_bundle, 'Headers'), True)
125 shutil.rmtree(os.path.join(fat_framework_bundle, 'Modules'), True)
126 shutil.rmtree(os.path.join(fat_framework_bundle, 'Resources'), True)
127 current_version_path = os.path.join(fat_framework_bundle, 'Versions', 'Current')
128 shutil.rmtree(current_version_path, True)
129 os.symlink('A', current_version_path)
130 os.symlink(
131 os.path.join('Versions', 'Current', 'FlutterMacOS'),
132 os.path.join(fat_framework_bundle, 'FlutterMacOS')
133 )
134 os.symlink(
135 os.path.join('Versions', 'Current', 'Headers'), os.path.join(fat_framework_bundle, 'Headers')
136 )
137 os.symlink(
138 os.path.join('Versions', 'Current', 'Modules'), os.path.join(fat_framework_bundle, 'Modules')
139 )
140 os.symlink(
141 os.path.join('Versions', 'Current', 'Resources'),
142 os.path.join(fat_framework_bundle, 'Resources')
143 )
144
145
146def embed_codesign_configuration(config_path, content):
147 with open(config_path, 'w') as file:
148 file.write(content)
149
150
151def process_framework(dst, args, fat_framework_bundle, fat_framework_binary):
152 if args.dsym:
153 dsym_out = os.path.splitext(fat_framework_bundle)[0] + '.dSYM'
154 subprocess.check_call([DSYMUTIL, '-o', dsym_out, fat_framework_binary])
155 if args.zip:
156 dsym_dst = os.path.join(dst, 'FlutterMacOS.dSYM')
157 subprocess.check_call(['zip', '-r', '-y', 'FlutterMacOS.dSYM.zip', '.'], cwd=dsym_dst)
158 # Double zip to make it consistent with legacy artifacts.
159 # TODO(fujino): remove this once https://github.com/flutter/flutter/issues/125067 is resolved
160 subprocess.check_call([
161 'zip',
162 '-y',
163 'FlutterMacOS.dSYM_.zip',
164 'FlutterMacOS.dSYM.zip',
165 ],
166 cwd=dsym_dst)
167 # Use doubled zipped file.
168 dsym_final_src_path = os.path.join(dsym_dst, 'FlutterMacOS.dSYM_.zip')
169 dsym_final_dst_path = os.path.join(dst, 'FlutterMacOS.dSYM.zip')
170 shutil.move(dsym_final_src_path, dsym_final_dst_path)
171
172 if args.strip:
173 # copy unstripped
174 unstripped_out = os.path.join(dst, 'FlutterMacOS.unstripped')
175 shutil.copyfile(fat_framework_binary, unstripped_out)
176
177 subprocess.check_call(['strip', '-x', '-S', fat_framework_binary])
178
179
180def zip_framework(dst, args):
181 # Zip FlutterMacOS.framework.
182 if args.zip:
183 filepath_with_entitlements = ''
184
185 framework_dst = os.path.join(dst, 'FlutterMacOS.framework')
186 # TODO(xilaizhang): Remove the zip file from the path when outer zip is removed.
187 filepath_without_entitlements = 'FlutterMacOS.framework.zip/Versions/A/FlutterMacOS'
188
190 os.path.join(framework_dst, 'entitlements.txt'), filepath_with_entitlements
191 )
192
194 os.path.join(framework_dst, 'without_entitlements.txt'), filepath_without_entitlements
195 )
196 subprocess.check_call([
197 'zip',
198 '-r',
199 '-y',
200 'FlutterMacOS.framework.zip',
201 '.',
202 ],
203 cwd=framework_dst)
204 # Double zip to make it consistent with legacy artifacts.
205 # TODO(fujino): remove this once https://github.com/flutter/flutter/issues/125067 is resolved
206 subprocess.check_call(
207 [
208 'zip',
209 '-y',
210 'FlutterMacOS.framework_.zip',
211 'FlutterMacOS.framework.zip',
212 # TODO(xilaizhang): Move these files to inner zip before removing the outer zip.
213 'entitlements.txt',
214 'without_entitlements.txt',
215 ],
216 cwd=framework_dst
217 )
218 # Use doubled zipped file.
219 final_src_path = os.path.join(framework_dst, 'FlutterMacOS.framework_.zip')
220 final_dst_path = os.path.join(dst, 'FlutterMacOS.framework.zip')
221 shutil.move(final_src_path, final_dst_path)
222
224
225
227 filepath_with_entitlements = ''
228 filepath_without_entitlements = (
229 'FlutterMacOS.xcframework/macos-arm64_x86_64/'
230 'FlutterMacOS.framework/Versions/A/FlutterMacOS'
231 )
232 embed_codesign_configuration(os.path.join(dst, 'entitlements.txt'), filepath_with_entitlements)
233
235 os.path.join(dst, 'without_entitlements.txt'), filepath_without_entitlements
236 )
237
238 subprocess.check_call([
239 'zip',
240 '-r',
241 '-y',
242 'framework.zip',
243 'FlutterMacOS.xcframework',
244 'entitlements.txt',
245 'without_entitlements.txt',
246 ],
247 cwd=dst)
248
249
250if __name__ == '__main__':
251 sys.exit(main())
def embed_codesign_configuration(config_path, content)
def process_framework(dst, args, fat_framework_bundle, fat_framework_binary)
def regenerate_symlinks(fat_framework_bundle)
Definition: main.py:1
def print(*args, **kwargs)
Definition: run_tests.py:49