Unverified Commit 0a58610c authored by jmagman's avatar jmagman Committed by GitHub

Clean ephemeral directories (#37966)

parent c7814d69
...@@ -28,41 +28,38 @@ class CleanCommand extends FlutterCommand { ...@@ -28,41 +28,38 @@ class CleanCommand extends FlutterCommand {
@override @override
Future<FlutterCommandResult> runCommand() async { Future<FlutterCommandResult> runCommand() async {
final FlutterProject flutterProject = FlutterProject.current();
final Directory buildDir = fs.directory(getBuildDirectory()); final Directory buildDir = fs.directory(getBuildDirectory());
_deleteFile(buildDir);
printStatus("Deleting '${buildDir.path}${fs.path.separator}'."); final FlutterProject flutterProject = FlutterProject.current();
if (buildDir.existsSync()) { _deleteFile(flutterProject.dartTool);
try {
buildDir.deleteSync(recursive: true);
} on FileSystemException catch (error) {
if (platform.isWindows) {
_windowsDeleteFailure(buildDir.path);
}
throwToolExit(error.toString());
}
}
printStatus("Deleting '${flutterProject.dartTool.path}${fs.path.separator}'."); final Directory androidEphemeralDirectory = flutterProject.android.ephemeralDirectory;
if (flutterProject.dartTool.existsSync()) { _deleteFile(androidEphemeralDirectory);
final Directory iosEphemeralDirectory = flutterProject.ios.ephemeralDirectory;
_deleteFile(iosEphemeralDirectory);
return const FlutterCommandResult(ExitStatus.success);
}
void _deleteFile(FileSystemEntity file) {
final String path = file.path;
printStatus("Deleting '$path${fs.path.separator}'.");
if (file.existsSync()) {
try { try {
flutterProject.dartTool.deleteSync(recursive: true); file.deleteSync(recursive: true);
} on FileSystemException catch (error) { } on FileSystemException catch (error) {
if (platform.isWindows) { if (platform.isWindows) {
_windowsDeleteFailure(flutterProject.dartTool.path); 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()); throwToolExit(error.toString());
} }
} }
return const FlutterCommandResult(ExitStatus.success);
}
void _windowsDeleteFailure(String path) {
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');
} }
} }
...@@ -293,14 +293,14 @@ class IosProject implements XcodeBasedProject { ...@@ -293,14 +293,14 @@ class IosProject implements XcodeBasedProject {
static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)'; static const String _productBundleIdVariable = r'$(PRODUCT_BUNDLE_IDENTIFIER)';
static const String _hostAppBundleName = 'Runner'; static const String _hostAppBundleName = 'Runner';
Directory get _ephemeralDirectory => parent.directory.childDirectory('.ios'); Directory get ephemeralDirectory => parent.directory.childDirectory('.ios');
Directory get _editableDirectory => parent.directory.childDirectory('ios'); Directory get _editableDirectory => parent.directory.childDirectory('ios');
/// This parent folder of `Runner.xcodeproj`. /// This parent folder of `Runner.xcodeproj`.
Directory get hostAppRoot { Directory get hostAppRoot {
if (!isModule || _editableDirectory.existsSync()) if (!isModule || _editableDirectory.existsSync())
return _editableDirectory; return _editableDirectory;
return _ephemeralDirectory; return ephemeralDirectory;
} }
/// The root directory of the iOS wrapping of Flutter and plugins. This is the /// The root directory of the iOS wrapping of Flutter and plugins. This is the
...@@ -309,7 +309,7 @@ class IosProject implements XcodeBasedProject { ...@@ -309,7 +309,7 @@ class IosProject implements XcodeBasedProject {
/// ///
/// This is the same as [hostAppRoot] except when the project is /// This is the same as [hostAppRoot] except when the project is
/// a Flutter module with an editable host app. /// a Flutter module with an editable host app.
Directory get _flutterLibRoot => isModule ? _ephemeralDirectory : _editableDirectory; Directory get _flutterLibRoot => isModule ? ephemeralDirectory : _editableDirectory;
/// The bundle name of the host app, `Runner.app`. /// The bundle name of the host app, `Runner.app`.
String get hostAppBundleName => '$_hostAppBundleName.app'; String get hostAppBundleName => '$_hostAppBundleName.app';
...@@ -413,17 +413,17 @@ class IosProject implements XcodeBasedProject { ...@@ -413,17 +413,17 @@ class IosProject implements XcodeBasedProject {
void _regenerateFromTemplateIfNeeded() { void _regenerateFromTemplateIfNeeded() {
if (!isModule) if (!isModule)
return; return;
final bool pubspecChanged = isOlderThanReference(entity: _ephemeralDirectory, referenceFile: parent.pubspecFile); final bool pubspecChanged = isOlderThanReference(entity: ephemeralDirectory, referenceFile: parent.pubspecFile);
final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(_ephemeralDirectory); final bool toolingChanged = Cache.instance.isOlderThanToolsStamp(ephemeralDirectory);
if (!pubspecChanged && !toolingChanged) if (!pubspecChanged && !toolingChanged)
return; return;
_deleteIfExistsSync(_ephemeralDirectory); _deleteIfExistsSync(ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), ephemeralDirectory);
// Add ephemeral host app, if a editable host app does not already exist. // Add ephemeral host app, if a editable host app does not already exist.
if (!_editableDirectory.existsSync()) { if (!_editableDirectory.existsSync()) {
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), ephemeralDirectory);
if (hasPlugins(parent)) { if (hasPlugins(parent)) {
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), ephemeralDirectory);
} }
} }
} }
...@@ -432,8 +432,8 @@ class IosProject implements XcodeBasedProject { ...@@ -432,8 +432,8 @@ class IosProject implements XcodeBasedProject {
assert(isModule); assert(isModule);
if (_editableDirectory.existsSync()) if (_editableDirectory.existsSync())
throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.'); throwToolExit('iOS host app is already editable. To start fresh, delete the ios/ folder.');
_deleteIfExistsSync(_ephemeralDirectory); _deleteIfExistsSync(ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'library'), ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _editableDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_ephemeral_cocoapods'), _editableDirectory);
_overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_editable_cocoapods'), _editableDirectory); _overwriteFromTemplate(fs.path.join('module', 'ios', 'host_app_editable_cocoapods'), _editableDirectory);
...@@ -484,15 +484,15 @@ class AndroidProject { ...@@ -484,15 +484,15 @@ class AndroidProject {
Directory get hostAppGradleRoot { Directory get hostAppGradleRoot {
if (!isModule || _editableHostAppDirectory.existsSync()) if (!isModule || _editableHostAppDirectory.existsSync())
return _editableHostAppDirectory; return _editableHostAppDirectory;
return _ephemeralDirectory; return ephemeralDirectory;
} }
/// The Gradle root directory of the Android wrapping of Flutter and plugins. /// The Gradle root directory of the Android wrapping of Flutter and plugins.
/// This is the same as [hostAppGradleRoot] except when the project is /// This is the same as [hostAppGradleRoot] except when the project is
/// a Flutter module with an editable host app. /// a Flutter module with an editable host app.
Directory get _flutterLibGradleRoot => isModule ? _ephemeralDirectory : _editableHostAppDirectory; Directory get _flutterLibGradleRoot => isModule ? ephemeralDirectory : _editableHostAppDirectory;
Directory get _ephemeralDirectory => parent.directory.childDirectory('.android'); Directory get ephemeralDirectory => parent.directory.childDirectory('.android');
Directory get _editableHostAppDirectory => parent.directory.childDirectory('android'); Directory get _editableHostAppDirectory => parent.directory.childDirectory('android');
/// True if the parent Flutter project is a module. /// True if the parent Flutter project is a module.
...@@ -543,8 +543,8 @@ class AndroidProject { ...@@ -543,8 +543,8 @@ class AndroidProject {
_regenerateLibrary(); _regenerateLibrary();
// Add ephemeral host app, if an editable host app does not already exist. // Add ephemeral host app, if an editable host app does not already exist.
if (!_editableHostAppDirectory.existsSync()) { if (!_editableHostAppDirectory.existsSync()) {
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_common'), ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_ephemeral'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'android', 'host_app_ephemeral'), ephemeralDirectory);
} }
} }
if (!hostAppGradleRoot.existsSync()) { if (!hostAppGradleRoot.existsSync()) {
...@@ -554,8 +554,8 @@ class AndroidProject { ...@@ -554,8 +554,8 @@ class AndroidProject {
} }
bool _shouldRegenerateFromTemplate() { bool _shouldRegenerateFromTemplate() {
return isOlderThanReference(entity: _ephemeralDirectory, referenceFile: parent.pubspecFile) return isOlderThanReference(entity: ephemeralDirectory, referenceFile: parent.pubspecFile)
|| Cache.instance.isOlderThanToolsStamp(_ephemeralDirectory); || Cache.instance.isOlderThanToolsStamp(ephemeralDirectory);
} }
Future<void> makeHostAppEditable() async { Future<void> makeHostAppEditable() async {
...@@ -576,10 +576,10 @@ class AndroidProject { ...@@ -576,10 +576,10 @@ class AndroidProject {
Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app'); Directory get pluginRegistrantHost => _flutterLibGradleRoot.childDirectory(isModule ? 'Flutter' : 'app');
void _regenerateLibrary() { void _regenerateLibrary() {
_deleteIfExistsSync(_ephemeralDirectory); _deleteIfExistsSync(ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'library'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'android', 'library'), ephemeralDirectory);
_overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), _ephemeralDirectory); _overwriteFromTemplate(fs.path.join('module', 'android', 'gradle'), ephemeralDirectory);
gradle.injectGradleWrapper(_ephemeralDirectory); gradle.injectGradleWrapper(ephemeralDirectory);
} }
void _overwriteFromTemplate(String path, Directory target) { void _overwriteFromTemplate(String path, Directory target) {
......
...@@ -20,6 +20,8 @@ void main() { ...@@ -20,6 +20,8 @@ void main() {
MockDirectory exampleDirectory; MockDirectory exampleDirectory;
MockDirectory buildDirectory; MockDirectory buildDirectory;
MockDirectory dartToolDirectory; MockDirectory dartToolDirectory;
MockDirectory androidEphemeralDirectory;
MockDirectory iosEphemeralDirectory;
MockFile pubspec; MockFile pubspec;
MockFile examplePubspec; MockFile examplePubspec;
MockPlatform windowsPlatform; MockPlatform windowsPlatform;
...@@ -30,6 +32,8 @@ void main() { ...@@ -30,6 +32,8 @@ void main() {
exampleDirectory = MockDirectory(); exampleDirectory = MockDirectory();
buildDirectory = MockDirectory(); buildDirectory = MockDirectory();
dartToolDirectory = MockDirectory(); dartToolDirectory = MockDirectory();
androidEphemeralDirectory = MockDirectory();
iosEphemeralDirectory = MockDirectory();
pubspec = MockFile(); pubspec = MockFile();
examplePubspec = MockFile(); examplePubspec = MockFile();
windowsPlatform = MockPlatform(); windowsPlatform = MockPlatform();
...@@ -39,6 +43,8 @@ void main() { ...@@ -39,6 +43,8 @@ void main() {
when(pubspec.path).thenReturn('/test/pubspec.yaml'); when(pubspec.path).thenReturn('/test/pubspec.yaml');
when(exampleDirectory.childFile('pubspec.yaml')).thenReturn(examplePubspec); when(exampleDirectory.childFile('pubspec.yaml')).thenReturn(examplePubspec);
when(currentDirectory.childDirectory('.dart_tool')).thenReturn(dartToolDirectory); when(currentDirectory.childDirectory('.dart_tool')).thenReturn(dartToolDirectory);
when(currentDirectory.childDirectory('.android')).thenReturn(androidEphemeralDirectory);
when(currentDirectory.childDirectory('.ios')).thenReturn(iosEphemeralDirectory);
when(examplePubspec.path).thenReturn('/test/example/pubspec.yaml'); when(examplePubspec.path).thenReturn('/test/example/pubspec.yaml');
when(mockFileSystem.isFileSync('/test/pubspec.yaml')).thenReturn(false); when(mockFileSystem.isFileSync('/test/pubspec.yaml')).thenReturn(false);
when(mockFileSystem.isFileSync('/test/example/pubspec.yaml')).thenReturn(false); when(mockFileSystem.isFileSync('/test/example/pubspec.yaml')).thenReturn(false);
...@@ -46,14 +52,18 @@ void main() { ...@@ -46,14 +52,18 @@ void main() {
when(mockFileSystem.path).thenReturn(fs.path); when(mockFileSystem.path).thenReturn(fs.path);
when(buildDirectory.existsSync()).thenReturn(true); when(buildDirectory.existsSync()).thenReturn(true);
when(dartToolDirectory.existsSync()).thenReturn(true); when(dartToolDirectory.existsSync()).thenReturn(true);
when(androidEphemeralDirectory.existsSync()).thenReturn(true);
when(iosEphemeralDirectory.existsSync()).thenReturn(true);
when(windowsPlatform.isWindows).thenReturn(true); when(windowsPlatform.isWindows).thenReturn(true);
}); });
group(CleanCommand, () { group(CleanCommand, () {
testUsingContext('removes build and .dart_tool directories', () async { testUsingContext('removes build and .dart_tool and ephemeral directories', () async {
await CleanCommand().runCommand(); await CleanCommand().runCommand();
verify(buildDirectory.deleteSync(recursive: true)).called(1); verify(buildDirectory.deleteSync(recursive: true)).called(1);
verify(dartToolDirectory.deleteSync(recursive: true)).called(1); verify(dartToolDirectory.deleteSync(recursive: true)).called(1);
verify(androidEphemeralDirectory.deleteSync(recursive: true)).called(1);
verify(iosEphemeralDirectory.deleteSync(recursive: true)).called(1);
}, overrides: <Type, Generator>{ }, overrides: <Type, Generator>{
Config: () => null, Config: () => null,
FileSystem: () => mockFileSystem, FileSystem: () => mockFileSystem,
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment