Gradle学习



目录[-]

Gradle介绍

Gradle是一种自动化构建工具。

其实,Gradle被设计为一种构建语言,而非一个严格的框架。Gradle的核心使用Java和Groovy语言实现,所以,你可使用Java或者Groovy语言来扩展Gradle。当然,你也可以使用Scala。

gradle命令行创建项目

gradle本身没有创建项目的命令。最好的解决方案就是使用第三方插件来实现。步骤:

  1. 新你的项目的文件夹project
  2. 进入文件项目文件夹添加文件build.gradle,并加入:
    apply from: 'http://www.tellurianring.com/projects/gradle-plugins/gradle-templates/1.3/apply.groovy'
  3. 运行gradle initJavaProject

用到的第三方插件:gradle-templates :

以下就是它的API:

createGradlePlugin - Creates a new Gradle Plugin project in a new directory named after your project.
createGroovyClass - Creates a new Groovy class in the current project.
createGroovyProject - Creates a new Gradle Groovy project in a new directory named after your project.
createJavaClass - Creates a new Java class in the current project.
createJavaProject - Creates a new Gradle Java project in a new directory named after your project.
createScalaClass - Creates a new Scala class in the current project.
createScalaObject - Creates a new Scala object in the current project.
createScalaProject - Creates a new Gradle Scala project in a new directory named after your project.
createWebappProject - Creates a new Gradle Webapp project in a new directory named after your project.
initGradlePlugin - Initializes a new Gradle Plugin project in the current directory.
initGroovyProject - Initializes a new Gradle Groovy project in the current directory.
initJavaProject - Initializes a new Gradle Java project in the current directory.
initScalaProject - Initializes a new Gradle Scala project in the current directory.
initWebappProject - Initializes a new Gradle Webapp project in the current directory.

构建Java应用程序

  1. 使用application插件:apply plugin: 'application'
  2. 设置主函数:mainClassName = "WebTest"
  3. 运行gradle run

Gradle 任务

定义task

task hello

为task分配行为(`action`)

task hello << {
    println "hello"
}

<<操作符代表task的doLast方法

task hello { 
    doLast {
        println "hello"
    }
}

还可以

def printTaskName = { task ->
    println "Running ${task.name}"
}

task 'five' {
    doFirst printTaskName
}

task 'two' << printTaskName

重复定义task的行为

task hello <<{
    println "hello"
}   

task world <<{
    println "world"
}

输出:

hello world

使用Action接口定义action

task first {
    doFirst (
        new Action(){
            void execute(task){
                println 'Running ${task.name}'
            }   
        }
    )
}

设置默认任务

defaultTasks 'first', 'second'

task first {
    doLast {
        println "I am first"
    }
}
task second {
    doFirst {
        println "I am second"
    }
}

gradle命令后不加入任何任务名时,就会执行默认任务。

task的配置

task initializeDatabase
initializeDatabase << { println 'connect to database' }
initializeDatabase << { println 'update database schema' }
initializeDatabase { print 'configuring' }
initializeDatabase { println 'database connection' }

输出:

print 'configuring'
println 'database connection'
println 'connect to database'
println 'update database schema'

配置闭包将会在Gradle的配置期(configuration lifecycle phase)执行。

task的方法与属性

task其实是一个对象,它也会有方法和属性,同时,也会有类型。默认情况下,定义的task继承自DefaultTask。

DefaultTask包含的方法

dependsOn(task) 设置依赖task
// Declare that world depends on hello
// Preserves any previously defined dependencies as well
task loadTestData {
    dependsOn createSchema
}
// An alternate way to express the same dependency
task loadTestData {
    dependsOn << createSchema
}
// Do the same using single quotes (which are usually optional)
task loadTestData {
    dependsOn 'createSchema'
}
// Explicitly call the method on the task object
task loadTestData

loadTestData.dependsOn createSchema

// A shortcut for declaring dependencies
task loadTestData(dependsOn: createSchema)

还可以多重依赖

// Declare dependencies one at a time
task loadTestData {
    dependsOn << compileTestClasses
    dependsOn << createSchema
}
// Pass dependencies as a variable-length list
task world {
    dependsOn compileTestClasses, createSchema
}

