You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gradle自动生成include列表失效,手动列表正常,求原因

问题

我有大量子项目,想自动加入构建。手动编写include的项目路径列表时一切正常,但用递归函数生成的内容完全一致(仅顺序不同)的列表时,运行engine implementation模块的测试会报错,提示该项目目录不在settings.gradle.kts定义的构建中。为什么自动生成的列表会失效?

问题代码与输出

settings.gradle.kts 相关代码

rootProject.name = "MyApplication"

// 递归遍历所有包含build.gradle.kts的目录并加入构建
fun includeModules(set: Set<String>, prefix: String, dir: File): Set<String> {
    val gradlePath = if (dir.name == ".") "" else "$prefix:${dir.name}"
    val intermediateSet = set + if (dir.listFiles()?.firstOrNull { it.name == "build.gradle.kts" } != null &&
        gradlePath != "") {
        println("including $gradlePath")
        setOf(gradlePath)
    } else {
        emptySet()
    }
    return dir.listFiles()?.filter { it.name != "build" && it.isDirectory }
        ?.fold(intermediateSet) { acc: Set<String>, file: File? ->
            acc + includeModules(
                intermediateSet,
                gradlePath,
                file ?: File(".")
            )
        } ?: emptySet()
}

val output = includeModules(emptySet(), "", File(".")).toList()
println("set $output")
val list = mutableListOf<String>()
list.add(":androidApp")
list.add(":shared")
list.add(":desktopApp")
list.add(":client-network:header")
list.add(":client-network:ktorImpl")
list.add(":irc_entities")
list.add(":irc_parser:header")
list.add(":irc_parser:header_test")
list.add(":irc_parser:implementation")
list.add(":TestUtils")
list.add(":client_state:header")
list.add(":client_irc_engine:header")
list.add(":client_irc_engine:implementation")
list.add(":Utility_Functions")
list.add(":bot_prototype:core")
list.add(":bot_prototype:dep_inject")
println("manual $list")
include(output)

Gradle 配置阶段输出

including :client_irc_engine:implementation
including :client_irc_engine:header
including :irc_parser:implementation
including :irc_parser:header_test
including :irc_parser:header
including :irc_entities
including :Utility_Functions
including :shared
including :client-network:ktorImpl
including :client-network:header
including :desktopApp
including :bot_prototype:core
including :bot_prototype:dep_inject
including :androidApp
including :client_state:header
including :TestUtils
set [:client_irc_engine:implementation, :client_irc_engine:header, :irc_parser:implementation, :irc_parser:header_test, :irc_parser:header, :irc_entities, :Utility_Functions, :shared, :client-network:ktorImpl, :client-network:header, :desktopApp, :bot_prototype:core, :bot_prototype:dep_inject, :androidApp, :client_state:header, :TestUtils]
manual [:androidApp, :shared, :desktopApp, :client-network:header, :client-network:ktorImpl, :irc_entities, :irc_parser:header, :irc_parser:header_test, :irc_parser:implementation, :TestUtils, :client_state:header, :client_irc_engine:header, :client_irc_engine:implementation, :Utility_Functions, :bot_prototype:core, :bot_prototype:dep_inject]

> Configure project :shared
w: The Default Kotlin Hierarchy Template was not applied to 'project ':shared'':
Explicit .dependsOn() edges were configured for the following source sets:
[iosArm64Main, iosMain, iosSimulatorArm64Main, iosX64Main]

Consider removing dependsOn-calls or disabling the default template by adding
    'kotlin.mpp.applyDefaultHierarchyTemplate=false'
to your gradle.properties

Learn more about hierarchy templates: https://kotl.in/hierarchy-template


