@@ -108,7 +108,23 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({
108
108
final Uri ? absolutePath = flutterTester ? buildUri : null ;
109
109
final Map <Asset , Asset > assetTargetLocations = _assetTargetLocations (nativeAssets, absolutePath);
110
110
final Map <AssetPath , List <Asset >> fatAssetTargetLocations = _fatAssetTargetLocations (nativeAssets, absolutePath);
111
- await copyNativeAssetsMacOSHost (buildUri, fatAssetTargetLocations, codesignIdentity, buildMode, fileSystem);
111
+ if (flutterTester) {
112
+ await _copyNativeAssetsMacOSFlutterTester (
113
+ buildUri,
114
+ fatAssetTargetLocations,
115
+ codesignIdentity,
116
+ buildMode,
117
+ fileSystem,
118
+ );
119
+ } else {
120
+ await _copyNativeAssetsMacOS (
121
+ buildUri,
122
+ fatAssetTargetLocations,
123
+ codesignIdentity,
124
+ buildMode,
125
+ fileSystem,
126
+ );
127
+ }
112
128
final Uri nativeAssetsUri = await writeNativeAssetsYaml (assetTargetLocations.values, yamlParentDirectory ?? buildUri, fileSystem);
113
129
return (nativeAssetsUri, dependencies.toList ());
114
130
}
@@ -125,22 +141,40 @@ Target _getNativeTarget(DarwinArch darwinArch) {
125
141
}
126
142
}
127
143
128
- Map <AssetPath , List <Asset >> _fatAssetTargetLocations (List <Asset > nativeAssets, Uri ? absolutePath) {
144
+ Map <AssetPath , List <Asset >> _fatAssetTargetLocations (
145
+ List <Asset > nativeAssets,
146
+ Uri ? absolutePath,
147
+ ) {
148
+ final Set <String > alreadyTakenNames = < String > {};
129
149
final Map <AssetPath , List <Asset >> result = < AssetPath , List <Asset >> {};
130
150
for (final Asset asset in nativeAssets) {
131
- final AssetPath path = _targetLocationMacOS (asset, absolutePath).path;
151
+ final AssetPath path = _targetLocationMacOS (
152
+ asset,
153
+ absolutePath,
154
+ alreadyTakenNames,
155
+ ).path;
132
156
result[path] ?? = < Asset > [];
133
157
result[path]! .add (asset);
134
158
}
135
159
return result;
136
160
}
137
161
138
- Map <Asset , Asset > _assetTargetLocations (List <Asset > nativeAssets, Uri ? absolutePath) => < Asset , Asset > {
139
- for (final Asset asset in nativeAssets)
140
- asset: _targetLocationMacOS (asset, absolutePath),
141
- };
162
+ Map <Asset , Asset > _assetTargetLocations (
163
+ List <Asset > nativeAssets,
164
+ Uri ? absolutePath,
165
+ ) {
166
+ final Set <String > alreadyTakenNames = < String > {};
167
+ return < Asset , Asset > {
168
+ for (final Asset asset in nativeAssets)
169
+ asset: _targetLocationMacOS (asset, absolutePath, alreadyTakenNames),
170
+ };
171
+ }
142
172
143
- Asset _targetLocationMacOS (Asset asset, Uri ? absolutePath) {
173
+ Asset _targetLocationMacOS (
174
+ Asset asset,
175
+ Uri ? absolutePath,
176
+ Set <String > alreadyTakenNames,
177
+ ) {
144
178
final AssetPath path = asset.path;
145
179
switch (path) {
146
180
case AssetSystemPath _:
@@ -157,9 +191,119 @@ Asset _targetLocationMacOS(Asset asset, Uri? absolutePath) {
157
191
// Flutter Desktop needs "absolute" paths inside the app.
158
192
// "relative" in the context of native assets would be relative to the
159
193
// kernel or aot snapshot.
160
- uri = Uri (path: fileName);
194
+ uri = frameworkUri (fileName, alreadyTakenNames);
195
+
161
196
}
162
197
return asset.copyWith (path: AssetAbsolutePath (uri));
163
198
}
164
199
throw Exception ('Unsupported asset path type ${path .runtimeType } in asset $asset ' );
165
200
}
201
+
202
+ /// Copies native assets into a framework per dynamic library.
203
+ ///
204
+ /// The framework contains symlinks according to
205
+ /// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
206
+ ///
207
+ /// For `flutter run -release` a multi-architecture solution is needed. So,
208
+ /// `lipo` is used to combine all target architectures into a single file.
209
+ ///
210
+ /// The install name is set so that it matches what the place it will
211
+ /// be bundled in the final app.
212
+ ///
213
+ /// Code signing is also done here, so that it doesn't have to be done in
214
+ /// in macos_assemble.sh.
215
+ Future <void > _copyNativeAssetsMacOS (
216
+ Uri buildUri,
217
+ Map <AssetPath , List <Asset >> assetTargetLocations,
218
+ String ? codesignIdentity,
219
+ BuildMode buildMode,
220
+ FileSystem fileSystem,
221
+ ) async {
222
+ if (assetTargetLocations.isNotEmpty) {
223
+ globals.logger.printTrace ('Copying native assets to ${buildUri .toFilePath ()}.' );
224
+ for (final MapEntry <AssetPath , List <Asset >> assetMapping in assetTargetLocations.entries) {
225
+ final Uri target = (assetMapping.key as AssetAbsolutePath ).uri;
226
+ final List <Uri > sources = < Uri > [
227
+ for (final Asset source in assetMapping.value)
228
+ (source.path as AssetAbsolutePath ).uri,
229
+ ];
230
+ final Uri targetUri = buildUri.resolveUri (target);
231
+ final String name = targetUri.pathSegments.last;
232
+ final Directory frameworkDir = fileSystem.file (targetUri).parent;
233
+ if (await frameworkDir.exists ()) {
234
+ await frameworkDir.delete (recursive: true );
235
+ }
236
+ // MyFramework.framework/ frameworkDir
237
+ // MyFramework -> Versions/Current/MyFramework dylibLink
238
+ // Resources -> Versions/Current/Resources resourcesLink
239
+ // Versions/ versionsDir
240
+ // A/ versionADir
241
+ // MyFramework dylibFile
242
+ // Resources/ resourcesDir
243
+ // Info.plist
244
+ // Current -> A currentLink
245
+ final Directory versionsDir = frameworkDir.childDirectory ('Versions' );
246
+ final Directory versionADir = versionsDir.childDirectory ('A' );
247
+ final Directory resourcesDir = versionADir.childDirectory ('Resources' );
248
+ await resourcesDir.create (recursive: true );
249
+ final File dylibFile = versionADir.childFile (name);
250
+ final Link currentLink = versionsDir.childLink ('Current' );
251
+ await currentLink.create (fileSystem.path.relative (
252
+ versionADir.path,
253
+ from: currentLink.parent.path,
254
+ ));
255
+ final Link resourcesLink = frameworkDir.childLink ('Resources' );
256
+ await resourcesLink.create (fileSystem.path.relative (
257
+ resourcesDir.path,
258
+ from: resourcesLink.parent.path,
259
+ ));
260
+ await lipoDylibs (dylibFile, sources);
261
+ final Link dylibLink = frameworkDir.childLink (name);
262
+ await dylibLink.create (fileSystem.path.relative (
263
+ versionsDir.childDirectory ('Current' ).childFile (name).path,
264
+ from: dylibLink.parent.path,
265
+ ));
266
+ await setInstallNameDylib (dylibFile);
267
+ await createInfoPlist (name, resourcesDir);
268
+ await codesignDylib (codesignIdentity, buildMode, frameworkDir);
269
+ }
270
+ globals.logger.printTrace ('Copying native assets done.' );
271
+ }
272
+ }
273
+
274
+
275
+ /// Copies native assets for flutter tester.
276
+ ///
277
+ /// For `flutter run -release` a multi-architecture solution is needed. So,
278
+ /// `lipo` is used to combine all target architectures into a single file.
279
+ ///
280
+ /// In contrast to [_copyNativeAssetsMacOS] , it does not set the install name.
281
+ ///
282
+ /// Code signing is also done here.
283
+ Future <void > _copyNativeAssetsMacOSFlutterTester (
284
+ Uri buildUri,
285
+ Map <AssetPath , List <Asset >> assetTargetLocations,
286
+ String ? codesignIdentity,
287
+ BuildMode buildMode,
288
+ FileSystem fileSystem,
289
+ ) async {
290
+ if (assetTargetLocations.isNotEmpty) {
291
+ globals.logger.printTrace ('Copying native assets to ${buildUri .toFilePath ()}.' );
292
+ for (final MapEntry <AssetPath , List <Asset >> assetMapping in assetTargetLocations.entries) {
293
+ final Uri target = (assetMapping.key as AssetAbsolutePath ).uri;
294
+ final List <Uri > sources = < Uri > [
295
+ for (final Asset source in assetMapping.value)
296
+ (source.path as AssetAbsolutePath ).uri,
297
+ ];
298
+ final Uri targetUri = buildUri.resolveUri (target);
299
+ final File dylibFile = fileSystem.file (targetUri);
300
+ final Directory targetParent = dylibFile.parent;
301
+ if (! await targetParent.exists ()) {
302
+ await targetParent.create (recursive: true );
303
+ }
304
+ await lipoDylibs (dylibFile, sources);
305
+ await codesignDylib (codesignIdentity, buildMode, dylibFile);
306
+ }
307
+ globals.logger.printTrace ('Copying native assets done.' );
308
+ }
309
+ }
0 commit comments