// Explicitly call the method on the task object
task world
world.dependsOn compileTestClasses, createSchema

// A shortcut for dependencies only
// Note the Groovy list syntax
task world(dependsOn: [ compileTestClasses, createSchema ])
doFirst(closure)

注意同时定义两个doFirst方法时的输出

task setupDatabaseTests << {
    println 'load test data'
}
setupDatabaseTests.doFirst {
    println 'create database schema'
}
setupDatabaseTests.doFirst {
    println 'drop database schema'
}

$ gradle world
:setupDatabaseTests
drop database schema
create database schema
load test data

也可以这样写:

// Initial task definition (maybe not easily editable)
task setupDatabaseTests << {
    println 'load test data'
}
// Our changes to the task (in a place we can edit them)
setupDatabaseTests {
    doFirst {
        println 'create database schema'
    }
    doFirst {
        println 'drop database schema'
    }
}
doLast(closure)
task setupDatabaseTests << {
    println 'create database schema'
}
setupDatabaseTests.doLast {
    println 'load test data'
}
setupDatabaseTests.doLast {
    println 'update version table'
}
onlyIf(closure) 只有在onlyIf返回true时才运行task
task createSchema << {
    println 'create database schema'
}
task loadTestData(dependsOn: createSchema) << {
    println 'load test data'
}
loadTestData.onlyIf {
    System.properties['load.data'] == 'true'
}


$ build loadTestData
create database schema
:loadTestData SKIPPED
$ gradle -Dload.data=true loadTestData
:createSchema
create database schema
:loadTestData
load test data

DefaultTask包含的属性

  • didWork

    apply plugin: 'java'
    task emailMe(dependsOn: compileJava) « {

    if(tasks.compileJava.didWork) {
        println 'SEND EMAIL ANNOUNCING SUCCESS'
    }
    

    }

  • enabled

    task templates « {

    println 'process email templates'
    

    }
    task sendEmails(dependsOn: templates) « {

    println 'send emails'
    

    }
    sendEmails.enabled = false

  • path,指此task的在构建文件中的路径

    task echoMyPath « {

    println "THIS TASK'S PATH IS ${path}"
    

    }

    $ gradle echoMyPath
    THIS TASK'S PATH IS :echoMyPath

    如果echoMyPath是子项目(subProject)下的一个task,那么它的路径将会是::subProject:echoMyPath

  • logger,实现的日志接口是:org.slf4j.Logger,同时有少量的日志级别添加。

    task logLevel « {

    def levels = ['DEBUG',
                    'INFO',
                    'LIFECYCLE',
                    'QUIET',
                    'WARN',
                    'ERROR']
    levels.each { level ->
        logging.level = level
        def logMessage = "SETTING LogLevel=${level}"
        logger.error logMessage
        logger.error '-' * logMessage.size()
        logger.debug 'DEBUG ENABLED'
        logger.info 'INFO ENABLED'
        logger.lifecycle 'LIFECYCLE ENABLED'
        logger.warn 'WARN ENABLED'
        logger.quiet 'QUIET ENABLED'
        logger.error 'ERROR ENABLED'
        println 'THIS IS println OUTPUT'
        logger.error ' '
    }
    

    }

  • description

    task helloWorld(description: 'Says hello to the world') « {

    println 'hello, world'
    

    }

    task helloWorld « {

    println 'hello, world'
    

    }
    helloWorld {

    description = 'Says hello to the world'
    

    }
    // Another way to do it
    helloWorld.description = 'Says hello to the world'

  • temporaryDir 临时目录

  • Dynamic Properties 动态属性

    task copyFiles {

    // Find files from wherever, copy them
    // (then hardcode a list of files for illustration)
    fileManifest = [ 'data.csv', 'config.json' ]
    

    }
    task createArtifact(dependsOn: copyFiles) « {

    println "FILES IN MANIFEST: ${copyFiles.fileManifest}"
    

    }

    $ gradle -b dynamic.gradle createArtifact
    FILES IN MANIFEST: [data.csv, config.json]

可以在任务中写Groovy代码

为任务分组

