gradle学习

android 构建脚本使用Gradle,有必要学习基本的gradle知识和groovy语法。
在一个普通的androidstudio gradle脚本中,如下代码的含义解释如下:
apply plugin:'java' //调用project的apply方法,关于参数,提供一个只有一个键值对的MAP。方 法调用省略括号。
Version = '0.1'//通过调用project的setter方法为项目设置版本属性
repositories{ //调用project的repositories方法,并传递一个闭包参数
mavenCentral()
}
dependencies{//调用project的dependencies方法,并传递一个闭包参数
compile 'commons-codec:commons-codec:1.6'//调用闭包委托的对象的compile方法,用一 个string作为参数,方法调用省略括号。
}

//Gradle语法、规则、基本知识等等。
1、对于在task中使用到的变量,可以在命令行执行task时输入:
task demo{
println axx
}
在命令行输入: gradle -P axx='hello world' demo,那么就会打印hello world
2、打印task执行信息:
gradle build -i/-d/-s 可查看task执行时的信息
3、重复执行某个操作的写法:
task count << {
    4.times {print "$it"}
}
就会输出0 1 2 3
4、任务依赖执行:
声明方式1:
task count << {
    4.times {print "$it"}
}
task intro(dependsOn:count) <<{
    println "I am Gradle"
}
声明方式2:
intro.dependsOn count
5、动态创建task:
4.times { counter -> 
    task "task$counter" << { 
    println "I am task number $counter" 
    }  
}
这个表示在项目构建阶段会动态创建4个task,然后在命令行输入gradle task1 就会执行task1
6、任务的doFirst和doLast可以执行多次,他们分别可以在任务动作列表的开始和结束加入动作,当任务执行的时候,动作列表里的其他动作将被按顺序执行。
7、当在脚本中定义一个任务时,其实就相当于给脚本添加了属性:
task hello << {
    println 'Hello Earth'
}
hello.doLast {
    println "Greetings from the $hello.name task!"
    }
8、给任务添加自定义属性:
task myTask << {
    ext.myProperty = 'MyValue'
}
task printTaskProperties <<{
    println myTask.myProperty
    }
