Add an ephemeral directory for Linux (#40587)

Moves the generated config into that directory. Matches the structure of
the other desktop projects.

Also fixes #40265
diff --git a/packages/flutter_tools/bin/tool_backend.dart b/packages/flutter_tools/bin/tool_backend.dart
index 1d2ca14..cb3c533 100644
--- a/packages/flutter_tools/bin/tool_backend.dart
+++ b/packages/flutter_tools/bin/tool_backend.dart
@@ -37,7 +37,7 @@
   String cacheDirectory;
   switch (targetPlatform) {
     case 'linux-x64':
-      cacheDirectory = 'linux/flutter';
+      cacheDirectory = 'linux/flutter/ephemeral';
       break;
     case 'windows-x64':
       cacheDirectory = 'windows/flutter/ephemeral';
diff --git a/packages/flutter_tools/lib/src/commands/clean.dart b/packages/flutter_tools/lib/src/commands/clean.dart
index 5603c28..be698a9 100644
--- a/packages/flutter_tools/lib/src/commands/clean.dart
+++ b/packages/flutter_tools/lib/src/commands/clean.dart
@@ -51,9 +51,15 @@
     final Directory iosEphemeralDirectory = flutterProject.ios.ephemeralDirectory;
     deleteFile(iosEphemeralDirectory);
 
+    final Directory linuxEphemeralDirectory = flutterProject.linux.ephemeralDirectory;
+    deleteFile(linuxEphemeralDirectory);
+
     final Directory macosEphemeralDirectory = flutterProject.macos.ephemeralDirectory;
     deleteFile(macosEphemeralDirectory);
 
+    final Directory windowsEphemeralDirectory = flutterProject.windows.ephemeralDirectory;
+    deleteFile(windowsEphemeralDirectory);
+
     return const FlutterCommandResult(ExitStatus.success);
   }
 
diff --git a/packages/flutter_tools/lib/src/linux/build_linux.dart b/packages/flutter_tools/lib/src/linux/build_linux.dart
index fc87fef..2c6a5ef 100644
--- a/packages/flutter_tools/lib/src/linux/build_linux.dart
+++ b/packages/flutter_tools/lib/src/linux/build_linux.dart
@@ -34,7 +34,7 @@
   }
 
   /// Cache flutter configuration files in the linux directory.
-  linuxProject.cacheDirectory.childFile('generated_config')
+  linuxProject.generatedMakeConfigFile
     ..createSync(recursive: true)
     ..writeAsStringSync(buffer.toString());
 
@@ -52,7 +52,7 @@
   final Process process = await processManager.start(<String>[
     'make',
     '-C',
-    linuxProject.editableHostAppDirectory.path,
+    linuxProject.makeFile.parent.path,
   ], runInShell: true);
   final Status status = logger.startProgress(
     'Building Linux application...',
diff --git a/packages/flutter_tools/lib/src/project.dart b/packages/flutter_tools/lib/src/project.dart
index f37fd3f..92aae9f 100644
--- a/packages/flutter_tools/lib/src/project.dart
+++ b/packages/flutter_tools/lib/src/project.dart
@@ -795,16 +795,26 @@
 
   final FlutterProject project;
 
-  Directory get editableHostAppDirectory => project.directory.childDirectory('linux');
+  Directory get _editableDirectory => project.directory.childDirectory('linux');
 
-  // TODO(stuartmorgan): Move to using an ephemeralDirectory to match the other
-  // desktop projects.
-  Directory get cacheDirectory => editableHostAppDirectory.childDirectory('flutter');
+  /// The directory in the project that is managed by Flutter. As much as
+  /// possible, files that are edited by Flutter tooling after initial project
+  /// creation should live here.
+  Directory get managedDirectory => _editableDirectory.childDirectory('flutter');
 
-  bool existsSync() => editableHostAppDirectory.existsSync();
+  /// The subdirectory of [managedDirectory] that contains files that are
+  /// generated on the fly. All generated files that are not intended to be
+  /// checked in should live here.
+  Directory get ephemeralDirectory => managedDirectory.childDirectory('ephemeral');
+
+  bool existsSync() => _editableDirectory.existsSync();
 
   /// The Linux project makefile.
-  File get makeFile => editableHostAppDirectory.childFile('Makefile');
+  File get makeFile => _editableDirectory.childFile('Makefile');
+
+  /// Contains definitions for FLUTTER_ROOT, LOCAL_ENGINE, and more flags for
+  /// the build.
+  File get generatedMakeConfigFile => ephemeralDirectory.childFile('generated_config.mk');
 }
 
 /// The Fuchisa sub project
diff --git a/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart b/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart
index 6fd98a6..dec46e1 100644
--- a/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/build_linux_test.dart
@@ -100,7 +100,7 @@
     await createTestCommandRunner(command).run(
       const <String>['build', 'linux']
     );
-    expect(fs.file('linux/flutter/generated_config').existsSync(), true);
+    expect(fs.file('linux/flutter/ephemeral/generated_config.mk').existsSync(), true);
   }, overrides: <Type, Generator>{
     FileSystem: () => MemoryFileSystem(),
     ProcessManager: () => mockProcessManager,
diff --git a/packages/flutter_tools/test/general.shard/commands/clean_test.dart b/packages/flutter_tools/test/general.shard/commands/clean_test.dart
index 932ea39..271cfab 100644
--- a/packages/flutter_tools/test/general.shard/commands/clean_test.dart
+++ b/packages/flutter_tools/test/general.shard/commands/clean_test.dart
@@ -41,7 +41,9 @@
     projectUnderTest.dartTool.createSync(recursive: true);
     projectUnderTest.android.ephemeralDirectory.createSync(recursive: true);
     projectUnderTest.ios.ephemeralDirectory.createSync(recursive: true);
+    projectUnderTest.linux.ephemeralDirectory.createSync(recursive: true);
     projectUnderTest.macos.ephemeralDirectory.createSync(recursive: true);
+    projectUnderTest.windows.ephemeralDirectory.createSync(recursive: true);
   });
 
   group(CleanCommand, () {
@@ -53,7 +55,9 @@
       expect(projectUnderTest.dartTool.existsSync(), isFalse);
       expect(projectUnderTest.android.ephemeralDirectory.existsSync(), isFalse);
       expect(projectUnderTest.ios.ephemeralDirectory.existsSync(), isFalse);
+      expect(projectUnderTest.linux.ephemeralDirectory.existsSync(), isFalse);
       expect(projectUnderTest.macos.ephemeralDirectory.existsSync(), isFalse);
+      expect(projectUnderTest.windows.ephemeralDirectory.existsSync(), isFalse);
 
       verify(xcodeProjectInterpreter.cleanWorkspace(any, 'Runner')).called(2);
     }, overrides: <Type, Generator>{