Add support for DarwinArchs when assembling macOS App.framework (#81243)
This commit is contained in:
@@ -85,7 +85,8 @@ BuildApp() {
|
||||
assemble \
|
||||
--no-version-check \
|
||||
${performance_measurement_option} \
|
||||
-dTargetPlatform=darwin-x64 \
|
||||
-dTargetPlatform=darwin \
|
||||
-dDarwinArchs=x86_64 \
|
||||
-dTargetFile="${target_path}" \
|
||||
-dBuildMode="${build_mode}" \
|
||||
-dTreeShakeIcons="${TREE_SHAKE_ICONS}" \
|
||||
|
||||
@@ -103,6 +103,14 @@ enum HostArtifact {
|
||||
pubExecutable,
|
||||
}
|
||||
|
||||
// TODO(knopp): Remove once darwin artifacts are universal and moved out of darwin-x64
|
||||
String _enginePlatformDirectoryName(TargetPlatform platform) {
|
||||
if (platform == TargetPlatform.darwin) {
|
||||
return 'darwin-x64';
|
||||
}
|
||||
return getNameForTargetPlatform(platform);
|
||||
}
|
||||
|
||||
String _artifactToFileName(Artifact artifact, [ TargetPlatform platform, BuildMode mode ]) {
|
||||
final String exe = platform == TargetPlatform.windows_x64 ? '.exe' : '';
|
||||
switch (artifact) {
|
||||
@@ -383,7 +391,7 @@ class CachedArtifacts implements Artifacts {
|
||||
return _getAndroidArtifactPath(artifact, platform, mode);
|
||||
case TargetPlatform.ios:
|
||||
return _getIosArtifactPath(artifact, platform, mode, environmentType);
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
case TargetPlatform.linux_x64:
|
||||
case TargetPlatform.linux_arm64:
|
||||
case TargetPlatform.windows_x64:
|
||||
@@ -498,7 +506,7 @@ class CachedArtifacts implements Artifacts {
|
||||
case Artifact.frontendServerSnapshotForEngineDartSdk:
|
||||
case Artifact.icuData:
|
||||
final String engineArtifactsPath = _cache.getArtifactDirectory('engine').path;
|
||||
final String platformDirName = getNameForTargetPlatform(platform);
|
||||
final String platformDirName = _enginePlatformDirectoryName(platform);
|
||||
return _fileSystem.path.join(engineArtifactsPath, platformDirName, _artifactToFileName(artifact, platform, mode));
|
||||
case Artifact.platformKernelDill:
|
||||
return _fileSystem.path.join(_getFlutterPatchedSdkPath(mode), _artifactToFileName(artifact));
|
||||
@@ -514,7 +522,7 @@ class CachedArtifacts implements Artifacts {
|
||||
// TODO(jonahwilliams): remove once debug desktop artifacts are uploaded
|
||||
// under a separate directory from the host artifacts.
|
||||
// https://github.com/flutter/flutter/issues/38935
|
||||
String platformDirName = getNameForTargetPlatform(platform);
|
||||
String platformDirName = _enginePlatformDirectoryName(platform);
|
||||
if (mode == BuildMode.profile || mode == BuildMode.release) {
|
||||
platformDirName = '$platformDirName-${getNameForBuildMode(mode)}';
|
||||
}
|
||||
@@ -532,7 +540,7 @@ class CachedArtifacts implements Artifacts {
|
||||
case Artifact.fontSubset:
|
||||
case Artifact.constFinder:
|
||||
return _cache.getArtifactDirectory('engine')
|
||||
.childDirectory(getNameForTargetPlatform(platform))
|
||||
.childDirectory(_enginePlatformDirectoryName(platform))
|
||||
.childFile(_artifactToFileName(artifact, platform, mode))
|
||||
.path;
|
||||
default:
|
||||
@@ -543,11 +551,11 @@ class CachedArtifacts implements Artifacts {
|
||||
|
||||
String _getEngineArtifactsPath(TargetPlatform platform, [ BuildMode mode ]) {
|
||||
final String engineDir = _cache.getArtifactDirectory('engine').path;
|
||||
final String platformName = getNameForTargetPlatform(platform);
|
||||
final String platformName = _enginePlatformDirectoryName(platform);
|
||||
switch (platform) {
|
||||
case TargetPlatform.linux_x64:
|
||||
case TargetPlatform.linux_arm64:
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
case TargetPlatform.windows_x64:
|
||||
// TODO(jonahwilliams): remove once debug desktop artifacts are uploaded
|
||||
// under a separate directory from the host artifacts.
|
||||
@@ -586,7 +594,7 @@ class CachedArtifacts implements Artifacts {
|
||||
|
||||
TargetPlatform _currentHostPlatform(Platform platform, OperatingSystemUtils operatingSystemUtils) {
|
||||
if (platform.isMacOS) {
|
||||
return TargetPlatform.darwin_x64;
|
||||
return TargetPlatform.darwin;
|
||||
}
|
||||
if (platform.isLinux) {
|
||||
return operatingSystemUtils.hostPlatform == HostPlatform.linux_x64 ?
|
||||
@@ -836,7 +844,7 @@ class CachedLocalEngineArtifacts implements LocalEngineArtifacts {
|
||||
}
|
||||
|
||||
String _genSnapshotPath() {
|
||||
const List<String> clangDirs = <String>['.', 'clang_x64', 'clang_x86', 'clang_i386'];
|
||||
const List<String> clangDirs = <String>['.', 'clang_x64', 'clang_x86', 'clang_i386', 'clang_arm64'];
|
||||
final String genSnapshotName = _artifactToFileName(Artifact.genSnapshot);
|
||||
for (final String clangDir in clangDirs) {
|
||||
final String genSnapshotPath = _fileSystem.path.join(engineOutPath, clangDir, genSnapshotName);
|
||||
|
||||
@@ -154,7 +154,7 @@ class AOTSnapshotter {
|
||||
}
|
||||
|
||||
final String assembly = _fileSystem.path.join(outputDir.path, 'snapshot_assembly.S');
|
||||
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) {
|
||||
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin) {
|
||||
genSnapshotArgs.addAll(<String>[
|
||||
'--snapshot_kind=app-aot-assembly',
|
||||
'--assembly=$assembly',
|
||||
@@ -218,7 +218,7 @@ class AOTSnapshotter {
|
||||
|
||||
// On iOS and macOS, we use Xcode to compile the snapshot into a dynamic library that the
|
||||
// end-developer can link into their app.
|
||||
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin_x64) {
|
||||
if (platform == TargetPlatform.ios || platform == TargetPlatform.darwin) {
|
||||
final RunResult result = await _buildFramework(
|
||||
appleArch: darwinArch,
|
||||
isIOS: platform == TargetPlatform.ios,
|
||||
@@ -311,7 +311,7 @@ class AOTSnapshotter {
|
||||
TargetPlatform.android_arm64,
|
||||
TargetPlatform.android_x64,
|
||||
TargetPlatform.ios,
|
||||
TargetPlatform.darwin_x64,
|
||||
TargetPlatform.darwin,
|
||||
TargetPlatform.linux_x64,
|
||||
TargetPlatform.linux_arm64,
|
||||
TargetPlatform.windows_x64,
|
||||
|
||||
@@ -360,7 +360,7 @@ String? validatedBuildNumberForPlatform(TargetPlatform targetPlatform, String bu
|
||||
return null;
|
||||
}
|
||||
if (targetPlatform == TargetPlatform.ios ||
|
||||
targetPlatform == TargetPlatform.darwin_x64) {
|
||||
targetPlatform == TargetPlatform.darwin) {
|
||||
// See CFBundleVersion at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
final RegExp disallowed = RegExp(r'[^\d\.]');
|
||||
String tmpBuildNumber = buildNumber.replaceAll(disallowed, '');
|
||||
@@ -407,7 +407,7 @@ String? validatedBuildNameForPlatform(TargetPlatform targetPlatform, String buil
|
||||
return null;
|
||||
}
|
||||
if (targetPlatform == TargetPlatform.ios ||
|
||||
targetPlatform == TargetPlatform.darwin_x64) {
|
||||
targetPlatform == TargetPlatform.darwin) {
|
||||
// See CFBundleShortVersionString at https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||
final RegExp disallowed = RegExp(r'[^\d\.]');
|
||||
String tmpBuildName = buildName.replaceAll(disallowed, '');
|
||||
@@ -458,8 +458,7 @@ bool isEmulatorBuildMode(BuildMode mode) {
|
||||
enum TargetPlatform {
|
||||
android,
|
||||
ios,
|
||||
// darwin_arm64 not yet supported, macOS desktop targets run in Rosetta as x86.
|
||||
darwin_x64,
|
||||
darwin,
|
||||
linux_x64,
|
||||
linux_arm64,
|
||||
windows_x64,
|
||||
@@ -536,6 +535,16 @@ DarwinArch getIOSArchForName(String arch) {
|
||||
throw Exception('Unsupported iOS arch name "$arch"');
|
||||
}
|
||||
|
||||
DarwinArch getDarwinArchForName(String arch) {
|
||||
switch (arch) {
|
||||
case 'arm64':
|
||||
return DarwinArch.arm64;
|
||||
case 'x86_64':
|
||||
return DarwinArch.x86_64;
|
||||
}
|
||||
throw Exception('Unsupported MacOS arch name "$arch"');
|
||||
}
|
||||
|
||||
String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch? darwinArch}) {
|
||||
switch (platform) {
|
||||
case TargetPlatform.android_arm:
|
||||
@@ -551,8 +560,11 @@ String getNameForTargetPlatform(TargetPlatform platform, {DarwinArch? darwinArch
|
||||
return 'ios-${getNameForDarwinArch(darwinArch)}';
|
||||
}
|
||||
return 'ios';
|
||||
case TargetPlatform.darwin_x64:
|
||||
return 'darwin-x64';
|
||||
case TargetPlatform.darwin:
|
||||
if (darwinArch != null) {
|
||||
return 'darwin-${getNameForDarwinArch(darwinArch)}';
|
||||
}
|
||||
return 'darwin';
|
||||
case TargetPlatform.linux_x64:
|
||||
return 'linux-x64';
|
||||
case TargetPlatform.linux_arm64:
|
||||
@@ -592,8 +604,12 @@ TargetPlatform? getTargetPlatformForName(String platform) {
|
||||
return TargetPlatform.fuchsia_x64;
|
||||
case 'ios':
|
||||
return TargetPlatform.ios;
|
||||
case 'darwin':
|
||||
// For backward-compatibility and also for Tester, where it must match
|
||||
// host platform name (HostPlatform.darwin_x64)
|
||||
case 'darwin-x64':
|
||||
return TargetPlatform.darwin_x64;
|
||||
case 'darwin-arm':
|
||||
return TargetPlatform.darwin;
|
||||
case 'linux-x64':
|
||||
return TargetPlatform.linux_x64;
|
||||
case 'linux-arm64':
|
||||
@@ -814,7 +830,7 @@ String _getCurrentHostPlatformArchName() {
|
||||
String getNameForTargetPlatformArch(TargetPlatform platform) {
|
||||
switch (platform) {
|
||||
case TargetPlatform.linux_x64:
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
case TargetPlatform.windows_x64:
|
||||
return 'x64';
|
||||
case TargetPlatform.linux_arm64:
|
||||
|
||||
@@ -66,12 +66,20 @@ const String kFileSystemRoots = 'FileSystemRoots';
|
||||
|
||||
/// The define to control what iOS architectures are built for.
|
||||
///
|
||||
/// This is expected to be a comma-separated list of architectures. If not
|
||||
/// This is expected to be a space-delimited list of architectures. If not
|
||||
/// provided, defaults to arm64.
|
||||
///
|
||||
/// The other supported value is armv7, the 32-bit iOS architecture.
|
||||
const String kIosArchs = 'IosArchs';
|
||||
|
||||
/// The define to control what macOS architectures are built for.
|
||||
///
|
||||
/// This is expected to be a space-delimited list of architectures. If not
|
||||
/// provided, defautls to x86_64.
|
||||
///
|
||||
/// Supported values are x86_64 and arm64.
|
||||
const String kDarwinArchs = 'DarwinArchs';
|
||||
|
||||
/// Path to the SDK root to be used as the isysroot.
|
||||
const String kSdkRoot = 'SdkRoot';
|
||||
|
||||
@@ -254,7 +262,7 @@ class KernelSnapshot extends Target {
|
||||
// See https://github.com/flutter/flutter/issues/44724
|
||||
bool forceLinkPlatform;
|
||||
switch (targetPlatform) {
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
case TargetPlatform.windows_x64:
|
||||
case TargetPlatform.linux_x64:
|
||||
forceLinkPlatform = true;
|
||||
|
||||
@@ -67,6 +67,58 @@ abstract class UnpackMacOS extends Target {
|
||||
'${result.stdout}\n---\n${result.stderr}',
|
||||
);
|
||||
}
|
||||
|
||||
final File frameworkBinary = environment.outputDir.childDirectory('FlutterMacOS.framework').childFile('FlutterMacOS');
|
||||
final String frameworkBinaryPath = frameworkBinary.path;
|
||||
if (!frameworkBinary.existsSync()) {
|
||||
throw Exception('Binary $frameworkBinaryPath does not exist, cannot thin');
|
||||
}
|
||||
_thinFramework(environment, frameworkBinaryPath);
|
||||
}
|
||||
|
||||
void _thinFramework(Environment environment, String frameworkBinaryPath) {
|
||||
final String archs = environment.defines[kDarwinArchs] ?? 'x86_64';
|
||||
final List<String> archList = archs.split(' ').toList();
|
||||
final ProcessResult infoResult = environment.processManager.runSync(<String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
frameworkBinaryPath,
|
||||
]);
|
||||
final String lipoInfo = infoResult.stdout as String;
|
||||
|
||||
final ProcessResult verifyResult = environment.processManager.runSync(<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');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -127,6 +179,15 @@ class DebugMacOSFramework extends Target {
|
||||
Future<void> build(Environment environment) async {
|
||||
final File outputFile = environment.fileSystem.file(environment.fileSystem.path.join(
|
||||
environment.buildDir.path, 'App.framework', 'App'));
|
||||
|
||||
final Iterable<DarwinArch> darwinArchs = environment.defines[kDarwinArchs]
|
||||
?.split(' ')
|
||||
?.map(getDarwinArchForName)
|
||||
?? <DarwinArch>[DarwinArch.x86_64];
|
||||
|
||||
final Iterable<String> darwinArchArguments =
|
||||
darwinArchs.expand((DarwinArch arch) => <String>['-arch', getNameForDarwinArch(arch)]);
|
||||
|
||||
outputFile.createSync(recursive: true);
|
||||
final File debugApp = environment.buildDir.childFile('debug_app.cc')
|
||||
..writeAsStringSync(r'''
|
||||
@@ -136,7 +197,7 @@ static const int Moo = 88;
|
||||
'-x',
|
||||
'c',
|
||||
debugApp.path,
|
||||
'-arch', 'x86_64',
|
||||
...darwinArchArguments,
|
||||
'-dynamiclib',
|
||||
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
|
||||
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
|
||||
@@ -177,21 +238,19 @@ class CompileMacOSFramework extends Target {
|
||||
if (buildMode == BuildMode.debug) {
|
||||
throw Exception('precompiled macOS framework only supported in release/profile builds.');
|
||||
}
|
||||
final String buildOutputPath = environment.buildDir.path;
|
||||
final String codeSizeDirectory = environment.defines[kCodeSizeDirectory];
|
||||
final String splitDebugInfo = environment.defines[kSplitDebugInfo];
|
||||
final bool dartObfuscation = environment.defines[kDartObfuscation] == 'true';
|
||||
final List<String> extraGenSnapshotOptions = decodeCommaSeparated(environment.defines, kExtraGenSnapshotOptions);
|
||||
|
||||
if (codeSizeDirectory != null) {
|
||||
final File codeSizeFile = environment.fileSystem
|
||||
.directory(codeSizeDirectory)
|
||||
.childFile('snapshot.${getNameForDarwinArch(DarwinArch.x86_64)}.json');
|
||||
extraGenSnapshotOptions.add('--write-v8-snapshot-profile-to=${codeSizeFile.path}');
|
||||
final File precompilerTraceFile = environment.fileSystem
|
||||
.directory(codeSizeDirectory)
|
||||
.childFile('trace.${getNameForDarwinArch(DarwinArch.x86_64)}.json');
|
||||
extraGenSnapshotOptions.add('--write-v8-snapshot-profile-to=${codeSizeFile.path}');
|
||||
extraGenSnapshotOptions.add('--trace-precompiler-to=${precompilerTraceFile.path}');
|
||||
final TargetPlatform targetPlatform = getTargetPlatformForName(environment.defines[kTargetPlatform]);
|
||||
final List<DarwinArch> darwinArchs = environment.defines[kDarwinArchs]
|
||||
?.split(' ')
|
||||
?.map(getDarwinArchForName)
|
||||
?.toList()
|
||||
?? <DarwinArch>[DarwinArch.x86_64];
|
||||
if (targetPlatform != TargetPlatform.darwin) {
|
||||
throw Exception('compile_macos_framework is only supported for darwin TargetPlatform.');
|
||||
}
|
||||
|
||||
final AOTSnapshotter snapshotter = AOTSnapshotter(
|
||||
@@ -202,19 +261,50 @@ class CompileMacOSFramework extends Target {
|
||||
artifacts: environment.artifacts,
|
||||
processManager: environment.processManager
|
||||
);
|
||||
final int result = await snapshotter.build(
|
||||
bitcode: false,
|
||||
buildMode: buildMode,
|
||||
mainPath: environment.buildDir.childFile('app.dill').path,
|
||||
outputPath: environment.buildDir.path,
|
||||
platform: TargetPlatform.darwin_x64,
|
||||
darwinArch: DarwinArch.x86_64,
|
||||
splitDebugInfo: splitDebugInfo,
|
||||
dartObfuscation: dartObfuscation,
|
||||
extraGenSnapshotOptions: extraGenSnapshotOptions,
|
||||
);
|
||||
if (result != 0) {
|
||||
throw Exception('gen shapshot failed.');
|
||||
|
||||
final List<Future<int>> pending = <Future<int>>[];
|
||||
for (final DarwinArch darwinArch in darwinArchs) {
|
||||
if (codeSizeDirectory != null) {
|
||||
final File codeSizeFile = environment.fileSystem
|
||||
.directory(codeSizeDirectory)
|
||||
.childFile('snapshot.${getNameForDarwinArch(darwinArch)}.json');
|
||||
final File precompilerTraceFile = environment.fileSystem
|
||||
.directory(codeSizeDirectory)
|
||||
.childFile('trace.${getNameForDarwinArch(darwinArch)}.json');
|
||||
extraGenSnapshotOptions.add('--write-v8-snapshot-profile-to=${codeSizeFile.path}');
|
||||
extraGenSnapshotOptions.add('--trace-precompiler-to=${precompilerTraceFile.path}');
|
||||
}
|
||||
|
||||
pending.add(snapshotter.build(
|
||||
bitcode: false,
|
||||
buildMode: buildMode,
|
||||
mainPath: environment.buildDir.childFile('app.dill').path,
|
||||
outputPath: environment.fileSystem.path.join(buildOutputPath, getNameForDarwinArch(darwinArch)),
|
||||
platform: TargetPlatform.darwin,
|
||||
darwinArch: darwinArch,
|
||||
splitDebugInfo: splitDebugInfo,
|
||||
dartObfuscation: dartObfuscation,
|
||||
extraGenSnapshotOptions: extraGenSnapshotOptions,
|
||||
));
|
||||
}
|
||||
|
||||
final List<int> results = await Future.wait(pending);
|
||||
if (results.any((int result) => result != 0)) {
|
||||
throw Exception('AOT snapshotter exited with code ${results.join()}');
|
||||
}
|
||||
|
||||
final String resultPath = environment.fileSystem.path.join(environment.buildDir.path, 'App.framework', 'App');
|
||||
environment.fileSystem.directory(resultPath).parent.createSync(recursive: true);
|
||||
final ProcessResult result = await environment.processManager.run(<String>[
|
||||
'lipo',
|
||||
...darwinArchs.map((DarwinArch iosArch) =>
|
||||
environment.fileSystem.path.join(buildOutputPath, getNameForDarwinArch(iosArch), 'App.framework', 'App')),
|
||||
'-create',
|
||||
'-output',
|
||||
resultPath,
|
||||
]);
|
||||
if (result.exitCode != 0) {
|
||||
throw Exception('lipo exited with code ${result.exitCode}.\n${result.stderr}');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -227,7 +317,7 @@ class CompileMacOSFramework extends Target {
|
||||
List<Source> get inputs => const <Source>[
|
||||
Source.pattern('{BUILD_DIR}/app.dill'),
|
||||
Source.pattern('{FLUTTER_ROOT}/packages/flutter_tools/lib/src/build_system/targets/macos.dart'),
|
||||
Source.artifact(Artifact.genSnapshot, mode: BuildMode.release, platform: TargetPlatform.darwin_x64),
|
||||
Source.artifact(Artifact.genSnapshot, mode: BuildMode.release, platform: TargetPlatform.darwin),
|
||||
];
|
||||
|
||||
@override
|
||||
@@ -291,7 +381,7 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
||||
final Depfile assetDepfile = await copyAssets(
|
||||
environment,
|
||||
assetDirectory,
|
||||
targetPlatform: TargetPlatform.darwin_x64,
|
||||
targetPlatform: TargetPlatform.darwin,
|
||||
);
|
||||
final DepfileService depfileService = DepfileService(
|
||||
fileSystem: environment.fileSystem,
|
||||
@@ -341,9 +431,9 @@ abstract class MacOSBundleFlutterAssets extends Target {
|
||||
// Copy precompiled runtimes.
|
||||
try {
|
||||
final String vmSnapshotData = environment.artifacts.getArtifactPath(Artifact.vmSnapshotData,
|
||||
platform: TargetPlatform.darwin_x64, mode: BuildMode.debug);
|
||||
platform: TargetPlatform.darwin, mode: BuildMode.debug);
|
||||
final String isolateSnapshotData = environment.artifacts.getArtifactPath(Artifact.isolateSnapshotData,
|
||||
platform: TargetPlatform.darwin_x64, mode: BuildMode.debug);
|
||||
platform: TargetPlatform.darwin, mode: BuildMode.debug);
|
||||
environment.fileSystem.file(vmSnapshotData).copySync(
|
||||
assetDirectory.childFile('vm_snapshot_data').path);
|
||||
environment.fileSystem.file(isolateSnapshotData).copySync(
|
||||
@@ -404,8 +494,8 @@ class DebugMacOSBundleFlutterAssets extends MacOSBundleFlutterAssets {
|
||||
List<Source> get inputs => <Source>[
|
||||
...super.inputs,
|
||||
const Source.pattern('{BUILD_DIR}/app.dill'),
|
||||
const Source.artifact(Artifact.isolateSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
|
||||
const Source.artifact(Artifact.vmSnapshotData, platform: TargetPlatform.darwin_x64, mode: BuildMode.debug),
|
||||
const Source.artifact(Artifact.isolateSnapshotData, platform: TargetPlatform.darwin, mode: BuildMode.debug),
|
||||
const Source.artifact(Artifact.vmSnapshotData, platform: TargetPlatform.darwin, mode: BuildMode.debug),
|
||||
];
|
||||
|
||||
@override
|
||||
|
||||
@@ -37,7 +37,7 @@ class BuildBundleCommand extends BuildSubCommand {
|
||||
'android-x86',
|
||||
'android-x64',
|
||||
'ios',
|
||||
'darwin-x64',
|
||||
'darwin',
|
||||
'linux-x64',
|
||||
'linux-arm64',
|
||||
'windows-x64',
|
||||
@@ -90,7 +90,7 @@ class BuildBundleCommand extends BuildSubCommand {
|
||||
}
|
||||
// Check for target platforms that are only allowed via feature flags.
|
||||
switch (platform) {
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
if (!featureFlags.isMacOSEnabled) {
|
||||
throwToolExit('macOS is not a supported target platform.');
|
||||
}
|
||||
|
||||
@@ -84,7 +84,7 @@ class FlutterApplicationPackageFactory extends ApplicationPackageFactory {
|
||||
: IOSApp.fromPrebuiltApp(applicationBinary);
|
||||
case TargetPlatform.tester:
|
||||
return FlutterTesterApp.fromCurrentDirectory(globals.fs);
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
return applicationBinary == null
|
||||
? MacOSApp.fromMacOSProject(FlutterProject.current().macos)
|
||||
: MacOSApp.fromPrebuiltApp(applicationBinary);
|
||||
|
||||
@@ -18,7 +18,7 @@ String flutterMacOSFrameworkDir(BuildMode mode, FileSystem fileSystem,
|
||||
Artifacts artifacts) {
|
||||
final String flutterMacOSFramework = artifacts.getArtifactPath(
|
||||
Artifact.flutterMacOSFramework,
|
||||
platform: TargetPlatform.darwin_x64,
|
||||
platform: TargetPlatform.darwin,
|
||||
mode: mode,
|
||||
);
|
||||
return fileSystem.path
|
||||
|
||||
@@ -50,18 +50,16 @@ class MacOSDevice extends DesktopDevice {
|
||||
@override
|
||||
String get name => 'macOS';
|
||||
|
||||
/// Returns [TargetPlatform.darwin_x64] even on macOS ARM devices.
|
||||
///
|
||||
/// Build system, artifacts rely on Rosetta to translate to x86_64 on ARM.
|
||||
@override
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.darwin_x64;
|
||||
Future<TargetPlatform> get targetPlatform async => TargetPlatform.darwin;
|
||||
|
||||
@override
|
||||
Future<String> get targetPlatformDisplayName async {
|
||||
if (_operatingSystemUtils.hostPlatform == HostPlatform.darwin_arm) {
|
||||
return 'darwin-arm64';
|
||||
} else {
|
||||
return 'darwin-x64';
|
||||
}
|
||||
return super.targetPlatformDisplayName;
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -1447,7 +1447,7 @@ DevelopmentArtifact artifactFromTargetPlatform(TargetPlatform targetPlatform) {
|
||||
return DevelopmentArtifact.web;
|
||||
case TargetPlatform.ios:
|
||||
return DevelopmentArtifact.iOS;
|
||||
case TargetPlatform.darwin_x64:
|
||||
case TargetPlatform.darwin:
|
||||
if (featureFlags.isMacOSEnabled) {
|
||||
return DevelopmentArtifact.macOS;
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ void main() {
|
||||
final AssembleCommand command = AssembleCommand(
|
||||
buildSystem: TestBuildSystem.all(BuildResult(success: true)));
|
||||
final CommandRunner<void> commandRunner = createTestCommandRunner(command);
|
||||
await commandRunner.run(<String>['assemble', '-o Output', '-dTargetPlatform=darwin-x64', 'debug_macos_bundle_flutter_assets']);
|
||||
await commandRunner.run(<String>['assemble', '-o Output', '-dTargetPlatform=darwin', '-dDarwinArchs=x86_64', 'debug_macos_bundle_flutter_assets']);
|
||||
|
||||
expect(await command.requiredArtifacts, <DevelopmentArtifact>{
|
||||
DevelopmentArtifact.macOS,
|
||||
|
||||
@@ -139,7 +139,7 @@ void main() {
|
||||
expect(() => runner.run(<String>[
|
||||
'bundle',
|
||||
'--no-pub',
|
||||
'--target-platform=darwin-x64',
|
||||
'--target-platform=darwin',
|
||||
]), throwsToolExit());
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
@@ -193,7 +193,7 @@ void main() {
|
||||
await runner.run(<String>[
|
||||
'bundle',
|
||||
'--no-pub',
|
||||
'--target-platform=darwin-x64',
|
||||
'--target-platform=darwin',
|
||||
]);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => MemoryFileSystem.test(),
|
||||
|
||||
@@ -178,7 +178,7 @@ void main() {
|
||||
'ios-release',
|
||||
);
|
||||
expect(
|
||||
artifacts.getEngineType(TargetPlatform.darwin_x64),
|
||||
artifacts.getEngineType(TargetPlatform.darwin),
|
||||
'darwin-x64',
|
||||
);
|
||||
});
|
||||
@@ -308,7 +308,7 @@ void main() {
|
||||
'android_debug_unopt',
|
||||
);
|
||||
expect(
|
||||
artifacts.getEngineType(TargetPlatform.darwin_x64),
|
||||
artifacts.getEngineType(TargetPlatform.darwin),
|
||||
'android_debug_unopt',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -271,7 +271,7 @@ void main() {
|
||||
'--sdk-root',
|
||||
artifacts.getArtifactPath(
|
||||
Artifact.flutterPatchedSdkPath,
|
||||
platform: TargetPlatform.darwin_x64,
|
||||
platform: TargetPlatform.darwin,
|
||||
mode: BuildMode.debug,
|
||||
) + '/',
|
||||
'--target=flutter',
|
||||
@@ -288,7 +288,7 @@ void main() {
|
||||
]);
|
||||
|
||||
await const KernelSnapshot().build(androidEnvironment
|
||||
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin_x64)
|
||||
..defines[kTargetPlatform] = getNameForTargetPlatform(TargetPlatform.darwin)
|
||||
..defines[kBuildMode] = getNameForBuildMode(BuildMode.debug)
|
||||
..defines[kTrackWidgetCreation] = 'false'
|
||||
);
|
||||
|
||||
@@ -25,55 +25,76 @@ void main() {
|
||||
FileSystem fileSystem;
|
||||
Artifacts artifacts;
|
||||
FakeProcessManager processManager;
|
||||
File binary;
|
||||
BufferLogger logger;
|
||||
FakeCommand copyFrameworkCommand;
|
||||
FakeCommand lipoInfoNonFatCommand;
|
||||
FakeCommand lipoInfoFatCommand;
|
||||
FakeCommand lipoVerifyX86_64Command;
|
||||
|
||||
setUp(() {
|
||||
processManager = FakeProcessManager.any();
|
||||
processManager = FakeProcessManager.empty();
|
||||
artifacts = Artifacts.test();
|
||||
fileSystem = MemoryFileSystem.test();
|
||||
logger = BufferLogger.test();
|
||||
environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
defines: <String, String>{
|
||||
kBuildMode: 'debug',
|
||||
kTargetPlatform: 'darwin-x64',
|
||||
kTargetPlatform: 'darwin',
|
||||
kDarwinArchs: 'x86_64',
|
||||
},
|
||||
inputs: <String, String>{},
|
||||
artifacts: artifacts,
|
||||
processManager: processManager,
|
||||
logger: BufferLogger.test(),
|
||||
logger: logger,
|
||||
fileSystem: fileSystem,
|
||||
engineVersion: '2'
|
||||
);
|
||||
|
||||
binary = environment.outputDir
|
||||
.childDirectory('FlutterMacOS.framework')
|
||||
.childFile('FlutterMacOS');
|
||||
|
||||
copyFrameworkCommand = FakeCommand(
|
||||
command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'Artifact.flutterMacOSFramework.debug',
|
||||
environment.outputDir.path,
|
||||
],
|
||||
);
|
||||
|
||||
lipoInfoNonFatCommand = FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binary.path,
|
||||
], stdout: 'Non-fat file:');
|
||||
|
||||
lipoInfoFatCommand = FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
'-info',
|
||||
binary.path,
|
||||
], stdout: 'Architectures in the fat file:');
|
||||
|
||||
lipoVerifyX86_64Command = FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
binary.path,
|
||||
'-verify_arch',
|
||||
'x86_64',
|
||||
]);
|
||||
});
|
||||
|
||||
testUsingContext('Copies files to correct cache directory', () async {
|
||||
final Directory outputDir = fileSystem.directory('output');
|
||||
final FakeProcessManager processManager = FakeProcessManager.list(<FakeCommand>[
|
||||
FakeCommand(
|
||||
command: <String>[
|
||||
'rsync',
|
||||
'-av',
|
||||
'--delete',
|
||||
'--filter',
|
||||
'- .DS_Store/',
|
||||
'Artifact.flutterMacOSFramework.debug',
|
||||
outputDir.path,
|
||||
],
|
||||
),
|
||||
binary.createSync(recursive: true);
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyFrameworkCommand,
|
||||
lipoInfoNonFatCommand,
|
||||
lipoVerifyX86_64Command,
|
||||
]);
|
||||
environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
defines: <String, String>{
|
||||
kBuildMode: 'debug',
|
||||
kTargetPlatform: 'darwin-x64',
|
||||
},
|
||||
inputs: <String, String>{},
|
||||
artifacts: artifacts,
|
||||
processManager: processManager,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
engineVersion: '2',
|
||||
outputDir: outputDir,
|
||||
);
|
||||
|
||||
await const DebugUnpackMacOS().build(environment);
|
||||
|
||||
@@ -83,6 +104,81 @@ void main() {
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('thinning fails when framework missing', () async {
|
||||
processManager.addCommand(copyFrameworkCommand);
|
||||
await expectLater(
|
||||
const DebugUnpackMacOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
contains('FlutterMacOS.framework/FlutterMacOS does not exist, cannot thin'),
|
||||
)));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('lipo fails when arch missing from framework', () async {
|
||||
environment.defines[kDarwinArchs] = 'arm64 x86_64';
|
||||
binary.createSync(recursive: true);
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyFrameworkCommand,
|
||||
lipoInfoFatCommand,
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
binary.path,
|
||||
'-verify_arch',
|
||||
'arm64',
|
||||
'x86_64',
|
||||
], exitCode: 1),
|
||||
]);
|
||||
|
||||
await expectLater(
|
||||
const DebugUnpackMacOS().build(environment),
|
||||
throwsA(isA<Exception>().having(
|
||||
(Exception exception) => exception.toString(),
|
||||
'description',
|
||||
contains('does not contain arm64 x86_64. Running lipo -info:\nArchitectures in the fat file:'),
|
||||
)));
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('skips thins framework', () async {
|
||||
binary.createSync(recursive: true);
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyFrameworkCommand,
|
||||
lipoInfoNonFatCommand,
|
||||
lipoVerifyX86_64Command,
|
||||
]);
|
||||
|
||||
await const DebugUnpackMacOS().build(environment);
|
||||
|
||||
expect(logger.traceText, contains('Skipping lipo for non-fat file /FlutterMacOS.framework/FlutterMacOS'));
|
||||
});
|
||||
|
||||
testUsingContext('thins fat framework', () async {
|
||||
binary.createSync(recursive: true);
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
copyFrameworkCommand,
|
||||
lipoInfoFatCommand,
|
||||
lipoVerifyX86_64Command,
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
'-output',
|
||||
binary.path,
|
||||
'-extract',
|
||||
'x86_64',
|
||||
binary.path,
|
||||
]),
|
||||
]);
|
||||
|
||||
await const DebugUnpackMacOS().build(environment);
|
||||
|
||||
expect(processManager, hasNoRemainingExpectations);
|
||||
});
|
||||
|
||||
testUsingContext('debug macOS application fails if App.framework missing', () async {
|
||||
fileSystem.directory(
|
||||
artifacts.getArtifactPath(
|
||||
@@ -113,13 +209,13 @@ void main() {
|
||||
fileSystem.file(
|
||||
artifacts.getArtifactPath(
|
||||
Artifact.vmSnapshotData,
|
||||
platform: TargetPlatform.darwin_x64,
|
||||
platform: TargetPlatform.darwin,
|
||||
mode: BuildMode.debug,
|
||||
)).createSync(recursive: true);
|
||||
fileSystem.file(
|
||||
artifacts.getArtifactPath(
|
||||
Artifact.isolateSnapshotData,
|
||||
platform: TargetPlatform.darwin_x64,
|
||||
platform: TargetPlatform.darwin,
|
||||
mode: BuildMode.debug,
|
||||
)).createSync(recursive: true);
|
||||
fileSystem.file('${environment.buildDir.path}/App.framework/App')
|
||||
@@ -217,4 +313,131 @@ void main() {
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('DebugMacOSFramework creates expected binary with arm64 only arch', () async {
|
||||
environment.defines[kDarwinArchs] = 'arm64';
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun',
|
||||
'clang',
|
||||
'-x',
|
||||
'c',
|
||||
environment.buildDir.childFile('debug_app.cc').path,
|
||||
'-arch',
|
||||
'arm64',
|
||||
'-dynamiclib',
|
||||
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
|
||||
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
|
||||
'-install_name', '@rpath/App.framework/App',
|
||||
'-o',
|
||||
environment.buildDir
|
||||
.childDirectory('App.framework')
|
||||
.childFile('App')
|
||||
.path,
|
||||
]),
|
||||
);
|
||||
|
||||
await const DebugMacOSFramework().build(environment);
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('DebugMacOSFramework creates universal binary', () async {
|
||||
environment.defines[kDarwinArchs] = 'arm64 x86_64';
|
||||
processManager.addCommand(
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun',
|
||||
'clang',
|
||||
'-x',
|
||||
'c',
|
||||
environment.buildDir.childFile('debug_app.cc').path,
|
||||
'-arch',
|
||||
'arm64',
|
||||
'-arch',
|
||||
'x86_64',
|
||||
'-dynamiclib',
|
||||
'-Xlinker', '-rpath', '-Xlinker', '@executable_path/Frameworks',
|
||||
'-Xlinker', '-rpath', '-Xlinker', '@loader_path/Frameworks',
|
||||
'-install_name', '@rpath/App.framework/App',
|
||||
'-o',
|
||||
environment.buildDir
|
||||
.childDirectory('App.framework')
|
||||
.childFile('App')
|
||||
.path,
|
||||
]),
|
||||
);
|
||||
|
||||
await const DebugMacOSFramework().build(environment);
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
|
||||
testUsingContext('CompileMacOSFramework creates universal binary', () async {
|
||||
environment.defines[kDarwinArchs] = 'arm64 x86_64';
|
||||
environment.defines[kBuildMode] = 'release';
|
||||
|
||||
processManager.addCommands(<FakeCommand>[
|
||||
FakeCommand(command: <String>[
|
||||
'Artifact.genSnapshot.TargetPlatform.darwin.release',
|
||||
'--deterministic',
|
||||
'--snapshot_kind=app-aot-assembly',
|
||||
'--assembly=${environment.buildDir.childFile('arm64/snapshot_assembly.S').path}',
|
||||
'--strip',
|
||||
environment.buildDir.childFile('app.dill').path
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'Artifact.genSnapshot.TargetPlatform.darwin.release',
|
||||
'--deterministic',
|
||||
'--snapshot_kind=app-aot-assembly',
|
||||
'--assembly=${environment.buildDir.childFile('x86_64/snapshot_assembly.S').path}',
|
||||
'--strip',
|
||||
environment.buildDir.childFile('app.dill').path
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun', 'cc', '-arch', 'arm64',
|
||||
'-c', environment.buildDir.childFile('arm64/snapshot_assembly.S').path,
|
||||
'-o', environment.buildDir.childFile('arm64/snapshot_assembly.o').path
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun', 'cc', '-arch', 'x86_64',
|
||||
'-c', environment.buildDir.childFile('x86_64/snapshot_assembly.S').path,
|
||||
'-o', environment.buildDir.childFile('x86_64/snapshot_assembly.o').path
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun', 'clang', '-arch', 'arm64', '-dynamiclib', '-Xlinker', '-rpath',
|
||||
'-Xlinker', '@executable_path/Frameworks', '-Xlinker', '-rpath',
|
||||
'-Xlinker', '@loader_path/Frameworks',
|
||||
'-install_name', '@rpath/App.framework/App',
|
||||
'-o', environment.buildDir.childFile('arm64/App.framework/App').path,
|
||||
environment.buildDir.childFile('arm64/snapshot_assembly.o').path
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'xcrun', 'clang', '-arch', 'x86_64', '-dynamiclib', '-Xlinker', '-rpath',
|
||||
'-Xlinker', '@executable_path/Frameworks', '-Xlinker', '-rpath',
|
||||
'-Xlinker', '@loader_path/Frameworks',
|
||||
'-install_name', '@rpath/App.framework/App',
|
||||
'-o', environment.buildDir.childFile('x86_64/App.framework/App').path,
|
||||
environment.buildDir.childFile('x86_64/snapshot_assembly.o').path
|
||||
]),
|
||||
FakeCommand(command: <String>[
|
||||
'lipo',
|
||||
environment.buildDir.childFile('arm64/App.framework/App').path,
|
||||
environment.buildDir.childFile('x86_64/App.framework/App').path,
|
||||
'-create',
|
||||
'-output',
|
||||
environment.buildDir.childFile('App.framework/App').path,
|
||||
]),
|
||||
]);
|
||||
|
||||
await const CompileMacOSFramework().build(environment);
|
||||
expect(processManager.hasRemainingExpectations, isFalse);
|
||||
|
||||
}, overrides: <Type, Generator>{
|
||||
FileSystem: () => fileSystem,
|
||||
ProcessManager: () => processManager,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ void main() {
|
||||
);
|
||||
final MockMacOSApp mockMacOSApp = MockMacOSApp();
|
||||
|
||||
expect(await device.targetPlatform, TargetPlatform.darwin_x64);
|
||||
expect(await device.targetPlatform, TargetPlatform.darwin);
|
||||
expect(device.name, 'macOS');
|
||||
expect(await device.installApp(mockMacOSApp), true);
|
||||
expect(await device.uninstallApp(mockMacOSApp), true);
|
||||
|
||||
Reference in New Issue
Block a user