Start Debugging

Исправление: e: Daemon compilation failed: null в сборке Flutter Android на Gradle

В Windows инкрементальная компиляция Kotlin падает, если проект Flutter и кеш pub находятся на разных дисках. Перенесите PUB_CACHE на диск проекта или отключите IC для плагинов из кеша pub.

Это происходит в Windows, когда проект Flutter лежит на одном диске (D:\), а кеш pub на другом (C:\Users\<you>\AppData\Local\Pub\Cache). Инкрементальный компилятор Kotlin хранит каждый исходный файл плагина как путь относительно папки android\. Относительного пути от D:\ к C:\ не существует, поэтому compileDebugKotlin падает на плагинах вроде shared_preferences_android. Лучшее исправление: разместить PUB_CACHE на том же диске, что и проекты, затем выполнить flutter clean и flutter pub get. Если это невозможно, задайте kotlin.incremental=false только для подпроектов плагинов (фрагмент ниже). На Kotlin Gradle Plugin 2.3.0 и более старых APK все равно собирается, и ошибка лишь засоряет вывод. Начиная с KGP 2.3.20 (шаблон Flutter 3.44) и KGP 2.4.0 (Flutter 3.47) сборка может завершиться неудачей.

Версии ниже проверены на Flutter 3.47.5 (Dart 3.13.4, AGP 9.1.0, Gradle 9.3.1, KGP 2.4.0), shared_preferences 2.5.5 / shared_preferences_android 2.4.28 и исходном коде Kotlin Gradle Plugin на тегах с v1.9.22 по v2.4.20.

Ошибка в контексте

Отчеты в flutter/flutter#173456 и flutter/flutter#144566 выглядят так, по одному разу на каждый плагин:

e: Daemon compilation failed: null
java.lang.Exception
	at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:69)
	at org.jetbrains.kotlin.daemon.common.CompileService$CallResult$Error.get(CompileService.kt:65)
	at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:244)
	...
Caused by: java.lang.AssertionError: java.lang.Exception: Could not close incremental caches in
  D:\src\my_app\build\shared_preferences_android\kotlin\compileReleaseKotlin\cacheable\caches-jvm\jvm\kotlin:
  class-fq-name-to-source.tab, source-to-classes.tab, internal-name-to-source.tab
	at org.jetbrains.kotlin.incremental.IncrementalCachesManager.close(IncrementalCachesManager.kt:55)
	...
	Suppressed: java.lang.IllegalArgumentException: this and base files have different roots:
	  C:\Users\me\AppData\Local\Pub\Cache\hosted\pub.dev\shared_preferences_android-2.4.28\android\src\main\kotlin\io\flutter\plugins\sharedpreferences\LegacySharedPreferencesPlugin.kt
	  and D:\src\my_app\android.

В первой строке стоит null, потому что демон оборачивает настоящую ошибку в голый java.lang.Exception без сообщения. Полезна последняя строка: this and base files have different roots. Если она есть в вашем журнале, эта статья решает вашу проблему. Если нет, переходите к разделу “Похожие ошибки” в конце.

Почему инкрементальному компилятору Kotlin нужен один диск

Инкрементальная компиляция Kotlin (IC) хранит таблицы соответствий в build/<module>/kotlin/compile<Variant>Kotlin/cacheable/caches-jvm. Эти таблицы сопоставляют каждый исходный файл с классами, которые из него получаются. Чтобы кеш сборки Gradle можно было переносить, начиная с Kotlin 1.9.20 эти пути хранятся относительно базового каталога, а не как абсолютные. Вот конвертер из build-common в репозитории Kotlin:

// Kotlin build-common, RelocatableFileToPathConverter.kt (unchanged through 2.4.20)
override fun toPath(file: File): String {
    // ...
    // Note: If the given file is located outside `baseDir`, the relative path will start with "../".
    // It's not "clean", but it can work.
    return file.relativeTo(baseDir).invariantSeparatorsPath
}

Для исходных файлов baseDir это корневой каталог проекта, который для приложения Flutter равен <project>\android. Плагины Flutter являются подпроектами Gradle, но их исходники лежат в кеше pub, за пределами этой папки. В macOS и Linux это работает, потому что у всех путей общий корень /. На моем Mac кеш IC для shared_preferences_android действительно содержит такую запись:

../../../../../../../../Users/marius/.pub-cache/hosted/pub.dev/shared_preferences_android-2.4.28/android/src/main/kotlin/io/flutter/plugins/sharedpreferences/LegacySharedPreferencesPlugin.kt

