多个设置为多个项目构建gradle文件

时间:2012-08-28 06:43:48

标签: build-automation gradle multi-project

我有以下项目结构

-->Starnderd Location
        -->Project1 
           -->settings.gradle 
           -->build.gradle
           -->Subproject11
              -->build.gradle
           -->Subproject12
              -->build.gradle
        -->Project2 
           -->settings.gradle 
           -->build.gradle
           -->Subproject21
              -->build.gradle
           -->Subproject22
              -->build.gradle
        -->build.gradle
        -->settings.gradle

上述项目结构的想法是我们有多个包含子项目的项目,每个项目都可以与其他项目有依赖关系。项目内的子项目也可以与同一项目中的其他子项目具有依赖关系。项目将在根目录中的settings.gradle中指定。此外,每个项目中的settings.gradle将说明该特定项目的子项目是什么。

我的settings.gradle在root中看起来像

include 'Project1',
         'Project2'

和Project1 settings.gradle将如下所示

include 'SubProject11'
         'SubProject12'

其他depndecy订单在相应的build.gradle文件中定义 如果我在根位置(标准位置)内进行gradle clean build安装,则它似乎不使用项目级别settings.gradle文件中的配置。

我在这做错了什么?

6 个答案:

答案 0 :(得分:15)

我能够以相对干净的方式解决这个问题。当然欢迎改进!

虽然Gradle不支持开箱即用的多个settings.gradle脚本,但可以创建各个子项目,每个子项目都有自己的settings.gradle文件。让我们假设您有多项目A依赖于多项目B,每个项目都有自己的子项目。您的目录结构可能如下所示:

A
- settings.gradle
- foo
- faz
\ B
  - settings.gradle
  - bar
  - bap

开箱即用,Gradle希望A/settings.gradle看起来像这样:

include ':foo', ':faz', 'B:bar', 'B:bap'

这个问题是,每次B添加新项目时,即使新项目仅由B使用,A/settings.gradle也必须更改。为避免这种情况,您可以尝试apply B/settings.gradle中的A/settings.gradle,而不是添加冗余声明:

apply from: 'B/settings.gradle'
include ':foo', ':faz'

如果您尝试此操作,则会发现Gradle失败,因为它会为projectDir:bar生成错误的:bap。它错误地假设B&#39}包含的settingsDir相对于从项目根调用Gradle时恰好是A/。要解决此问题,您可以添加另一个脚本,例如B/settings-parent.gradle(确切名称并不重要):

apply from: 'settings.gradle'

rootProject.children.each { project ->
    String relativeProjectPath = project.projectDir.path.replace(settingsDir.path, "")
    project.projectDir = new File("B/$relativeProjectPath")
}

这会剥离settingsDir.path并在路径前加B/。通过让每个层将自身添加到路径上,可以将其扩展到多个settings[-parent].gradle文件层。现在,您将此脚本应用于A/settings.gradle

apply from: 'B/settings-parent.gradle'
include ':foo', ':faz'

使用此方案,新的B项目不会不必要地中断A/settings.gradle,并且可以在不明确引用B子项目的情况下使用所有项目。例如,如果':foo'想要使用'B:bap',则可以简单地声明:

compile project(':bap')

答案 1 :(得分:8)

目前,Gradle仅支持每个版本的单个settings.gradle个文件。这可能在将来发生变化。

答案 2 :(得分:8)

如果您使用的是Gradle 3.x,请尝试使用includeBuild(): https://docs.gradle.org/current/userguide/composite_builds.html

// settings.gradle
includeBuild './Project1'
includeBuild './Project2'

如果您使用的是Gradle 2.x,我已经为此功能编写了一个演示。希望它能帮到你: https://github.com/xujiaao/gradle-composite-build-demo

// settings.gradle
def includeProject(String path, Closure closure) {
    ...

    apply {
        from ...
        to new SettingsProxy(...)
    }
}

class SettingsProxy {
    ...

    public getRootProject() { ... }

    public void include(String... paths) {
        for (String path : paths) {
            if (!mProjectSpec.accept(path)) {
                continue
            }

            def descendantPath = generateDescendantPath(path)
            mSettings.include(descendantPath)

            def descendantProjectDir = new File(mProject.projectDir, path.replace(':', '/'))
            mSettings.project(descendantPath).projectDir = descendantProjectDir
        }
    }
}

