美文网首页Gradle
Gradle初级教程

Gradle初级教程

作者: 坚持到底v2 | 来源:发表于2017-12-28 08:57 被阅读403次

    一、安装

    https://gradle.org/install

    1. 前提 JDK 7+

    2. 下载安装

    tmpHomePath="/home"
    tarName="gradle-4.0-all.zip"
    baseUrl="https://services.gradle.org/distributions"
    
    # 下载
    curl -o "${tmpHomePath}/${tarName}" ${baseUrl}/${tarName} 
      
    # 解压
    unzip -d ${tmpHomePath} ${tmpHomePath}/${tarName} 
    dirName=$(unzip -l -qq ${tmpHomePath}/${tarName} | head -1 | awk '{print $4}')
    dirName=${dirName/%\//}
      
    # 设置环境变量
    echo "
    export GRADLE_HOME=${tmpHomePath}/${dirName}
    export PATH=\$GRADLE_HOME/bin:\$PATH
    " >> /etc/bashrc
    sed -i '1a\GRADLE_OPTS="\${GRADLE_OPTS} -Xmx1024m"' ${tmpHomePath}/${dirName}/bin/gradle
    
    # 验证
    gradle -v
    

    2.1 使用gradlew

    使用gradlew是为了保证编译项目时使用的gradle版本一致

    # cd 到项目目录
    # 生成 gradle wrapper 文件,设定期望的 gradle版本
    gradle wrapper --gradle-version=4.0 --distribution-type=bin
    # 或者如果已经生成过gradlew,也可以使用gradlew命令
    ./gradlew wrapper --gradle-version=4.0 --distribution-type=bin
    
    # 然后使用gradlew命令时会下载期望版本的gradle到./.gradle/中
    ./gradlew tasks
    

    也可以在build.gradle中设定wrapper task如下(当然除了版本信息还可以设置配置文件中的其他信息)

    task wrapper(type: Wrapper) { gradleVersion='4.0' } 
    

    然后执行

    gradle wrapper # 或 
    ./gradlew wrapper
    

    2.2 设置代理

    针对单个项目设置gradle代理或针对所有的gradle项目设置代理,
    可以编辑或新建 项目中的./gradle.properties文件或~/.gradle/gradle.properties ,
    增加如下配置项:(不能使用变量,只能直接设定)

    systemProp.http.proxyHost=10.0.86.71
    systemProp.http.proxyPort=31888
    systemProp.https.proxyHost=10.0.86.71
    systemProp.https.proxyPort=31888
    

    当然也可以在命令行上指定要使用的代理

    proxyipiponly=10.0.86.71
    proxyport=31888
    ./gradlew -Dhttps.proxyHost=${proxyipiponly} -Dhttps.proxyPort=${proxyport} \
    -Dhttp.proxyHost=${proxyipiponly} -Dhttp.proxyPort=${proxyport} \
    --no-daemon \
    tasks
    

    gradlew 如果想使用本地的gradle软件,而不用再去网上下载,
    可以修改 ./gradle/wrapper/gradle-wrapper.properties 文件中的distributionUrl属性,
    该属性定义了从哪里获取gradle的distribution文件,
    同时该文件也定义了zip存储的位置和distribution存储的位置,
    一般是 GRADLE_USER_HOME/wrapper/dists 。一般而言 GRADLE_USER_HOME~/.gradle 目录


    二、开始使用gradle

    1. 基本命令和参数

    显示帮助

    gradle help
    

    列出依赖情况

    gradle dependencies
    
    # 解释在依赖图中一个依赖如何被选择,为什么会被选择
    gradle dependencyInsight 
    
    # 检查特定依赖
    gradle dependencyInsight --dependency apache-commons 
    
    # 检查compile依赖以外的依赖
    gradle dependencyInsight --configuration 
    

    查看可用的tasks

    gradle tasks
    
    # -q 表示以安静模式运行
    gradle -q tasks 
    
    # --all 表示所有的tasks和更多细节
    gradle -q task --all 
    

    查看项目可用的属性

    有些属性是由Gradle的Project对象提供的。
    其他属性是用户自定义的属性,可能来自于属性文件、属性命令行选项或直接在构建脚本中定义

    gradle properties
    

    查看 task的更多细节

    gradle help --task <task>
    

    运行task

    gradle <task> ...
    
    构建设置类 task
    # 通过创建build.gradle和settings.gradle来初始化一个Gradle项目,设置wrapper文件. Gradle会尝试从pom.xml中获取项目信息
    gradle setupBuild 
    
    # 为Java项目创建一个标准的build.gradle,项目中不能有pom.xml
    gradle generateBuildFile 
    

    显示命令行选项

    gradle --help
    
    # 安静模式
    gradle -q | --quiet
    
    # 指定编译文件,默认是 build.gradle
    gradle --build-file | -b build.gradle
    
    # 指定设置文件,默认是 settings.gradle
    gradle --settings-file | -c settings.gradle
    
    # 设定JVM系统参数
    gradle -D | --system-prop key=value
    
    # 设定项目参数
    gradle -P | --project-prop key=value
    
    # 输出更多的日志信息
    gradle -i | --info | -d | --debug
    
    # 打印异常的堆栈跟踪信息
    gradle -s | --stacktrace | -S | --full-stacktrace
    
    # 在一个task执行失败后继续执行
    # 在这多项目构建中很有用,它会让你在构建时发现所有可能的问题,并一起修复它们
    gradle --continue
    
    # 指定 gradle-user-home,默认是~/.gradle目录
    gradle --gradle-user-home | -g ~/.gradle
    
    # 设置一个初始化脚本,这个脚本会在每个task执行之前执行
    gradle --init-script | -I someScript
    
    # 设置构建目录,默认是当前目录
    gradle --project-dir | -p someDir
    
    # 并行构建参与多项目的子项目
    gradle --parallel
    
    # 指定并行构建时的线程数
    gradle --parallel-threads
    
    # 打印task的执行顺序,而不是真正执行它们
    gradle --dry-run | -m
    
    # 重新运行task,忽略task的UP-TO-DATE状态
    gradle --rerun-task
    
    # 告诉Gradle不要在父目录寻找设置文件(settings.file),从而节约时间
    gradle --no-search-upwards | -u
    
    # 指定排除运行的task,例如不想执行单元测试可以 gradle -x test build
    gradle --exclude-task | -x
      
    
    # 每次初始化一个构建都要启动一次JVM
    # 所以gradle守护进程应运而生
    gradle --daemon
    # 然后可以使用 ps -ef|grep gradle
    # daemon进程会在3小时空闲期后自动过期
    
    # 不使用守护进程编译
    gradle --no-daemon
    
    # 关闭守护进程
    gradle --stop
    
    # 注意!!: gradle默认就是使用的daemon模式
    # 所以使用-D设定系统属性时要考虑使用--no-daemon或先关闭daemon,以防止-D设置的系统属性(例如代理属性)不生效
    
    # 以前台形式运行Gradle守护进程,用于调试和监控
    gradle --foreground
    
      
    # 缓存选项
    # 仅仅在本地缓存中检查依赖
    gradle --offline
    
    # 强制检查依赖的版本
    gradle --refresh-dependencies
    
    # 设置项目缓存目录,默认为~/.gradle
    gradle --project-cache-dir
    
    #  清空缓存
    gradle --recompile-scripts
    

    2. Groovy简单介绍

    PDF下载: 链接:http://pan.baidu.com/s/1bo1bQmr 密码:hj63

    2.1 优化:

    • 表达式后面的分号是可选的
    • 类、构造器和方法默认都是public的
    • 方法体中的最后一个表达式的值会被作为返回值,return语句是可选的
    • getter和setter是自动生成的. 类的属性通过.来获取,但实际执行的是getter和setter方法。
    • == 实际调用的是 equals()方法,避免了可能的NullPointerExceptions.

    2.2 Groovy Web终端

    http://groovyconsole.appsot.com

    2.3 assert

    def version=12
    assert version==12
    

    如果assert失败,Groovy会提供有用的输出用于查找问题的根源

    2.4 变量类型

    Groovy不强制你显式声明变量类型、方法参数和返回类型.
    如果一个方法没有返回值,则应该声明void而不是def作为返回类型,
    def表示Object占位符.
    当然你也可以使用强类型来声明

    def aStr='Gradle'
    assert aStr.class==java.lang.String
    def add(a,b) {
      return a+b
    }
    

    2.5 可选的括号

    如果方法的参数至少有一个时,可以省略括号,例如

    add 2,3
    

    2.6 字符串

    可以使用 "' 括起来,
    也可以使用 """''' 声明多行字符串,
    当然你也可以使用 \n? 在行尾加 \ 来生成多行字符串,
    但也许你会喜欢'''这种方式

    2.6.1 ${}引用

    GString 支持类似于shell中的$var的引用方式,
    字符串必须使用 " 而不是 ' , 例如

    aa='aa';
    bb="$aa bb";
    println bb 
    

    其实$的完整用法应该是 ${} ,花括号中是Groovy表达式.

    2.7 List

    使用[],例如

    def aList=['a','b']
    assert aList.class==java.util.ArrayList
    assert aList.size()==2
    assert aList[1]=='b'
    
    //追加元素
    aList << 'c'
    
    //遍历aList
    aList.each { tmpStr ->
     println tmpStr
    }
    
    def aList=(1..4)
    //等价于[1,2,3,4]
    
    aList=aList+[1]
    // [1,2,3,4,1]
    
    aList=aList-[1]
    //[2,3,4]
    
    aList=[2,3,4]*2
    //[2,3,4,2,3,4]
    
    aList=[1,[2,3]].flatten()
    //[1,2,3]
    

    2.8 Map:

    例子:

    
    def aMap=[key1:1,"key2":2]
    //表示key的字符串的引号可省略,表示value的字符串的引号不可以省略
    
    
    assert aMap.getClass()==java.util.LinkedHashMap
    assert aMap.size() == 2
    assert aMap.key1==1
    assert aMap['key1']==1
    
    //增加kv
    aMap['key3']=3
    
    //遍历Map
    aMap.each { tmpKey,tmpVal ->
     println "key:$tmpKey  value:$tmpVal"
    }
    

    2.9 命名参数

    如果类没有定义构造器,那么Groovy默认实现了一个接收Map的构造器,
    它会遍历map中的key然后调用类中对应的setter函数.
    例如

    class TestClass {
      Integer major
    }
    
    TestClass test=new TestClass(major:1)
    //其实接收的是一个map参数
    //test=new TestClass('major':1)
    assert test.major==1
    

    2.10 闭包

    闭包总是会返回一个值(最后一条语句的值),
    例子:

    def add1= {Integer aInt ->
      //声明闭包参数,Integer声明了参数为强类型,可以省略
    
      ++aInt
      //最后一条语句的值作为返回值
    }
    def add1={
      ++it
      //不声明闭包参数时,it代表了第一个传入的参数
    }
    

    闭包的类型为Closure

    2.11 闭包委托

    闭包代码在委托的闭包上执行,默认这个委托就是闭包的所有者,
    例如你在Groovy脚本中定义了一个闭包,那么所有者就是一个groovy.lang.Script实例.
    闭包的隐式变量delegate允许你重新定义所有者.
    例子:

    class ProjectVersion {
      Integer major
      void increment(Closure closure) {
        closure.resolveStrategy=Closure.DELEGATE_ONLY
        closure.delegate=this
        closure()
      }
    }
    ProjectVersion pv=new ProjectVersion(major:1)
    pv.increment { major+=1 }
    

    2.12 Groovy开发工具库 (GDK)

    网址: http://groovy.codehaus.org/groovy-jdk/
    有很多关于String、Collection、File和Stream相关的有用的方法。
    例子:

    assert 'gradle'.capitalize()=='Gradle'
     new File('build.gradle').eachLine { line ->
      println line
     }
    

    2.13 Gradle脚本中的Groovy

    每个构建脚本都至少有一个对应的org.gradle.api.Project实例。
    大多数情况下,在构建脚本中调用的属性和方法都自动委托给了这个Project实例。
    例子:

    apply plugin:'java'
    //等价于调用Project的apply方法,该方法接收一个只有单键值对的map
    
    
    project.apply ['plugin':'java']
    
    version='0.1'
    //等价于 project.setVersion('0.1')
    
    repositories {
      mavenCentral()
    }
    //等价于调用 project.repositories 方法,并传递一个闭包给它.
    
    
    dependencies {
      compile 'commons-codec:commons-codec:1.6'
    }
    //等价于调用 project.dependencies方法,并传递一个闭包给它
    

    三、gradle脚本

    1. 脚本示例

    创建一个名为build.gradle的文件

    task helloWorld {
      //doLast和doFirst可以多次添加,执行顺序是doLast后添加的后执行,doFirst后添加的先执行
      doLast {
         println 'Hello world!'
      }
    }
    helloWorld.doFirst {print "first"}
    helloWorld << {
      //在task方法中可以直接访问project的logger实例和vesion属性
      //如果要访问group和description属性,需要使用project.group,因为Task实例也有自己的group属性和description属性
      logger.quiet "Version:$version"
    }
    task hello2(denpendsOn: helloWorld)
    task hello3
    hello3.dependsOn hello2,helloWorld
    

    2. 构建脚本

    2.1 构件块

    每个构建脚本都包含3个基本构建块: project、task、property

    project

    project 对应org.gradle.api.Project类,并可以通过project变量使其隐式可用.
    也就是说不需要指定project变量,
    例如可以直接使用setDescription("xx")
    Project类的方法有:

    apply(options:Map<String,?>),
    buildscript(config:Closure),
    dependencies(config:Closure),
    configurations(config:Closure),
    getDependencies(),
    getConfigurations(),
    getAnt(),
    getName(),
    getDescription(),
    getGroup(),
    getPath(),
    getVersion(),
    getLogger(),
    setDescription(descString),
    setVersion(version:Object),
    file(path:Object),
    files(paths:Object...),
    fileTree(baseDir:Object),
    task(nameStr,Closure),
    task(nameStr),
    task(args:argsMap,nameStr,Closure)
    
    task

    task对应org.gradle.api.Task接口的实例,
    默认为org.gradle.api.DefaultTask类实例,
    该接口的方法有:

    dependsOn(tasks:Object...)
    doFirst(action:Closure),
    doLast(action:Closure),
    getActions()
    getInputs(),
    getOutputs()
    getAnt(),
    getDescription(),
    getEnabled(),
    getGroup(),
    setDescription(descStr),
    setEnabled(boolean),
    setGroup(grpStr)
    

    2.2 扩展属性

    //只有在初始声明的时候需要使用ext命名空间,引用属性时可以不加ext
    project.ext.myProp='myValue'
    ext { someOtherProp=123 }
    
    通过属性文件提供扩展属性

    可以在 ~/.gradle/gradle.properties 或 ./gradle.properties 中直接声明属性,如下所示:

    exampleProp=myValue
    someOtherProp=123
    

    然后可以直接在构建脚本中使用该属性,不用加ext.

    通过命令行提供属性

    -P=项目属性 -D=系统属性

    通过环境变量设置

    ORG_GRADLE_PROJECT_propertyName=someValue

    2.3 task依赖

    设置依赖
    task hello4(dependsOn:[hello1,hello2]);
    
    增加依赖
    hello4.dependsOn hello3;//或者 hello4.dependsOn(hello3) ,引号可以不加
    

    多个依赖的执行顺序不是添加顺序,
    它们的执行顺序是内置的,可以认为是不确定的

    task依赖还可以使用隐式引用的方式,
    例如在task中使用其他task的输出作为自己的输入,
    也可以让Gradle来推断task之间的依赖

    2.4 task终结器

    hello1.finalizedBy hello2
    

    2.5 task配置块

    配置块永远在task动作执行之前被执行。
    无论何时执行Gradle构建,都会运行3个不同的生命周期阶段: 初始化、配置和执行

    在初始化阶段,Gradle为项目创建了一个Project实例.
    在多项目构建中,Gradle会找出哪些项目依赖需要参与到构建中。

    在配置阶段,Gradle构造了一个模型来表示任务,并参与到构建中来.
    增量式构建特性决定了模型中的task是否需要被运行.
    这个阶段非常适用于为项目或指定task设置所需的配置.

    在执行阶段,task被执行.
    如果任务被认为没有修改过,将被跳过. 即 声明task的inputs和outputs.

    DefaultTask类的inputs对应的是TaskInputs接口,
    该接口有4个方法:

    dir(dirPath:Object),
    file(path:Object),
    files(paths:Object...),
    property(name:String,value:Object)
    

    即输入可以是一个目录,一个或多个文件或一个任意属性.

    DefaultTask类的outputs对应的是TaskOutputs接口,
    该接口有3个方法:

    dir(dirPath:Object),
    file(path:Object),
    files(paths:Object...),
    upToDateWhen(Closure)
    

    即输出可以是一个目录,一个或多个文件,或一个闭包执行的结果
    (该闭包是在task执行阶段才执行,如果返回true则表明outputs是upToDate状态).

    第一次执行task后,生成inputs和outputs内容后,只要输入和输出都没有发生过变化,那么task就会被认为是UpToDate状态,并且会跳过执行.

    2.6 编写和使用自定义Task

    class MyTask extends DefaultTask {
      //使用注解声明task的inputs和outputs
      @Input Boolean release;
      @OutputFile File destFile;
      
      //使用注解声明将被执行的方法
      @TaskAction
      void start() {
        //...
      }
    }
    
    task myTask(type:MyTask) {
      release=version.release
      destFile=versionFile
    }
    

    2.7 Gradle内置的task类型

    都是DefaultTask的派生类,例如Zip和Copy,
    例如:

    task createZip(type:Zip,dependsOn:makeReleaseVersion) {
      //使用war这个task的outputs作为自己的输入,达到隐式依赖
      from war.outputs.file; 
      //...other code...
    }
    

    2.8 task规则命名模式 动态生成task

    tasks.addRule("规则的描述信息") {String taskName ->
      if (taskName.startsWith("incre") && taskName.endsWith("Version")) { //根据预定义模式检查task名称
        task(taskName) << { //动态生成task,并增加一个doLast方法
          String classifier=(taskName-'incr'-'Version').toLowerCase();//从完整的taskName中抽出动态的部分,以便进行不同的task处理
        //...
        }
      }
    }
    

    使用tasks.addRule增加的task在 gradle tasks中位于 Rules下

    2.9 自定义的类放在buildSrc目录下!

    Gradle会自动编译 buildSrc/src/main/groovy/目录下的文件,并加入到构建脚本的classpath中.

    2.10 编写构建生命周期的回调事件

    编写构建生命周期的回调事件 有2种方式:
    闭包(注册钩子回调)
    或 通过Gradle API所提供的监听器接口.

    闭包实现的方式

    例如
    在配置阶段之前的回调 gradle.beforeProject{project -> }
    在执行阶段之前的 gradle.taskGraph.whenReady{ graph -> }
    在执行阶段之后的 gradle.buildFinished(result -> }

    在配置阶段Gradle决定了在执行阶段task的执行顺序DAG,
    注意这个DAG没有一个闭环,即每个task只能被执行一次.
    这个DAG可以使用一个TaskExecutionGraph接口表示.

    project实例有一个方法 getGradle()
    该方法返回一个Gradle接口实例,
    Gradle接口有一个方法 getTaskGraph()
    该方法返回一个 TaskExecutionGraph接口实例,
    TaskExecutionGraph接口有一个方法 whenReady(Closure)
    该方法实现了生命周期钩子.
    例子:

    gradle.taskGraph.whenReady(TaskExecutionGraph taskGraph ->
     if (taskGraph.hasTask(release) { //判断task执行图中是否包含release,如果包含
      if (!version.release) { //如果version.release属性不是true
        version.release=true
        ...
      }
     }
    }
    

    如何通过Gradle API所提供的监听器接口挂接到构建生命周期呢?
    Gradle接口有一个方法 addListener(listener:Object) 、
    TaskExecutionGraph接口有一个方法 addTaskExecutionGraphListener(listener:TaskExecutionGraphListener) 用于实现监听器的注册,

    所以你需要做的就是实现TaskExecutionGraphListener这个接口,并注册.
    例子:

    class ReleaseVersionListener implements TaskExecutionGraphListener {
      final static String releaseTaskPath=':release'
      @Override
      void graphPopulated(TaskExecutionGraph taskGraph){ //实现接口方法
       if (taskGraph.hasTask(releaseTaskPath)) {
         List<Task> allTasks=taskGraph.allTasks;
         Task releaseTask=allTasks.find {it.path==releaseTaskPath}
         Project project=releaseTask.project;
         if (!project.version.release) {
          project.version.release=true;
        ...
         }
       }
      }
    }
    def listener=new ReleaseVersionListener()
    gradle.taskGraph.addTaskExecutionGraphListener(listener)
    

    2.11 初始化构建环境:

    例子: Gradle有一个核心插件 build-announcements ,这个插件可以在构建完成后发送通告到本地通知系统.
    你可以在每个 build.gradle 中都增加如下内容来应用这个插件

    apply plugin:'build-announcements'
    

    但是你可以有更好的方式,那就是在 ~/.gradle/init.d/ 文件夹下创建初始化脚本,
    Gradle会执行该目录下所有以 .gradle 为扩展名的初始化脚本.
    我们可以新建一个 build-announcements.gradle 脚本文件,
    然后在其中增加如下内容:

    gradle.projectsLoaded { Gradle gradle ->
     gradle.rootProject {
      apply plugin:'build-announcements'
     }
    }
    

    3. 依赖管理

    3.1 理解配置

    配置可以直接在项目的根级别添加和访问;
    你可以使用插件所提供的配置,或声明自己的配置.

    project实例的 configurations(Closure) 方法可以添加配置,
    getConfigurations() 获取一个 ConfigurationContainer 接口实例.

    ConfigurationContainer接口提供了 add(name:String)getByName(name:String) 方法来增加或获取一个 Configuration 接口实例.
    Configuration接口提供了 getDependencies() 等方法.

    project实例的 dependencies(c:Closure) 可以为配置增加依赖, getDependencies() 获取一个DependencyHandler 接口实例.
    获取一个DependencyHandler接口提供了 add(configName:String,depNotation:Object,c Closure) 来为某个配置增加依赖...

    3.2 排除传递性依赖

    dependencies {
     cargo 'org.codehaus.cargo:cargo-ant:1.3.1' {
       exclude group:'xml-apis',module:'xml-apis'
     }
     cargo 'xml-apis:xml-apis:2.0.2'
    }
    

    3.3 排除所有的传递性依赖

    dependencies {
     cargo 'org.codehaus.cargo:cargo-ant:1.3.1' {
       transitive=false 
     }
    }
    

    3.4 动态版本声明

    dependencies {
     cargo 'org.codehaus.cargo:cargo-ant:1.+'
    }
    

    3.4 文件依赖

    dependencies {
     compile fileTree(dir: 'libs', include: '*.jar')
    }
    

    3.5 配置仓库

    repositories {
     mavenCentral()
     mavenLocal()
     maven {
       name 'Custom Maven Repository'
       url 'http://.../release/'
     }
     flatDir (dir:'libs',name:'local libs dir')
    }
    

    3.6 Gradle缓存的依赖存储在哪里了?

    ~/.gradle/caches/artifacts-15/filestore/ 目录下,
    artifacts-15是一个标识符,用来指定Gradle版本,可以打印如下内容来查看

    project.configurations.getByName('cargo').each { dependency ->
      println dependency
    }
    

    3.7 应对依赖版本冲突1: 遇到冲突问题是让构建失败

    configurations.cargo.resolutionStrategy {
     failOnVersionConflict()
    }
    

    3.8 强制指定一个版本

    configurations.cargo.resolutionStrategy {
     force 'org.codehaus.cargo:cargo-ant:1.3.0'
    }
    

    3.9 手动刷新缓存

    命令行选项 --refresh-dependencies

    3.10 对依赖的SNAPSHOT版本和动态版本模式声明的依赖

    gradle 针对 SNAPSHOT版本和动态版本模式声明的依赖 的默认的缓存策略是 缓存24小时,
    你可以更改它:

    configurations.cargo.resolutionStrategy {
     cacheDynamicVersionsFor 0,'seconds' //动态版本模式声明的依赖的缓存 0秒超时
     cacheChangingModulesFor 0,'seconds' //SNAPSHOT版本的依赖的缓存 0秒超时
    }
    

    4. 多项目构建

    在多项目构建中是通过 settings.gradle 文件来声明子项目的.
    其内容一般是:

    include 'model','repository','web' 
    //传递的是子项目路径,不是文件路径
    
    include 'model:todo:items'
    //构建更深层次的项目结构
    

    settings.gradle对应的是Settings接口实例.
    你可以在settings.gradle文件中面向Settings接口进行编码,
    Settings中的任何方法都可以被直接调用,就像include一样.

    settings.gradle被加载和解析后,如果你需要在build.gradle中访问Settings实例,可以注册一个生命周期闭包或监听器.
    gradle.settingsEvaluated(Closure)是一个不错的起点,它提供了Settings对象作为闭包参数.

    4.1 settings 是在初始化阶段被执行

    4.2 找到settings.gradle

    只要项目根目录或任何子项目目录中包含build.gradle,Gradle就允许你从相应位置进行构建.
    那么Gradle如何知道一个子项目是一个多项目构建的一部分呢?
    首先在当前目录下寻找settings.gradle,
    如果没有找到就在父目录中查找,
    如果找到了settings.gradle,并在它的定义中包含了这个项目,那么该项目就被认为是多项目构建的一部分,
    否则这个项目将作为一个单项目构建.

    你可以通过命令行参数来控制该搜索行为:
    --no-search-upward:不去父目录查找settings.gradle,
    --settings-file:指定settings文件名称.

    4.3 分层布局与扁平布局

    分层布局是指子项目作为根项目的子目录存在;
    扁平布局是指子项目和根项目平级存在,
    相应的扁平布局的settings.gradle文件内容一般是:

    //使用includeFlat方法
    includeFlat 'model','repository','web' 
    

    4.4 配置子项目:

    • (1) 根项目和所有子项目应该使用相同的group和version属性
    • (2) 在子项目之间建模依赖关系

    4.5 与多项目构建相关的Project API:

    (1) 特定的项目配置:

    project(path:String)
    project(path:String,config:Closure)
    path使用 :model 在子项目前加:的方式

    (2) 公共的项目配置:

    allprojects(action:Action<? super Project>)
    allprojects(action:config:Closure)
    subprojects(action)
    subprojects(action:config)

    (3) 项目解析顺序:

    evaluationDependsOn(path:String)
    evaluationDependsOnChildren()

    在多项目构建中,项目的默认执行顺序是基于它们的字母名称,
    为了显式地控制在构建生命周期的配置阶段的执行顺序,你可以使用前面的2个方法.

    4.6 属性继承

    在一个项目中定义的属性会自动被其子项目继承.

    4.7 执行子项目的task

    例子:
    执行model子项目的build task
    gradle :model:build

    4.8 声明项目依赖:例子:

    project (':repository'){
     //...
     dependencies {
      compile project(':model')
     }
    }
    

    有了依赖声明,Gradle在初始化阶段之后就有了一个项目依赖的内部模型,
    你就不需要从一个特定的子项目执行task,你可以为构建的所有子项目执行task.
    假设你只执行了

    //为所有子项目执行 build task
    gradle build 
    

    你会看到期望的执行顺序:
    先执行 :model 子项目的task到build,
    然后是 :repository 子项目的task到build

    4.9 部分构建

    通过命令行选项 --no-rebuild,例子:

    gradle :repository:build --no-rebuild
    #你会看到不再执行 :model 子项目的构建
    
    #同时构建和测试当前项目所依赖的项目
    gradle :repository:buildNeeded
    
    #同时构建和测试依赖当前项目的项目
    gradle :repository:buildDependents 
    

    4.10 如果多个子项目中有相同名称的task,而且它们之间没有依赖关系,那么它们的执行顺序是什么样的?

    首先执行的是根项目的task,
    然后是按项目名称的字母顺序执行.

    你可以通过跨项目的task依赖来确定task执行顺序,
    例如:

    project(':model') {
     task hello(dependsOn:':repository:hello') << {
       println 'hello from model project'
     }
    }
    

    4.11 定义公共行为:

    allprojects {
     group='com.manning.gia'
     version='0.1'
    }
    subprojects {
     apply plugin:'java'
    }
    

    4.12 到目前为止,我们所定义的多项目构建只包含一个build.gradle和settings.gradle.

    你可以为每个子项目创建单独的build.gradle.

    4.13 自定义 build.gradle 文件名称.

    settings.gradle 例子:

    include 'model','repository','web'
    rootProject.name='todo'
    rootProject.children.each {
     it.buildFileName=it.name+'.gradle'
    }
    

    5. Gradle测试

    5.1 测试配置

    java插件引入了2个新的配置: testCompile和testRuntime,
    例子:

    dependencies {
      testCompile 'junit:junit:4.11'
    }
    

    testCompile不影响编译后的classpath,但是它扩展了compile依赖,

    同样,testRuntime扩展了runtime依赖,同时testRuntime也扩展了testCompile依赖

    6. 插件开发:

    插件分为2类:
    1类是脚本插件 使用 apply from:'cloudbees.gradle' //脚本插件可以是任何uri,包括http和https
    2类是对象插件 实现 org.gradle.api.Plugin 接口,对象插件的源代码一般放在buildSrc目录下.

    6.1 脚本插件

    脚本插件其实就是把task定义到其他脚本文件中,
    然后使用 apply from:'cloudbees.gradle' 引入其脚本,
    然后就可以直接使用脚本中定义的task

    buildscript(Closure)

    buildscript的作用是为了在构建脚本中直接使用外部类库(不是编译工程文件时使用的外部类库).
    该依赖定义在classpath配置分组中,
    例如:

    buildscript {
     repositories {
       mavenCentral();
     }
     dependencies {
       classpath 'com.cloudbees:cloudbees-api-client:1.4.0'
     }
    }
    

    6.2 定制task

    继承DefaultTask,实现自己的Task类,
    在 buildSrc目录下创建自己的Task类,
    其下的目录有src/main/groovy和src/main/java,
    还有自己的build.gradle文件,
    例如如果需要使用外部类库则需要在build.gradle中加入:

    repositories {
      mavenCentral();
    }
    dependencies {
      compile 'com.cloudbees:cloudbees-api-client:1.4.0'
    }
    

    然后继承DefaultTask类并使用@TaskAction声明task的动作,
    例如:

    package com.test.ludq
    class CloudBeesAppInfo extends DefaultTask {
     @taskAction
     void start() { //函数名称可以任意,只要不是execute()
       println "hello"
       //...
     }
    }
    

    使用自定义的Task类时,如下所示:

    import com.test.ludq.CloudBeesAppInfo //导入需要使用的类
    task cloudBeesAppInfo(type:CloudBeesAppInfo) {
      //...some configure code...
    }
    

    6.3 定制plugin:

    继承Plugin<Project>接口

    package com.ludq.test
    import org.gradle.api.Plugin
    import org.gradle.api.Project
    import org.gradle.api.DefaultTask
    class CloudBeesPlugin implments Plugin<Project> {
      @Override
      void apply(Project project) {
        //可以对现有的任务进行赋值
        //可以创建新的task
      }
    }
    

    还可以对约定和配置进行扩展
    在构建脚本 build.gradle 中声明一个闭包(扩展可以被添加到许多Gradle对象中,例如Project和Task都是扩展可知的),

    cloudBees {
     apiUrl='https://api.cloudbees.com/api'
     apiKey=project.apiKey
    }
    

    然后在自定义Plugin中使用

    project.extensions.create('cloudBees',CloudBeesPluginExtension) // CloudBeesPluginExtension是一个POJO类
    def extension=project.extensions.findByName('cloudBees');
    conventionMapping.apiUrl= { extension.apiUrl } //每个继承DefaultTask类的Task都有conventionMapping这个属性,使用这个属性将扩展模型的值赋给task的输入或输出字段.通过将扩展模型值包装成一个闭包,实现惰性赋值,为了获取存储在约定映射中的一个属性值,需要显式使用getter方法,否则只会返回一个null值.
    

    还可以给插件定义一个简称,
    可以在 src/main/META-INF/gradle-plugins/ 目录下新建一个文件,
    例如 helloplugin.properties ,该文件暴露的就是插件简称,
    其内容是

    implementation-class=com.ludq.test.HelloPlugin
    

    使用自定义的Plugin类,

    apply plugin:'java' //使用插件的简称要加引号
    apply plugin:org.gradle.api.plugins.JavaPlugin //使用插件的全称不要加引号
    

    Gradle标准类库提供在Gradle安装目录下的 lib/plugins 目录中
    非标准的外部插件需要使用 buildscript 声明repositories和dependencies

    7. 代码质量管理和监测

    7.1 JaCoCo插件:

    JavaCodeCoverage,通过运行时二进制代码检测方法来测量代码覆盖率.
    JaCoCo在JVM类加载器上附加了一个Java代理,这个代理收集指令执行信息,并将这些信息写入到文件中.
    支持Java7项目. 执行test类型的task的时候,JaCoCo的代理会针对运行的测试类来收集相关的运行时信息.

    7.2 Cobertura插件:

    采用对编译后的二进制代码添加指令的方式来工作.
    不支持Java7项目.
    该插件引入了两个新的task,
    其中一个用来将检测指令插入到编译后的类文件中;
    另一个用来生成代码覆盖率报告. 执行check task.

    7.3 Checkstyle插件:

    定义统一的源代码格式、结构和注解,以便代码可读、可维护,这就是Checkstyle的作用.

    7.4 PMD插件:

    关注无用代码或重复代码、过于复杂的代码以及可能的bug.

    7.5 FindBugs插件:

    它要发现的潜在bug包括:
    equals/hashCode实现问题、
    多余的null检查,甚至是性能问题.

    FindBugs操作的是Java二进制代码,而不是源代码,这带来的结果就是更耗时.

    7.6 JDepend插件:

    用来衡量代码的质量设计产生分析结果,
    它会扫描Java代码的所有包、计算类和接口的数量,这个信息会帮助你识别不必要的组件或强耦合部分.

    7.7 集成Sonar:

    你已经知道了如何使用各种代码分析工具为项目做代码检查和分析,这些工具所提供的报告都需要单独做检查. 在每个构建当中,已经存在的报告可能会被删除,并且有新的报告被创建,所以你很难看出代码在一段时间内是提高了还是降低了,你需要一个工具能够集中监控、可视化和整合报告信息.Sonar应运而生.
    Sonar能和大部分工具很好的集成,
    对于非常规的工具或者语言,Sonar也可以通过插件的形式扩展.
    Gradle通过Sonar Runner插件和Sonar很好地集成.

    相关文章

      网友评论

        本文标题:Gradle初级教程

        本文链接:https://www.haomeiwen.com/subject/ynkxpxtx.html