Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions .ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4069,6 +4069,17 @@ targets:
["devicelab", "ios", "mac"]
task_name: microbenchmarks_ios

# TODO(vashworth): Remove after Xcode 15 and iOS 17 are in CI (https://github.com/flutter/flutter/issues/132128)
- name: Mac_ios microbenchmarks_ios_xcode_debug
recipe: devicelab/devicelab_drone
presubmit: false
timeout: 60
properties:
tags: >
["devicelab", "ios", "mac"]
task_name: microbenchmarks_ios_xcode_debug
bringup: true

- name: Mac_ios native_assets_ios_simulator
recipe: devicelab/devicelab_drone
presubmit: false
Expand Down
1 change: 1 addition & 0 deletions TESTOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@
/dev/devicelab/bin/tasks/large_image_changer_perf_ios.dart @zanderso @flutter/engine
/dev/devicelab/bin/tasks/macos_chrome_dev_mode.dart @zanderso @flutter/tool
/dev/devicelab/bin/tasks/microbenchmarks_ios.dart @cyanglaz @flutter/engine
/dev/devicelab/bin/tasks/microbenchmarks_ios_xcode_debug.dart @vashworth @flutter/engine
/dev/devicelab/bin/tasks/native_assets_ios_simulator.dart @dacoharkes @flutter/ios
/dev/devicelab/bin/tasks/native_assets_ios.dart @dacoharkes @flutter/ios
/dev/devicelab/bin/tasks/native_platform_view_ui_tests_ios.dart @hellohuanlin @flutter/ios
Expand Down
21 changes: 21 additions & 0 deletions dev/devicelab/bin/tasks/microbenchmarks_ios_xcode_debug.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// 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 'package:flutter_devicelab/framework/devices.dart';
import 'package:flutter_devicelab/framework/framework.dart';
import 'package:flutter_devicelab/tasks/microbenchmarks.dart';