def taskGroup = 'base'
task first2(description: 'Base task', group: taskGroup) << {
    println "I am first"
}
task second2(dependsOn: first2, description: 'Secondary task', group: taskGroup) << {
    println "I am second"
}

分组似乎只是用于gradle tasks时,显示更好看。因为同一组的任务分显示在一组里。

忽略任务
  • 使用onlyIf断言
    每一个任务都会有一个onlyIf方法,如果方法返回true则执行任务,否则跳过。

    task longrunning {
        onlyIf { task ->
            def now = Calendar.instance
            def weekDay = now[DAY_OF_WEEK]
            def weekDayInWeekend = weekDay in [SATURDAY, SUNDAY]
            return weekDayInWeekend
        }
        doLast {
            println "Do long running stuff"
        }
    }
    
  • 实现Spec()方法

    def file = new File('data.sample')
    task 'handleFile' << {
        println "Work with file ${file.name}"
    }
    handleFile.onlyIf(new Spec() {
        boolean isSatisfiedBy(task) {
            file.exists()
    
        }
    })
    
  • 抛出异常StopExecutionException

    def printTaskName = { task ->
        println "Running ${task.name}"
    }
    task first << printTaskName
    first.doFirst {
        def today = Calendar.instance
        def workingHours = today[Calendar.HOUR_OF_DAY] in 8..17
        if (workingHours) {
            throw new StopExecutionException()
        }
    }
    task second(dependsOn: 'first') << printTaskName
    
  • 设置任务有效或者失效

    task 'listDirectory' {
        def dir = new File('assemble')
        enabled = dir.exists()
        doLast {
            println "List directory contents: ${dir.listFiles().join(',')}"
        }
    }
    
  • 使用命令行参数 -x

    gradle third -x second

增量构建的任务

在source发生变化时才执行任务

task convert  {
    def source = new File('source.xml')
    def output = new File('output.txt')
    // Define input file
    inputs.file source
    // Define output file
    outputs.file output
    doLast {
        def xml = new XmlSlurper().parse(source)
        output.withPrintWriter { writer ->
            xml.person.each { person ->
                writer.println "${person.name},${person.email}"
            }
        }
        println "Converted ${source.name} to ${output.name}"

    } 
}

或者

task createVersionDir {
    def outputDir = new File('output')
    // If project.version changes then the
    // task is no longer up-to-date
    inputs.property 'version', project.version
    outputs.dir outputDir
    doLast {
        println "Making directory ${outputDir.name}"
        mkdir outputDir
    }
}

task convertFiles {
    // Define multiple files to be checked as inputs.
    inputs.files 'input/input1.xml', 'input/input2.xml'

    // Or use inputs.dir 'input' to check a complete directory.
    // Use upToDateWhen method to define predicate.
    outputs.upToDateWhen {
        // If output directory contains any file which name
        // starts with output and has the txt extension,
        // then the task is up-to-date.
        new File('output').listFiles().any {
            it.name ==~ /output.*\.txt$/ 
        }
    }
    doLast {
        println "Running convertFiles"
    }
}
task类型
  • copy

    task copyFiles(type: Copy) {
        from 'resources'
        into 'target'
        include '**/*.xml', '**/*.txt', '**/*.properties'
    }
    
  • jar

    apply plugin: 'java'
    task customJar(type: Jar) {
        manifest {
            attributes firstKey: 'firstValue', secondKey: 'secondValue'
        }
        archiveName = 'hello.jar'
        destinationDir = file("${buildDir}/jars")
        from sourceSets.main.classes
    }
    
  • JavaExec 运行一个java类的main方法

    apply plugin: 'java'
    repositories {
        mavenCentral()
    }
    dependencies {
        runtime 'commons-codec:commons-codec:1.5'
    }
    task encode(type: JavaExec, dependsOn: classes) {
        main = 'org.gradle.example.commandline.MetaphoneEncoder'
        args = "The rain in Spain falls mainly in the plain".split().toList()
        classpath sourceSets.main.classesDir
        classpath configurations.runtime
    }
    