> Task :commonizeNativeDistribution UP-TO-DATE
> Task :commonize UP-TO-DATE
> Task :prepareKotlinIdeaImport UP-TO-DATE
> Task :androidApp:commonizeCInterop UP-TO-DATE
> Task :androidApp:copyCommonizeCInteropForIde UP-TO-DATE
> Task :androidApp:commonize UP-TO-DATE
> Task :androidApp:prepareKotlinIdeaImport UP-TO-DATE
> Task :desktopApp:commonizeCInterop UP-TO-DATE
> Task :desktopApp:copyCommonizeCInteropForIde UP-TO-DATE
> Task :desktopApp:commonize UP-TO-DATE
> Task :desktopApp:prepareKotlinIdeaImport UP-TO-DATE
> Task :irc_entities:commonizeCInterop UP-TO-DATE
> Task :irc_entities:copyCommonizeCInteropForIde UP-TO-DATE
> Task :irc_entities:commonize UP-TO-DATE
> Task :irc_entities:prepareKotlinIdeaImport UP-TO-DATE
> Task :shared:commonizeCInterop UP-TO-DATE
> Task :shared:copyCommonizeCInteropForIde UP-TO-DATE
> Task :shared:commonize UP-TO-DATE
> Task :shared:transformIosMainCInteropDependenciesMetadataForIde UP-TO-DATE
> Task :shared:prepareKotlinIdeaImport UP-TO-DATE
> Task :TestUtils:commonizeCInterop UP-TO-DATE
> Task :TestUtils:copyCommonizeCInteropForIde UP-TO-DATE
> Task :TestUtils:commonize UP-TO-DATE
> Task :TestUtils:prepareKotlinIdeaImport UP-TO-DATE
> Task :Utility_Functions:commonizeCInterop UP-TO-DATE
> Task :Utility_Functions:copyCommonizeCInteropForIde UP-TO-DATE
> Task :Utility_Functions:commonize UP-TO-DATE
> Task :Utility_Functions:prepareKotlinIdeaImport UP-TO-DATE
> Task :client-network:header:commonizeCInterop UP-TO-DATE
> Task :client-network:header:copyCommonizeCInteropForIde UP-TO-DATE
> Task :client-network:header:commonize UP-TO-DATE
> Task :client-network:header:prepareKotlinIdeaImport UP-TO-DATE
> Task :client-network:ktorImpl:commonizeCInterop UP-TO-DATE
> Task :client-network:ktorImpl:copyCommonizeCInteropForIde UP-TO-DATE
> Task :client-network:ktorImpl:commonize UP-TO-DATE
> Task :client-network:ktorImpl:prepareKotlinIdeaImport UP-TO-DATE
> Task :client_irc_engine:header:commonizeCInterop UP-TO-DATE
> Task :client_irc_engine:header:copyCommonizeCInteropForIde UP-TO-DATE
> Task :client_irc_engine:header:commonize UP-TO-DATE
> Task :client_irc_engine:header:prepareKotlinIdeaImport UP-TO-DATE
> Task :client_irc_engine:implementation:commonizeCInterop UP-TO-DATE
> Task :client_irc_engine:implementation:copyCommonizeCInteropForIde UP-TO-DATE
> Task :client_irc_engine:implementation:commonize UP-TO-DATE
> Task :client_irc_engine:implementation:prepareKotlinIdeaImport UP-TO-DATE
> Task :client_state:header:commonizeCInterop UP-TO-DATE
> Task :client_state:header:copyCommonizeCInteropForIde UP-TO-DATE
> Task :client_state:header:commonize UP-TO-DATE
> Task :client_state:header:prepareKotlinIdeaImport UP-TO-DATE
> Task :irc_parser:header:commonizeCInterop UP-TO-DATE
> Task :irc_parser:header:copyCommonizeCInteropForIde UP-TO-DATE
> Task :irc_parser:header:commonize UP-TO-DATE
> Task :irc_parser:header:prepareKotlinIdeaImport UP-TO-DATE
> Task :irc_parser:header_test:commonizeCInterop UP-TO-DATE
> Task :irc_parser:header_test:copyCommonizeCInteropForIde UP-TO-DATE
> Task :irc_parser:header_test:commonize UP-TO-DATE
> Task :irc_parser:header_test:prepareKotlinIdeaImport UP-TO-DATE
> Task :irc_parser:implementation:commonizeCInterop UP-TO-DATE
> Task :irc_parser:implementation:copyCommonizeCInteropForIde UP-TO-DATE
> Task :irc_parser:implementation:commonize UP-TO-DATE
> Task :irc_parser:implementation:prepareKotlinIdeaImport UP-TO-DATE
> Task :prepareKotlinBuildScriptModel UP-TO-DATE