9、获取目录文件,以及调用Ant任务:
获取某个目录下的所有文件:
File[] fileList(String dir){
    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
    }
    调用Ant任务
    task loadFile <<{
    fileList('../someDir').each { File file ->
        ant.loadFile(srcFile:file,property:file.name)
        println "I am fond of $file.name"
        }
10、可以定义默认任务:
defaultTasks 'clean' ,'run'
task clean << {}
task run << {}
输入:gradle -q 这2个任务就会默认执行
11、gradle分为配置阶段和执行阶段,在配置阶段后,gradle会知道所有应该执行的任务,这样gradle就给我们提供了一个钩子,以便利用这些信息。例如:判断发布的任务是否在要被执行的任务中,根据这一点,就可以给一些变量指定不同的值。
task distribution << {
    println "We build the zip with version=$version"
    }
    
    task release (dependsOn:'distribution'){
        println 'We release now'
        }
       
        gradle.taskGraph.whenReady { taskGraph ->
        if(taskGraph.hasTask(release)){
            version = '1.0'
}else{
            version = '1.0-SNAPSHOT'
            }
            }
输入:gradle -q distribution 
输出:We build the zip with version = 1.0-SNAPSHOT
输入:gradle -q release 
输出:We build the zip with version = 1.0
We release now\
//Gradle构建Java项目
1、依赖。
Dependencies {
compile 'xx:xx:xx'
provide 'yy:yy:yy'
}
compile表示将xx用在编译阶段,并且会打包进生成的的jar文件;provided表示只用于编译, 不会打包进最后的jar.
2、定制Manifest.mf文件。
SourceCompatibility = 1.5
version = '1.0'
jar {
manifest{
attributes 'Implementation-Title':'Gradle QuickStart','Implementayion-Version':version }
}
3、发布Jar文件。
UploadArchives { //这个方法是java插件提供的,我们需要做的只是覆写这个方法
repositories {
flatDir {
dirs 'repos'
}
}
}
输入:gradle uploadArchives来发布jar文件
4、多项目构建。你必须在根目录创建一个设置文件(settings.gradle)
    文件名:setting.gradle
文件内容; include “shared”,”api”,”xx”
在根项目下的build.gradle中的设置是对所有子项目起效的,即通用配置。
项目依赖(不是针对于根项目):
dependencies{
compile project(':shared')
}
这样能保证shared项目总是被先构建。
5、仓库。
设置仓库为maven中央仓库。
repositories{
mavenCentral()
}

repositories {
使用远程的maven仓库
maven {
url “http://repo.mycompany.com/maven2”
}
使用远程的lvy仓库
ivy {
url “http://repo.mycompany.com/repo”
}
使用本地的ivy仓库
ivy {
url “../local-repo”
}
}
一个项目可以有好几个库. Gradle 会根据依赖定义的顺序在各个库里寻找它们, 在第一个库里
找到了就不会再在第二个库里找它了.
6、发布工件(artifacts)


//发布工件到ivy仓库
uploadArchives {
repositories {
ivy {
credentials {
username “username”
password “pw”
}
url “http://repo.mycompany.com”
}
}
}
//发布工件到maven仓库
apply plugin:'maven'
uploadArchives {
repositories {
mavenDeployer {
repository (url : ”file://localhost/tmp/myRepo”)
}
}
}
注意发布到maven仓库需要使用maven 插件。

//Gradle命令行操作
1、排除任务
使用命令行选项-x来排除某些任务。
输入:gradle task0 -x task1
这样,即使task1是task0的依赖,task1也不会执行。
2、失败后,继续执行构建。默认情况下,不加—continue参数,如果task0执行失败,那么task1也就 不会执行;加了continue参数后,如果task0执行失败,task1仍然会执行。备注:如果task0失败, 那么task0的依赖任务也不会继续执行。
输入:gradle  task0 task1—continue
3、可以简化任务调用。
例如:gradle task0 可以简写为gradle ta
4、选择执行构建。默认是执行当前目录下的build.gradle,不过可以使用-b参数来指定其他目录下构建 文件。例如选择文件:subdir/myproject.gradle进行构建。
输入:gradle -q -b subdir/myproject.gradle hello
还可以使用-p参数,直接指定构建目录:
gradle -q -p subdir hello 效果与上面一样。
5、可以为项目设置描述信息:
description = 'The shared API for the application'
输入:gradle projects
输出:+-- Project ':api' – The shared API for the application.
6、设置任务的描述和groupid.
Task dists {
description = 'Build the distribution'
group = 'build'
}
7、获取依赖列表:gradle dependencies 会以树形结构列出依赖列表。
8、查看项目属性:
输入:gradle properties
输出:allprojects: [project ':api']
ant: org.gradle.api.internal.project.DefaultAntBuilder@12345
antBuilderFactory: org.gradle.api.internal.project.DefaultAntBuilderFactory@12345
artifacts: org.gradle.api.internal.artifacts.dsl.DefaultArtifactHandler@12345
asDynamicObject: org.gradle.api.internal.ExtensibleDynamicObject@12345
baseClassLoaderScope: org.gradle.api.internal.initialization.DefaultClassLoaderScope@1
2345
buildDir: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build
buildFile: /home/user/gradle/samples/userguide/tutorial/projectReports/api/build.gradle


//编写gradle构建脚本
1、标准项目属性。

 
Name Type Default Value    
Project Project Project实例对象    
name String 项目目录的名称    
path String 项目的绝对路径    
description String 项目描述    
projectDir File 包含构建脚本的目录    
build File ProjectDir/build    
group Object NA    
version Object NA    
ant AntBuilder Ant实例对象  
2、变量声明:
局部变量:def dest = 'dest'
扩展属性:例子如下
apply plugin: "java"
ext {
springVersion = "3.1.0.RELEASE"
emailNotification = "build@master.org"
}
  sourceSets.all { ext.purpose = null }
sourceSets {
main {
purpose = "production"
}
test {
purpose = "test"
}
plugin {
purpose = "production"
}
}
  task printProperties << {
println springVersion
println emailNotification
sourceSets.matching { it.purpose == "production" }.each { println it.name }
}
使用gradle -q printProperties输出结果
> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin
值得学习的是sourceSets.matching{it.purpose == 'production'}.each{println it.name}
collection.matching操作返回的是一个聚合,集合调用each方法进行遍历。
3、Task学习。
定义task:
//直接使用闭包进行定义
task(copy,type:Copy){
    from(file('srcDir'))
    into(buildDir)
    }
//配置的方法进行定义
Copy myCopy = task(myCopy,type:Copy)
myCopy.from  'resources'
myCopy.into 'target'
myCopy.include('**/*.txt','**/*.xml','**/*.properties')

project的任何一个task都是这个project的一个属性。如下途径获取task
通过tasks collection获取tasks
task hello
println tasks.hello.name
println tasks['hello'].name
project(':projectA'){
    task hello
    }
    println tasks.getByPath('hello').path
    println tasks.getByPath(':hello').path
    println tasks.getByPath('projectA:hello').path
    println tasks.getByPath(':projectA:hello').path


task的依赖
方式一:taskX(dependsOn:'taskY')
方式二:taskX{} taskY{} taskX.dependsOn taskY
方式三:taskX {} taskX.dependsOn { tasks.findAll { task → task.name.startWith('lib')}}


Task执行顺序的设置:
目前有2种可用的排序规则:’must run after‘和’should run after‘.需要明白一点,任务排序并不影 响任务执行。
TaskX{} taskY{} taskY.shouldRunAfter taskX 
输入:gradle taskY
输出:taskY
并没有执行taskX,说明只影响了执行顺序,并没有影响是否执行。
基本上must run after 和 should run after 效果一样。should run after在如下情况下会被忽略:在 一个执行循环中。
TaskX.dependsOn taskY
taskY.dependsOn taskZ
taskZ.shouldRunAfter taskX//这种情况下shouldRunAfter会被忽略


给task添加描述
taskX {
description 'hello world!'
}


替换tasks
就是相当于java语言中的复写。
TaskX{
println 'x'
}
taskX(overwrite:true){
println 'y'
}
输入:gradle taskX
输出:y

跳过tasks
方式一:使用判断条件
task hello <<{
println 'hello world'
}
hello.onlyIf {!project.hasProperty('skipHello')}
输入:gradle hello -P skipHello
这样就会跳过hello任务
方式二:使用StopExecutionException
task compile <<{
        println 'We are doing the compile'
}


    compile.doFirst {
        if(true){
              throw new StopExecutionException()
        }
    }
    task myTask(dependsOn:'compile') <<{
        println 'I am not affected'
    }
输入:gradle myTask
输出:i am not affected

激活和注销tasks
taskX{} 
taskX.enabled = false

task的up-to-date
task transform {
    ext.srcFile = file('mountains/xml')
    ext.destDir = new File(buildDir,'generated')
    doLast {
        println 'Transforming source file.'
        destDir.mkdirs()
        def mountains = new XmlParser().parse(srcFile)
        mountains.mountain.each{mountain ->
        ~~~~~
        }
        }
如果连续执行这个任务2遍,那么doLast()里面的代码就会执行2遍,虽然第二遍一点用没有。
这是因为doLast()里面是一个闭包,gradle没法判断闭包执行第二次时,是否是重复的。那么为 了让gradle在执行任务的时候能判断是否有必要执行,所以需要给task定义输入输出。即inputs和 outputs.将上述代码修改为如下:
task的up-to-date
task transform {
    ext.srcFile = file('mountains/xml')
    ext.destDir = new File(buildDir,'generated')
inputs.file srcFile
outputs.dir destDir
    doLast {
        println 'Transforming source file.'
        destDir.mkdirs()
        def mountains = new XmlParser().parse(srcFile)
        mountains.mountain.each{mountain ->
        ~~~~~
        }
        }
这样当执行2遍transform task时,第二次会直接跳过。inputs.file 判断的是输入文件,outputs.dir 判断的是输出目录,所以即使输出目录中文件数量变了,执行该任务时,gradle仍然会认为没有变 化,直接up-to-date.如果想自己控制输出目录是否变化来控制task的执行,需要使用 outputs.upToDateWhen{1 ==2  },这样的话,每次都会重新执行任务。

终止Task
task taskX{
}
task taskY{
}
taskX.finalizedBy taskY
输入:gradle taskX
输出:taskX taskY
要使用终止任务,必须使用task.finalizeBy(),一个任务的实例,任务名都可以作为参数输入。例如: 当构建创建了一个资源,无论构建失败或者成功,这个资源必须被清除的时候,终止任务就非常有 用。
4、Gradle脚本配置。
Other.gradle
println “configuring $project”
task hello << {
println 'hello from other srcipt'
}
build.gradle
apply from:'other.gradle'
这样就能在当前目录执行other.gradle中的task


使用其他的脚本配置任意对象
task config << {
def pos = new java.text.FieldPosition(10)
apply from: 'other.gradle',to: pos
printlb pos.beginIndex
println pos.endIndex

other.gradle 
beginIndex = 1
endIndex = 5
输入:gradle configuring
输出:1,5

配置任意对象
task configure <<{
def pos = configure(new java.text.FieldPosition(10)){
beginIndex = 1
endIndex = 5
}
println pos.beginIndex
println pos.endIndex 
}
输入:gradle configure
输出:1,5


脚本缓存
gradle默认会缓存所有编译后的脚本,包括所有的构建脚本,初始化脚本,还有其他脚本。如果这个脚本自动它被编译后就再也没有被改动过,gradle会优先使用编译后的脚本,否则会重新编译脚本。如果使用gradle –recompile—scripts运行脚本,那么缓存的脚本就会被删除。


5、文件操作。
使用project.file()方法能够相对项目目录定位一个文件。
//使用一个相对路径
File configFile = file('src/config.xml')
//使用一个绝对路径
configFile = file(configFile.absolutePath)
//使用一个项目路径的文件对象
configFile = file(new File('src/config.xml'))
注意:file()是依赖于项目目录的,所以如果想操作项目目录意外的文件,请使用new File(absolutePath)

文件集合:FileCollection,使用project.files()可以获得一个文件集合的实例
FileCollection collection = files('src/file1.txt',new File('src/file2.txt',['src/file3.txt','src/file4.txt'])
使用闭包创建一个文件集合:collection = files{ srcDir.listFiles()}
操作文件集合:
//对文件集合进行迭代
collection.each{ File file →
println file.name
}
//转换文件集合为其他类型
Set set = collection.files
Set set = collection as Set
List list = collection as List
//增加和减少文件集合
def union = collection + files('src/file3.txt')
def different = collection – files('src/file3.txt')

文件树:

FileTree tree = fileTree(dir:'src/main')
//添加包含和排除规则
tree.include '**/*.java'
tree.exclue '**/Abstract*'


//使用路径创建一个树
tree= fileTree('src').include('**/*.java')


//使用闭合创建一个树
tree = fileTree('src'){
include '**/*.java'
}

//使用map创建一个树
tree = fileTree(dir:'src',include:'**/*.java')
tree  = fileTree(dir:'src',includes:['**/*.java,'**/*.xml'])
tree = fileTree(dir:'src',include:'**/*.java',exclude:'**/*test*/**')

//遍历文件树
tree.each{
File file →
println file
}


//过滤文件树
Filetree filtered = tree.matching{
include 'org/gradle/api/**'
}


//合并文件树
Filetree sum = tree+fileTree(dir:'src/test')


//访问文件树的元素
tree.visit{ element->
println “$element.relativePath => $element.file”
}

归档压缩文件也可以成为一个filetree.
//使用路径创建一个zip文件
FileTree zip = zipTree('somefile.zip')
//使用路径创建一个tar文件
FileTree tar = tarTree('someFile.tar')


//tar tree 能够根据文件扩展名得到压缩方式,如果想明确地指定压缩方式,可以使用如下代码
FileTree someTar = tarTree(resources.gzip('someTar.ext'))








指定输入文件:

compile {
//有多种方式可以设置源目录
source = file('src/main/java')
source = 'src/main/java'
source = ['src/main/java,'../shared/java'']
source = fileTree(dir:'src/main/java').matching{include 'org/gradle/api/**'}
source = {   file('src').listFiles().findAll{it.name.endsWith('.zip')}.collect {zipTree(it)}
  }
}
通常情况下,会有一个方法名和属性名相同的方法能够附件一组文件,这个方法接收files()方法支持的任何类型的值。
如下代码相当于有一个方法source(params p),方法内部会将参数设置给source属性。
Compile{
source 'src/main/java','src/main/groovy'
source file('../shared/java')
source { file('src/test/').lisfFiles() }
}

复制文件:


task copytask(type:Copy){
from 'src/main/webapp' //接收任何files()方法支持的参数。如果是目录,那么目录下的文件会 被递归复制
to 'build/explodeWar'
}
from 参数也可以是任务
task anotherCopyTask (type:Copy){
from 'src/main/webapp'
from 'src/staging/index.html'
复制一个任务输出的文件
from copyTask
显示使用任务的outputs属性复制复制任务的输出文件
from copyTaskWithPatterns.outputs
from zipTree('src/main/assets.zip')
//最后指定目标目录
into { getDestDir() }
}
还可以针对复制任务添加过滤规则
task copyTaskWithPatterns(type:Copy){
from 'src/main/webapp'
into 'build/exploadWar'
include '**/*.html'
include '**/*.jsp'
exclude { details → details.file.name.endsWith('.html') && details.file.text.contains('staging')}

}
project对象也有一个专门用于文件复制的方法copy(),有一些限制,不详述,尽量使用copy任务,而不是project自带的copy方法。


Task copyMethod << {
copy {
from 'src/main/webapp'
into 'build/exploadedWar'
include '**/*.html'
incldue '**/*.jsp'
}
}


重命名文件:

task rename(type:Copy){
from 'src/main/webapp'
into 'build/exploaded'
rename{ String filename →
fileName.replace('-staging-','')
}
rename '(.+)-staging-(.+)','$1$2'
rename (/(.+)-staging-(.+)/,'$1$2')
}


使用同步任务:同步任务继承子复制任务,当它执行时,它会复制源文件到目标目录中,然后从目标目录中删除所有非复制的文件,这种方式非常有用。
Task libs(type:Sync){
from configurations.runtime
into “$buildDir/libs”
}


创建归档文件
创建zip,tar,jar,war,ear
创建一个zip文档
apply plugin: 'java'
task zip(type: Zip){
from 'src/dist'
into('libs'){
from configurations.runtime
}
}
6、插件使用。
buildscript{
repositories{
jcenter()
}
dependencies{
classpath “com.jfrog.bintray.gradle:gradle-bintray-”
}
}

apply plugin: ”com.jfrog.bintray”

使用插件的插件dsl

plugins{
id “com.jfrog.bintray” version “0.4.1”
}




























Groovy:
1、一个方法的最后一行的表达式的值会作为返回值返回;如果不需要返回值,那么方法的返回值需要声明为void:
def initProjectVersion(major,minor){
new ProjectVersion(major,minor)
}
上述代码就表示返回一个ProjectVersion的实例。
2、字符串:
单引号:‘xxx’ 包括的就是字符串常量;
双引号:“xxx $var” 包含字符串常量,$var表示取变量的值
3、集合操作。
List:在方括号中放入一系列以‘,’隔开的值,会自动生成一个arrayList;
def buildtools = ['maven','gradle'] 这样buildtools就是一个arraylist,如果需要向buildtools中添加一个字符串,可以这样写:buildTools << 'Ant',非常灵活。
buildtools.find{it == 'gradle'} == 'gradle' //为true
buildtools.every {it.size() >4} == true//为true
Map:在方括号中以键值对表示,并以‘,’隔开,就会生成一个LinkedHashMap
def inceptionYears = ['Ant':2000,'Maven':2004]
如果需要添加一个元素,这样写:inceptionYears['Gradle']=2009
4、闭包及隐式参数。每个没有显示定义参数的闭包都可以访问第一个隐式的参数it,it代表调用这个闭包传进来的第一个参数,如果没有传参数,那么就是null.
//隐式闭包参数的写法
def incrementaMajorProjectVersion = {
  it.major++
}
ProjectVersion projectVersion = new ProjectVersion(major:1,minor:10)
incrementaMajorProjectVersion(projectVersion)
assert projectVersion.major ==2


//显示的单一闭包参数
def incrementaMajorProjectVersion = { ProjectVersion projectVersion ->
    projectVersion.major++
}
ProjectVersion projectVersion = new ProjectVersion(major:1,minor:10)
incrementaMajorProjectVersion(projectVersion)


//无类型的多个闭包参数
def setFullProjectVersion = {  projectVersion,major,minor ->
    projectVersion.major = major
    projectVersion.minor = minor
}
ProjectVersion projectVersion = new ProjectVersion(major:1,minor:10)
setFullProjectVersion(projectVersion,2,1)
assert projectVersion.major ==2
assert projectVersion.minor ==1
闭包总会返回值,如果没有显示的return语句,闭包就返回最后一条可执行语句的值,如果最后一条语句没有值,就会返回null.
闭包也可作为方法参数:
Integer incrementVersion(Closure closure ,Integer count){
        closure() + count
    }
    ProjectVersion projectVersion = new ProjectVersion(major:1,minor:10)
    assert incrementVersion ({projectVersion.minor},2)== 12
闭包委托的概念:
闭包代码在委托的闭包上执行,默认的,这个委托就是闭包的所有者。如果一个task执行一个闭包,在不设置委托的情况下,那么默认的委托就是这个task。如何设置闭包委托,代码如下:
class ProjectVersion{
Integer major
Integer minor
void increment(Closure closure){
        closure.resolveStrategy = Closure.DELEGATE_ONLY
        closure.delegate = this // 这2行代码就是设置了闭包委托
        closure()
        }
}


ProjectVersion version = new ProjectVersion(major:1,minor:10)
version.increment {major +=1}
assert version.major ==2
version.increment {minor +=5}
assert version.minor ==15
4、Groovy Jdk
属性存取器:
println project.buildir == println getProject().getBuildDir()
project.buildDir = 'target'==getProject().setBuildDir('target')
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值