mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
Share common logic between UnpackMacOS and UnpackIOS build targets (#168034)
`UnpackIOS` and `UnpackMacOS` have shared logic, which is currently basically duplicate code. We also plan to add more shared logic for SwiftPM. To simply that, this PR creates an abstract `UnpackDarwin` class with the shared logic. No functionality is changed other than slightly changed error logs for `UnpackMacOS`. Code is tested by: - https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/build_system/targets/ios_test.dart - https://github.com/flutter/flutter/blob/master/packages/flutter_tools/test/general.shard/build_system/targets/macos_test.dart Fixes https://github.com/flutter/flutter/issues/168029. ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [x] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
057a77b49e
commit
07b0983657
@ -4,8 +4,10 @@
|
|||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
import '../../artifacts.dart';
|
||||||
import '../../base/common.dart';
|
import '../../base/common.dart';
|
||||||
import '../../base/file_system.dart';
|
import '../../base/file_system.dart';
|
||||||
|
import '../../base/io.dart';
|
||||||
import '../../build_info.dart';
|
import '../../build_info.dart';
|
||||||
import '../../flutter_plugins.dart';
|
import '../../flutter_plugins.dart';
|
||||||
import '../../globals.dart' as globals;
|
import '../../globals.dart' as globals;
|
||||||
@ -152,3 +154,106 @@ abstract class CheckDevDependencies extends Target {
|
|||||||
globals.stdio.stderrWrite('error: $message');
|
globals.stdio.stderrWrite('error: $message');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
abstract class UnpackDarwin extends Target {
|
||||||
|
const UnpackDarwin();
|
||||||
|
|
||||||
|
/// Copies the [framework] artifact using `rsync` to the [environment.outputDir].
|
||||||
|
/// Throws an error if copy fails.
|
||||||
|
@protected
|
||||||
|
Future<void> copyFramework(
|
||||||
|
Environment environment, {
|
||||||
|
EnvironmentType? environmentType,
|
||||||
|
TargetPlatform? targetPlatform,
|
||||||
|
required Artifact framework,
|
||||||
|
required BuildMode buildMode,
|
||||||
|
}) async {
|
||||||
|
final String basePath = environment.artifacts.getArtifactPath(
|
||||||
|
framework,
|
||||||
|
platform: targetPlatform,
|
||||||
|
mode: buildMode,
|
||||||
|
environmentType: environmentType,
|
||||||
|
);
|
||||||
|
|
||||||
|
final ProcessResult result = await environment.processManager.run(<String>[
|
||||||
|
'rsync',
|
||||||
|
'-av',
|
||||||
|
'--delete',
|
||||||
|
'--filter',
|
||||||
|
'- .DS_Store/',
|
||||||
|
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
|
||||||
|
basePath,
|
||||||
|
environment.outputDir.path,
|
||||||
|
]);
|
||||||
|
if (result.exitCode != 0) {
|
||||||
|
throw Exception(
|
||||||
|
'Failed to copy framework (exit ${result.exitCode}:\n'
|
||||||
|
'${result.stdout}\n---\n${result.stderr}',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Verifies and destructively thins the framework binary found at [frameworkBinaryPath]
|
||||||
|
/// to include only the architectures specified in [archs].
|
||||||
|
///
|
||||||
|
/// [archs] should be a space separated list passed from Xcode containing one or
|
||||||
|
/// more architectures (e.g. "x86_64 arm64", "arm64", "x86_64").
|
||||||
|
///
|
||||||
|
/// Throws an error if the binary does not contain the [archs] or fails to thin.
|
||||||
|
@protected
|
||||||
|
Future<void> thinFramework(
|
||||||
|
Environment environment,
|
||||||
|
String frameworkBinaryPath,
|
||||||
|
String archs,
|
||||||
|
) async {
|
||||||
|
final List<String> archList = archs.split(' ').toList();
|
||||||
|
final ProcessResult infoResult = await environment.processManager.run(<String>[
|
||||||
|
'lipo',
|
||||||
|
'-info',
|
||||||
|
frameworkBinaryPath,
|
||||||
|
]);
|
||||||
|
final String lipoInfo = infoResult.stdout as String;
|
||||||
|
|
||||||
|
final ProcessResult verifyResult = await environment.processManager.run(<String>[
|
||||||
|
'lipo',
|
||||||
|
frameworkBinaryPath,
|
||||||
|
'-verify_arch',
|
||||||
|
...archList,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (verifyResult.exitCode != 0) {
|
||||||
|
throw Exception(
|
||||||
|
'Binary $frameworkBinaryPath does not contain architectures "$archs".\n'
|
||||||
|
'\n'
|
||||||
|
'lipo -info:\n'
|
||||||
|
'$lipoInfo',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip thinning for non-fat executables.
|
||||||
|
if (lipoInfo.startsWith('Non-fat file:')) {
|
||||||
|
environment.logger.printTrace('Skipping lipo for non-fat file $frameworkBinaryPath');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Thin in-place.
|
||||||
|
final ProcessResult extractResult = await environment.processManager.run(<String>[
|
||||||
|
'lipo',
|
||||||
|
'-output',
|
||||||
|
frameworkBinaryPath,
|
||||||
|
for (final String arch in archList) ...<String>['-extract', arch],
|
||||||
|
frameworkBinaryPath,
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (extractResult.exitCode != 0) {
|
||||||
|
throw Exception(
|
||||||
|
'Failed to extract architectures "$archs" for $frameworkBinaryPath.\n'
|
||||||
|
'\n'
|
||||||
|
'stderr:\n'
|
||||||
|
'${extractResult.stderr}\n\n'
|
||||||
|
'lipo -info:\n'
|
||||||
|
'$lipoInfo',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -239,7 +239,7 @@ class DebugUniversalFramework extends Target {
|
|||||||
/// This class is abstract to share logic between the three concrete
|
/// This class is abstract to share logic between the three concrete
|
||||||
/// implementations. The shelling out is done to avoid complications with
|
/// implementations. The shelling out is done to avoid complications with
|
||||||
/// preserving special files (e.g., symbolic links) in the framework structure.
|
/// preserving special files (e.g., symbolic links) in the framework structure.
|
||||||
abstract class UnpackIOS extends Target {
|
abstract class UnpackIOS extends UnpackDarwin {
|
||||||
const UnpackIOS();
|
const UnpackIOS();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -271,7 +271,20 @@ abstract class UnpackIOS extends Target {
|
|||||||
if (archs == null) {
|
if (archs == null) {
|
||||||
throw MissingDefineException(kIosArchs, name);
|
throw MissingDefineException(kIosArchs, name);
|
||||||
}
|
}
|
||||||
await _copyFramework(environment, sdkRoot);
|
|
||||||
|
// Copy Flutter framework.
|
||||||
|
final EnvironmentType? environmentType = environmentTypeFromSdkroot(
|
||||||
|
sdkRoot,
|
||||||
|
environment.fileSystem,
|
||||||
|
);
|
||||||
|
await copyFramework(
|
||||||
|
environment,
|
||||||
|
environmentType: environmentType,
|
||||||
|
framework: Artifact.flutterFramework,
|
||||||
|
targetPlatform: TargetPlatform.ios,
|
||||||
|
buildMode: buildMode,
|
||||||
|
);
|
||||||
|
await _copyFrameworkDysm(environment, sdkRoot: sdkRoot, environmentType: environmentType);
|
||||||
|
|
||||||
final File frameworkBinary = environment.outputDir
|
final File frameworkBinary = environment.outputDir
|
||||||
.childDirectory('Flutter.framework')
|
.childDirectory('Flutter.framework')
|
||||||
@ -280,40 +293,15 @@ abstract class UnpackIOS extends Target {
|
|||||||
if (!await frameworkBinary.exists()) {
|
if (!await frameworkBinary.exists()) {
|
||||||
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
|
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
|
||||||
}
|
}
|
||||||
await _thinFramework(environment, frameworkBinaryPath, archs);
|
await thinFramework(environment, frameworkBinaryPath, archs);
|
||||||
await _signFramework(environment, frameworkBinary, buildMode);
|
await _signFramework(environment, frameworkBinary, buildMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _copyFramework(Environment environment, String sdkRoot) async {
|
Future<void> _copyFrameworkDysm(
|
||||||
// Copy Flutter framework.
|
Environment environment, {
|
||||||
final EnvironmentType? environmentType = environmentTypeFromSdkroot(
|
required String sdkRoot,
|
||||||
sdkRoot,
|
EnvironmentType? environmentType,
|
||||||
environment.fileSystem,
|
}) async {
|
||||||
);
|
|
||||||
final String basePath = environment.artifacts.getArtifactPath(
|
|
||||||
Artifact.flutterFramework,
|
|
||||||
platform: TargetPlatform.ios,
|
|
||||||
mode: buildMode,
|
|
||||||
environmentType: environmentType,
|
|
||||||
);
|
|
||||||
|
|
||||||
final ProcessResult result = await environment.processManager.run(<String>[
|
|
||||||
'rsync',
|
|
||||||
'-av',
|
|
||||||
'--delete',
|
|
||||||
'--filter',
|
|
||||||
'- .DS_Store/',
|
|
||||||
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
|
|
||||||
basePath,
|
|
||||||
environment.outputDir.path,
|
|
||||||
]);
|
|
||||||
if (result.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Failed to copy framework (exit ${result.exitCode}:\n'
|
|
||||||
'${result.stdout}\n---\n${result.stderr}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy Flutter framework dSYM (debug symbol) bundle, if present.
|
// Copy Flutter framework dSYM (debug symbol) bundle, if present.
|
||||||
final Directory frameworkDsym = environment.fileSystem.directory(
|
final Directory frameworkDsym = environment.fileSystem.directory(
|
||||||
environment.artifacts.getArtifactPath(
|
environment.artifacts.getArtifactPath(
|
||||||
@ -342,63 +330,6 @@ abstract class UnpackIOS extends Target {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destructively thin Flutter.framework to include only the specified architectures.
|
|
||||||
Future<void> _thinFramework(
|
|
||||||
Environment environment,
|
|
||||||
String frameworkBinaryPath,
|
|
||||||
String archs,
|
|
||||||
) async {
|
|
||||||
final List<String> archList = archs.split(' ').toList();
|
|
||||||
final ProcessResult infoResult = await environment.processManager.run(<String>[
|
|
||||||
'lipo',
|
|
||||||
'-info',
|
|
||||||
frameworkBinaryPath,
|
|
||||||
]);
|
|
||||||
final String lipoInfo = infoResult.stdout as String;
|
|
||||||
|
|
||||||
final ProcessResult verifyResult = await environment.processManager.run(<String>[
|
|
||||||
'lipo',
|
|
||||||
frameworkBinaryPath,
|
|
||||||
'-verify_arch',
|
|
||||||
...archList,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (verifyResult.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Binary $frameworkBinaryPath does not contain architectures "$archs".\n'
|
|
||||||
'\n'
|
|
||||||
'lipo -info:\n'
|
|
||||||
'$lipoInfo',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip thinning for non-fat executables.
|
|
||||||
if (lipoInfo.startsWith('Non-fat file:')) {
|
|
||||||
environment.logger.printTrace('Skipping lipo for non-fat file $frameworkBinaryPath');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thin in-place.
|
|
||||||
final ProcessResult extractResult = await environment.processManager.run(<String>[
|
|
||||||
'lipo',
|
|
||||||
'-output',
|
|
||||||
frameworkBinaryPath,
|
|
||||||
for (final String arch in archList) ...<String>['-extract', arch],
|
|
||||||
...<String>[frameworkBinaryPath],
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (extractResult.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Failed to extract architectures "$archs" for $frameworkBinaryPath.\n'
|
|
||||||
'\n'
|
|
||||||
'stderr:\n'
|
|
||||||
'${extractResult.stderr}\n\n'
|
|
||||||
'lipo -info:\n'
|
|
||||||
'$lipoInfo',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpack the release prebuilt engine framework.
|
/// Unpack the release prebuilt engine framework.
|
||||||
|
@ -32,7 +32,7 @@ import 'native_assets.dart';
|
|||||||
/// * [DebugUnpackMacOS]
|
/// * [DebugUnpackMacOS]
|
||||||
/// * [ProfileUnpackMacOS]
|
/// * [ProfileUnpackMacOS]
|
||||||
/// * [ReleaseUnpackMacOS]
|
/// * [ReleaseUnpackMacOS]
|
||||||
abstract class UnpackMacOS extends Target {
|
abstract class UnpackMacOS extends UnpackDarwin {
|
||||||
const UnpackMacOS();
|
const UnpackMacOS();
|
||||||
|
|
||||||
@override
|
@override
|
||||||
@ -55,30 +55,15 @@ abstract class UnpackMacOS extends Target {
|
|||||||
throw MissingDefineException(kBuildMode, 'unpack_macos');
|
throw MissingDefineException(kBuildMode, 'unpack_macos');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copy Flutter framework.
|
// Copy FlutterMacOS framework.
|
||||||
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
final BuildMode buildMode = BuildMode.fromCliName(buildModeEnvironment);
|
||||||
final String basePath = environment.artifacts.getArtifactPath(
|
await copyFramework(
|
||||||
Artifact.flutterMacOSFramework,
|
environment,
|
||||||
mode: buildMode,
|
framework: Artifact.flutterMacOSFramework,
|
||||||
|
buildMode: buildMode,
|
||||||
);
|
);
|
||||||
final ProcessResult result = environment.processManager.runSync(<String>[
|
|
||||||
'rsync',
|
|
||||||
'-av',
|
|
||||||
'--delete',
|
|
||||||
'--filter',
|
|
||||||
'- .DS_Store/',
|
|
||||||
'--chmod=Du=rwx,Dgo=rx,Fu=rw,Fgo=r',
|
|
||||||
basePath,
|
|
||||||
environment.outputDir.path,
|
|
||||||
]);
|
|
||||||
|
|
||||||
_removeDenylistedFiles(environment.outputDir);
|
_removeDenylistedFiles(environment.outputDir);
|
||||||
if (result.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Failed to copy framework (exit ${result.exitCode}:\n'
|
|
||||||
'${result.stdout}\n---\n${result.stderr}',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
final File frameworkBinary = environment.outputDir
|
final File frameworkBinary = environment.outputDir
|
||||||
.childDirectory('FlutterMacOS.framework')
|
.childDirectory('FlutterMacOS.framework')
|
||||||
@ -90,7 +75,11 @@ abstract class UnpackMacOS extends Target {
|
|||||||
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
|
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
|
||||||
}
|
}
|
||||||
|
|
||||||
await _thinFramework(environment, frameworkBinaryPath);
|
await thinFramework(
|
||||||
|
environment,
|
||||||
|
frameworkBinaryPath,
|
||||||
|
environment.defines[kDarwinArchs] ?? 'x86_64 arm64',
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Files that should not be copied to build output directory if found during framework copy step.
|
/// Files that should not be copied to build output directory if found during framework copy step.
|
||||||
@ -110,51 +99,6 @@ abstract class UnpackMacOS extends Target {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _thinFramework(Environment environment, String frameworkBinaryPath) async {
|
|
||||||
final String archs = environment.defines[kDarwinArchs] ?? 'x86_64 arm64';
|
|
||||||
final List<String> archList = archs.split(' ').toList();
|
|
||||||
final ProcessResult infoResult = await environment.processManager.run(<String>[
|
|
||||||
'lipo',
|
|
||||||
'-info',
|
|
||||||
frameworkBinaryPath,
|
|
||||||
]);
|
|
||||||
final String lipoInfo = infoResult.stdout as String;
|
|
||||||
|
|
||||||
final ProcessResult verifyResult = await environment.processManager.run(<String>[
|
|
||||||
'lipo',
|
|
||||||
frameworkBinaryPath,
|
|
||||||
'-verify_arch',
|
|
||||||
...archList,
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (verifyResult.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Binary $frameworkBinaryPath does not contain $archs. Running lipo -info:\n$lipoInfo',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skip thinning for non-fat executables.
|
|
||||||
if (lipoInfo.startsWith('Non-fat file:')) {
|
|
||||||
environment.logger.printTrace('Skipping lipo for non-fat file $frameworkBinaryPath');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Thin in-place.
|
|
||||||
final ProcessResult extractResult = environment.processManager.runSync(<String>[
|
|
||||||
'lipo',
|
|
||||||
'-output',
|
|
||||||
frameworkBinaryPath,
|
|
||||||
for (final String arch in archList) ...<String>['-extract', arch],
|
|
||||||
...<String>[frameworkBinaryPath],
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (extractResult.exitCode != 0) {
|
|
||||||
throw Exception(
|
|
||||||
'Failed to extract $archs for $frameworkBinaryPath.\n${extractResult.stderr}\nRunning lipo -info:\n$lipoInfo',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Unpack the release prebuilt engine framework.
|
/// Unpack the release prebuilt engine framework.
|
||||||
|
@ -286,7 +286,7 @@ void main() {
|
|||||||
(Exception exception) => exception.toString(),
|
(Exception exception) => exception.toString(),
|
||||||
'description',
|
'description',
|
||||||
contains(
|
contains(
|
||||||
'does not contain arm64 x86_64. Running lipo -info:\nArchitectures in the fat file:',
|
'does not contain architectures "arm64 x86_64".\n\nlipo -info:\nArchitectures in the fat file:',
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
Loading…
Reference in New Issue
Block a user