使用Gradle进行多项目构建时如何处理空项目

18

当我们将一个多项目的Gradle结构应用到我们的项目中时,我的settings.gradle看起来像这样:

include "source:compA:api"
include "source:compA:core"
include "source:compB"

Gradle项目给了我。

Root project 'tmp'
\--- Project ':source'
     +--- Project ':source:compA'
     |    +--- Project ':source:compA:api'
     |    \--- Project ':source:compA:core'
     \--- Project ':source:compB'

这就是目录结构!
在我的根目录下,我有一个 build.gradle 文件,将 java 插件应用于所有子项目:

subprojects {
    apply plugin: 'java'
}

构建时,我最终会得到一些:source:compA的构件,但它们是空的,因为这实际上不是一个项目,只有子目录apicore是合适的Java项目。

如何避免出现空构件?


为什么不在它们各自的脚本中应用Java插件到API和核心? - Benjamin Muschko
我可以这样做,但是我的实际项目结构更加嵌套,有很多项目。我认为这是传统项目结构的常见问题。我正在寻找一种灵活的解决方案,使每个项目都不需要了解它所在的构建过程太多。 - VolkerK
1
在我看来,子模块应该明确知道它是一个Java项目。这将使任何查看它的人比在根级别声明此信息更具表现力。 - Benjamin Muschko
3个回答

19
你可以尝试使用Gradle自己的settings.gradle文件中使用的技巧。请注意,每个子项目都位于'subprojects/${projectName}'文件夹中,但subprojects文件夹本身不是一个项目。
因此,在你的情况下,你需要进行以下操作:
include "source:compA-api"
include "source:compA-core"
include "source:compB"

project(':source:compA-api').projectDir = new File(settingsDir, 'source/compA/api')
project(':source:compA-core').projectDir = new File(settingsDir, 'source/compA/core')

我故意省略了compAapi之间的冒号,以确保source:compA不会被评估为项目容器。
或者,您可以尝试排除source:compA项目应用java插件,方法如下:
def javaProjects() { 
  return subprojects.findAll { it.name != 'compA' }
} 

configure(javaProjects()) { 
  apply plugin: 'java' 
} 

编辑: 或者您可以尝试类似这样的内容(根据您的喜好进行调整):

def javaProjects() { 
  return subprojects.findAll { new File(it.projectDir, "src").exists() }
} 

configure(javaProjects()) { 
  apply plugin: 'java' 
} 

谢谢您的回答。我希望有类似于“skipIfEmpty”的设置。 - VolkerK
你可以通过自定义 findAll 子句来实现与此相当的功能。请参见上面的编辑。 - rodion
这是一个好的解决方案。我仍然需要适应构建文件是Groovy脚本的事实 ;) - VolkerK

2
从Gradle 6.7开始,Gradle用户手册建议不要使用“跨项目配置”功能来配置子项目,而应该避免使用subprojectsallprojectsrecommends against 另一个灰心丧气的在子项目之间共享构建逻辑的方式是通过subprojects{}allprojects{} DSL结构进行跨项目配置。使用交叉配置,可以将构建逻辑注入到子项目中,在查看子项目的构建脚本时并不明显,这使得理解特定子项目的逻辑变得更困难。从长远来看,交叉配置通常会随着越来越多的条件逻辑和更高的维护负担而变得复杂。交叉配置还可能在项目之间引入配置时间耦合,这可能会阻止像按需配置这样的优化正常工作。
建议采用约定插件的方法来定义共同特征。
Gradle 推荐使用其插件系统来组织构建逻辑。插件应该定义子项目的类型。事实上,Gradle 核心插件 也是以同样的方式进行建模 - 例如,Java 插件 配置一个通用的 Java 项目,而 Java 库插件 在内部应用 Java 插件 并配置特定于 Java 库的方面。类似地,应用程序插件 应用并配置 Java 插件分发插件
您可以通过应用和配置核心和外部插件来组成自定义构建逻辑,并创建定义新项目类型并配置特定于您的项目或组织的约定的自定义插件。对于本节开头的每个示例特征,我们都可以编写一个插件,封装给定类型的子项目的通用逻辑。
我们建议将约定插件的源代码和测试放在项目根目录中的特殊 buildSrc 目录中。有关 buildSrc 的更多信息,请参阅 使用 buildSrc 组织构建逻辑
在您的情况下,您可以按照Gradle 示例中给出的方法进行操作:
├── buildSrc
│   ├── build.gradle
│   ├── src
│   │   ├── main
│   │   │   └── groovy
│   │   │       ├── source.java-conventions.gradle

"

buildSrc/build.gradle 文件中只包含 groovy-gradle-plugin

"
plugins {
    id 'groovy-gradle-plugin'
}

"buildSrc/src/main/groovy/source.java-conventions.gradle" 包含了 Java 项目的通用逻辑。在您的示例中,您只是应用了 Java 插件,但您还可以添加任何其他 Java 插件的通用性,这些通用性不会与非 Java 项目共享。"
plugins {
    id 'groovy-gradle-plugin'
}

每个 Java 项目都将包括约定插件:
plugins {
    id 'source.java-conventions'
}

请注意,如果字面上唯一共同点是Java插件,那么这并没有太多的作用;您只是用另一个插件替换了一个插件。但是,一旦您拥有更多的共享构建逻辑,它就开始在跨项目一致性和减少重复代码方面产生回报。

1
我有第二种情况。空的父目录被视为项目。我们可以进行一些检查来忽略这个项目。
project.getBuildFile().exists()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接