at master 9.2 kB view raw
1This patch introduces an intermediate Gradle build step to alter the behavior 2of flutter_tools' Gradle project, specifically moving the creation of `build` 3and `.gradle` directories from within the Nix Store to somewhere in `$HOME/.cache/flutter/nix-flutter-tools-gradle/$engineShortRev`. 4 5Without this patch, flutter_tools' Gradle project tries to generate `build` and `.gradle` 6directories within the Nix Store. Resulting in read-only errors when trying to build a 7Flutter Android app at runtime. 8 9This patch takes advantage of the fact settings.gradle takes priority over settings.gradle.kts to build the intermediate Gradle project 10when a Flutter app runs `includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")` 11 12`rootProject.buildFileName = "/dev/null"` so that the intermediate project doesn't use `build.gradle.kts` that's in the same directory. 13 14The intermediate project makes a `settings.gradle` file in `$HOME/.cache/flutter/nix-flutter-tools-gradle/<short engine rev>/` and `includeBuild`s it. 15This Gradle project will build the actual `packages/flutter_tools/gradle` project by setting 16`rootProject.projectDir = new File("$settingsDir")` and `apply from: new File("$settingsDir/settings.gradle.kts")`. 17 18To move `build` to `$HOME/.cache/flutter/nix-flutter-tools-gradle/<short engine rev>/`, we need to set `buildDirectory`. 19To move `.gradle` as well, the `--project-cache-dir` argument must be passed to the Gradle wrapper. 20Changing the `GradleUtils.getExecutable` function signature is a delibarate choice, to ensure that no new unpatched usages slip in. 21--- /dev/null 22+++ b/packages/flutter_tools/gradle/settings.gradle 23@@ -0,0 +1,19 @@ 24+rootProject.buildFileName = "/dev/null" 25+ 26+def engineShortRev = (new File("$settingsDir/../../../bin/internal/engine.version")).text.take(10) 27+def dir = new File("$System.env.HOME/.cache/flutter/nix-flutter-tools-gradle/$engineShortRev") 28+dir.mkdirs() 29+def file = new File(dir, "settings.gradle") 30+ 31+file.text = """ 32+rootProject.projectDir = new File("$settingsDir") 33+apply from: new File("$settingsDir/settings.gradle.kts") 34+ 35+gradle.allprojects { project -> 36+ project.beforeEvaluate { 37+ project.layout.buildDirectory = new File("$dir/build") 38+ } 39+} 40+""" 41+ 42+includeBuild(dir) 43--- a/packages/flutter_tools/gradle/build.gradle.kts 44+++ b/packages/flutter_tools/gradle/build.gradle.kts 45@@ -4,6 +4,11 @@ 46 47 import org.jetbrains.kotlin.gradle.dsl.JvmTarget 48 49+// While flutter_tools runs Gradle with a --project-cache-dir, this startParameter 50+// is not passed correctly to the Kotlin Gradle plugin for some reason, and so 51+// must be set here as well. 52+gradle.startParameter.projectCacheDir = layout.buildDirectory.dir("cache").get().asFile 53+ 54 plugins { 55 `java-gradle-plugin` 56 groovy 57--- a/packages/flutter_tools/lib/src/android/gradle.dart 58+++ b/packages/flutter_tools/lib/src/android/gradle.dart 59@@ -474,9 +474,9 @@ class AndroidGradleBuilder implements AndroidBuilder { 60 // from the local.properties file. 61 updateLocalProperties(project: project, buildInfo: androidBuildInfo.buildInfo); 62 63- final options = <String>[]; 64- 65- final String gradleExecutablePath = _gradleUtils.getExecutable(project); 66+ final [String gradleExecutablePath, ...List<String> options] = _gradleUtils.getExecutable( 67+ project, 68+ ); 69 70 // All automatically created files should exist. 71 if (configOnly) { 72@@ -797,7 +797,7 @@ class AndroidGradleBuilder implements AndroidBuilder { 73 'aar_init_script.gradle', 74 ); 75 final command = <String>[ 76- _gradleUtils.getExecutable(project), 77+ ..._gradleUtils.getExecutable(project), 78 '-I=$initScript', 79 '-Pflutter-root=$flutterRoot', 80 '-Poutput-dir=${outputDirectory.path}', 81@@ -912,6 +912,10 @@ class AndroidGradleBuilder implements AndroidBuilder { 82 final results = <String>[]; 83 84 try { 85+ final [String gradleExecutablePath, ...List<String> options] = _gradleUtils.getExecutable( 86+ project, 87+ ); 88+ 89 exitCode = await _runGradleTask( 90 _kBuildVariantTaskName, 91 preRunTask: () { 92@@ -927,10 +931,10 @@ class AndroidGradleBuilder implements AndroidBuilder { 93 ), 94 ); 95 }, 96- options: const <String>['-q'], 97+ options: <String>[...options, '-q'], 98 project: project, 99 localGradleErrors: gradleErrors, 100- gradleExecutablePath: _gradleUtils.getExecutable(project), 101+ gradleExecutablePath: gradleExecutablePath, 102 outputParser: (String line) { 103 if (_kBuildVariantRegex.firstMatch(line) case final RegExpMatch match) { 104 results.add(match.namedGroup(_kBuildVariantRegexGroupName)!); 105@@ -964,6 +968,10 @@ class AndroidGradleBuilder implements AndroidBuilder { 106 late Stopwatch sw; 107 var exitCode = 1; 108 try { 109+ final [String gradleExecutablePath, ...List<String> options] = _gradleUtils.getExecutable( 110+ project, 111+ ); 112+ 113 exitCode = await _runGradleTask( 114 taskName, 115 preRunTask: () { 116@@ -979,10 +987,10 @@ class AndroidGradleBuilder implements AndroidBuilder { 117 ), 118 ); 119 }, 120- options: <String>['-q', '-PoutputPath=$outputPath'], 121+ options: <String>[...options, '-q', '-PoutputPath=$outputPath'], 122 project: project, 123 localGradleErrors: gradleErrors, 124- gradleExecutablePath: _gradleUtils.getExecutable(project), 125+ gradleExecutablePath: gradleExecutablePath, 126 ); 127 } on Error catch (error) { 128 _logger.printError(error.toString()); 129--- a/packages/flutter_tools/lib/src/android/gradle_errors.dart 130+++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart 131@@ -228,7 +228,12 @@ final flavorUndefinedHandler = GradleHandledError( 132 }, 133 handler: ({required String line, required FlutterProject project, required bool usesAndroidX}) async { 134 final RunResult tasksRunResult = await globals.processUtils.run( 135- <String>[globals.gradleUtils!.getExecutable(project), 'app:tasks', '--all', '--console=auto'], 136+ <String>[ 137+ ...globals.gradleUtils!.getExecutable(project), 138+ 'app:tasks', 139+ '--all', 140+ '--console=auto', 141+ ], 142 throwOnError: true, 143 workingDirectory: project.android.hostAppGradleRoot.path, 144 environment: globals.java?.environment, 145--- a/packages/flutter_tools/lib/src/android/gradle_utils.dart 146+++ b/packages/flutter_tools/lib/src/android/gradle_utils.dart 147@@ -3,6 +3,7 @@ 148 // found in the LICENSE file. 149 150 import 'package:meta/meta.dart'; 151+import 'package:path/path.dart'; 152 import 'package:process/process.dart'; 153 import 'package:unified_analytics/unified_analytics.dart'; 154 155@@ -197,9 +198,29 @@ class GradleUtils { 156 final Logger _logger; 157 final OperatingSystemUtils _operatingSystemUtils; 158 159+ List<String> get _requiredArguments { 160+ final String cacheDir = join( 161+ switch (globals.platform.environment['XDG_CACHE_HOME']) { 162+ final String cacheHome => cacheHome, 163+ _ => join( 164+ globals.fsUtils.homeDirPath ?? throwToolExit('No cache directory has been specified.'), 165+ '.cache', 166+ ), 167+ }, 168+ 'flutter', 169+ 'nix-flutter-tools-gradle', 170+ globals.flutterVersion.engineRevision.substring(0, 10), 171+ ); 172+ 173+ return <String>[ 174+ '--project-cache-dir=${join(cacheDir, 'cache')}', 175+ '-Pkotlin.project.persistent.dir=${join(cacheDir, 'kotlin')}', 176+ ]; 177+ } 178+ 179 /// Gets the Gradle executable path and prepares the Gradle project. 180 /// This is the `gradlew` or `gradlew.bat` script in the `android/` directory. 181- String getExecutable(FlutterProject project) { 182+ List<String> getExecutable(FlutterProject project) { 183 final Directory androidDir = project.android.hostAppGradleRoot; 184 injectGradleWrapperIfNeeded(androidDir); 185 186@@ -210,7 +231,7 @@ class GradleUtils { 187 // If the Gradle executable doesn't have execute permission, 188 // then attempt to set it. 189 _operatingSystemUtils.makeExecutable(gradle); 190- return gradle.absolute.path; 191+ return <String>[gradle.absolute.path, ..._requiredArguments]; 192 } 193 throwToolExit( 194 'Unable to locate gradlew script. Please check that ${gradle.path} ' 195--- a/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart 196+++ b/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart 197@@ -2606,8 +2606,8 @@ Gradle Crashed 198 199 class FakeGradleUtils extends Fake implements GradleUtils { 200 @override 201- String getExecutable(FlutterProject project) { 202- return 'gradlew'; 203+ List<String> getExecutable(FlutterProject project) { 204+ return const <String>['gradlew']; 205 } 206 } 207 208--- a/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart 209+++ b/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart 210@@ -1633,8 +1633,8 @@ Platform fakePlatform(String name) { 211 212 class FakeGradleUtils extends Fake implements GradleUtils { 213 @override 214- String getExecutable(FlutterProject project) { 215- return 'gradlew'; 216+ List<String> getExecutable(FlutterProject project) { 217+ return const <String>['gradlew']; 218 } 219 } 220