В Windows у D:\src\my_app\android и C:\Users\...\Pub\Cache разные корни. Никакая цепочка ..\ не переведет с одного диска на другой, поэтому File.relativeTo выбрасывает IllegalArgumentException. Исключение возникает в момент, когда IC записывает свои кеши, поэтому оно проявляется как “Could not close incremental caches”, а демон сообщает о нем как “Daemon compilation failed”.

Этим же объясняется, почему работают классические обходные пути: “перенесите проект на C:”, “откатите Kotlin до 1.9.10” (последняя версия до относительных путей) и “падает только с некоторыми плагинами” (через компилятор Kotlin проходят только плагины с исходниками на Kotlin). JetBrains отслеживает первопричину как KT-63983, и задача по-прежнему в статусе “To be discussed”. Инженер JetBrains отметил, что тривиальное исправление (relativeToOrSelf) приведет к некорректным попаданиям в кеш сборки. Со стороны Flutter это flutter/flutter#105395, открытая с 2022 года.

Такой же сбой возникает в любой конфигурации, где исходники и корневой проект находятся на разных корнях: виртуальные диски subst (KT-65155), каталог сборки на RAM-диске или путь WSL вроде \mnt\d\project вперемешку с D:\project.

Почему APK иногда все равно собирается

Многие сообщают, что журнал полон строк e:, а затем выводится √ Built build\app\outputs\flutter-apk\app-release.apk. Другие, особенно начиная с Flutter 3.44, получают настоящий BUILD FAILED. Разница в том, какой путь компиляции выбирает Kotlin Gradle Plugin после сбоя демона. Я прочитал это в исходниках KGP на каждом теге:

Версия KGPПуть компиляции по умолчаниюЗапасной вариант после сбоя демонаРезультат для проекта на разных дисках
от 1.9.20 до 2.3.0GradleKotlinCompilerWorkcompileInProcess, который явно неинкрементальный (“in-process execution strategy is non-incremental”)Шумный вывод e:, APK собирается
2.3.20 и новееBuild Tools API (kotlin.compiler.runViaBuildToolsApi по умолчанию true)performCompilation(IN_PROCESS) с той же инкрементальной конфигурацией, включая ROOT_PROJECT_DIRЗапасной путь натыкается на тот же relativeTo, сборка может упасть

Шаблон Flutter фиксирует версию KGP в android/settings.gradle.kts, поэтому версия Flutter на момент flutter create определяет, в какой строке вы оказались:

Шаблон FluttertemplateKotlinGradlePluginVersion
3.35.02.1.0
3.38.0, 3.41.02.2.20
3.44.02.3.20
от 3.47.0 до 3.47.52.4.0

Это совпадает с обсуждениями в задачах. В отчетах 2025 года на Flutter 3.32 и 3.35 говорится “APK все равно собирается”. В комментариях июня 2026 года пишут “столкнулся с этим после обновления до Flutter 3.44”. В отчете августа 2026 года на 3.47.0 с AGP 9.1.0 и KGP 2.4.0 задача :shared_preferences_android:compileDebugKotlin роняет сборку на чистом запуске. Эти люди видят не новую ошибку. Просто запасной путь Kotlin, который раньше скрывал старую, больше этого не делает.

Минимальное воспроизведение

Нужна Windows с двумя дисками (или одним диском subst). Оставьте кеш pub по умолчанию на C::

# Windows 11, Flutter 3.47.5, default PUB_CACHE on C:
D:
cd \src
flutter create --platforms=android daemon_repro
cd daemon_repro
flutter pub add shared_preferences
flutter build apk --debug

Только что созданный проект 3.47.5 получает com.android.application 9.1.0, org.jetbrains.kotlin.android 2.4.0 и Gradle 9.3.1, при этом IC для Kotlin включена по умолчанию. Без плагина у шаблонного приложения нет ничего за пределами android\, что Kotlin должен компилировать, поэтому оно собирается без ошибок. Это объясняет частое наблюдение “сломалось, как только я добавил один пакет”.

Исправление 1: разместите кеш pub на том же диске, что и проекты

Это рекомендуемое исправление. Оно устраняет причину и сохраняет инкрементальную компиляцию везде. Выберите папку на диске, где лежат ваши проекты, укажите на нее PUB_CACHE и заново разрешите зависимости:

# Windows, any Flutter 3.x: user-level env var, picked up by new shells and IDEs
[Environment]::SetEnvironmentVariable("PUB_CACHE", "D:\PubCache", "User")