答案 3 :(得分:4)

我也研究了这个,你可以做到这一点,但它非常难看!这对我们起作用的原因是,绝大多数时候,我们只想从最顶层建立。

如果它对您有所帮助,您需要做的是让最顶层的settings.gradle文件直接正确引用每个项目子项目。让这个工作第一。

然后,如果Project1和Project2(等等)能够彼此独立构建,则可以为该项目创建本地settings.gradle文件。因为,正如我上面所说,这不是我们通常做的,我们称之为文件settings.project1。如果我们要使用此文件,请将其复制到settings.gradle。我知道很难看。

但它实际上变得更糟了:)一旦你把这个settings.gradle文件放在适当的位置,你从Project1构建将不再会看到你可能需要定义的顶级build.gradle文件。要调用它,您需要在每个项目级build.gradle文件中添加类似的内容:

if (project.hasProperty('local')) {
    apply from: '../build.gradle'
}

然后你可以运行build as:gradle -Plocal build

丑陋,但如果你需要它,它至少可以起作用。并且为了全面披露,几周前已经实现了这一点,没有一个开发人员需要和/或使用它。如果继续不使用它,可能会在几周内将其删除。

请记住,如果您从子项目本身构建,则只构建该子项目(以及任何相关项目)(尽管将编译/评估所有gradle脚本)。

答案 4 :(得分:4)

因为这个主题现在经常超越我的日常工作而且它的改进(GRADLE-803)仍然是开放的,我也想分享我的方法:

乍一看,它看起来与詹姆斯沃尔德的answer相似,但有一点不同。您不需要在子项目中以某种方式拆分设置文件。如果您可以接受,那么在超级项目中有一个干净的方法来完成所有事情。通常,您的小子项目不应该关心周围的超级项目。它们包括它们的子依赖关系,就是这样。如何命名模块目录也应该无关紧要,在Wald的方法中,模块本身需要知道它的目录名称('B'):

  

project.projectDir = new File("B/$relativeProjectPath")

相反,通常,超级项目很好地了解其子项目和目录,因为它们可能是git子模块,例如,它们具有明确定义的修订名称,从超级项目的角度来看,可以安全地引用它们。

这是我的设置(使用Gradle 2.4):

Super Project
├── build.gradle
├── settings.gradle (include 'A' include 'B')
├── <Subproject A>
    ├── build.gradle
    ├── settings.gradle (include 'subA')
    ├── <Subsubproject subA>
        ├── build.gradle
├── <Subproject B>
    ├── build.gradle
    ├── settings.gradle (include 'subB')
    ├── <Subsubproject subB>
        ├── build.gradle

在超级项目settings.gradle中,您现在可以编写以下代码:

include 'A'
include 'B'
apply from: 'A/settings.gradle'
apply from: 'B/settings.gradle'
project(':subA').projectDir = new File(rootDir, 'A/subA')
project(':subB').projectDir = new File(rootDir, 'B/subB')

它仍然看起来相当冗长(并且仍然没有添加真正的分层行为),但是在超级项目中保留了额外的工作量,通常您需要知道有关所包含模块的所有内容。

其余的再次非常直接。

如果你想在实践中看到我的方法,请阅读本blog post的第5.)节,其中我明确要求模块之间的这种独立性,或者只是在cross-language benchmarks上查看我的github项目。但请注意,您需要一个正在运行的本机编译器工具链,如gcc或Clang来执行它;)

希望这有帮助! 干杯 本

答案 5 :(得分:2)

在早期答案的基础上,这就是我的结果。

interface Includer {
  def include(String... args)
}

def applyFrom(String folder) {
  apply from: folder + '/settings.gradle'
  def includer = [
    include: { args ->
          args.each {
            project(it.startsWith(':') ? it : ':' + it).projectDir =
                new File(rootDir, folder + "/" + (it.startsWith(':') ? it.substring(1) : it).replace(':', '/'))
          }
    }] as Includer

  apply from: folder + '/settings.gradle', to: includer
}

applyFrom('A')
applyFrom('B')

优点是没有重复。