多项目结构

多项目结构

每一个活跃的项目会随着时间慢慢增长的,一开始可能只是个很小的项目到后面可能包含很多包和类。为了提高可维护性和解藕的目的,你可能想把项目根据逻辑和功能来划分成一个个模块。模块通常按照等级来组织,相互之间可以定义依赖。Gradle 给项目模块化提供了强大的支持,在 Gradle 中每个模块都是一个项目,我们称之为多项目构建。

子项目定义

在 Gradle 中,使用文件 settings.gradle 定义当前项目的子项目,格式如下所示:

include 'sub-project1', 'sub-project2', 'sub-project3'

它表示在当前的项目下建立三个子项目,分别为’sub-project1’, ‘sub-project2’, ‘sub-project3’。默认情况下,每个子项目的名称对应着当前操作系统目录下的一个子目录。当 Gradle 运行时,会根据 settings.gradle 的配置情况,构建一个单根节点的项目树。其中的每个子节点代表一个项目(Project),每个项目都有一个唯一的路径表示它在当前树中的位置,路径的定义方式类似:

Root:<Level1-子节点>:<Level2-子节点>:<Level3-子节点>

也可以简写成 :<Level1-子节点>:<Level2-子节点>:<Level3-子节点>。借助这种路径的定义方式,我们可以在 build.gradle 去访问不同的子项目。另外,对于单项目,实际上是一种特殊的、只存在根节点,没有子节点的项目树。

例如,我们有个产品 A,包括以下几个组件 core,web,mobile。分别代表"核心逻辑"、“网站”、“手机客户端”。因为每个组件是独立的部分,这个时候最好我们能定义多个子项目,让每个子项目分别管理自己的构建。于是我们可以这样定义 A/settings.gradle

include 'core', 'web', 'mobile'

按照之前描述的,core 组件对应 A/core 目录,web 组件对应 A/web 目录,mobile 组件对应 A/mobile 目录。接下来,我们就可以在每个组件内部,定义 build.gradle 负责管理当前组件的构建。

Gradle 提供了一个内建的 task ‘gradle projects’,可以 帮助我们查看当前项目所包含的子项目,下面让我们看看 gradle projects 的输出结果:

$ gradle projects
:projects
------------------------------------------------------------
Root project
------------------------------------------------------------
Root project 'A'
+--- Project ':core'
+--- Project ':mobile'
\--- Project ':web

结果一目了然,首先是 Root 级别的项目 A,然后是 A 下面的子项目’core’, ‘mobile’, ‘mobile’,最终的文件以及目录结构如下所示:

A
   --settings.gradle
   --build.gradle
   --core
     --build.gradle
   --web
      --build.gradle
   --mobile
      --build.gradle

如果你不喜欢这种默认的结构,也可以按照如下方式定义子项目的名称和物理目录结构:

include(':core)
project(':core').projectDir = new File(settingsDir, 'core-xxx')

include(':web)
project(':web').projectDir = new File(settingsDir, 'web-xxx')

include(':mobile)
project(':mobile').projectDir = new File(settingsDir, 'mobile-xxx')

在这个例子中,子项目 core 实际上对应的物理目录为 A/core-xxx,web 实际上对应的是 A/web-xxx,mobile 也类似。虽然我们更改了子项目的物理目录结构,不过由于我们在 build.gradle 中使用的是类似 “ :”的方式访问对应的子项目,所以目录结构的改变,对我们 Gradle 的构建脚本并不会产生影响。

接下来,考虑一个更复杂的情况,随着产品的发展,mobile 这个组件慢慢的划分成了 Android 和 IOS 两个部分,这时我们只需要在目录 A/mobile 下定义新的 settings.gradle,并加入如下部分:

include 'android', 'ios'

现在,mobile 组件下将存在两个新的子项目 “android” 和 “ios”;于是,这时候’gradle projects’的目录结构就变成

A
    --settings.gradle
    --core
      --build.gradle
    --web
      --build.gradle
    --mobile
      --settings.gradle
    --ios
      --build.gradle
    --android
      --build.gradle

子项目任务继承

在 Gradle 中,当在当前项目上执行 gradle <Task> 时,gradle 会遍历当前项目以及其所有的子项目,依次执行所有的同名 Task,注意:子项目的遍历顺序并不是按照 setting.gradle 中的定义顺序,而是按照子项目的首字母排列顺序。基于刚才的例子,如果我们在根目录下,执行 gradle hello,那么所有子项目的“hello” Task 都会被执行。如果我们在 mobile 目录下执行 gradle hello,那么 mobile、android 以及 IOS 的“hello” Task 都会被执行。

对于大多数构建工具,对于子项目的配置,都是基于继承的方式。Gradle 除了提供继承的方式来设置子项目,还提供了另外一种集中的配置方式,方便我们统一管理子项目的信息。下面看一个例子,打开 A/build.gradle,输入如下部分:

allprojects {
    task hello << {task -> println "I'm $task.project.name"
}

subprojects {
    allprojects {
        task hello << {task -> println "I'm $task.project.name"
    }
}

allprojects {
    task hello << {task -> println "I'm $task.project.name" }
}

subprojects {
    hello << {println "- I am the sub project of A"}
}


project(':core').hello << {
    println "- I'm the core component and provide service for other parts."
}

对于上面所示的代码,已经很表意了:

  • allprojects{xxx} 这段代码表示,对于所有的 project,Gradle 都将定义一个名称是 hello 的 Task { println "I'm $task.[project.name](http://project.name/)"}

  • subprojects{xxxx} 的这段代码表示,对于所有的子 project,将在名称为 hello 的 Task 上追加 Action {println "- I am the sub project of A"}

  • project(':core') 的这段代码表示,对于名称为 core 的 project,将在名称为 hello 的 Task 上追加 Action { println "- I'm the core component and provide service for other parts." }

下一页