mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Native assets: package in framework on iOS and MacOS (#140907)
Packages the native assets for iOS and MacOS in frameworks. Issue: * https://github.com/flutter/flutter/issues/140544 * https://github.com/flutter/flutter/issues/129757 ## Details * [x] This packages dylibs from the native assets feature in frameworks. It packages every dylib in a separate framework. * [x] The dylib name is updated to use `@rpath` instead of `@executable_path`. * [x] The dylibs for flutter-tester are no longer modified to change the install name. (Previously it was wrongly updating the install name to the location the dylib would have once deployed in an app.) * [x] Use symlinking on MacOS.
This commit is contained in:
parent
77c3807c80
commit
2e229be2ff
@ -250,9 +250,7 @@ dependencies:
|
|||||||
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$dartPluginName.framework'));
|
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$dartPluginName.framework'));
|
||||||
|
|
||||||
// Native assets embedded, no embedded framework.
|
// Native assets embedded, no embedded framework.
|
||||||
const String libFfiPackageDylib = 'lib$ffiPackageName.dylib';
|
checkFileExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$ffiPackageName.framework', ffiPackageName));
|
||||||
checkFileExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', libFfiPackageDylib));
|
|
||||||
checkDirectoryNotExists(path.join(ephemeralIOSHostApp.path, 'Frameworks', '$ffiPackageName.framework'));
|
|
||||||
|
|
||||||
section('Clean and pub get module');
|
section('Clean and pub get module');
|
||||||
|
|
||||||
@ -385,7 +383,8 @@ end
|
|||||||
|
|
||||||
checkFileExists(path.join(
|
checkFileExists(path.join(
|
||||||
hostFrameworksDirectory,
|
hostFrameworksDirectory,
|
||||||
libFfiPackageDylib,
|
'$ffiPackageName.framework',
|
||||||
|
ffiPackageName,
|
||||||
));
|
));
|
||||||
|
|
||||||
section('Check the NOTICE file is correct');
|
section('Check the NOTICE file is correct');
|
||||||
@ -491,7 +490,8 @@ end
|
|||||||
checkFileExists(path.join(
|
checkFileExists(path.join(
|
||||||
archivedAppPath,
|
archivedAppPath,
|
||||||
'Frameworks',
|
'Frameworks',
|
||||||
libFfiPackageDylib,
|
'$ffiPackageName.framework',
|
||||||
|
ffiPackageName,
|
||||||
));
|
));
|
||||||
|
|
||||||
// The host app example builds plugins statically, url_launcher_ios.framework
|
// The host app example builds plugins statically, url_launcher_ios.framework
|
||||||
|
@ -106,7 +106,7 @@ Future<List<Uri>> buildNativeAssetsIOS({
|
|||||||
ensureNoLinkModeStatic(nativeAssets);
|
ensureNoLinkModeStatic(nativeAssets);
|
||||||
globals.logger.printTrace('Building native assets for $targets done.');
|
globals.logger.printTrace('Building native assets for $targets done.');
|
||||||
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets);
|
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets);
|
||||||
await copyNativeAssetsMacOSHost(
|
await _copyNativeAssetsIOS(
|
||||||
buildUri,
|
buildUri,
|
||||||
fatAssetTargetLocations,
|
fatAssetTargetLocations,
|
||||||
codesignIdentity,
|
codesignIdentity,
|
||||||
@ -145,21 +145,25 @@ Target _getNativeTarget(DarwinArch darwinArch) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Map<AssetPath, List<Asset>> _fatAssetTargetLocations(List<Asset> nativeAssets) {
|
Map<AssetPath, List<Asset>> _fatAssetTargetLocations(List<Asset> nativeAssets) {
|
||||||
|
final Set<String> alreadyTakenNames = <String>{};
|
||||||
final Map<AssetPath, List<Asset>> result = <AssetPath, List<Asset>>{};
|
final Map<AssetPath, List<Asset>> result = <AssetPath, List<Asset>>{};
|
||||||
for (final Asset asset in nativeAssets) {
|
for (final Asset asset in nativeAssets) {
|
||||||
final AssetPath path = _targetLocationIOS(asset).path;
|
final AssetPath path = _targetLocationIOS(asset, alreadyTakenNames).path;
|
||||||
result[path] ??= <Asset>[];
|
result[path] ??= <Asset>[];
|
||||||
result[path]!.add(asset);
|
result[path]!.add(asset);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Asset, Asset> _assetTargetLocations(List<Asset> nativeAssets) => <Asset, Asset>{
|
Map<Asset, Asset> _assetTargetLocations(List<Asset> nativeAssets) {
|
||||||
|
final Set<String> alreadyTakenNames = <String>{};
|
||||||
|
return <Asset, Asset>{
|
||||||
for (final Asset asset in nativeAssets)
|
for (final Asset asset in nativeAssets)
|
||||||
asset: _targetLocationIOS(asset),
|
asset: _targetLocationIOS(asset, alreadyTakenNames),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Asset _targetLocationIOS(Asset asset) {
|
Asset _targetLocationIOS(Asset asset, Set<String> alreadyTakenNames) {
|
||||||
final AssetPath path = asset.path;
|
final AssetPath path = asset.path;
|
||||||
switch (path) {
|
switch (path) {
|
||||||
case AssetSystemPath _:
|
case AssetSystemPath _:
|
||||||
@ -168,7 +172,52 @@ Asset _targetLocationIOS(Asset asset) {
|
|||||||
return asset;
|
return asset;
|
||||||
case AssetAbsolutePath _:
|
case AssetAbsolutePath _:
|
||||||
final String fileName = path.uri.pathSegments.last;
|
final String fileName = path.uri.pathSegments.last;
|
||||||
return asset.copyWith(path: AssetAbsolutePath(Uri(path: fileName)));
|
return asset.copyWith(
|
||||||
|
path: AssetAbsolutePath(frameworkUri(fileName, alreadyTakenNames)),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw Exception(
|
||||||
|
'Unsupported asset path type ${path.runtimeType} in asset $asset');
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Copies native assets into a framework per dynamic library.
|
||||||
|
///
|
||||||
|
/// For `flutter run -release` a multi-architecture solution is needed. So,
|
||||||
|
/// `lipo` is used to combine all target architectures into a single file.
|
||||||
|
///
|
||||||
|
/// The install name is set so that it matches what the place it will
|
||||||
|
/// be bundled in the final app.
|
||||||
|
///
|
||||||
|
/// Code signing is also done here, so that it doesn't have to be done in
|
||||||
|
/// in xcode_backend.dart.
|
||||||
|
Future<void> _copyNativeAssetsIOS(
|
||||||
|
Uri buildUri,
|
||||||
|
Map<AssetPath, List<Asset>> assetTargetLocations,
|
||||||
|
String? codesignIdentity,
|
||||||
|
BuildMode buildMode,
|
||||||
|
FileSystem fileSystem,
|
||||||
|
) async {
|
||||||
|
if (assetTargetLocations.isNotEmpty) {
|
||||||
|
globals.logger
|
||||||
|
.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
|
||||||
|
for (final MapEntry<AssetPath, List<Asset>> assetMapping
|
||||||
|
in assetTargetLocations.entries) {
|
||||||
|
final Uri target = (assetMapping.key as AssetAbsolutePath).uri;
|
||||||
|
final List<Uri> sources = <Uri>[
|
||||||
|
for (final Asset source in assetMapping.value)
|
||||||
|
(source.path as AssetAbsolutePath).uri
|
||||||
|
];
|
||||||
|
final Uri targetUri = buildUri.resolveUri(target);
|
||||||
|
final File dylibFile = fileSystem.file(targetUri);
|
||||||
|
final Directory frameworkDir = dylibFile.parent;
|
||||||
|
if (!await frameworkDir.exists()) {
|
||||||
|
await frameworkDir.create(recursive: true);
|
||||||
|
}
|
||||||
|
await lipoDylibs(dylibFile, sources);
|
||||||
|
await setInstallNameDylib(dylibFile);
|
||||||
|
await createInfoPlist(targetUri.pathSegments.last, frameworkDir);
|
||||||
|
await codesignDylib(codesignIdentity, buildMode, frameworkDir);
|
||||||
|
}
|
||||||
|
globals.logger.printTrace('Copying native assets done.');
|
||||||
}
|
}
|
||||||
throw Exception('Unsupported asset path type ${path.runtimeType} in asset $asset');
|
|
||||||
}
|
}
|
||||||
|
@ -108,7 +108,23 @@ Future<(Uri? nativeAssetsYaml, List<Uri> dependencies)> buildNativeAssetsMacOS({
|
|||||||
final Uri? absolutePath = flutterTester ? buildUri : null;
|
final Uri? absolutePath = flutterTester ? buildUri : null;
|
||||||
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
|
final Map<Asset, Asset> assetTargetLocations = _assetTargetLocations(nativeAssets, absolutePath);
|
||||||
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets, absolutePath);
|
final Map<AssetPath, List<Asset>> fatAssetTargetLocations = _fatAssetTargetLocations(nativeAssets, absolutePath);
|
||||||
await copyNativeAssetsMacOSHost(buildUri, fatAssetTargetLocations, codesignIdentity, buildMode, fileSystem);
|
if (flutterTester) {
|
||||||
|
await _copyNativeAssetsMacOSFlutterTester(
|
||||||
|
buildUri,
|
||||||
|
fatAssetTargetLocations,
|
||||||
|
codesignIdentity,
|
||||||
|
buildMode,
|
||||||
|
fileSystem,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
await _copyNativeAssetsMacOS(
|
||||||
|
buildUri,
|
||||||
|
fatAssetTargetLocations,
|
||||||
|
codesignIdentity,
|
||||||
|
buildMode,
|
||||||
|
fileSystem,
|
||||||
|
);
|
||||||
|
}
|
||||||
final Uri nativeAssetsUri = await writeNativeAssetsYaml(assetTargetLocations.values, yamlParentDirectory ?? buildUri, fileSystem);
|
final Uri nativeAssetsUri = await writeNativeAssetsYaml(assetTargetLocations.values, yamlParentDirectory ?? buildUri, fileSystem);
|
||||||
return (nativeAssetsUri, dependencies.toList());
|
return (nativeAssetsUri, dependencies.toList());
|
||||||
}
|
}
|
||||||
@ -125,22 +141,40 @@ Target _getNativeTarget(DarwinArch darwinArch) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<AssetPath, List<Asset>> _fatAssetTargetLocations(List<Asset> nativeAssets, Uri? absolutePath) {
|
Map<AssetPath, List<Asset>> _fatAssetTargetLocations(
|
||||||
|
List<Asset> nativeAssets,
|
||||||
|
Uri? absolutePath,
|
||||||
|
) {
|
||||||
|
final Set<String> alreadyTakenNames = <String>{};
|
||||||
final Map<AssetPath, List<Asset>> result = <AssetPath, List<Asset>>{};
|
final Map<AssetPath, List<Asset>> result = <AssetPath, List<Asset>>{};
|
||||||
for (final Asset asset in nativeAssets) {
|
for (final Asset asset in nativeAssets) {
|
||||||
final AssetPath path = _targetLocationMacOS(asset, absolutePath).path;
|
final AssetPath path = _targetLocationMacOS(
|
||||||
|
asset,
|
||||||
|
absolutePath,
|
||||||
|
alreadyTakenNames,
|
||||||
|
).path;
|
||||||
result[path] ??= <Asset>[];
|
result[path] ??= <Asset>[];
|
||||||
result[path]!.add(asset);
|
result[path]!.add(asset);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
Map<Asset, Asset> _assetTargetLocations(List<Asset> nativeAssets, Uri? absolutePath) => <Asset, Asset>{
|
Map<Asset, Asset> _assetTargetLocations(
|
||||||
|
List<Asset> nativeAssets,
|
||||||
|
Uri? absolutePath,
|
||||||
|
) {
|
||||||
|
final Set<String> alreadyTakenNames = <String>{};
|
||||||
|
return <Asset, Asset>{
|
||||||
for (final Asset asset in nativeAssets)
|
for (final Asset asset in nativeAssets)
|
||||||
asset: _targetLocationMacOS(asset, absolutePath),
|
asset: _targetLocationMacOS(asset, absolutePath, alreadyTakenNames),
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
Asset _targetLocationMacOS(Asset asset, Uri? absolutePath) {
|
Asset _targetLocationMacOS(
|
||||||
|
Asset asset,
|
||||||
|
Uri? absolutePath,
|
||||||
|
Set<String> alreadyTakenNames,
|
||||||
|
) {
|
||||||
final AssetPath path = asset.path;
|
final AssetPath path = asset.path;
|
||||||
switch (path) {
|
switch (path) {
|
||||||
case AssetSystemPath _:
|
case AssetSystemPath _:
|
||||||
@ -157,9 +191,119 @@ Asset _targetLocationMacOS(Asset asset, Uri? absolutePath) {
|
|||||||
// Flutter Desktop needs "absolute" paths inside the app.
|
// Flutter Desktop needs "absolute" paths inside the app.
|
||||||
// "relative" in the context of native assets would be relative to the
|
// "relative" in the context of native assets would be relative to the
|
||||||
// kernel or aot snapshot.
|
// kernel or aot snapshot.
|
||||||
uri = Uri(path: fileName);
|
uri = frameworkUri(fileName, alreadyTakenNames);
|
||||||
|
|
||||||
}
|
}
|
||||||
return asset.copyWith(path: AssetAbsolutePath(uri));
|
return asset.copyWith(path: AssetAbsolutePath(uri));
|
||||||
}
|
}
|
||||||
throw Exception('Unsupported asset path type ${path.runtimeType} in asset $asset');
|
throw Exception('Unsupported asset path type ${path.runtimeType} in asset $asset');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Copies native assets into a framework per dynamic library.
|
||||||
|
///
|
||||||
|
/// The framework contains symlinks according to
|
||||||
|
/// https://developer.apple.com/library/archive/documentation/MacOSX/Conceptual/BPFrameworks/Concepts/FrameworkAnatomy.html
|
||||||
|
///
|
||||||
|
/// For `flutter run -release` a multi-architecture solution is needed. So,
|
||||||
|
/// `lipo` is used to combine all target architectures into a single file.
|
||||||
|
///
|
||||||
|
/// The install name is set so that it matches what the place it will
|
||||||
|
/// be bundled in the final app.
|
||||||
|
///
|
||||||
|
/// Code signing is also done here, so that it doesn't have to be done in
|
||||||
|
/// in macos_assemble.sh.
|
||||||
|
Future<void> _copyNativeAssetsMacOS(
|
||||||
|
Uri buildUri,
|
||||||
|
Map<AssetPath, List<Asset>> assetTargetLocations,
|
||||||
|
String? codesignIdentity,
|
||||||
|
BuildMode buildMode,
|
||||||
|
FileSystem fileSystem,
|
||||||
|
) async {
|
||||||
|
if (assetTargetLocations.isNotEmpty) {
|
||||||
|
globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
|
||||||
|
for (final MapEntry<AssetPath, List<Asset>> assetMapping in assetTargetLocations.entries) {
|
||||||
|
final Uri target = (assetMapping.key as AssetAbsolutePath).uri;
|
||||||
|
final List<Uri> sources = <Uri>[
|
||||||
|
for (final Asset source in assetMapping.value)
|
||||||
|
(source.path as AssetAbsolutePath).uri,
|
||||||
|
];
|
||||||
|
final Uri targetUri = buildUri.resolveUri(target);
|
||||||
|
final String name = targetUri.pathSegments.last;
|
||||||
|
final Directory frameworkDir = fileSystem.file(targetUri).parent;
|
||||||
|
if (await frameworkDir.exists()) {
|
||||||
|
await frameworkDir.delete(recursive: true);
|
||||||
|
}
|
||||||
|
// MyFramework.framework/ frameworkDir
|
||||||
|
// MyFramework -> Versions/Current/MyFramework dylibLink
|
||||||
|
// Resources -> Versions/Current/Resources resourcesLink
|
||||||
|
// Versions/ versionsDir
|
||||||
|
// A/ versionADir
|
||||||
|
// MyFramework dylibFile
|
||||||
|
// Resources/ resourcesDir
|
||||||
|
// Info.plist
|
||||||
|
// Current -> A currentLink
|
||||||
|
final Directory versionsDir = frameworkDir.childDirectory('Versions');
|
||||||
|
final Directory versionADir = versionsDir.childDirectory('A');
|
||||||
|
final Directory resourcesDir = versionADir.childDirectory('Resources');
|
||||||
|
await resourcesDir.create(recursive: true);
|
||||||
|
final File dylibFile = versionADir.childFile(name);
|
||||||
|
final Link currentLink = versionsDir.childLink('Current');
|
||||||
|
await currentLink.create(fileSystem.path.relative(
|
||||||
|
versionADir.path,
|
||||||
|
from: currentLink.parent.path,
|
||||||
|
));
|
||||||
|
final Link resourcesLink = frameworkDir.childLink('Resources');
|
||||||
|
await resourcesLink.create(fileSystem.path.relative(
|
||||||
|
resourcesDir.path,
|
||||||
|
from: resourcesLink.parent.path,
|
||||||
|
));
|
||||||
|
await lipoDylibs(dylibFile, sources);
|
||||||
|
final Link dylibLink = frameworkDir.childLink(name);
|
||||||
|
await dylibLink.create(fileSystem.path.relative(
|
||||||
|
versionsDir.childDirectory('Current').childFile(name).path,
|
||||||
|
from: dylibLink.parent.path,
|
||||||
|
));
|
||||||
|
await setInstallNameDylib(dylibFile);
|
||||||
|
await createInfoPlist(name, resourcesDir);
|
||||||
|
await codesignDylib(codesignIdentity, buildMode, frameworkDir);
|
||||||
|
}
|
||||||
|
globals.logger.printTrace('Copying native assets done.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Copies native assets for flutter tester.
|
||||||
|
///
|
||||||
|
/// For `flutter run -release` a multi-architecture solution is needed. So,
|
||||||
|
/// `lipo` is used to combine all target architectures into a single file.
|
||||||
|
///
|
||||||
|
/// In contrast to [_copyNativeAssetsMacOS], it does not set the install name.
|
||||||
|
///
|
||||||
|
/// Code signing is also done here.
|
||||||
|
Future<void> _copyNativeAssetsMacOSFlutterTester(
|
||||||
|
Uri buildUri,
|
||||||
|
Map<AssetPath, List<Asset>> assetTargetLocations,
|
||||||
|
String? codesignIdentity,
|
||||||
|
BuildMode buildMode,
|
||||||
|
FileSystem fileSystem,
|
||||||
|
) async {
|
||||||
|
if (assetTargetLocations.isNotEmpty) {
|
||||||
|
globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
|
||||||
|
for (final MapEntry<AssetPath, List<Asset>> assetMapping in assetTargetLocations.entries) {
|
||||||
|
final Uri target = (assetMapping.key as AssetAbsolutePath).uri;
|
||||||
|
final List<Uri> sources = <Uri>[
|
||||||
|
for (final Asset source in assetMapping.value)
|
||||||
|
(source.path as AssetAbsolutePath).uri,
|
||||||
|
];
|
||||||
|
final Uri targetUri = buildUri.resolveUri(target);
|
||||||
|
final File dylibFile = fileSystem.file(targetUri);
|
||||||
|
final Directory targetParent = dylibFile.parent;
|
||||||
|
if (!await targetParent.exists()) {
|
||||||
|
await targetParent.create(recursive: true);
|
||||||
|
}
|
||||||
|
await lipoDylibs(dylibFile, sources);
|
||||||
|
await codesignDylib(codesignIdentity, buildMode, dylibFile);
|
||||||
|
}
|
||||||
|
globals.logger.printTrace('Copying native assets done.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -13,41 +13,40 @@ import '../build_info.dart';
|
|||||||
import '../convert.dart';
|
import '../convert.dart';
|
||||||
import '../globals.dart' as globals;
|
import '../globals.dart' as globals;
|
||||||
|
|
||||||
/// The target location for native assets on macOS.
|
/// Create an `Info.plist` in [target] for a framework with a single dylib.
|
||||||
///
|
///
|
||||||
/// Because we need to have a multi-architecture solution for
|
/// The framework must be named [name].framework and the dylib [name].
|
||||||
/// `flutter run --release`, we use `lipo` to combine all target architectures
|
Future<void> createInfoPlist(
|
||||||
/// into a single file.
|
String name,
|
||||||
///
|
Directory target,
|
||||||
/// We need to set the install name so that it matches what the place it will
|
|
||||||
/// be bundled in the final app.
|
|
||||||
///
|
|
||||||
/// Code signing is also done here, so that we don't have to worry about it
|
|
||||||
/// in xcode_backend.dart and macos_assemble.sh.
|
|
||||||
Future<void> copyNativeAssetsMacOSHost(
|
|
||||||
Uri buildUri,
|
|
||||||
Map<AssetPath, List<Asset>> assetTargetLocations,
|
|
||||||
String? codesignIdentity,
|
|
||||||
BuildMode buildMode,
|
|
||||||
FileSystem fileSystem,
|
|
||||||
) async {
|
) async {
|
||||||
if (assetTargetLocations.isNotEmpty) {
|
final File infoPlistFile = target.childFile('Info.plist');
|
||||||
globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
|
await infoPlistFile.writeAsString('''
|
||||||
final Directory buildDir = fileSystem.directory(buildUri.toFilePath());
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
if (!buildDir.existsSync()) {
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
buildDir.createSync(recursive: true);
|
<plist version="1.0">
|
||||||
}
|
<dict>
|
||||||
for (final MapEntry<AssetPath, List<Asset>> assetMapping in assetTargetLocations.entries) {
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
final Uri target = (assetMapping.key as AssetAbsolutePath).uri;
|
<string>en</string>
|
||||||
final List<Uri> sources = <Uri>[for (final Asset source in assetMapping.value) (source.path as AssetAbsolutePath).uri];
|
<key>CFBundleExecutable</key>
|
||||||
final Uri targetUri = buildUri.resolveUri(target);
|
<string>$name</string>
|
||||||
final String targetFullPath = targetUri.toFilePath();
|
<key>CFBundleIdentifier</key>
|
||||||
await lipoDylibs(targetFullPath, sources);
|
<string>io.flutter.flutter.native_assets.$name</string>
|
||||||
await setInstallNameDylib(targetUri);
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
await codesignDylib(codesignIdentity, buildMode, targetFullPath);
|
<string>6.0</string>
|
||||||
}
|
<key>CFBundleName</key>
|
||||||
globals.logger.printTrace('Copying native assets done.');
|
<string>$name</string>
|
||||||
}
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>FMWK</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
<key>CFBundleSignature</key>
|
||||||
|
<string>????</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1.0</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
||||||
|
''');
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Combines dylibs from [sources] into a fat binary at [targetFullPath].
|
/// Combines dylibs from [sources] into a fat binary at [targetFullPath].
|
||||||
@ -55,13 +54,13 @@ Future<void> copyNativeAssetsMacOSHost(
|
|||||||
/// The dylibs must have different architectures. E.g. a dylib targeting
|
/// The dylibs must have different architectures. E.g. a dylib targeting
|
||||||
/// arm64 ios simulator cannot be combined with a dylib targeting arm64
|
/// arm64 ios simulator cannot be combined with a dylib targeting arm64
|
||||||
/// ios device or macos arm64.
|
/// ios device or macos arm64.
|
||||||
Future<void> lipoDylibs(String targetFullPath, List<Uri> sources) async {
|
Future<void> lipoDylibs(File target, List<Uri> sources) async {
|
||||||
final ProcessResult lipoResult = await globals.processManager.run(
|
final ProcessResult lipoResult = await globals.processManager.run(
|
||||||
<String>[
|
<String>[
|
||||||
'lipo',
|
'lipo',
|
||||||
'-create',
|
'-create',
|
||||||
'-output',
|
'-output',
|
||||||
targetFullPath,
|
target.path,
|
||||||
for (final Uri source in sources) source.toFilePath(),
|
for (final Uri source in sources) source.toFilePath(),
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
@ -78,25 +77,27 @@ Future<void> lipoDylibs(String targetFullPath, List<Uri> sources) async {
|
|||||||
/// dylib itself does not correspond to the path that the file is at. Therefore,
|
/// dylib itself does not correspond to the path that the file is at. Therefore,
|
||||||
/// native assets copied into their final location also need their install name
|
/// native assets copied into their final location also need their install name
|
||||||
/// updated with the `install_name_tool`.
|
/// updated with the `install_name_tool`.
|
||||||
Future<void> setInstallNameDylib(Uri targetUri) async {
|
Future<void> setInstallNameDylib(File dylibFile) async {
|
||||||
final String fileName = targetUri.pathSegments.last;
|
final String fileName = dylibFile.basename;
|
||||||
final ProcessResult installNameResult = await globals.processManager.run(
|
final ProcessResult installNameResult = await globals.processManager.run(
|
||||||
<String>[
|
<String>[
|
||||||
'install_name_tool',
|
'install_name_tool',
|
||||||
'-id',
|
'-id',
|
||||||
'@executable_path/Frameworks/$fileName',
|
'@rpath/$fileName.framework/$fileName',
|
||||||
targetUri.toFilePath(),
|
dylibFile.path,
|
||||||
],
|
],
|
||||||
);
|
);
|
||||||
if (installNameResult.exitCode != 0) {
|
if (installNameResult.exitCode != 0) {
|
||||||
throwToolExit('Failed to change the install name of $targetUri:\n${installNameResult.stderr}');
|
throwToolExit(
|
||||||
|
'Failed to change the install name of $dylibFile:\n${installNameResult.stderr}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> codesignDylib(
|
Future<void> codesignDylib(
|
||||||
String? codesignIdentity,
|
String? codesignIdentity,
|
||||||
BuildMode buildMode,
|
BuildMode buildMode,
|
||||||
String targetFullPath,
|
FileSystemEntity target,
|
||||||
) async {
|
) async {
|
||||||
if (codesignIdentity == null || codesignIdentity.isEmpty) {
|
if (codesignIdentity == null || codesignIdentity.isEmpty) {
|
||||||
codesignIdentity = '-';
|
codesignIdentity = '-';
|
||||||
@ -110,12 +111,17 @@ Future<void> codesignDylib(
|
|||||||
// Mimic Xcode's timestamp codesigning behavior on non-release binaries.
|
// Mimic Xcode's timestamp codesigning behavior on non-release binaries.
|
||||||
'--timestamp=none',
|
'--timestamp=none',
|
||||||
],
|
],
|
||||||
targetFullPath,
|
target.path,
|
||||||
];
|
];
|
||||||
globals.logger.printTrace(codesignCommand.join(' '));
|
globals.logger.printTrace(codesignCommand.join(' '));
|
||||||
final ProcessResult codesignResult = await globals.processManager.run(codesignCommand);
|
final ProcessResult codesignResult = await globals.processManager.run(
|
||||||
|
codesignCommand,
|
||||||
|
);
|
||||||
if (codesignResult.exitCode != 0) {
|
if (codesignResult.exitCode != 0) {
|
||||||
throwToolExit('Failed to code sign binary:\n${codesignResult.stderr}');
|
throwToolExit(
|
||||||
|
'Failed to code sign binary: exit code: ${codesignResult.exitCode} '
|
||||||
|
'${codesignResult.stdout} ${codesignResult.stderr}',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
globals.logger.printTrace(codesignResult.stdout as String);
|
globals.logger.printTrace(codesignResult.stdout as String);
|
||||||
globals.logger.printTrace(codesignResult.stderr as String);
|
globals.logger.printTrace(codesignResult.stderr as String);
|
||||||
@ -125,7 +131,9 @@ Future<void> codesignDylib(
|
|||||||
///
|
///
|
||||||
/// Use the `clang`, `ar`, and `ld` that would be used if run with `xcrun`.
|
/// Use the `clang`, `ar`, and `ld` that would be used if run with `xcrun`.
|
||||||
Future<CCompilerConfig> cCompilerConfigMacOS() async {
|
Future<CCompilerConfig> cCompilerConfigMacOS() async {
|
||||||
final ProcessResult xcrunResult = await globals.processManager.run(<String>['xcrun', 'clang', '--version']);
|
final ProcessResult xcrunResult = await globals.processManager.run(
|
||||||
|
<String>['xcrun', 'clang', '--version'],
|
||||||
|
);
|
||||||
if (xcrunResult.exitCode != 0) {
|
if (xcrunResult.exitCode != 0) {
|
||||||
throwToolExit('Failed to find clang with xcrun:\n${xcrunResult.stderr}');
|
throwToolExit('Failed to find clang with xcrun:\n${xcrunResult.stderr}');
|
||||||
}
|
}
|
||||||
@ -139,3 +147,61 @@ Future<CCompilerConfig> cCompilerConfigMacOS() async {
|
|||||||
ld: Uri.file('$installPath/ld'),
|
ld: Uri.file('$installPath/ld'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Converts [fileName] into a suitable framework name.
|
||||||
|
///
|
||||||
|
/// On MacOS and iOS, dylibs need to be packaged in a framework.
|
||||||
|
///
|
||||||
|
/// In order for resolution to work, the file name inside the framework must be
|
||||||
|
/// equal to the framework name.
|
||||||
|
///
|
||||||
|
/// Dylib names on MacOS/iOS usually have a dylib extension. If so, remove it.
|
||||||
|
///
|
||||||
|
/// Dylib names on MacOS/iOS are usually prefixed with 'lib'. So, if the file is
|
||||||
|
/// a dylib, try to remove the prefix.
|
||||||
|
///
|
||||||
|
/// The bundle ID string must contain only alphanumeric characters
|
||||||
|
/// (A–Z, a–z, and 0–9), hyphens (-), and periods (.).
|
||||||
|
/// https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundleidentifier
|
||||||
|
///
|
||||||
|
/// This name can contain up to 15 characters.
|
||||||
|
/// https://developer.apple.com/documentation/bundleresources/information_property_list/cfbundlename
|
||||||
|
///
|
||||||
|
/// The [alreadyTakenNames] are used to ensure that the framework name does not
|
||||||
|
/// conflict with previously chosen names.
|
||||||
|
Uri frameworkUri(String fileName, Set<String> alreadyTakenNames) {
|
||||||
|
final List<String> splitFileName = fileName.split('.');
|
||||||
|
final bool isDylib;
|
||||||
|
if (splitFileName.length >= 2) {
|
||||||
|
isDylib = splitFileName.last == 'dylib';
|
||||||
|
if (isDylib) {
|
||||||
|
fileName = splitFileName.sublist(0, splitFileName.length - 1).join('.');
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
isDylib = false;
|
||||||
|
}
|
||||||
|
if (isDylib && fileName.startsWith('lib')) {
|
||||||
|
fileName = fileName.replaceFirst('lib', '');
|
||||||
|
}
|
||||||
|
fileName = fileName.replaceAll(RegExp(r'[^A-Za-z0-9_-]'), '');
|
||||||
|
if (fileName.length > 15) {
|
||||||
|
fileName = fileName.substring(0, 15);
|
||||||
|
}
|
||||||
|
if (alreadyTakenNames.contains(fileName)) {
|
||||||
|
if (fileName.length > 12) {
|
||||||
|
fileName = fileName.substring(0, 12);
|
||||||
|
}
|
||||||
|
final String prefixName = fileName;
|
||||||
|
for (int i = 1; i < 1000; i++) {
|
||||||
|
fileName = '$prefixName$i';
|
||||||
|
if (!alreadyTakenNames.contains(fileName)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (alreadyTakenNames.contains(fileName)) {
|
||||||
|
throwToolExit('Failed to rename $fileName in native assets packaging.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
alreadyTakenNames.add(fileName);
|
||||||
|
return Uri(path: '$fileName.framework/$fileName');
|
||||||
|
}
|
||||||
|
@ -135,7 +135,7 @@ void main() {
|
|||||||
linkMode: native_assets_cli.LinkMode.dynamic,
|
linkMode: native_assets_cli.LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.iOSArm64,
|
target: native_assets_cli.Target.iOSArm64,
|
||||||
path: native_assets_cli.AssetAbsolutePath(
|
path: native_assets_cli.AssetAbsolutePath(
|
||||||
Uri.file('libfoo.dylib'),
|
Uri.file('foo.framework/foo'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
], dependencies: <Uri>[
|
], dependencies: <Uri>[
|
||||||
@ -165,7 +165,7 @@ void main() {
|
|||||||
nativeAssetsYaml.readAsStringSync(),
|
nativeAssetsYaml.readAsStringSync(),
|
||||||
stringContainsInOrder(<String>[
|
stringContainsInOrder(<String>[
|
||||||
'package:foo/foo.dart',
|
'package:foo/foo.dart',
|
||||||
'libfoo.dylib',
|
'foo.framework',
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
@ -130,13 +130,13 @@ void main() {
|
|||||||
id: 'package:bar/bar.dart',
|
id: 'package:bar/bar.dart',
|
||||||
linkMode: LinkMode.dynamic,
|
linkMode: LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.macOSArm64,
|
target: native_assets_cli.Target.macOSArm64,
|
||||||
path: AssetAbsolutePath(Uri.file('bar.dylib')),
|
path: AssetAbsolutePath(Uri.file('libbar.dylib')),
|
||||||
),
|
),
|
||||||
Asset(
|
Asset(
|
||||||
id: 'package:bar/bar.dart',
|
id: 'package:bar/bar.dart',
|
||||||
linkMode: LinkMode.dynamic,
|
linkMode: LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.macOSX64,
|
target: native_assets_cli.Target.macOSX64,
|
||||||
path: AssetAbsolutePath(Uri.file('bar.dylib')),
|
path: AssetAbsolutePath(Uri.file('libbar.dylib')),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -219,16 +219,16 @@ void main() {
|
|||||||
'lipo',
|
'lipo',
|
||||||
'-create',
|
'-create',
|
||||||
'-output',
|
'-output',
|
||||||
'/build/native_assets/ios/bar.dylib',
|
'/build/native_assets/ios/bar.framework/bar',
|
||||||
'bar.dylib',
|
'libbar.dylib',
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
command: <Pattern>[
|
command: <Pattern>[
|
||||||
'install_name_tool',
|
'install_name_tool',
|
||||||
'-id',
|
'-id',
|
||||||
'@executable_path/Frameworks/bar.dylib',
|
'@rpath/bar.framework/bar',
|
||||||
'/build/native_assets/ios/bar.dylib',
|
'/build/native_assets/ios/bar.framework/bar'
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const FakeCommand(
|
const FakeCommand(
|
||||||
@ -238,7 +238,7 @@ void main() {
|
|||||||
'--sign',
|
'--sign',
|
||||||
'-',
|
'-',
|
||||||
'--timestamp=none',
|
'--timestamp=none',
|
||||||
'/build/native_assets/ios/bar.dylib',
|
'/build/native_assets/ios/bar.framework',
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -267,7 +267,7 @@ void main() {
|
|||||||
id: 'package:bar/bar.dart',
|
id: 'package:bar/bar.dart',
|
||||||
linkMode: LinkMode.dynamic,
|
linkMode: LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.iOSArm64,
|
target: native_assets_cli.Target.iOSArm64,
|
||||||
path: AssetAbsolutePath(Uri.file('bar.dylib')),
|
path: AssetAbsolutePath(Uri.file('libbar.dylib')),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
@ -0,0 +1,68 @@
|
|||||||
|
// Copyright 2014 The Flutter Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style license that can be
|
||||||
|
// found in the LICENSE file.
|
||||||
|
|
||||||
|
import 'package:flutter_tools/src/macos/native_assets_host.dart';
|
||||||
|
|
||||||
|
import '../../src/common.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('framework name', () {
|
||||||
|
expect(
|
||||||
|
frameworkUri('libfoo.dylib', <String>{}),
|
||||||
|
equals(Uri.file('foo.framework/foo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('foo', <String>{}),
|
||||||
|
equals(Uri.file('foo.framework/foo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('foo_foo', <String>{}),
|
||||||
|
equals(Uri.file('foo_foo.framework/foo_foo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('foo-foo', <String>{}),
|
||||||
|
equals(Uri.file('foo-foo.framework/foo-foo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri(r'foo$foo', <String>{}),
|
||||||
|
equals(Uri.file('foofoo.framework/foofoo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('foo.foo', <String>{}),
|
||||||
|
equals(Uri.file('foofoo.framework/foofoo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('libatoolongfilenameforaframework.dylib', <String>{}),
|
||||||
|
equals(Uri.file('atoolongfilenam.framework/atoolongfilenam')),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('framework name name confilicts', () {
|
||||||
|
final Set<String> alreadyTakenNames = <String>{};
|
||||||
|
expect(
|
||||||
|
frameworkUri('libfoo.dylib', alreadyTakenNames),
|
||||||
|
equals(Uri.file('foo.framework/foo')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('libfoo.dylib', alreadyTakenNames),
|
||||||
|
equals(Uri.file('foo1.framework/foo1')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('libfoo.dylib', alreadyTakenNames),
|
||||||
|
equals(Uri.file('foo2.framework/foo2')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('libatoolongfilenameforaframework.dylib', alreadyTakenNames),
|
||||||
|
equals(Uri.file('atoolongfilenam.framework/atoolongfilenam')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('libatoolongfilenameforaframework.dylib', alreadyTakenNames),
|
||||||
|
equals(Uri.file('atoolongfile1.framework/atoolongfile1')),
|
||||||
|
);
|
||||||
|
expect(
|
||||||
|
frameworkUri('libatoolongfilenameforaframework.dylib', alreadyTakenNames),
|
||||||
|
equals(Uri.file('atoolongfile2.framework/atoolongfile2')),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
@ -149,13 +149,13 @@ void main() {
|
|||||||
id: 'package:bar/bar.dart',
|
id: 'package:bar/bar.dart',
|
||||||
linkMode: LinkMode.dynamic,
|
linkMode: LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.macOSArm64,
|
target: native_assets_cli.Target.macOSArm64,
|
||||||
path: AssetAbsolutePath(Uri.file('bar.dylib')),
|
path: AssetAbsolutePath(Uri.file('libbar.dylib')),
|
||||||
),
|
),
|
||||||
Asset(
|
Asset(
|
||||||
id: 'package:bar/bar.dart',
|
id: 'package:bar/bar.dart',
|
||||||
linkMode: LinkMode.dynamic,
|
linkMode: LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.macOSX64,
|
target: native_assets_cli.Target.macOSX64,
|
||||||
path: AssetAbsolutePath(Uri.file('bar.dylib')),
|
path: AssetAbsolutePath(Uri.file('libbar.dylib')),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -236,35 +236,47 @@ void main() {
|
|||||||
if (flutterTester) {
|
if (flutterTester) {
|
||||||
testName += ' flutter tester';
|
testName += ' flutter tester';
|
||||||
}
|
}
|
||||||
|
final String dylibPath;
|
||||||
|
final String signPath;
|
||||||
|
if (flutterTester) {
|
||||||
|
// Just the dylib.
|
||||||
|
dylibPath = '/build/native_assets/macos/libbar.dylib';
|
||||||
|
signPath = '/build/native_assets/macos/libbar.dylib';
|
||||||
|
} else {
|
||||||
|
// Packaged in framework.
|
||||||
|
dylibPath = '/build/native_assets/macos/bar.framework/Versions/A/bar';
|
||||||
|
signPath = '/build/native_assets/macos/bar.framework';
|
||||||
|
}
|
||||||
testUsingContext('build with assets$testName', overrides: <Type, Generator>{
|
testUsingContext('build with assets$testName', overrides: <Type, Generator>{
|
||||||
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
FeatureFlags: () => TestFeatureFlags(isNativeAssetsEnabled: true),
|
||||||
ProcessManager: () => FakeProcessManager.list(
|
ProcessManager: () => FakeProcessManager.list(
|
||||||
<FakeCommand>[
|
<FakeCommand>[
|
||||||
const FakeCommand(
|
FakeCommand(
|
||||||
command: <Pattern>[
|
command: <Pattern>[
|
||||||
'lipo',
|
'lipo',
|
||||||
'-create',
|
'-create',
|
||||||
'-output',
|
'-output',
|
||||||
'/build/native_assets/macos/bar.dylib',
|
dylibPath,
|
||||||
'bar.dylib',
|
'libbar.dylib',
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const FakeCommand(
|
if (!flutterTester)
|
||||||
|
FakeCommand(
|
||||||
command: <Pattern>[
|
command: <Pattern>[
|
||||||
'install_name_tool',
|
'install_name_tool',
|
||||||
'-id',
|
'-id',
|
||||||
'@executable_path/Frameworks/bar.dylib',
|
'@rpath/bar.framework/bar',
|
||||||
'/build/native_assets/macos/bar.dylib',
|
dylibPath,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
const FakeCommand(
|
FakeCommand(
|
||||||
command: <Pattern>[
|
command: <Pattern>[
|
||||||
'codesign',
|
'codesign',
|
||||||
'--force',
|
'--force',
|
||||||
'--sign',
|
'--sign',
|
||||||
'-',
|
'-',
|
||||||
'--timestamp=none',
|
'--timestamp=none',
|
||||||
'/build/native_assets/macos/bar.dylib',
|
signPath,
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
@ -292,7 +304,7 @@ void main() {
|
|||||||
id: 'package:bar/bar.dart',
|
id: 'package:bar/bar.dart',
|
||||||
linkMode: LinkMode.dynamic,
|
linkMode: LinkMode.dynamic,
|
||||||
target: native_assets_cli.Target.macOSArm64,
|
target: native_assets_cli.Target.macOSArm64,
|
||||||
path: AssetAbsolutePath(Uri.file('bar.dylib')),
|
path: AssetAbsolutePath(Uri.file('libbar.dylib')),
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@ -315,10 +327,10 @@ void main() {
|
|||||||
'package:bar/bar.dart',
|
'package:bar/bar.dart',
|
||||||
if (flutterTester)
|
if (flutterTester)
|
||||||
// Tests run on host system, so the have the full path on the system.
|
// Tests run on host system, so the have the full path on the system.
|
||||||
'- ${projectUri.resolve('build/native_assets/macos/bar.dylib').toFilePath()}'
|
'- ${projectUri.resolve('build/native_assets/macos/libbar.dylib').toFilePath()}'
|
||||||
else
|
else
|
||||||
// Apps are a bundle with the dylibs on their dlopen path.
|
// Apps are a bundle with the dylibs on their dlopen path.
|
||||||
'- bar.dylib',
|
'- bar.framework/bar',
|
||||||
]),
|
]),
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -292,18 +292,48 @@ void main() {
|
|||||||
void expectDylibIsBundledMacOS(Directory appDirectory, String buildMode) {
|
void expectDylibIsBundledMacOS(Directory appDirectory, String buildMode) {
|
||||||
final Directory appBundle = appDirectory.childDirectory('build/$hostOs/Build/Products/${buildMode.upperCaseFirst()}/$exampleAppName.app');
|
final Directory appBundle = appDirectory.childDirectory('build/$hostOs/Build/Products/${buildMode.upperCaseFirst()}/$exampleAppName.app');
|
||||||
expect(appBundle, exists);
|
expect(appBundle, exists);
|
||||||
final Directory dylibsFolder = appBundle.childDirectory('Contents/Frameworks');
|
final Directory frameworksFolder =
|
||||||
expect(dylibsFolder, exists);
|
appBundle.childDirectory('Contents/Frameworks');
|
||||||
final File dylib = dylibsFolder.childFile(OS.macOS.dylibFileName(packageName));
|
expect(frameworksFolder, exists);
|
||||||
expect(dylib, exists);
|
|
||||||
|
// MyFramework.framework/
|
||||||
|
// MyFramework -> Versions/Current/MyFramework
|
||||||
|
// Resources -> Versions/Current/Resources
|
||||||
|
// Versions/
|
||||||
|
// A/
|
||||||
|
// MyFramework
|
||||||
|
// Resources/
|
||||||
|
// Info.plist
|
||||||
|
// Current -> A
|
||||||
|
final String frameworkName = packageName.substring(0, 15);
|
||||||
|
final Directory frameworkDir =
|
||||||
|
frameworksFolder.childDirectory('$frameworkName.framework');
|
||||||
|
final Directory versionsDir = frameworkDir.childDirectory('Versions');
|
||||||
|
final Directory versionADir = versionsDir.childDirectory('A');
|
||||||
|
final Directory resourcesDir = versionADir.childDirectory('Resources');
|
||||||
|
expect(resourcesDir, exists);
|
||||||
|
final File dylibFile = versionADir.childFile(frameworkName);
|
||||||
|
expect(dylibFile, exists);
|
||||||
|
final Link currentLink = versionsDir.childLink('Current');
|
||||||
|
expect(currentLink, exists);
|
||||||
|
expect(currentLink.resolveSymbolicLinksSync(), versionADir.path);
|
||||||
|
final Link resourcesLink = frameworkDir.childLink('Resources');
|
||||||
|
expect(resourcesLink, exists);
|
||||||
|
expect(resourcesLink.resolveSymbolicLinksSync(), resourcesDir.path);
|
||||||
|
final Link dylibLink = frameworkDir.childLink(frameworkName);
|
||||||
|
expect(dylibLink, exists);
|
||||||
|
expect(dylibLink.resolveSymbolicLinksSync(), dylibFile.path);
|
||||||
}
|
}
|
||||||
|
|
||||||
void expectDylibIsBundledIos(Directory appDirectory, String buildMode) {
|
void expectDylibIsBundledIos(Directory appDirectory, String buildMode) {
|
||||||
final Directory appBundle = appDirectory.childDirectory('build/ios/${buildMode.upperCaseFirst()}-iphoneos/Runner.app');
|
final Directory appBundle = appDirectory.childDirectory('build/ios/${buildMode.upperCaseFirst()}-iphoneos/Runner.app');
|
||||||
expect(appBundle, exists);
|
expect(appBundle, exists);
|
||||||
final Directory dylibsFolder = appBundle.childDirectory('Frameworks');
|
final Directory frameworksFolder = appBundle.childDirectory('Frameworks');
|
||||||
expect(dylibsFolder, exists);
|
expect(frameworksFolder, exists);
|
||||||
final File dylib = dylibsFolder.childFile(OS.iOS.dylibFileName(packageName));
|
final String frameworkName = packageName.substring(0, 15);
|
||||||
|
final File dylib = frameworksFolder
|
||||||
|
.childDirectory('$frameworkName.framework')
|
||||||
|
.childFile(frameworkName);
|
||||||
expect(dylib, exists);
|
expect(dylib, exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -379,7 +409,10 @@ void expectDylibIsBundledAndroid(Directory appDirectory, String buildMode) {
|
|||||||
void expectDylibIsBundledWithFrameworks(Directory appDirectory, String buildMode, String os) {
|
void expectDylibIsBundledWithFrameworks(Directory appDirectory, String buildMode, String os) {
|
||||||
final Directory frameworksFolder = appDirectory.childDirectory('build/$os/framework/${buildMode.upperCaseFirst()}');
|
final Directory frameworksFolder = appDirectory.childDirectory('build/$os/framework/${buildMode.upperCaseFirst()}');
|
||||||
expect(frameworksFolder, exists);
|
expect(frameworksFolder, exists);
|
||||||
final File dylib = frameworksFolder.childFile(OS.macOS.dylibFileName(packageName));
|
final String frameworkName = packageName.substring(0, 15);
|
||||||
|
final File dylib = frameworksFolder
|
||||||
|
.childDirectory('$frameworkName.framework')
|
||||||
|
.childFile(frameworkName);
|
||||||
expect(dylib, exists);
|
expect(dylib, exists);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user