• gradle学习之旅(六) 使用Task(上)


    本节以软件项目的版本管理作为例子来学习gradle的task的使用

    gradle构建生命周期

    想要理解task的使用方法,理解gradle'build lifecycle phases(gradle构建生命周期各阶段)是至关重要的,无论什么时候执行gradle构建,都会运行三个不同的生命周期:如下

    • 初始化阶段(Initialization phase)
      这个阶段,gradle在内存中为项目创建了一个Project实例。根据在执行的项目,gradle找出哪些项目依赖需要参与到构建中,在这个阶段当前已有的构建脚本代码并不会被执行。
    • 配置阶段(Configuration phase)
      初始化阶段完成后,进入配置阶段。在这个阶段gradle构造任务的实例,所以这个阶段非常适合于为项目或task设置所需的配置。
      需要明确,写在gradle构建脚本中的代码分两种:配置代码和动作代码
      • 配置代码(Configuration code):
        在配置阶段执行的代码,除动作代码之外的代码都是配置代码。注意,项目每一次构建的任何配置代码都可以被执行,即使你只指定gradle tasks
      • 动作代码(Action code)
        Task接口提供了两个相关的方法来声明task动作:doFirst(Closure)和doLast(Closure),当task被执行时,动作逻辑被定义为闭包参数被依次执行。
    • 执行阶段(Execution phase)
      在执行阶段,所有的task的动作逻辑都应该以正确顺序被执行,执行顺序由它们的依赖决定的。如果任务被认为没有修改过,则将被跳过。

    声明task动作(action)

    version = '0.1-SNAPSHOT'
    task printVersion{
    	doLast{
    	 println "Version: $version"
    	}
    }
    

    添加动作

    printVersion.doFirst { println "First action" }
    printVersion << { println "Last version" }
    

    在内部,每个task都保持了一个动作列表,在运行时,他们按顺序执行。

    访问DefaultTask属性

    关于DefaultTask的全部属性可以通过查看源代码或者在官网的gradleAPI文档查看。
    Gradle的Project和Task都提供了一个logger属性,logger是一个基于SLF4J日志库的logger实现。除了实现常规范围内的日至级别(DEBUG、ERROR、INFO、TRACE、WARN)之外,Gradle还增加了一些额外的日至级别。直接调用logger,就相当于调用它的get方法。(牢记在groovy中访问属性是groovy提供的语法糖)

    下面使用logger打印QUIET日志级别版本号

    task printVersion << {
    	logger.quiet "Version: $version"
    }
    

    Task的属性group制定了该task的分组,description指定task描述。在使用命令gradle tasks时可以看到task的分组与描述。在创建时可以用如下方式给task的属性赋值:

    task printVersion(group: 'versioning',description:'Prints project version'){
        logger.quiet "Version:$version"
    }
    

    或者去掉语法糖

    task ([group: 'versioning',description:'Prints project version'],'printVersion',{
        logger.quiet "Version:$version"
    })
    

    或者直接在委托闭包中使用set来赋值

    task printVersion{
    	group = `versioning`
    	description = 'Prints project version.'
    	doLast {
    		logger.quiet "Version:$version"
    	}
    }
    

    定义task的紧前与紧后

    这里借用工作流程中紧前工序与紧后工序来理解下面两个概念:

    • task依赖(task dependencies):
      相当于目前task的紧前task。
      dependsOn方法允许声明依赖一个或多个task,或者使用dependsOn的set方法来设置task的依赖,下面的例子体现了这两种声明task依赖方式:
    task first << {println "first"}
    task second << {print "second"}
    task printVersion(dependOn: [second,first]) << {
    	logger.quiet "Version: $version"
    }
    

    这种方式属于调用dependsOn的set方法来声明依赖,这是一种类似于groovy"命名参数"语法糖的一种糖,实际上调用了Task的如下方法:

    或者像下面这样声明依赖:

    task third << { println "third" }
    third.dependOn('printVersion')
    

    这种方式是调用Task的dependOn方法:

    需要明确一点,dependsOn方法只是定义了所以来的task需要先执行,而没有定义真正的task执行顺序,在gradle中,执行顺序是由task的 input/output 自动确定的。

    • 终结器task(Finalizer tasks):
      定义终结器task相当于定义目前task的紧后task
      与dependsOn类似,gradle提供了finalizedBy来声明终结器task,用法也和dependsOn相同,因为它们的源码形式都相同:

    添加任意代码

    在gradle构建脚本中可以添加面向对象代码来实现对POJO的抽象,提高代码复用性。当然也可以把这些封装的类写在单独的文件中,在这里先写在构建脚本中

    version = new ProjectVersion(0,1) //在gradle中version属性类型是Object,输出它默认使用toString方法
    class ProjectVersion{
    	Integer major
    	Integer minor
    	Boolean release
    	ProjectVersion(Integer major, Integer minor){
    		this.major = major
    		this.minor = minor
    		this.release = Boolean.FALSE
    	}
    	ProjectVersion(Integer major, Integer minor,Boolean release){
    	this.major = major
    		this.minor = minor
    		this.release = release
    	}
    	@oberride
    	String toString(){
    		"$major.$minor${release? '' : '-SNAPSHOT'}"
    	}
    }
    

    添加task配置

    在前面了解了gradle生命周期,明确了配置代码与动作代码的区别,所以在为项目添加版本号时,可以把版本号写在外部文件中,然后在gradle配置代码中读取它,然后再利用读取到的版本号执行task动作代码,这样可以避免因为版本更替而需要修改构建脚本的问题。

    • 创建一个名为version.properties的属性文件,并写入如下内容作为最初的版本属性:
    major = 0
    minor = 1
    release = false
    
    • 定义一个新的project属性,用来代表属性文件的File对象,添加一个loadVersion task ,用作task配置:
    ext.versionFile = file('version.properties')
    task loadVersion {
    	project.version = readVersion()
    }
    ProjectVersion readVersion() {
    	logger.quiet 'Reading the version file'
    	if(!versionFile.exists()){
    	throw new GradleException("Required version file does not exist : $versionFile.canonicalPath")
    	Properties versionProps = new Properties()
    	versionFile.withInputStream{
    	stream  ->
    	versionProps.load(stream)
    	}
    	new ProjectVersion(versionProps.major.toInteger(),
    									versionProps.minor.toInteger(),
    									versionProps.release.toBoolean())
    	}
    }
    

    这时,若使用命令gradle printVersion,就会发现loadVersion和printVersion被调用。这是因为loadVersion中的配置代码会在配置阶段就被调用,及时使用命令gradle tasks,如果不加限制,所有的配置代码都会执行。
    如:在脚本中写入

    task printVersion(group: 'versioning',description:'print current version'){
        logger.quiet "Version:$version"
    }
    printVersion{
        println "END printVersion"
    }
    

    然后执行gradle tasks,输出如下:

    下一节继续学习task的高级使用方法,包括增量式构建、自定义Task、gradle内置task类型、task依赖推断、task规则等内容

  • 相关阅读:
    2019.9.10 IEnumerable
    2019.9.02 按位或,按位与, 按位异或
    2019.9.01 五大基本原则
    2019.9.01 运算符重载
    2019.9.01 封装、继承、多态
    2019.8.22 1.属性
    2019.8.22 1.封装
    2019.8.22 1.隐式转换&显示转换
    2019.8.21 Class & InterFace &abstract& 属性
    2019.8.20 1.C#中this.關鍵字的應用 2.枚舉類的定義和簡單調用 3.struct(結構體)與Class(類)的定義與區別
  • 原文地址:https://www.cnblogs.com/Theshy/p/7890819.html
Copyright © 2020-2023  润新知