50

我有以下项目结构

-->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 文件中的配置。

我在这里做错了什么?

4

6 回答 6

26

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

尽管 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 使用,也必须更改。为避免这种情况,您可以尝试使用apply B/settings.gradleinA/settings.gradle而不是添加冗余声明:

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

如果你尝试这个,你会发现 Gradle 失败了,因为它生成了错误projectDir:barand :bap。它错误地假设 B 的包含是相对于从该项目根调用 Gradle 时settingsDir发生的。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')
于 2015-04-16T02:57:47.973 回答
13

如果您使用的是 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
        }
    }
}
于 2017-03-06T06:20:58.357 回答
11

Currently, Gradle only supports a single settings.gradle files per build. This may change in the future.

于 2012-08-28T12:28:36.247 回答
7

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

乍一看,它看起来与詹姆斯沃尔德的答案相似,但有一个不同之处。您不需要在子项目中以某种方式拆分设置文件。如果您可以接受的话,有一种干净的方法可以在超级项目中完成所有工作。通常你的小子项目不应该关心周围的超级项目。它们包括它们的子依赖项,仅此而已。模块目录的命名方式也应该是无关紧要的,在 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')

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

剩下的就很简单了。

如果你想在实践中看到我的方法,请阅读这篇博文的第 5 节。我明确要求模块之间的这种独立性,或者只需查看我的跨语言基准测试的 github 项目。但请注意,您需要一个运行的本地编译器工具链,如 gcc 或 Clang 来执行它;)

希望这可以帮助!干杯本

于 2015-06-15T17:09:00.850 回答
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 脚本都将被编译/评估)。

于 2012-08-28T13:45:33.270 回答
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')

优点是没有重复。

于 2015-10-28T16:52:16.883 回答