Clean Xcode workspace during flutter clean (#38992)
diff --git a/packages/flutter_tools/lib/src/commands/clean.dart b/packages/flutter_tools/lib/src/commands/clean.dart index 7121134..5603c28 100644 --- a/packages/flutter_tools/lib/src/commands/clean.dart +++ b/packages/flutter_tools/lib/src/commands/clean.dart
@@ -4,11 +4,15 @@ import 'dart:async'; -import '../base/common.dart'; +import 'package:meta/meta.dart'; + import '../base/file_system.dart'; +import '../base/logger.dart'; import '../base/platform.dart'; import '../build_info.dart'; import '../globals.dart'; +import '../ios/xcodeproj.dart'; +import '../macos/xcode.dart'; import '../project.dart'; import '../runner/flutter_command.dart'; @@ -28,38 +32,70 @@ @override Future<FlutterCommandResult> runCommand() async { - final Directory buildDir = fs.directory(getBuildDirectory()); - _deleteFile(buildDir); - + // Clean Xcode to remove intermediate DerivedData artifacts. + // Do this before removing ephemeral directory, which would delete the xcworkspace. final FlutterProject flutterProject = FlutterProject.current(); - _deleteFile(flutterProject.dartTool); + if (xcode.isInstalledAndMeetsVersionCheck) { + await _cleanXcode(flutterProject.ios); + await _cleanXcode(flutterProject.macos); + } + + final Directory buildDir = fs.directory(getBuildDirectory()); + deleteFile(buildDir); + + deleteFile(flutterProject.dartTool); final Directory androidEphemeralDirectory = flutterProject.android.ephemeralDirectory; - _deleteFile(androidEphemeralDirectory); + deleteFile(androidEphemeralDirectory); final Directory iosEphemeralDirectory = flutterProject.ios.ephemeralDirectory; - _deleteFile(iosEphemeralDirectory); + deleteFile(iosEphemeralDirectory); + + final Directory macosEphemeralDirectory = flutterProject.macos.ephemeralDirectory; + deleteFile(macosEphemeralDirectory); return const FlutterCommandResult(ExitStatus.success); } - void _deleteFile(FileSystemEntity file) { - final String path = file.path; - printStatus("Deleting '$path${fs.path.separator}'."); - if (file.existsSync()) { - try { - file.deleteSync(recursive: true); - } on FileSystemException catch (error) { - if (platform.isWindows) { - printError( - 'Failed to remove $path. ' + Future<void> _cleanXcode(XcodeBasedProject xcodeProject) async { + if (!xcodeProject.existsSync()) { + return; + } + final Status xcodeStatus = logger.startProgress('Cleaning Xcode workspace...', timeout: timeoutConfiguration.slowOperation); + try { + final Directory xcodeWorkspace = xcodeProject.xcodeWorkspace; + final XcodeProjectInfo projectInfo = await xcodeProjectInterpreter.getInfo(xcodeWorkspace.parent.path); + for (String scheme in projectInfo.schemes) { + xcodeProjectInterpreter.cleanWorkspace(xcodeWorkspace.path, scheme); + } + } catch (error) { + printTrace('Could not clean Xcode workspace: $error'); + } finally { + xcodeStatus?.stop(); + } + } + + @visibleForTesting + void deleteFile(FileSystemEntity file) { + if (!file.existsSync()) { + return; + } + final Status deletionStatus = logger.startProgress('Deleting ${file.basename}...', timeout: timeoutConfiguration.fastOperation); + try { + file.deleteSync(recursive: true); + } on FileSystemException catch (error) { + final String path = file.path; + if (platform.isWindows) { + printError( + 'Failed to remove $path. ' 'A program may still be using a file in the directory or the directory itself. ' 'To find and stop such a program, see: ' 'https://superuser.com/questions/1333118/cant-delete-empty-folder-because-it-is-used'); - } - throwToolExit(error.toString()); + } else { + printError('Failed to remove $path: $error'); } + } finally { + deletionStatus.stop(); } } } -
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart index 12d7c8e..834e03f 100644 --- a/packages/flutter_tools/lib/src/commands/run.dart +++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -15,7 +15,6 @@ import '../device.dart'; import '../features.dart'; import '../globals.dart'; -import '../macos/xcode.dart'; import '../project.dart'; import '../reporting/reporting.dart'; import '../resident_runner.dart'; @@ -237,19 +236,6 @@ } @override - void printNoConnectedDevices() { - super.printNoConnectedDevices(); - if (getCurrentHostPlatform() == HostPlatform.darwin_x64 && - xcode.isInstalledAndMeetsVersionCheck) { - printStatus(''); - printStatus("Run 'flutter emulators' to list and start any available device emulators."); - printStatus(''); - printStatus('If you expected your device to be detected, please run "flutter doctor" to diagnose'); - printStatus('potential issues, or visit https://flutter.dev/setup/ for troubleshooting tips.'); - } - } - - @override bool get shouldRunPub { // If we are running with a prebuilt application, do not run pub. if (runningWithPrebuiltApplication)
diff --git a/packages/flutter_tools/lib/src/ios/xcodeproj.dart b/packages/flutter_tools/lib/src/ios/xcodeproj.dart index bd9b6d1..ccd5c02 100644 --- a/packages/flutter_tools/lib/src/ios/xcodeproj.dart +++ b/packages/flutter_tools/lib/src/ios/xcodeproj.dart
@@ -266,6 +266,18 @@ } } + void cleanWorkspace(String workspacePath, String scheme) { + runSync(<String>[ + _executable, + '-workspace', + workspacePath, + '-scheme', + scheme, + '-quiet', + 'clean' + ], workingDirectory: fs.currentDirectory.path); + } + Future<XcodeProjectInfo> getInfo(String projectPath) async { final RunResult result = await runCheckedAsync(<String>[ _executable, '-list',
diff --git a/packages/flutter_tools/lib/src/macos/xcode.dart b/packages/flutter_tools/lib/src/macos/xcode.dart index 7b3b915..6fea9ad 100644 --- a/packages/flutter_tools/lib/src/macos/xcode.dart +++ b/packages/flutter_tools/lib/src/macos/xcode.dart
@@ -7,6 +7,7 @@ import '../base/context.dart'; import '../base/file_system.dart'; import '../base/io.dart'; +import '../base/platform.dart'; import '../base/process.dart'; import '../base/process_manager.dart'; import '../ios/xcodeproj.dart'; @@ -17,7 +18,7 @@ Xcode get xcode => context.get<Xcode>(); class Xcode { - bool get isInstalledAndMeetsVersionCheck => isInstalled && isVersionSatisfactory; + bool get isInstalledAndMeetsVersionCheck => platform.isMacOS && isInstalled && isVersionSatisfactory; String _xcodeSelectPath; String get xcodeSelectPath {
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart index dd23247..c82a00d 100644 --- a/packages/flutter_tools/lib/src/runner/flutter_command.dart +++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -555,10 +555,6 @@ return deviceList.single; } - void printNoConnectedDevices() { - printStatus(userMessages.flutterNoConnectedDevices); - } - @protected @mustCallSuper Future<void> validateCommand() async {