【问题标题】:Multiple settings gradle files for multiple projects building用于构建多个项目的多个设置 gradle 文件
【发布时间】:2012-08-22 15:39:26
【问题描述】:

我有以下项目结构

-->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 看起来像

include 'Project1',
         'Project2'

Project1 settings.gradle 看起来像

include 'SubProject11'
         'SubProject12'

其他依赖顺序在各自的 build.gradle 文件中定义 如果我 rand gradle clean build install 在根位置(标准位置)内,它似乎没有使用项目级别 settings.gradle 文件中的配置。

我在这里做错了什么?

【问题讨论】:

  • 您找到解决方法了吗?

标签: build-automation gradle multi-project


【解决方案1】:

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

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

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

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

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

这样做的问题是每次B添加一个新项目,A/settings.gradle也必须改变,即使这个新项目只被B使用。为了避免这种情况,你可以尝试在applyB/settings.gradle A/settings.gradle 而不是添加多余的声明:

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

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

apply from: 'settings.gradle'

def updateProjectPaths(Set<ProjectDescriptor> projects) {
    projects.each { ProjectDescriptor project ->
        String relativeProjectPath = project.projectDir.path.replace(settingsDir.path, "")
        project.projectDir = new File("B/$relativeProjectPath")
        // Recursively update paths for all children
        updateProjectPaths(project.children)
    }
}

updateProjectPaths(rootProject.children)

这会去除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')

【讨论】:

  • 是否需要合并各个子项目的build.gradle文件。我按照此处的说明进行操作,但我遇到了缺少插件的问题。我的问题中的详细信息stackoverflow.com/questions/37330659/…
【解决方案2】:

由于这个话题现在经常出现在我的日常工作中,并且对它的改进 (GRADLE-803) 仍然开放,我也想分享我的方法:

乍一看,它与 James Wald 的 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)来执行它;)

希望这会有所帮助! 干杯 本

【讨论】:

  • 目录名称是硬编码的,这很好。修改该代码应该非常简单,因此它使用 File.getName() 或类似的东西获取名称。
【解决方案3】:

如果您使用的是 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
        }
    }
}

【讨论】:

    【解决方案4】:

    在之前的答案的基础上,这就是我得出的结论。

    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')
    

    优点是没有重复。

    【讨论】:

      【解决方案5】:

      我也研究过这个,你可以做到,但它很丑!这对我们完全有效的原因是绝大多数时间,我们只想从最顶层构建。

      如果它对您有帮助,您需要做的是让最顶层的 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'
      }
      

      然后你可以运行构建为:gradle -Plocal build

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

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

      【讨论】:

      • 认为没有必要复制设置文件。可能投票较少的答案在这里有所帮助。你还在项目中按照你的建议那样支持它吗?
      • @BenSteinert 如我的回答中所述,虽然我们把它放进去,但没有人使用它,我也不知道什么时候,但在一两次之内它就被删除了。
      【解决方案6】:

      目前,Gradle 每次构建仅支持单个 settings.gradle 文件。这在未来可能会改变。

      【讨论】:

      • 有人知道这是否已经改变了吗?当前的 gradle 版本是 1.10。我没有看到它发生了任何变化,但希望它发生了变化。
      • 它还没有改变。
      • 虽然还不支持,但我还是能得到想要的效果:stackoverflow.com/a/29664571/204480
      • 没有直接支持,但像 Gradle 一样,有一定的可能性:)。感兴趣的用户可以在该主题上跟踪 GRADLE-803 或查看 James 和我在下面的示例。
      猜你喜欢
      • 1970-01-01
      • 2023-03-21
      • 1970-01-01
      • 2014-11-18
      • 1970-01-01
      • 1970-01-01
      • 2015-04-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多