diff --git a/pkgs/development/compilers/flutter/versions/3_35/patches/gradle-flutter-tools-wrapper.patch b/pkgs/development/compilers/flutter/versions/3_35/patches/gradle-flutter-tools-wrapper.patch new file mode 100644 index 000000000000..4c5b258be373 --- /dev/null +++ b/pkgs/development/compilers/flutter/versions/3_35/patches/gradle-flutter-tools-wrapper.patch @@ -0,0 +1,220 @@ +This patch introduces an intermediate Gradle build step to alter the behavior +of flutter_tools' Gradle project, specifically moving the creation of `build` +and `.gradle` directories from within the Nix Store to somewhere in `$HOME/.cache/flutter/nix-flutter-tools-gradle/$engineShortRev`. + +Without this patch, flutter_tools' Gradle project tries to generate `build` and `.gradle` +directories within the Nix Store. Resulting in read-only errors when trying to build a +Flutter Android app at runtime. + +This patch takes advantage of the fact settings.gradle takes priority over settings.gradle.kts to build the intermediate Gradle project +when a Flutter app runs `includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle")` + +`rootProject.buildFileName = "/dev/null"` so that the intermediate project doesn't use `build.gradle.kts` that's in the same directory. + +The intermediate project makes a `settings.gradle` file in `$HOME/.cache/flutter/nix-flutter-tools-gradle//` and `includeBuild`s it. +This Gradle project will build the actual `packages/flutter_tools/gradle` project by setting +`rootProject.projectDir = new File("$settingsDir")` and `apply from: new File("$settingsDir/settings.gradle.kts")`. + +To move `build` to `$HOME/.cache/flutter/nix-flutter-tools-gradle//`, we need to set `buildDirectory`. +To move `.gradle` as well, the `--project-cache-dir` argument must be passed to the Gradle wrapper. +Changing the `GradleUtils.getExecutable` function signature is a delibarate choice, to ensure that no new unpatched usages slip in. +--- /dev/null ++++ b/packages/flutter_tools/gradle/settings.gradle +@@ -0,0 +1,19 @@ ++rootProject.buildFileName = "/dev/null" ++ ++def engineShortRev = (new File("$settingsDir/../../../bin/internal/engine.version")).text.take(10) ++def dir = new File("$System.env.HOME/.cache/flutter/nix-flutter-tools-gradle/$engineShortRev") ++dir.mkdirs() ++def file = new File(dir, "settings.gradle") ++ ++file.text = """ ++rootProject.projectDir = new File("$settingsDir") ++apply from: new File("$settingsDir/settings.gradle.kts") ++ ++gradle.allprojects { project -> ++ project.beforeEvaluate { ++ project.layout.buildDirectory = new File("$dir/build") ++ } ++} ++""" ++ ++includeBuild(dir) +--- a/packages/flutter_tools/gradle/build.gradle.kts ++++ b/packages/flutter_tools/gradle/build.gradle.kts +@@ -4,6 +4,11 @@ + + import org.jetbrains.kotlin.gradle.dsl.JvmTarget + ++// While flutter_tools runs Gradle with a --project-cache-dir, this startParameter ++// is not passed correctly to the Kotlin Gradle plugin for some reason, and so ++// must be set here as well. ++gradle.startParameter.projectCacheDir = layout.buildDirectory.dir("cache").get().asFile ++ + plugins { + `java-gradle-plugin` + groovy +--- a/packages/flutter_tools/lib/src/android/gradle.dart ++++ b/packages/flutter_tools/lib/src/android/gradle.dart +@@ -474,9 +474,9 @@ class AndroidGradleBuilder implements AndroidBuilder { + // from the local.properties file. + updateLocalProperties(project: project, buildInfo: androidBuildInfo.buildInfo); + +- final options = []; +- +- final String gradleExecutablePath = _gradleUtils.getExecutable(project); ++ final [String gradleExecutablePath, ...List options] = _gradleUtils.getExecutable( ++ project, ++ ); + + // All automatically created files should exist. + if (configOnly) { +@@ -797,7 +797,7 @@ class AndroidGradleBuilder implements AndroidBuilder { + 'aar_init_script.gradle', + ); + final command = [ +- _gradleUtils.getExecutable(project), ++ ..._gradleUtils.getExecutable(project), + '-I=$initScript', + '-Pflutter-root=$flutterRoot', + '-Poutput-dir=${outputDirectory.path}', +@@ -912,6 +912,10 @@ class AndroidGradleBuilder implements AndroidBuilder { + final results = []; + + try { ++ final [String gradleExecutablePath, ...List options] = _gradleUtils.getExecutable( ++ project, ++ ); ++ + exitCode = await _runGradleTask( + _kBuildVariantTaskName, + preRunTask: () { +@@ -927,10 +931,10 @@ class AndroidGradleBuilder implements AndroidBuilder { + ), + ); + }, +- options: const ['-q'], ++ options: [...options, '-q'], + project: project, + localGradleErrors: gradleErrors, +- gradleExecutablePath: _gradleUtils.getExecutable(project), ++ gradleExecutablePath: gradleExecutablePath, + outputParser: (String line) { + if (_kBuildVariantRegex.firstMatch(line) case final RegExpMatch match) { + results.add(match.namedGroup(_kBuildVariantRegexGroupName)!); +@@ -964,6 +968,10 @@ class AndroidGradleBuilder implements AndroidBuilder { + late Stopwatch sw; + var exitCode = 1; + try { ++ final [String gradleExecutablePath, ...List options] = _gradleUtils.getExecutable( ++ project, ++ ); ++ + exitCode = await _runGradleTask( + taskName, + preRunTask: () { +@@ -979,10 +987,10 @@ class AndroidGradleBuilder implements AndroidBuilder { + ), + ); + }, +- options: ['-q', '-PoutputPath=$outputPath'], ++ options: [...options, '-q', '-PoutputPath=$outputPath'], + project: project, + localGradleErrors: gradleErrors, +- gradleExecutablePath: _gradleUtils.getExecutable(project), ++ gradleExecutablePath: gradleExecutablePath, + ); + } on Error catch (error) { + _logger.printError(error.toString()); +--- a/packages/flutter_tools/lib/src/android/gradle_errors.dart ++++ b/packages/flutter_tools/lib/src/android/gradle_errors.dart +@@ -228,7 +228,12 @@ final flavorUndefinedHandler = GradleHandledError( + }, + handler: ({required String line, required FlutterProject project, required bool usesAndroidX}) async { + final RunResult tasksRunResult = await globals.processUtils.run( +- [globals.gradleUtils!.getExecutable(project), 'app:tasks', '--all', '--console=auto'], ++ [ ++ ...globals.gradleUtils!.getExecutable(project), ++ 'app:tasks', ++ '--all', ++ '--console=auto', ++ ], + throwOnError: true, + workingDirectory: project.android.hostAppGradleRoot.path, + environment: globals.java?.environment, +--- a/packages/flutter_tools/lib/src/android/gradle_utils.dart ++++ b/packages/flutter_tools/lib/src/android/gradle_utils.dart +@@ -3,6 +3,7 @@ + // found in the LICENSE file. + + import 'package:meta/meta.dart'; ++import 'package:path/path.dart'; + import 'package:process/process.dart'; + import 'package:unified_analytics/unified_analytics.dart'; + +@@ -197,9 +198,29 @@ class GradleUtils { + final Logger _logger; + final OperatingSystemUtils _operatingSystemUtils; + ++ List get _requiredArguments { ++ final String cacheDir = join( ++ switch (globals.platform.environment['XDG_CACHE_HOME']) { ++ final String cacheHome => cacheHome, ++ _ => join( ++ globals.fsUtils.homeDirPath ?? throwToolExit('No cache directory has been specified.'), ++ '.cache', ++ ), ++ }, ++ 'flutter', ++ 'nix-flutter-tools-gradle', ++ globals.flutterVersion.engineRevision.substring(0, 10), ++ ); ++ ++ return [ ++ '--project-cache-dir=${join(cacheDir, 'cache')}', ++ '-Pkotlin.project.persistent.dir=${join(cacheDir, 'kotlin')}', ++ ]; ++ } ++ + /// Gets the Gradle executable path and prepares the Gradle project. + /// This is the `gradlew` or `gradlew.bat` script in the `android/` directory. +- String getExecutable(FlutterProject project) { ++ List getExecutable(FlutterProject project) { + final Directory androidDir = project.android.hostAppGradleRoot; + injectGradleWrapperIfNeeded(androidDir); + +@@ -210,7 +231,7 @@ class GradleUtils { + // If the Gradle executable doesn't have execute permission, + // then attempt to set it. + _operatingSystemUtils.makeExecutable(gradle); +- return gradle.absolute.path; ++ return [gradle.absolute.path, ..._requiredArguments]; + } + throwToolExit( + 'Unable to locate gradlew script. Please check that ${gradle.path} ' +--- a/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart ++++ b/packages/flutter_tools/test/general.shard/android/android_gradle_builder_test.dart +@@ -2606,8 +2606,8 @@ Gradle Crashed + + class FakeGradleUtils extends Fake implements GradleUtils { + @override +- String getExecutable(FlutterProject project) { +- return 'gradlew'; ++ List getExecutable(FlutterProject project) { ++ return const ['gradlew']; + } + } + +--- a/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart ++++ b/packages/flutter_tools/test/general.shard/android/gradle_errors_test.dart +@@ -1633,8 +1633,8 @@ Platform fakePlatform(String name) { + + class FakeGradleUtils extends Fake implements GradleUtils { + @override +- String getExecutable(FlutterProject project) { +- return 'gradlew'; ++ List getExecutable(FlutterProject project) { ++ return const ['gradlew']; + } + } +