Avoid using watchOS SDK in CI tests (#94190)
This commit is contained in:
parent
7d3ca78682
commit
62dc238b6c
@ -2,10 +2,8 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_devicelab/common.dart';
|
||||
import 'package:flutter_devicelab/framework/framework.dart';
|
||||
import 'package:flutter_devicelab/framework/ios.dart';
|
||||
import 'package:flutter_devicelab/framework/task_result.dart';
|
||||
@ -16,8 +14,6 @@ Future<void> main() async {
|
||||
await task(() async {
|
||||
section('Copy test Flutter App with watchOS Companion');
|
||||
|
||||
String? watchDeviceID;
|
||||
String? phoneDeviceID;
|
||||
final Directory tempDir = Directory.systemTemp
|
||||
.createTempSync('flutter_ios_app_with_extensions_test.');
|
||||
final Directory projectDir =
|
||||
@ -32,11 +28,23 @@ Future<void> main() async {
|
||||
|
||||
section('Create release build');
|
||||
|
||||
// This only builds the iOS app, not the companion watchOS app. The watchOS app
|
||||
// has been removed as a build dependency and is not embedded in the app to avoid
|
||||
// requiring the watchOS being available in CI.
|
||||
// Instead, validate the tool detects that there is a watch companion, and omits
|
||||
// the "-sdk iphoneos" option, which fails to build the watchOS app.
|
||||
// See https://github.com/flutter/flutter/pull/94190.
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
final String buildOutput = await evalFlutter(
|
||||
'build',
|
||||
options: <String>['ios', '--no-codesign', '--release', '--verbose'],
|
||||
);
|
||||
if (!buildOutput.contains('Watch companion app found')) {
|
||||
throw TaskResult.failure('Did not detect watch companion');
|
||||
}
|
||||
if (buildOutput.contains('-sdk iphoneos -destination')) {
|
||||
throw TaskResult.failure('-sdk must be omitted for app with watch companion');
|
||||
}
|
||||
});
|
||||
|
||||
final String appBundle = Directory(path.join(
|
||||
@ -64,228 +72,17 @@ Future<void> main() async {
|
||||
await _checkFlutterFrameworkArchs(appFrameworkPath);
|
||||
await _checkFlutterFrameworkArchs(flutterFrameworkPath);
|
||||
|
||||
// Check the watch extension framework added in the Podfile
|
||||
// is in place with the expected watch archs.
|
||||
final String watchExtensionFrameworkPath = path.join(
|
||||
appBundle,
|
||||
'Watch',
|
||||
'watch.app',
|
||||
'PlugIns',
|
||||
'watch Extension.appex',
|
||||
'Frameworks',
|
||||
'EFQRCode.framework',
|
||||
'EFQRCode',
|
||||
);
|
||||
unawaited(_checkWatchExtensionFrameworkArchs(watchExtensionFrameworkPath));
|
||||
|
||||
section('Clean build');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter('clean');
|
||||
});
|
||||
|
||||
section('Create debug build');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter(
|
||||
'build',
|
||||
options: <String>['ios', '--debug', '--no-codesign', '--verbose'],
|
||||
);
|
||||
});
|
||||
|
||||
checkDirectoryExists(appBundle);
|
||||
await _checkFlutterFrameworkArchs(appFrameworkPath);
|
||||
await _checkFlutterFrameworkArchs(flutterFrameworkPath);
|
||||
unawaited(_checkWatchExtensionFrameworkArchs(watchExtensionFrameworkPath));
|
||||
|
||||
section('Clean build');
|
||||
|
||||
await inDirectory(projectDir, () async {
|
||||
await flutter('clean');
|
||||
});
|
||||
|
||||
section('Run app on simulator device');
|
||||
|
||||
// Xcode 11.4 simctl create makes the runtime argument optional, and defaults to latest.
|
||||
// TODO(jmagman): Remove runtime parsing when devicelab upgrades to Xcode 11.4 https://github.com/flutter/flutter/issues/54889
|
||||
final String availableRuntimes = await eval(
|
||||
'xcrun',
|
||||
<String>[
|
||||
'simctl',
|
||||
'list',
|
||||
'runtimes',
|
||||
],
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
|
||||
// Example simctl list:
|
||||
// == Runtimes ==
|
||||
// iOS 10.3 (10.3.1 - 14E8301) - com.apple.CoreSimulator.SimRuntime.iOS-10-3
|
||||
// iOS 13.4 (13.4 - 17E255) - com.apple.CoreSimulator.SimRuntime.iOS-13-4
|
||||
// tvOS 13.4 (13.4 - 17L255) - com.apple.CoreSimulator.SimRuntime.tvOS-13-4
|
||||
// watchOS 6.2 (6.2 - 17T256) - com.apple.CoreSimulator.SimRuntime.watchOS-6-2
|
||||
String? iOSSimRuntime;
|
||||
String? watchSimRuntime;
|
||||
|
||||
final RegExp iOSRuntimePattern = RegExp(r'iOS .*\) - (.*)');
|
||||
final RegExp watchOSRuntimePattern = RegExp(r'watchOS .*\) - (.*)');
|
||||
|
||||
for (final String runtime in LineSplitter.split(availableRuntimes)) {
|
||||
// These seem to be in order, so allow matching multiple lines so it grabs
|
||||
// the last (hopefully latest) one.
|
||||
final RegExpMatch? iOSRuntimeMatch = iOSRuntimePattern.firstMatch(runtime);
|
||||
if (iOSRuntimeMatch != null) {
|
||||
iOSSimRuntime = iOSRuntimeMatch.group(1)!.trim();
|
||||
continue;
|
||||
}
|
||||
final RegExpMatch? watchOSRuntimeMatch = watchOSRuntimePattern.firstMatch(runtime);
|
||||
if (watchOSRuntimeMatch != null) {
|
||||
watchSimRuntime = watchOSRuntimeMatch.group(1)!.trim();
|
||||
}
|
||||
}
|
||||
if (iOSSimRuntime == null || watchSimRuntime == null) {
|
||||
String message;
|
||||
if (iOSSimRuntime != null) {
|
||||
message = 'Found "$iOSSimRuntime", but no watchOS simulator runtime found.';
|
||||
} else if (watchSimRuntime != null) {
|
||||
message = 'Found "$watchSimRuntime", but no iOS simulator runtime found.';
|
||||
} else {
|
||||
message = 'watchOS and iOS simulator runtimes not found.';
|
||||
}
|
||||
return TaskResult.failure('$message Available runtimes:\n$availableRuntimes');
|
||||
}
|
||||
|
||||
// Create iOS simulator.
|
||||
phoneDeviceID = await eval(
|
||||
'xcrun',
|
||||
<String>[
|
||||
'simctl',
|
||||
'create',
|
||||
'TestFlutteriPhoneWithWatch',
|
||||
'com.apple.CoreSimulator.SimDeviceType.iPhone-11',
|
||||
iOSSimRuntime,
|
||||
],
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
|
||||
// Create watchOS simulator.
|
||||
watchDeviceID = await eval(
|
||||
'xcrun',
|
||||
<String>[
|
||||
'simctl',
|
||||
'create',
|
||||
'TestFlutterWatch',
|
||||
'com.apple.CoreSimulator.SimDeviceType.Apple-Watch-Series-5-44mm',
|
||||
watchSimRuntime,
|
||||
],
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
|
||||
// Pair watch with phone.
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'pair', watchDeviceID, phoneDeviceID],
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
|
||||
// Boot simulator devices.
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'bootstatus', phoneDeviceID, '-b'],
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'bootstatus', watchDeviceID, '-b'],
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
|
||||
// Start app on simulated device.
|
||||
final Process process = await startProcess(
|
||||
path.join(flutterDirectory.path, 'bin', 'flutter'),
|
||||
<String>['run', '-d', phoneDeviceID],
|
||||
workingDirectory: projectDir.path);
|
||||
|
||||
process.stdout
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
print('stdout: $line');
|
||||
// Wait for app startup to complete and quit immediately afterwards.
|
||||
if (line.startsWith('An Observatory debugger')) {
|
||||
process.stdin.write('q');
|
||||
}
|
||||
});
|
||||
process.stderr
|
||||
.transform<String>(utf8.decoder)
|
||||
.transform<String>(const LineSplitter())
|
||||
.listen((String line) {
|
||||
print('stderr: $line');
|
||||
});
|
||||
|
||||
final int exitCode = await process.exitCode;
|
||||
|
||||
if (exitCode != 0) {
|
||||
return TaskResult.failure(
|
||||
'Failed to start flutter iOS app with WatchOS companion on simulated device.');
|
||||
}
|
||||
|
||||
final String simulatorAppBundle = Directory(path.join(
|
||||
projectDir.path,
|
||||
'build',
|
||||
'ios',
|
||||
'iphonesimulator',
|
||||
'Runner.app',
|
||||
)).path;
|
||||
|
||||
checkDirectoryExists(simulatorAppBundle);
|
||||
checkFileExists(path.join(
|
||||
simulatorAppBundle,
|
||||
'Frameworks',
|
||||
'App.framework',
|
||||
'App',
|
||||
));
|
||||
checkFileExists(path.join(
|
||||
simulatorAppBundle,
|
||||
'Frameworks',
|
||||
'Flutter.framework',
|
||||
'Flutter',
|
||||
));
|
||||
|
||||
return TaskResult.success(null);
|
||||
} catch (e) {
|
||||
return TaskResult.failure(e.toString());
|
||||
} finally {
|
||||
rmTree(tempDir);
|
||||
// Delete simulator devices
|
||||
if (watchDeviceID != null && watchDeviceID != '') {
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'shutdown', watchDeviceID],
|
||||
canFail: true,
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'delete', watchDeviceID],
|
||||
canFail: true,
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
}
|
||||
if (phoneDeviceID != null && phoneDeviceID != '') {
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'shutdown', phoneDeviceID],
|
||||
canFail: true,
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
await eval(
|
||||
'xcrun',
|
||||
<String>['simctl', 'delete', phoneDeviceID],
|
||||
canFail: true,
|
||||
workingDirectory: flutterDirectory.path,
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -302,15 +99,3 @@ Future<void> _checkFlutterFrameworkArchs(String frameworkPath) async {
|
||||
throw TaskResult.failure('$frameworkPath x86_64 architecture unexpectedly present');
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _checkWatchExtensionFrameworkArchs(String frameworkPath) async {
|
||||
checkFileExists(frameworkPath);
|
||||
final String archs = await fileType(frameworkPath);
|
||||
if (!archs.contains('armv7k')) {
|
||||
throw TaskResult.failure('$frameworkPath armv7k architecture missing');
|
||||
}
|
||||
|
||||
if (!archs.contains('arm64_32')) {
|
||||
throw TaskResult.failure('$frameworkPath arm64_32 architecture missing');
|
||||
}
|
||||
}
|
||||
|
@ -18,7 +18,6 @@
|
||||
49C15B60243E340E0025F804 /* ExtensionDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C15B5F243E340E0025F804 /* ExtensionDelegate.swift */; };
|
||||
49C15B62243E340F0025F804 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49C15B61243E340F0025F804 /* Assets.xcassets */; };
|
||||
49C15B65243E340F0025F804 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49C15B64243E340F0025F804 /* Preview Assets.xcassets */; };
|
||||
49C15B69243E340F0025F804 /* watch.app in Embed Watch Content */ = {isa = PBXBuildFile; fileRef = 49C15B4A243E340B0025F804 /* watch.app */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };
|
||||
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
|
||||
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
|
||||
@ -34,13 +33,6 @@
|
||||
remoteGlobalIDString = 49C15B55243E340E0025F804;
|
||||
remoteInfo = "watch Extension";
|
||||
};
|
||||
49C15B67243E340F0025F804 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 97C146E61CF9000F007C117D /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 49C15B49243E340B0025F804;
|
||||
remoteInfo = watch;
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXCopyFilesBuildPhase section */
|
||||
@ -55,17 +47,6 @@
|
||||
name = "Embed App Extensions";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
49C15B73243E340F0025F804 /* Embed Watch Content */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
dstPath = "$(CONTENTS_FOLDER_PATH)/Watch";
|
||||
dstSubfolderSpec = 16;
|
||||
files = (
|
||||
49C15B69243E340F0025F804 /* watch.app in Embed Watch Content */,
|
||||
);
|
||||
name = "Embed Watch Content";
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */ = {
|
||||
isa = PBXCopyFilesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@ -307,13 +288,11 @@
|
||||
97C146EC1CF9000F007C117D /* Resources */,
|
||||
9705A1C41CF9048500538489 /* Embed Frameworks */,
|
||||
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
|
||||
49C15B73243E340F0025F804 /* Embed Watch Content */,
|
||||
DF3DAF4426EF33A40B49B448 /* [CP] Embed Pods Frameworks */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
49C15B68243E340F0025F804 /* PBXTargetDependency */,
|
||||
);
|
||||
name = Runner;
|
||||
productName = Runner;
|
||||
@ -531,11 +510,6 @@
|
||||
target = 49C15B55243E340E0025F804 /* watch Extension */;
|
||||
targetProxy = 49C15B58243E340E0025F804 /* PBXContainerItemProxy */;
|
||||
};
|
||||
49C15B68243E340F0025F804 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 49C15B49243E340B0025F804 /* watch */;
|
||||
targetProxy = 49C15B67243E340F0025F804 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
|
@ -27,8 +27,6 @@
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
</Testables>
|
||||
<MacroExpansion>
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
@ -38,8 +36,8 @@
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</MacroExpansion>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
<Testables>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
@ -61,8 +59,6 @@
|
||||
ReferencedContainer = "container:Runner.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
<AdditionalOptions>
|
||||
</AdditionalOptions>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Profile"
|
||||
|
Loading…
x
Reference in New Issue
Block a user