Skip to content

Commit 6259b69

Browse files
authored
feature/clean-a-specific-scheme: Add this-scheme new flag for clean command (#116733)
Co-authored-by: Enguerrand_ARMINJON_MAC_2 <earminjon@sqli.com>
1 parent 902d86e commit 6259b69

3 files changed

Lines changed: 90 additions & 5 deletions

File tree

packages/flutter_tools/lib/src/commands/clean.dart

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'package:meta/meta.dart';
66

77
import '../../src/macos/xcode.dart';
8+
import '../base/common.dart';
89
import '../base/file_system.dart';
910
import '../base/logger.dart';
1011
import '../build_info.dart';
@@ -18,6 +19,10 @@ class CleanCommand extends FlutterCommand {
1819
bool verbose = false,
1920
}) : _verbose = verbose {
2021
requiresPubspecYaml();
22+
argParser.addOption(
23+
'scheme',
24+
help: 'When cleaning Xcode schemes, clean only the specified scheme.',
25+
);
2126
}
2227

2328
final bool _verbose;
@@ -81,11 +86,27 @@ class CleanCommand extends FlutterCommand {
8186
try {
8287
final XcodeProjectInterpreter xcodeProjectInterpreter = globals.xcodeProjectInterpreter!;
8388
final XcodeProjectInfo projectInfo = (await xcodeProjectInterpreter.getInfo(xcodeWorkspace.parent.path))!;
84-
for (final String scheme in projectInfo.schemes) {
89+
if (argResults?.wasParsed('scheme') ?? false) {
90+
final String scheme = argResults!['scheme'] as String;
91+
if (scheme.isEmpty) {
92+
throwToolExit('No scheme was specified for --scheme');
93+
}
94+
if (!projectInfo.schemes.contains(scheme)) {
95+
throwToolExit('Scheme "$scheme" not found in ${projectInfo.schemes}');
96+
}
8597
await xcodeProjectInterpreter.cleanWorkspace(xcodeWorkspace.path, scheme, verbose: _verbose);
98+
} else {
99+
for (final String scheme in projectInfo.schemes) {
100+
await xcodeProjectInterpreter.cleanWorkspace(xcodeWorkspace.path, scheme, verbose: _verbose);
101+
}
86102
}
87103
} on Exception catch (error) {
88-
globals.printTrace('Could not clean Xcode workspace: $error');
104+
final String message = 'Could not clean Xcode workspace: $error';
105+
if (argResults?.wasParsed('scheme') ?? false) {
106+
throwToolExit(message);
107+
} else {
108+
globals.printTrace(message);
109+
}
89110
} finally {
90111
xcodeStatus.stop();
91112
}
@@ -110,8 +131,7 @@ class CleanCommand extends FlutterCommand {
110131
} on FileSystemException catch (error) {
111132
final String path = file.path;
112133
if (globals.platform.isWindows) {
113-
globals.printError(
114-
'Failed to remove $path. '
134+
globals.printError('Failed to remove $path. '
115135
'A program may still be using a file in the directory or the directory itself. '
116136
'To find and stop such a program, see: '
117137
'https://superuser.com/questions/1333118/cant-delete-empty-folder-because-it-is-used');

packages/flutter_tools/test/commands.shard/hermetic/clean_test.dart

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,14 @@
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
44

5+
import 'package:args/command_runner.dart';
56
import 'package:file/memory.dart';
67
import 'package:file_testing/file_testing.dart';
78
import 'package:flutter_tools/src/base/file_system.dart';
89
import 'package:flutter_tools/src/base/logger.dart';
910
import 'package:flutter_tools/src/base/platform.dart';
1011
import 'package:flutter_tools/src/base/version.dart';
12+
import 'package:flutter_tools/src/cache.dart';
1113
import 'package:flutter_tools/src/commands/clean.dart';
1214
import 'package:flutter_tools/src/ios/xcodeproj.dart';
1315
import 'package:flutter_tools/src/macos/xcode.dart';
@@ -17,13 +19,15 @@ import 'package:test/fake.dart';
1719

1820
import '../../src/common.dart';
1921
import '../../src/context.dart';
22+
import '../../src/test_flutter_command_runner.dart';
2023

2124
void main() {
2225
group('clean command', () {
2326
late Xcode xcode;
2427
late FakeXcodeProjectInterpreter xcodeProjectInterpreter;
2528

2629
setUp(() {
30+
Cache.disableLocking();
2731
xcodeProjectInterpreter = FakeXcodeProjectInterpreter();
2832
xcode = Xcode.test(
2933
processManager: FakeProcessManager.any(),
@@ -37,6 +41,7 @@ void main() {
3741

3842
setUp(() {
3943
fs = MemoryFileSystem.test();
44+
fs.file('pubspec.yaml').createSync(recursive: true);
4045

4146
final Directory currentDirectory = fs.currentDirectory;
4247
buildDirectory = currentDirectory.childDirectory('build');
@@ -72,7 +77,30 @@ void main() {
7277

7378
expect(xcodeProjectInterpreter.workspaces, const <CleanWorkspaceCall>[
7479
CleanWorkspaceCall('/ios/Runner.xcworkspace', 'Runner', false),
80+
CleanWorkspaceCall('/ios/Runner.xcworkspace', 'custom-scheme', false),
7581
CleanWorkspaceCall('/macos/Runner.xcworkspace', 'Runner', false),
82+
CleanWorkspaceCall('/macos/Runner.xcworkspace', 'custom-scheme', false),
83+
]);
84+
}, overrides: <Type, Generator>{
85+
FileSystem: () => fs,
86+
ProcessManager: () => FakeProcessManager.any(),
87+
Xcode: () => xcode,
88+
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
89+
});
90+
91+
testUsingContext('$CleanCommand removes a specific xcode scheme --scheme', () async {
92+
setupProjectUnderTest(fs.currentDirectory, true);
93+
// Xcode is installed and version satisfactory.
94+
xcodeProjectInterpreter.isInstalled = true;
95+
xcodeProjectInterpreter.version = Version(1000, 0, 0);
96+
97+
final CleanCommand command = CleanCommand();
98+
final CommandRunner<void> runner = createTestCommandRunner(command);
99+
await runner.run(<String>['clean', '--scheme=custom-scheme']);
100+
101+
expect(xcodeProjectInterpreter.workspaces, <CleanWorkspaceCall>[
102+
const CleanWorkspaceCall('/ios/Runner.xcworkspace', 'custom-scheme', false),
103+
const CleanWorkspaceCall('/macos/Runner.xcworkspace', 'custom-scheme', false),
76104
]);
77105
}, overrides: <Type, Generator>{
78106
FileSystem: () => fs,
@@ -96,6 +124,28 @@ void main() {
96124
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
97125
});
98126

127+
testUsingContext('$CleanCommand throws when given an invalid value for --scheme', () async {
128+
setupProjectUnderTest(fs.currentDirectory, true);
129+
// Xcode is installed and version satisfactory.
130+
xcodeProjectInterpreter.isInstalled = true;
131+
xcodeProjectInterpreter.version = Version(1000, 0, 0);
132+
133+
final CleanCommand command = CleanCommand();
134+
expect(
135+
() => createTestCommandRunner(command).run(<String>['clean', '--scheme']),
136+
throwsUsageException(),
137+
);
138+
expect(
139+
() => createTestCommandRunner(command).run(<String>['clean', '--scheme=unknown']),
140+
throwsToolExit(),
141+
);
142+
}, overrides: <Type, Generator>{
143+
FileSystem: () => fs,
144+
ProcessManager: () => FakeProcessManager.any(),
145+
Xcode: () => xcode,
146+
XcodeProjectInterpreter: () => xcodeProjectInterpreter,
147+
});
148+
99149
testUsingContext('$CleanCommand cleans Xcode verbosely for iOS and macOS', () async {
100150
setupProjectUnderTest(fs.currentDirectory, true);
101151
// Xcode is installed and version satisfactory.
@@ -106,7 +156,9 @@ void main() {
106156

107157
expect(xcodeProjectInterpreter.workspaces, const <CleanWorkspaceCall>[
108158
CleanWorkspaceCall('/ios/Runner.xcworkspace', 'Runner', true),
159+
CleanWorkspaceCall('/ios/Runner.xcworkspace', 'custom-scheme', true),
109160
CleanWorkspaceCall('/macos/Runner.xcworkspace', 'Runner', true),
161+
CleanWorkspaceCall('/macos/Runner.xcworkspace', 'custom-scheme', true),
110162
]);
111163
}, overrides: <Type, Generator>{
112164
FileSystem: () => fs,
@@ -209,7 +261,7 @@ class FakeXcodeProjectInterpreter extends Fake implements XcodeProjectInterprete
209261
return XcodeProjectInfo(
210262
const <String>[],
211263
const <String>[],
212-
<String>['Runner'],
264+
<String>['Runner', 'custom-scheme'],
213265
BufferLogger.test(),
214266
);
215267
}

packages/flutter_tools/test/src/common.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
import 'dart:async';
66

7+
import 'package:args/command_runner.dart';
78
import 'package:file/memory.dart';
89
import 'package:flutter_tools/src/base/common.dart';
910
import 'package:flutter_tools/src/base/context.dart';
@@ -103,6 +104,18 @@ Matcher throwsToolExit({ int? exitCode, Pattern? message }) {
103104
/// Matcher for [ToolExit]s.
104105
final TypeMatcher<ToolExit> _isToolExit = isA<ToolExit>();
105106

107+
/// Matcher for functions that throw [UsageException].
108+
Matcher throwsUsageException({Pattern? message }) {
109+
Matcher matcher = _isUsageException;
110+
if (message != null) {
111+
matcher = allOf(matcher, (UsageException e) => e.message.contains(message));
112+
}
113+
return throwsA(matcher);
114+
}
115+
116+
/// Matcher for [UsageException]s.
117+
final TypeMatcher<UsageException> _isUsageException = isA<UsageException>();
118+
106119
/// Matcher for functions that throw [ProcessException].
107120
Matcher throwsProcessException({ Pattern? message }) {
108121
Matcher matcher = _isProcessException;

0 commit comments

Comments
 (0)