自定义task类型
  • 在构建文件中定义

    task createDatabase(type: MySqlTask) {

    sql = 'CREATE DATABASE IF NOT EXISTS example'
    

    }
    task createUser(type: MySqlTask, dependsOn: createDatabase) {

    sql = "GRANT ALL PRIVILEGES ON example.*
    TO exampleuser@localhost IDENTIFIED BY 'passw0rd'"
    

    }
    task createTable(type: MySqlTask, dependsOn: createUser) {

    username = 'exampleuser'
    password = 'passw0rd'
    database = 'example'
    sql = 'CREATE TABLE IF NOT EXISTS users
    (id BIGINT PRIMARY KEY, username VARCHAR(100))'
    

    }
    class MySqlTask extends DefaultTask {

    def hostname = 'localhost'
    def port = 3306
    def sql
    def database
    def username = 'root'
    def password = 'password'
    
    @TaskAction
    def runQuery() {
        def cmd
        if(database) {
            cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} ${database} -e "
        }
        else {
            cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e "
        }
    project.exec {
        commandLine = cmd.split().toList() + sql
    }
    

    }

  • 在源码树中定义

    在构建文件中:

    task createDatabase(type: MySqlTask) {
        sql = 'CREATE DATABASE IF NOT EXISTS example'
    }
    task createUser(type: MySqlTask, dependsOn: createDatabase) {
        sql = "GRANT ALL PRIVILEGES ON example.* TO exampleuser@localhost IDENTIFIED BY 'passw0rd'"
    }
    task createTable(type: MySqlTask, dependsOn: createUser) {
        username = 'exampleuser'
        password = 'passw0rd'
        database = 'example'
        sql = 'CREATE TABLE IF NOT EXISTS users (id BIGINT PRIMARY KEY, username VARCHAR(100))'
    }
    

    buildSrc文件夹中新建一个MySqlTask.groovy

    import org.gradle.api.DefaultTask
    import org.gradle.api.tasks.TaskAction
    
    
    class MySqlTask extends DefaultTask {
        def hostname = 'localhost'
        def port = 3306
        def sql 
        def database
        def username = 'root'
        def password = password
    
        @TastAction
        def runQuery(){
            def cmd
            if(database){
                cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} ${database} -e"
            }else{
                cmd = "mysql -u ${username} -p${password} -h ${hostname} -P ${port} -e"
            }
            project.exec{
                commandLine = cmd.split().toList() + sql
            }
        }
    
    }
    

四个位置可以写你的自定义构建代码

  1. 在构建文件中的task代码块中
  2. buildSr文件夹中,此文件夹在.gradle文件同级
  3. 将分散的构建文件写入到主构建文件中
  4. 使用java或groovy写插件

Gradle 守护进程

Gradle需要运行在一个Java虚拟机中,每一次执行gradle命令就意味着一个新的Java虚拟机被启动,然后加载Gradle类和库,最后执行构建。这样,构建起来会花费大量的时间在Java虚拟机的启动与关闭。

通过Gradle 守护进程,只需要启动一次Java虚拟机,之后就可以再利用,无需再次重启Java虚拟机。这样就达到缩短构建时间的目的。

方法是在执行gradle命令时加上--daemon参数,或者-m参数。中止Gradle守护进程的方法是执行gradle -stop命令。