Deprecated Gradle features were used in this build, making it incompatible with Gradle 9.0.

You can use '--warning-mode all' to show the individual deprecation warnings and determine if they come from your own scripts or plugins.

For more on this, please refer to https://docs.gradle.org/8.2.1/userguide/command_line_interface.html#sec:command_line_warnings in the Gradle documentation.

BUILD SUCCESSFUL in 2s
30 actionable tasks: 30 up-to-date

测试阶段报错信息

Project directory '/Users/kylemcburnett/IdeaProjects/kmirc/client_irc_engine/implementation' is not part of the build defined by settings file '/Users/kylemcburnett/IdeaProjects/kmirc/settings.gradle.kts'. If this is an unrelated build, it must have its own settings file.

* Try:
> Run with --stacktrace option to get the stack trace.
> Run with --info or --debug option to get more log output.
> Run with --scan to get full insights.
> Get more help at https://help.gradle.org.
BUILD FAILED in 17ms
原因分析

核心问题出在递归函数的fold逻辑里:你每次递归调用includeModules时,传入的是intermediateSet而非累加后的acc。这会导致父模块(比如:client_irc_engine)的路径没有被正确加入到集合中。

Gradle的规则是必须先包含父模块,才能包含其子模块。如果直接先尝试包含子模块:client_irc_engine:implementation,而父模块:client_irc_engine还没被include,Gradle无法识别这个子模块的归属关系,就会抛出“项目目录不在构建中”的错误。

手动列表之所以正常,是因为它先包含了:client_irc_engine:header,此时Gradle会自动隐式创建父模块:client_irc_engine,后续再包含:client_irc_engine:implementation时就能正常识别。但递归生成的列表先包含了:client_irc_engine:implementation,此时父模块还未被处理,就触发了报错。

修复方案

修改递归函数中fold lambda里的参数传递,把递归调用includeModules的第一个参数从intermediateSet改成acc,确保每次递归都传递已经累加的模块集合,让父模块路径被正确包含:

rootProject.name = "MyApplication"

// 递归遍历所有包含build.gradle.kts的目录并加入构建
fun includeModules(set: Set<String>, prefix: String, dir: File): Set<String> {
    val gradlePath = if (dir.name == ".") "" else "$prefix:${dir.name}"
    val intermediateSet = set + if (dir.listFiles()?.firstOrNull { it.name == "build.gradle.kts" } != null &&
        gradlePath != "") {
        println("including $gradlePath")
        setOf(gradlePath)
    } else {
        emptySet()
    }
    return dir.listFiles()?.filter { it.name != "build" && it.isDirectory }
        ?.fold(intermediateSet) { acc: Set<String>, file: File? ->
            acc + includeModules(
                acc, // 修复:传递累加后的集合而非当前目录的临时集合
                gradlePath,
                file ?: File(".")
            )
        } ?: emptySet()
}

val output = includeModules(emptySet(), "", File(".")).toList()
println("set $output")
// 手动列表可保留用于对比,实际使用时可注释
val list = mutableListOf<String>()
list.add(":androidApp")
list.add(":shared")
list.add(":desktopApp")
list.add(":client-network:header")
list.add(":client-network:ktorImpl")
list.add(":irc_entities")
list.add(":irc_parser:header")
list.add(":irc_parser:header_test")
list.add(":irc_parser:implementation")
list.add(":TestUtils")
list.add(":client_state:header")
list.add(":client_irc_engine:header")
list.add(":client_irc_engine:implementation")
list.add(":Utility_Functions")
list.add(":bot_prototype:core")
list.add(":bot_prototype:dep_inject")
println("manual $list")
include(output)

修改后,递归函数会正确传递累加的模块集合,确保父模块被提前包含,Gradle就能正常识别所有子模块的归属,测试也能正常运行。

内容的提问来源于stack exchange,提问作者Kyle M

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 04:39:52