flutter/packages/flutter_tools/lib/src/macos/native_assets_host.dart
Daco Harkes f5442bf937
Native assets: roll deps (#141684)
Rolls the packages from https://github.com/dart-lang/native in the native assets implementation.

Most notable we're refactoring `package:native_assets_cli` for `build.dart` use.
Therefore, all imports to that package for Flutter/Dart should be to the implementation internals that are no longer visible for `build.dart` writers. Hence all the import updates.

No behavior in Flutter apps should change.

This PR also updates the template to use the latests version of `package:native_assets_cli` which no longer exposes all the implementation details.
2024-01-17 21:20:36 +00:00

143 lines
5.2 KiB
Dart

// 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.
// Shared logic between iOS and macOS implementations of native assets.
import 'package:native_assets_cli/native_assets_cli_internal.dart'
hide BuildMode;
import '../base/common.dart';
import '../base/file_system.dart';
import '../base/io.dart';
import '../build_info.dart';
import '../convert.dart';
import '../globals.dart' as globals;
/// The target location for native assets on macOS.
///
/// Because we need to have a multi-architecture solution for
/// `flutter run --release`, we use `lipo` to combine all target architectures
/// into a single file.
///
/// 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 {
if (assetTargetLocations.isNotEmpty) {
globals.logger.printTrace('Copying native assets to ${buildUri.toFilePath()}.');
final Directory buildDir = fileSystem.directory(buildUri.toFilePath());
if (!buildDir.existsSync()) {
buildDir.createSync(recursive: true);
}
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 targetFullPath = targetUri.toFilePath();
await lipoDylibs(targetFullPath, sources);
await setInstallNameDylib(targetUri);
await codesignDylib(codesignIdentity, buildMode, targetFullPath);
}
globals.logger.printTrace('Copying native assets done.');
}
}
/// Combines dylibs from [sources] into a fat binary at [targetFullPath].
///
/// The dylibs must have different architectures. E.g. a dylib targeting
/// arm64 ios simulator cannot be combined with a dylib targeting arm64
/// ios device or macos arm64.
Future<void> lipoDylibs(String targetFullPath, List<Uri> sources) async {
final ProcessResult lipoResult = await globals.processManager.run(
<String>[
'lipo',
'-create',
'-output',
targetFullPath,
for (final Uri source in sources) source.toFilePath(),
],
);
if (lipoResult.exitCode != 0) {
throwToolExit('Failed to create universal binary:\n${lipoResult.stderr}');
}
globals.logger.printTrace(lipoResult.stdout as String);
globals.logger.printTrace(lipoResult.stderr as String);
}
/// Sets the install name in a dylib with a Mach-O format.
///
/// On macOS and iOS, opening a dylib at runtime fails if the path inside the
/// 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
/// updated with the `install_name_tool`.
Future<void> setInstallNameDylib(Uri targetUri) async {
final String fileName = targetUri.pathSegments.last;
final ProcessResult installNameResult = await globals.processManager.run(
<String>[
'install_name_tool',
'-id',
'@executable_path/Frameworks/$fileName',
targetUri.toFilePath(),
],
);
if (installNameResult.exitCode != 0) {
throwToolExit('Failed to change the install name of $targetUri:\n${installNameResult.stderr}');
}
}
Future<void> codesignDylib(
String? codesignIdentity,
BuildMode buildMode,
String targetFullPath,
) async {
if (codesignIdentity == null || codesignIdentity.isEmpty) {
codesignIdentity = '-';
}
final List<String> codesignCommand = <String>[
'codesign',
'--force',
'--sign',
codesignIdentity,
if (buildMode != BuildMode.release) ...<String>[
// Mimic Xcode's timestamp codesigning behavior on non-release binaries.
'--timestamp=none',
],
targetFullPath,
];
globals.logger.printTrace(codesignCommand.join(' '));
final ProcessResult codesignResult = await globals.processManager.run(codesignCommand);
if (codesignResult.exitCode != 0) {
throwToolExit('Failed to code sign binary:\n${codesignResult.stderr}');
}
globals.logger.printTrace(codesignResult.stdout as String);
globals.logger.printTrace(codesignResult.stderr as String);
}
/// Flutter expects `xcrun` to be on the path on macOS hosts.
///
/// Use the `clang`, `ar`, and `ld` that would be used if run with `xcrun`.
Future<CCompilerConfig> cCompilerConfigMacOS() async {
final ProcessResult xcrunResult = await globals.processManager.run(<String>['xcrun', 'clang', '--version']);
if (xcrunResult.exitCode != 0) {
throwToolExit('Failed to find clang with xcrun:\n${xcrunResult.stderr}');
}
final String installPath = LineSplitter.split(xcrunResult.stdout as String)
.firstWhere((String s) => s.startsWith('InstalledDir: '))
.split(' ')
.last;
return CCompilerConfig(
cc: Uri.file('$installPath/clang'),
ar: Uri.file('$installPath/ar'),
ld: Uri.file('$installPath/ld'),
);
}