mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00

When using SwiftPM, we use `flutter assemble` in an Xcode Pre-action to run the `debug_unpack_macos` (or profile/release) target. This target is also later used in a Run Script build phase. Depending on `ARCHS` build setting, the Flutter/FlutterMacOS binary is thinned. In the Run Script build phase, `ARCHS` is filtered to the active arch. However, in the Pre-action it doesn't always filter to the active arch. As a workaround, assume arm64 if the [`NATIVE_ARCH`](https://developer.apple.com/documentation/xcode/build-settings-reference/#NATIVEARCH) is arm, otherwise assume x86_64. Also, this PR adds a define flag `PreBuildAction`, which gives the Pre-action a [unique configuration of defines](fdb74fd3e7/packages/flutter_tools/lib/src/build_system/build_system.dart (L355-L372)
) and therefore a separate filecache from the Run Script build phase filecache. This improves caching so the Run Script build phase action doesn't find missing targets in the filecache. It also uses this flag to skip cleaning up the previous build files. Lastly, skip the Pre-action if the build command is `clean`. Note: For iOS, if [`CodesignIdentity`](14df7be3f9/packages/flutter_tools/bin/xcode_backend.dart (L470-L473)
) is set, the Pre-action and Run Script build phase will not match because the Pre-action does not send the `EXPANDED_CODE_SIGN_IDENTITY` and therefore will codesign it with [`-`](14df7be3f9/packages/flutter_tools/lib/src/build_system/targets/ios.dart (L695)
) instead. This will cause `debug_unpack_macos` to invalidate and rerun every time. A potential solution would be to move [codesigning out of the Run Script build phase](14df7be3f9/packages/flutter_tools/lib/src/build_system/targets/ios.dart (L299)
) to the [Thin Binary build phase](14df7be3f9/packages/flutter_tools/bin/xcode_backend.dart (L204-L257)
) after it's copied into the TARGET_BUILD_DIR, like we do with [macOS](14df7be3f9/packages/flutter_tools/bin/macos_assemble.sh (L179-L183)
).
382 lines
12 KiB
Dart
382 lines
12 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.
|
|
|
|
import 'dart:convert';
|
|
|
|
import 'package:flutter_tools/src/base/file_system.dart';
|
|
import 'package:flutter_tools/src/base/io.dart';
|
|
|
|
import '../src/common.dart';
|
|
import 'test_utils.dart';
|
|
|
|
class SwiftPackageManagerUtils {
|
|
static Future<void> enableSwiftPackageManager(
|
|
String flutterBin,
|
|
String workingDirectory,
|
|
) async {
|
|
final ProcessResult result = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
...getLocalEngineArguments(),
|
|
'config',
|
|
'--enable-swift-package-manager',
|
|
'-v',
|
|
],
|
|
workingDirectory: workingDirectory,
|
|
);
|
|
expect(
|
|
result.exitCode,
|
|
0,
|
|
reason: 'Failed to enable Swift Package Manager: \n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
verbose: true,
|
|
);
|
|
}
|
|
|
|
static Future<void> disableSwiftPackageManager(
|
|
String flutterBin,
|
|
String workingDirectory,
|
|
) async {
|
|
final ProcessResult result = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
...getLocalEngineArguments(),
|
|
'config',
|
|
'--no-enable-swift-package-manager',
|
|
'-v',
|
|
],
|
|
workingDirectory: workingDirectory,
|
|
);
|
|
expect(
|
|
result.exitCode,
|
|
0,
|
|
reason: 'Failed to disable Swift Package Manager: \n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
verbose: true,
|
|
);
|
|
}
|
|
|
|
static Future<String> createApp(
|
|
String flutterBin,
|
|
String workingDirectory, {
|
|
required String platform,
|
|
required String iosLanguage,
|
|
required List<String> options,
|
|
bool usesSwiftPackageManager = false,
|
|
}) async {
|
|
final String appTemplateType = usesSwiftPackageManager ? 'spm' : 'default';
|
|
|
|
final String appName = '${platform}_${iosLanguage}_${appTemplateType}_app';
|
|
final ProcessResult result = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
...getLocalEngineArguments(),
|
|
'create',
|
|
'--org',
|
|
'io.flutter.devicelab',
|
|
'-i',
|
|
iosLanguage,
|
|
...options,
|
|
appName,
|
|
],
|
|
workingDirectory: workingDirectory,
|
|
);
|
|
|
|
expect(
|
|
result.exitCode,
|
|
0,
|
|
reason: 'Failed to create app: \n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
);
|
|
|
|
return fileSystem.path.join(
|
|
workingDirectory,
|
|
appName,
|
|
);
|
|
}
|
|
|
|
static Future<void> buildApp(
|
|
String flutterBin,
|
|
String workingDirectory, {
|
|
required List<String> options,
|
|
List<Pattern>? expectedLines,
|
|
List<String>? unexpectedLines,
|
|
}) async {
|
|
final List<Pattern> remainingExpectedLines = expectedLines ?? <Pattern>[];
|
|
final List<String> unexpectedLinesFound = <String>[];
|
|
final List<String> command = <String>[
|
|
flutterBin,
|
|
...getLocalEngineArguments(),
|
|
'build',
|
|
...options,
|
|
];
|
|
|
|
final ProcessResult result = await processManager.run(
|
|
command,
|
|
workingDirectory: workingDirectory,
|
|
);
|
|
|
|
final List<String> stdout = LineSplitter.split(result.stdout.toString()).toList();
|
|
final List<String> stderr = LineSplitter.split(result.stderr.toString()).toList();
|
|
final List<String> output = stdout + stderr;
|
|
for (final String line in output) {
|
|
// Remove "[ +3 ms] " prefix
|
|
String trimmedLine = line.trim();
|
|
if (trimmedLine.startsWith('[')) {
|
|
final int prefixEndIndex = trimmedLine.indexOf(']');
|
|
if (prefixEndIndex > 0) {
|
|
trimmedLine = trimmedLine
|
|
.substring(prefixEndIndex + 1, trimmedLine.length)
|
|
.trim();
|
|
}
|
|
}
|
|
remainingExpectedLines.remove(trimmedLine);
|
|
remainingExpectedLines.removeWhere((Pattern expectedLine) => trimmedLine.contains(expectedLine));
|
|
if (unexpectedLines != null) {
|
|
if (unexpectedLines.where((String unexpectedLine) => trimmedLine.contains(unexpectedLine)).firstOrNull != null) {
|
|
unexpectedLinesFound.add(trimmedLine);
|
|
}
|
|
}
|
|
}
|
|
expect(
|
|
result.exitCode,
|
|
0,
|
|
reason: 'Failed to build app for "${command.join(' ')}":\n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
);
|
|
expect(
|
|
remainingExpectedLines,
|
|
isEmpty,
|
|
reason: 'Did not find expected lines for "${command.join(' ')}":\n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
);
|
|
expect(
|
|
unexpectedLinesFound,
|
|
isEmpty,
|
|
reason: 'Found unexpected lines for "${command.join(' ')}":\n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
);
|
|
}
|
|
|
|
static Future<void> cleanApp(String flutterBin, String workingDirectory) async {
|
|
final ProcessResult result = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
...getLocalEngineArguments(),
|
|
'clean',
|
|
],
|
|
workingDirectory: workingDirectory,
|
|
);
|
|
expect(
|
|
result.exitCode,
|
|
0,
|
|
reason: 'Failed to clean app: \n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
);
|
|
}
|
|
|
|
static Future<SwiftPackageManagerPlugin> createPlugin(
|
|
String flutterBin,
|
|
String workingDirectory, {
|
|
required String platform,
|
|
required String iosLanguage,
|
|
bool usesSwiftPackageManager = false,
|
|
}) async {
|
|
final String dependencyManager = usesSwiftPackageManager ? 'spm' : 'cocoapods';
|
|
|
|
// Create plugin
|
|
final String pluginName = '${platform}_${iosLanguage}_${dependencyManager}_plugin';
|
|
final ProcessResult result = await processManager.run(
|
|
<String>[
|
|
flutterBin,
|
|
...getLocalEngineArguments(),
|
|
'create',
|
|
'--org',
|
|
'io.flutter.devicelab',
|
|
'--template=plugin',
|
|
'--platforms=$platform',
|
|
'-i',
|
|
iosLanguage,
|
|
pluginName,
|
|
],
|
|
workingDirectory: workingDirectory,
|
|
);
|
|
|
|
expect(
|
|
result.exitCode,
|
|
0,
|
|
reason: 'Failed to create plugin: \n'
|
|
'stdout: \n${result.stdout}\n'
|
|
'stderr: \n${result.stderr}\n',
|
|
);
|
|
|
|
final Directory pluginDirectory = fileSystem.directory(
|
|
fileSystem.path.join(workingDirectory, pluginName),
|
|
);
|
|
|
|
return SwiftPackageManagerPlugin(
|
|
pluginName: pluginName,
|
|
pluginPath: pluginDirectory.path,
|
|
platform: platform,
|
|
);
|
|
}
|
|
|
|
static void addDependency({
|
|
required SwiftPackageManagerPlugin plugin,
|
|
required String appDirectoryPath,
|
|
}) {
|
|
final File pubspec = fileSystem.file(
|
|
fileSystem.path.join(appDirectoryPath, 'pubspec.yaml'),
|
|
);
|
|
final String pubspecContent = pubspec.readAsStringSync();
|
|
pubspec.writeAsStringSync(
|
|
pubspecContent.replaceFirst(
|
|
'\ndependencies:\n',
|
|
'\ndependencies:\n ${plugin.pluginName}:\n path: ${plugin.pluginPath}\n',
|
|
),
|
|
);
|
|
}
|
|
|
|
static void disableSwiftPackageManagerByPubspec({
|
|
required String appDirectoryPath,
|
|
}) {
|
|
final File pubspec = fileSystem.file(
|
|
fileSystem.path.join(appDirectoryPath, 'pubspec.yaml'),
|
|
);
|
|
final String pubspecContent = pubspec.readAsStringSync();
|
|
pubspec.writeAsStringSync(
|
|
pubspecContent.replaceFirst(
|
|
'\n# The following section is specific to Flutter packages.\nflutter:\n',
|
|
'\n# The following section is specific to Flutter packages.\nflutter:\n disable-swift-package-manager: true',
|
|
),
|
|
);
|
|
}
|
|
|
|
static SwiftPackageManagerPlugin integrationTestPlugin(String platform) {
|
|
final String flutterRoot = getFlutterRoot();
|
|
return SwiftPackageManagerPlugin(
|
|
platform: platform,
|
|
pluginName:
|
|
(platform == 'ios') ? 'integration_test' : 'integration_test_macos',
|
|
pluginPath: (platform == 'ios')
|
|
? fileSystem.path.join(flutterRoot, 'packages', 'integration_test')
|
|
: fileSystem.path.join(flutterRoot, 'packages', 'integration_test', 'integration_test_macos'),
|
|
);
|
|
}
|
|
|
|
static List<Pattern> expectedLines({
|
|
required String platform,
|
|
required String appDirectoryPath,
|
|
SwiftPackageManagerPlugin? cocoaPodsPlugin,
|
|
SwiftPackageManagerPlugin? swiftPackagePlugin,
|
|
bool swiftPackageMangerEnabled = false,
|
|
bool migrated = false,
|
|
}) {
|
|
final String frameworkName = platform == 'ios' ? 'Flutter' : 'FlutterMacOS';
|
|
final String appPlatformDirectoryPath = fileSystem.path.join(
|
|
appDirectoryPath,
|
|
platform,
|
|
);
|
|
|
|
final List<Pattern> expectedLines = <Pattern>[];
|
|
if (swiftPackageMangerEnabled) {
|
|
expectedLines.addAll(<String>[
|
|
'FlutterGeneratedPluginSwiftPackage: $appPlatformDirectoryPath/Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage',
|
|
"➜ Explicit dependency on target 'FlutterGeneratedPluginSwiftPackage' in project 'FlutterGeneratedPluginSwiftPackage'",
|
|
]);
|
|
}
|
|
if (swiftPackagePlugin != null) {
|
|
// If using a Swift Package plugin, but Swift Package Manager is not enabled, it falls back to being used as a CocoaPods plugin.
|
|
if (swiftPackageMangerEnabled) {
|
|
expectedLines.addAll(<Pattern>[
|
|
RegExp('${swiftPackagePlugin.pluginName}: [/private]*${swiftPackagePlugin.pluginPath}/$platform/${swiftPackagePlugin.pluginName} @ local'),
|
|
"➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project '${swiftPackagePlugin.pluginName}'",
|
|
]);
|
|
} else {
|
|
expectedLines.addAll(<String>[
|
|
'-> Installing ${swiftPackagePlugin.pluginName} (0.0.1)',
|
|
"➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project 'Pods'",
|
|
]);
|
|
}
|
|
}
|
|
if (cocoaPodsPlugin != null) {
|
|
expectedLines.addAll(<String>[
|
|
'Running pod install...',
|
|
'-> Installing $frameworkName (1.0.0)',
|
|
'-> Installing ${cocoaPodsPlugin.pluginName} (0.0.1)',
|
|
"Target 'Pods-Runner' in project 'Pods'",
|
|
"➜ Explicit dependency on target '$frameworkName' in project 'Pods'",
|
|
"➜ Explicit dependency on target '${cocoaPodsPlugin.pluginName}' in project 'Pods'",
|
|
]);
|
|
}
|
|
if (migrated) {
|
|
expectedLines.addAll(<String>[
|
|
'Adding Swift Package Manager integration...',
|
|
'Running pod install...',
|
|
"Target 'Pods-Runner' in project 'Pods'",
|
|
]);
|
|
}
|
|
return expectedLines;
|
|
}
|
|
|
|
static List<String> unexpectedLines({
|
|
required String platform,
|
|
required String appDirectoryPath,
|
|
SwiftPackageManagerPlugin? cocoaPodsPlugin,
|
|
SwiftPackageManagerPlugin? swiftPackagePlugin,
|
|
bool swiftPackageMangerEnabled = false,
|
|
bool migrated = false,
|
|
}) {
|
|
final String frameworkName = platform == 'ios' ? 'Flutter' : 'FlutterMacOS';
|
|
final List<String> unexpectedLines = <String>[];
|
|
if (cocoaPodsPlugin == null && !migrated) {
|
|
unexpectedLines.addAll(<String>[
|
|
'Running pod install...',
|
|
'-> Installing $frameworkName (1.0.0)',
|
|
"Target 'Pods-Runner' in project 'Pods'",
|
|
]);
|
|
}
|
|
if (swiftPackagePlugin != null) {
|
|
if (swiftPackageMangerEnabled) {
|
|
unexpectedLines.addAll(<String>[
|
|
'-> Installing ${swiftPackagePlugin.pluginName} (0.0.1)',
|
|
"➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project 'Pods'",
|
|
]);
|
|
} else {
|
|
unexpectedLines.addAll(<String>[
|
|
'${swiftPackagePlugin.pluginName}: ${swiftPackagePlugin.pluginPath}/$platform/${swiftPackagePlugin.pluginName} @ local',
|
|
"➜ Explicit dependency on target '${swiftPackagePlugin.pluginName}' in project '${swiftPackagePlugin.pluginName}'",
|
|
]);
|
|
}
|
|
}
|
|
if (!migrated) {
|
|
unexpectedLines.addAll(<String>[
|
|
'Adding Swift Package Manager integration...',
|
|
]);
|
|
}
|
|
return unexpectedLines;
|
|
}
|
|
}
|
|
|
|
class SwiftPackageManagerPlugin {
|
|
SwiftPackageManagerPlugin({
|
|
required this.pluginName,
|
|
required this.pluginPath,
|
|
required this.platform,
|
|
});
|
|
|
|
final String pluginName;
|
|
final String pluginPath;
|
|
final String platform;
|
|
String get exampleAppPath => fileSystem.path.join(pluginPath, 'example');
|
|
String get exampleAppPlatformPath => fileSystem.path.join(exampleAppPath, platform);
|
|
}
|