如果希望能每一次的构建都使用Gradle的守护进程进行,那么可以通过设置Gradle的环境变量来达到目的。方法是添加GRADLE_OPTS="-Dorg.gradle.daemon=true"`到系统环境变量中。

Gradle的生命周期

  1. initialization(初始化)
    在多项目的构建中,决定哪个项目是主项目
  2. configuration(配置)
    将所有的task对象装配到一个叫做DAG(for directed acyclic graph)的对象模型中
  3. execution(运行)
    根据task之间的依赖执行task

项目的属性

  • 项目中默认的属性

    version = '1.0'
    group = 'Sample'
    description = 'Sample build file to show project properties'
    task defaultProperties « {

    println "Project: $project"
    println "Name: $name"
    println "Path: $path"
    println "Project directory: $projectDir"
    println "Build directory: $buildDir"
    println "Version: $version"
    println "Group: $project.group"
    println "Description: $project.description"
    println "AntBuilder: $ant"
    println "customProperty: $customProperty"
    println "customProperty1: $customProperty1"
    println "customProperty2: $customProperty2"
    

    }

  • 在项目构建脚本中自定义项目属性

    ext.customProperty = 'customProperty'

    ext {

    customProperty1 = "customProperty1"
    customProperty2 = "customProperty2"
    

    }

  • 通过命令行设置项目属性

    gradle -Pversion=1.1 -PcustomProperty=custom showProperties

  • 通过系统属性设置项目属性

    gradle -Dorg.gradle.project.version=2.0 -Dorg.gradle.project.customProperty=custom showProperties

  • 通过引用外部配置文件设置项目属性
    在项目目录下新建一个纯文本文件:gradle.properties。在此文件中写入键值对,就可以了。

  • 从其它构建文件读取配置信息

    • build.gradle
      apply from: 'other.gradle'
    • other.gradle
      println “configuring $project”
      task hello « {
      println 'hello from other script'
      
      }
  • 判断项目中是否有设置某个属性

    hasProperty('propertyName')

Gradle包装器

可以在机器中没有gradle的情况下,进行构建

依赖管理

在Gradle的构建文件中,可以将一组依赖定义在一个配置里。每一个配置都有一个名字,同时,它可以继承自其它配置。

每一个Gradle构建文件都有一个ConfigurationContainer对象。可以通过project属性访问这个对象。ConfigurationContainer下可以定义一批配置,但它们至少有一个名称。

configurations {
    commonsLib {
        description = 'Common libraries'
    }
    mainLib {
        description = 'Main libraries'
        extendsFrom commonsLib
    }
}
println configurations['mainLib'].name
println configurations.commonsLib.name


//取消间接依赖下载
dependencies {
    // Configure transitive property with closure.
    compile('org.slf4j:slf4j-simple:1.6.4') {
        transitive = false
    }
    // Or we can use the transitive property
    // as method argument.
    compile group: 'org.slf4j', name: 'slf4j-simple', version:
    '1.6.4', transitive: false
}

//排除某个间接依赖
dependencies {
    // Configure transitive property with closure.
    compile('org.slf4j:slf4j-simple:1.6.4') {
        exclude 'org.slf4j:slf4j-api'
    }
}

//当依赖需要不同版本的jdk时
dependencies {
    // Use artifact-only notation with @ symbol
    // together with classifier jdk16.
    compile('sample:simple:1.0:jdk16@jar')
    // Or we can use the classifier property
    // as method argument.
    compile group: 'sample', name: 'simple', version: '1.0',classifier: 'jdk16'
}

//依赖其它子项目
dependencies {
    compile project(':projectA')
    compile project(':projectB') {
    c   onfiguration = 'compile'
    }
}

//依赖文件或文件夹
dependencies {
    compile files('spring-core.jar', 'spring-aap.jar')
    compile fileTree(dir: 'deps', include: '*.jar')
}

Repository管理

repository支持各种仓库,包括远程和本地的。

repositories {
    mavenLocal()
    mavenCentral()
    maven {
        // Name is optional. If not set url property is used
        name = 'Main Maven repository'
        url = 'http://intranet/repo'
    }
    mavenRepo(name: 'Snapshot repository', url: 'http://intranet/snapshots')

    //XML描述文件和jar包不在同一个地方的时候的定义方式:
    maven {
        url: 'http://intranet/mvn'
        artifactUrls 'http://intranet/jars'
        artifactUrls 'http://intranet/snapshot-jars'
    }

    //有权限控制的仓库
    maven(name: 'Secured repository') {
        credentials {
            username = 'username'
            password = 'password'
        }
        url = 'http://intranet/repo'
    }


    //本地仓库
    repositories {
        flatDir(dir: '../lib', name: 'libs directory')
        flatDir {
            dirs '../project-files', '/volumes/shared-libs'
            name = 'All dependency directories'
        }
    }

}

Gradle常用命令

gradle tasks --all 查看所有的可执行的任务

gradle -m 查看可执行的任务


原文 http://my.oschina.net/zjzhai/blog/220028

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值