/// Runs microbenchmarks on iOS.
Future<void> main() async {
// XcodeDebug workflow is used for CoreDevices (iOS 17+ and Xcode 15+). Use
// FORCE_XCODE_DEBUG environment variable to force the use of XcodeDebug
// workflow in CI to test from older versions since devicelab has not yet been
// updated to iOS 17 and Xcode 15.
deviceOperatingSystem = DeviceOperatingSystem.ios;
await task(createMicrobenchmarkTask(
environment: <String, String>{
'FORCE_XCODE_DEBUG': 'true',
},
));
}
6 changes: 6 additions & 0 deletions dev/devicelab/lib/microbenchmarks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ Future<Map<String, double>> readJsonResults(Process process) {
// See https://github.com/flutter/flutter/issues/19208
process.stdin.write('q');
await process.stdin.flush();

// Give the process a couple of seconds to exit and run shutdown hooks
// before sending kill signal.
// TODO(fujino): https://github.com/flutter/flutter/issues/134566
await Future<void>.delayed(const Duration(seconds: 2));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

has this been an issue, or are you adding this just in case?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

actually, reading the comment from lines 61-64, I actually fixed that in #99871.

Were there particular shutdown hooks you were concerned about?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was an issue when I ran the test locally because the shutdown hook which kills Xcode wasn't being triggered because the kill signal was sent before the q input could even be processed.

I don't know if it's been an issue in the past

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

so actually, do we run the shutdownHooks when we send q? Let me check.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, shutdown hooks are run when we send q.

It seems like shutdown hooks aren't run when you send a sigint kill signal

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

well, this would be a serious bug, but anyway, this is fine as is for now, i'll file a tracking issue for us not handling SIGINT correctly.


// Also send a kill signal in case the `q` above didn't work.
process.kill(ProcessSignal.sigint);
try {
Expand Down
7 changes: 5 additions & 2 deletions dev/devicelab/lib/tasks/microbenchmarks.dart
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,10 @@ import '../microbenchmarks.dart';

/// Creates a device lab task that runs benchmarks in
/// `dev/benchmarks/microbenchmarks` reports results to the dashboard.
TaskFunction createMicrobenchmarkTask({bool? enableImpeller}) {
TaskFunction createMicrobenchmarkTask({
bool? enableImpeller,
Map<String, String> environment = const <String, String>{},
}) {
return () async {
final Device device = await devices.workingDevice;
await device.unlock();
Expand All @@ -41,9 +44,9 @@ TaskFunction createMicrobenchmarkTask({bool? enableImpeller}) {
return startFlutter(
'run',
options: options,
environment: environment,
);
});

return readJsonResults(flutterProcess);
}

Expand Down
30 changes: 29 additions & 1 deletion packages/flutter_tools/lib/src/ios/devices.dart
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import 'ios_deploy.dart';
import 'ios_workflow.dart';
import 'iproxy.dart';
import 'mac.dart';
import 'xcode_build_settings.dart';
import 'xcode_debug.dart';
import 'xcodeproj.dart';

Expand Down Expand Up @@ -500,7 +501,6 @@ class IOSDevice extends Device {
targetOverride: mainPath,
activeArch: cpuArchitecture,
deviceID: id,
isCoreDevice: isCoreDevice || forceXcodeDebugWorkflow,
);
if (!buildResult.success) {
_logger.printError('Could not build the precompiled application for the device.');
Expand Down Expand Up @@ -551,6 +551,7 @@ class IOSDevice extends Device {
debuggingOptions: debuggingOptions,
package: package,
launchArguments: launchArguments,
mainPath: mainPath,
discoveryTimeout: discoveryTimeout,
shutdownHooks: shutdownHooks ?? globals.shutdownHooks,
) ? 0 : 1;
Expand Down Expand Up @@ -784,6 +785,7 @@ class IOSDevice extends Device {
required DebuggingOptions debuggingOptions,
required IOSApp package,
required List<String> launchArguments,
required String? mainPath,
required ShutdownHooks shutdownHooks,
@visibleForTesting Duration? discoveryTimeout,
}) async {
Expand Down Expand Up @@ -822,6 +824,7 @@ class IOSDevice extends Device {
});

XcodeDebugProject debugProject;
final FlutterProject flutterProject = FlutterProject.current();

if (package is PrebuiltIOSApp) {
debugProject = await _xcodeDebug.createXcodeProjectWithCustomBundle(
Expand All @@ -830,6 +833,19 @@ class IOSDevice extends Device {
verboseLogging: _logger.isVerbose,
);
} else if (package is BuildableIOSApp) {
// Before installing/launching/debugging with Xcode, update the build
// settings to use a custom configuration build directory so Xcode
// knows where to find the app bundle to launch.
final Directory bundle = _fileSystem.directory(
package.deviceBundlePath,
);
await updateGeneratedXcodeProperties(
project: flutterProject,
buildInfo: debuggingOptions.buildInfo,
targetOverride: mainPath,
configurationBuildDir: bundle.parent.absolute.path,
);

final IosProject project = package.project;
final XcodeProjectInfo? projectInfo = await project.projectInfo();
if (projectInfo == null) {
Expand Down Expand Up @@ -870,6 +886,18 @@ class IOSDevice extends Device {
shutdownHooks.addShutdownHook(() => _xcodeDebug.exit(force: true));
}

if (package is BuildableIOSApp) {
// After automating Xcode, reset the Generated settings to not include
// the custom configuration build directory. This is to prevent
// confusion if the project is later ran via Xcode rather than the
// Flutter CLI.
await updateGeneratedXcodeProperties(
project: flutterProject,
buildInfo: debuggingOptions.buildInfo,
targetOverride: mainPath,
);
}

return debugSuccess;
}
}
Expand Down
2 changes: 0 additions & 2 deletions packages/flutter_tools/lib/src/ios/mac.dart
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,6 @@ Future<XcodeBuildResult> buildXcodeProject({
DarwinArch? activeArch,
bool codesign = true,
String? deviceID,
bool isCoreDevice = false,
bool configOnly = false,
XcodeBuildAction buildAction = XcodeBuildAction.build,
}) async {
Expand Down Expand Up @@ -242,7 +241,6 @@ Future<XcodeBuildResult> buildXcodeProject({
project: project,
targetOverride: targetOverride,
buildInfo: buildInfo,
usingCoreDevice: isCoreDevice,
);
await processPodsIfNeeded(project.ios, getIosBuildDirectory(), buildInfo.mode);
if (configOnly) {
Expand Down
13 changes: 7 additions & 6 deletions packages/flutter_tools/lib/src/ios/xcode_build_settings.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,15 @@ Future<void> updateGeneratedXcodeProperties({
String? targetOverride,
bool useMacOSConfig = false,
String? buildDirOverride,
bool usingCoreDevice = false,
String? configurationBuildDir,
}) async {
final List<String> xcodeBuildSettings = await _xcodeBuildSettingsLines(
project: project,
buildInfo: buildInfo,
targetOverride: targetOverride,
useMacOSConfig: useMacOSConfig,
buildDirOverride: buildDirOverride,
usingCoreDevice: usingCoreDevice,
configurationBuildDir: configurationBuildDir,
);

_updateGeneratedXcodePropertiesFile(
Expand Down Expand Up @@ -145,7 +145,7 @@ Future<List<String>> _xcodeBuildSettingsLines({
String? targetOverride,
bool useMacOSConfig = false,
String? buildDirOverride,
bool usingCoreDevice = false,
String? configurationBuildDir,
}) async {
final List<String> xcodeBuildSettings = <String>[];

Expand Down Expand Up @@ -174,9 +174,10 @@ Future<List<String>> _xcodeBuildSettingsLines({
xcodeBuildSettings.add('FLUTTER_BUILD_NUMBER=$buildNumber');

// CoreDevices in debug and profile mode are launched, but not built, via Xcode.
// Set the BUILD_DIR so Xcode knows where to find the app bundle to launch.
if (usingCoreDevice && !buildInfo.isRelease) {
xcodeBuildSettings.add('BUILD_DIR=${globals.fs.path.absolute(getIosBuildDirectory())}');
// Set the CONFIGURATION_BUILD_DIR so Xcode knows where to find the app
// bundle to launch.
if (configurationBuildDir != null) {
xcodeBuildSettings.add('CONFIGURATION_BUILD_DIR=$configurationBuildDir');
}

final LocalEngineInfo? localEngineInfo = globals.artifacts?.localEngineInfo;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,82 @@ void main() {
Xcode: () => xcode,
});

testUsingContext('updates Generated.xcconfig before and after launch', () async {
final Completer<void> debugStartedCompleter = Completer<void>();
final Completer<void> debugEndedCompleter = Completer<void>();
final IOSDevice iosDevice = setUpIOSDevice(
fileSystem: fileSystem,
processManager: FakeProcessManager.any(),
logger: logger,
artifacts: artifacts,
isCoreDevice: true,
coreDeviceControl: FakeIOSCoreDeviceControl(),
xcodeDebug: FakeXcodeDebug(
expectedProject: XcodeDebugProject(
scheme: 'Runner',
xcodeWorkspace: fileSystem.directory('/ios/Runner.xcworkspace'),
xcodeProject: fileSystem.directory('/ios/Runner.xcodeproj'),
),
expectedDeviceId: '123',
expectedLaunchArguments: <String>['--enable-dart-profiling'],
debugStartedCompleter: debugStartedCompleter,
debugEndedCompleter: debugEndedCompleter,
),
);

setUpIOSProject(fileSystem);
final FlutterProject flutterProject = FlutterProject.fromDirectory(fileSystem.currentDirectory);
final BuildableIOSApp buildableIOSApp = BuildableIOSApp(flutterProject.ios, 'flutter', 'My Super Awesome App.app');
fileSystem.directory('build/ios/Release-iphoneos/My Super Awesome App.app').createSync(recursive: true);

final FakeDeviceLogReader deviceLogReader = FakeDeviceLogReader();

iosDevice.portForwarder = const NoOpDevicePortForwarder();
iosDevice.setLogReader(buildableIOSApp, deviceLogReader);

// Start writing messages to the log reader.
Timer.run(() {
deviceLogReader.addLine('Foo');
deviceLogReader.addLine('The Dart VM service is listening on http://127.0.0.1:456');
});

final Future<LaunchResult> futureLaunchResult = iosDevice.startApp(
buildableIOSApp,
debuggingOptions: DebuggingOptions.enabled(const BuildInfo(
BuildMode.debug,
null,
buildName: '1.2.3',
buildNumber: '4',
treeShakeIcons: false,
)),
platformArgs: <String, Object>{},
);

await debugStartedCompleter.future;

// Validate CoreDevice build settings were used
final File config = fileSystem.directory('ios').childFile('Flutter/Generated.xcconfig');
expect(config.existsSync(), isTrue);

String contents = config.readAsStringSync();
expect(contents, contains('CONFIGURATION_BUILD_DIR=/build/ios/iphoneos'));

debugEndedCompleter.complete();

await futureLaunchResult;

// Validate CoreDevice build settings were removed after launch
contents = config.readAsStringSync();
expect(contents.contains('CONFIGURATION_BUILD_DIR'), isFalse);
}, overrides: <Type, Generator>{
ProcessManager: () => FakeProcessManager.any(),
FileSystem: () => fileSystem,
Logger: () => logger,
Platform: () => macPlatform,
XcodeProjectInterpreter: () => fakeXcodeProjectInterpreter,
Xcode: () => xcode,
});

testUsingContext('fails when Xcode project is not found', () async {
final IOSDevice iosDevice = setUpIOSDevice(
fileSystem: fileSystem,
Expand Down Expand Up @@ -750,20 +826,25 @@ class FakeXcodeDebug extends Fake implements XcodeDebug {
this.expectedProject,
this.expectedDeviceId,
this.expectedLaunchArguments,
this.debugStartedCompleter,
this.debugEndedCompleter,
});

final bool debugSuccess;

final XcodeDebugProject? expectedProject;
final String? expectedDeviceId;
final List<String>? expectedLaunchArguments;
final Completer<void>? debugStartedCompleter;
final Completer<void>? debugEndedCompleter;

@override
Future<bool> debugApp({
required XcodeDebugProject project,
required String deviceId,
required List<String> launchArguments,
}) async {
debugStartedCompleter?.complete();
if (expectedProject != null) {
expect(project.scheme, expectedProject!.scheme);
expect(project.xcodeWorkspace.path, expectedProject!.xcodeWorkspace.path);
Expand All @@ -776,6 +857,7 @@ class FakeXcodeDebug extends Fake implements XcodeDebug {
if (expectedLaunchArguments != null) {
expect(expectedLaunchArguments, launchArguments);
}
await debugEndedCompleter?.future;
return debugSuccess;
}
}
Expand Down
Loading