# open a NEW terminal (and restart VS Code / Android Studio), then:
cd D:\src\my_app
flutter clean
flutter pub get
flutter build apk --debug

flutter pub get скачивает пакеты в новый кеш и заново генерирует .dart_tool\package_config.json и .flutter-plugins-dependencies. Плагин Flutter для Gradle читает пути плагинов из этих файлов, поэтому каждый подпроект плагина теперь указывает на D:\PubCache\.... flutter clean важен, потому что старые кеши IC в build\ все еще содержат пути из прежней раскладки. Старый C:\Users\<you>\AppData\Local\Pub\Cache после этого можно удалить.

Ограничение этого исправления: если проекты лежат на нескольких дисках, с кешем может совпасть только один из них. Для такого случая используйте исправление 2.

Исправление 2: отключите инкрементальную компиляцию только для подпроектов плагинов

Плагины из кеша pub между сборками не меняются, поэтому IC на них ничего не экономит. IC окупается в вашем собственном модуле app, а его исходники находятся внутри android\, так что проблема его не касается. KGP читает kotlin.incremental для каждого проекта отдельно, включая extra-свойства проекта, поэтому переключатель можно ограничить. Добавьте это в конец android/build.gradle.kts:

// android/build.gradle.kts, Flutter 3.47.5, AGP 9.1.0, KGP 2.4.0
// Kotlin incremental compilation stores source paths relative to this
// directory. Plugins from the pub cache live outside it, which breaks on
// Windows when the cache is on another drive (KT-63983). Turn IC off for
// those subprojects only; the :app module stays incremental.
subprojects {
    if (!projectDir.canonicalPath.startsWith(rootDir.canonicalPath)) {
        extra["kotlin.incremental"] = "false"
    }
}

Для Groovy-файла android/build.gradle эквивалент такой:

// android/build.gradle, Flutter 3.35 to 3.47
subprojects {
    if (!projectDir.canonicalPath.startsWith(rootDir.canonicalPath)) {
        ext.set("kotlin.incremental", "false")
    }
}

Я проверил это в macOS на проекте воспроизведения 3.47.5, посмотрев, какие модули записывают кеши IC после flutter clean && flutter build apk --debug. Без фрагмента существуют и build/app/kotlin/compileDebugKotlin/cacheable/caches-jvm, и build/shared_preferences_android/.../caches-jvm. С ним остается только кеш app, и APK собирается. Вариант на Groovy дал тот же результат. Машины с Windows и вторым диском у меня под рукой нет, поэтому исчезновение сбоя в Windows я сам не видел, но механизм тот же: при отключенной IC KGP не строит инкрементальную конфигурацию, и RelocatableFileToPathConverter никто не вызывает.

Два подхода, которые выглядят правильными, не работают, и я попробовал оба на 3.47.5:

Установка свойства, которое читает сам KGP, остается единственным переключателем на уровне модуля, который не перезаписывается.

Зависимость по пути внутри вашего репозитория (path: ../packages/my_plugin) тоже находится за пределами android\, поэтому фрагмент отключает IC и для нее. Это стоит полной перекомпиляции Kotlin-кода этого плагина при каждой сборке, обычно секунда или две. Если это важно, сузьте проверку до путей кеша pub, например projectDir.canonicalPath.contains("Pub${File.separator}Cache").

Исправление 3: отключите инкрементальную компиляцию Kotlin глобально

Самое грубое исправление, и именно его чаще всего цитируют в обсуждениях. В android/gradle.properties:

# android/gradle.properties, any Flutter / KGP version
kotlin.incremental=false

Оно работает, и я убедился, что после этого папка caches-jvm не создается ни для одного модуля. Цена в том, что Kotlin-код вашего модуля app тоже перекомпилируется с нуля при каждой сборке. Для единственного MainActivity.kt в шаблоне Flutter это неважно. Для приложения с большим объемом нативного Kotlin (каналы платформы, виджет, модуль Wear OS) это накапливается во время flutter run. В таком случае предпочтите исправление 1 или 2.

Чего делать не стоит

Похожие ошибки

Не каждая строка Daemon compilation failed означает эту ошибку. Проверьте строки Caused by и Suppressed:

Необработанные журналы демона лежат в android\.kotlin\errors\errors-<timestamp>.log и %TEMP%\kotlin-daemon.*.log. Ищите в них different roots, если вывод консоли обрезан.

Связанные материалы

Источники

Comments

Sign in with GitHub to comment. Reactions and replies thread back to the comments repo.

< Назад