码迷,mamicode.com
首页 > 其他好文 > 详细

Gradle cookbook(转)

时间:2015-05-24 14:14:22      阅读:762      评论:0      收藏:0      [点我收藏+]

标签:

 

原文地址 http://www.gradle.org/docs/current/userguide/installation.html

4.1. Prerequisites先决条件

Gradle依赖于JDK,需要已经安装好5.0(包括)以上的版本。虽然Gradle是基于Groovy配置的,但是不需要安装Groovy,它自带了。如果你已经安装了Groovy,没有关系,它会忽略掉的。

Gradle会在Path路径寻找JDK,找到哪个版本就用哪个版本。你自己可以用 java -version来检查你的Java版本。你也可以设置JAVA_HOME 环境变量指明已安装和期望使用的是哪个JDK。

4.2. Download下载

可以到这个地方下载Gradle的发行版:Gradle web site.

4.3. Unpacking解压

Gradle发行版是一个ZIP包。包括以下及部分:

  • 二进制文件

  • 用户手册,HTML和PDF的

  • DSL引用指南。

  • API文档,包括Javadoc和Groovydoc.

  • 丰富的例子,包括用户手册中的例子和其他完整的复杂的例子,你可以从简单的开始看。

  • 二进制源码。这只是用来引用的,如果你要自己构建Gradle需要去下载源码发行版。详情到这来看吧:Gradle web site.

4.4. Environment variables环境变量

要运行Gradle,需要让PATH变量包含 GRADLE_HOME/bin . 仅此而已吗,足够了。

4.5. Running and testing your installation测试安装

可以通过 gradle 命令行运行Gradle。要看Gradle是否正确安装了,用 gradle -v最简单了。输出是Gradle的版本还有其他一些环境配置信息。如果你看到的显示版本根本不是你自己下载的,哼哼,撞墙去吧。

4.6. JVM options JVM选项

你可以通过环境变量设置虚拟机选项,使用GRADLE_OPTS或者JAVA_OPTS就行,或者一起上吧。JAVA_OPTS一般的是给很用应用使用的。比如设置HTTP代理使用JAVA_OPTS,而设置内存选项使用GRADLE_OPTS。你可以在gradle 或 gradlew 脚本的开头设置这些变量。

 

http://somefuture.iteye.com/blog/2002052

 

翻译:Gradle之构建脚本入门

 

原文地址http://www.gradle.org/docs/current/userguide/tutorial_using_tasks.html

6.1. Projects and tasks工程和任务

Gradle中的任何东西都基于俩概念: projects 工程 和 tasks 任务。

一个Gradle构建由一个或多个工程构成。一个工程 就是项目里的几个组件,这确切的依赖于要构建的项目。比如,一个工程可以是一个jar库文件,也可以是一个web应用。它可以是从其他项目jar中汇聚成的一个zip包。它不一定是标识要做的事,也可能是已经完成的事,比如部署项目。啊好凌乱啊有木有?没关系,Gradle的基于约定配置对工程有明确的定义,可以去看看。

每个工程由众多任务构成(有没有想到ant?),一个任务是构建过程的一些原子片段。可以是编译,打包,生成文档,甚至发布到库。

好了好了,别睡着了,坐起来看我们要开工了。先是一个简单的工程,后续有负责的任务和工程引导。

 

6.2. Hello world牛刀初试

使用 gradle 命令行进行构建,它会寻找当前目录的 build.gradle 文件(是不是和其他构建工具一样啊),该文件称为构建脚本文件,当然严格的说它是构建配置脚本文件。构建脚本定义了工程和它的任务。

自己不来一发嘛,创建 build.gradle 文件并写入下面的脚本:

Example 6.1. The first build script

build.gradle

task hello {
    doLast {
        println ‘Hello world!‘
    }
}

在当前目录下执行gradle -q hello 来运行:

What does -q do?

我们的例子会经常用到-q参数。它会抑制Gradle的日志输出而只有任务输出。这样看起来清爽一些。如果不需要完全可以不用。在18章讲日志中会详细说明各参数对输出的影响。

Example 6.2. Execution of a build script

 

> gradle -q hello
Hello world!

喔喔喔,发生什么了呢?构建脚本定义了一个认为叫hello并给它赋了一个动作。当执行 gradle hello , Gradle 会执行hello任务,也就是执行它里面的动作。动作是一些正确可执行的Groovy代码放在大括号里。

 是不是和Ant很像呢,呵呵。Gradle的任务就相当于Ant里的目标,不过更强悍一些。官方反复斟酌认为任务这个属于比目标更适合用于Gradle。不过这和Ant里的任务冲突了(ant把它的命令叫任务,比如javac,copy),所以我们现在说任务,你要记得它总是指的Gradle的任务,也就是Ant里的目标。需要的话,我们就明确说Ant的任务如何如何。

 

6.3. A shortcut task definition任务定义新方法--快捷定义

有一种更简明的方法定义任务:

Example 6.3. A task definition shortcut

build.gradle

task hello << {
    println ‘Hello world!‘
}

我们用一个大括号定义了Hello任务,以后我们就都这样了哈。

 

6.4. Build scripts are code 何处不coding?

构建脚本的强大得益于Groovy的强大,我们来看下:

Example 6.4. Using Groovy in Gradle‘s tasks

build.gradle

task upper << {
    String someString = ‘mY_nAmE‘
    println "Original: " + someString 
    println "Upper case: " + someString.toUpperCase()
}

Output of gradle -q upper

> gradle -q upper
Original: mY_nAmE
Upper case: MY_NAME

下一个

Example 6.5. Using Groovy in Gradle‘s tasks

build.gradle

task count << {
    4.times { print "$it " }
}

Output of gradle -q count

> gradle -q count
0 1 2 3

 

6.5. Task dependencies任务依赖

你肯定早想到了任务之间可以依赖吧(没办法,谁让你是工程师而我是心理学家呢)

Example 6.6. Declaration of dependencies between tasks

build.gradle

task hello << {
    println ‘Hello world!‘
}
task intro(dependsOn: hello) << {
    println "I‘m Gradle"
}

Output of gradle -q intro

> gradle -q intro
Hello world!
I‘m Gradle

任务依赖的其他任务可以不预先定义

Example 6.7. Lazy dependsOn - the other task does not exist (yet)

build.gradle

task taskX(dependsOn: ‘taskY‘) << {
    println ‘taskX‘
}
task taskY << {
    println ‘taskY‘
}

Output of gradle -q taskX

> gradle -q taskX
taskY
taskX

看见了吧我没瞎说,taskX依赖于taskY而taskY定义在后面,这样做对于多工程构建很重要。任务依赖的希艾娜关系讨论在Section 15.4, “Adding dependencies to a task”.

呔注意了,不能用快捷记号引用任务的喔。

 

6.6. Dynamic tasks动态任务

Groovy的强大可不是只能来定义任务干什么,你还可以用它动态创建任务:

Example 6.8. Dynamic creation of a task

build.gradle

4.times { counter ->
    task "task$counter" << {
        println "I‘m task number $counter"
    }
}

Output of gradle -q task1

> gradle -q task1
I‘m task number 1

 

6.7. Manipulating existing tasks操纵任务

和Ant不同的是Gradle的任务创建后可以通过API来访问,比如增加依赖。

Example 6.9. Accessing a task via API - adding a dependency

build.gradle

4.times { counter ->
    task "task$counter" << {
        println "I‘m task number $counter"
    }
}
task0.dependsOn task2, task3

Output of gradle -q task0

> gradle -q task0
I‘m task number 2
I‘m task number 3
I‘m task number 0

或者你可以增加任务的行为

Example 6.10. Accessing a task via API - adding behaviour

build.gradle

task hello << {
    println ‘Hello Earth‘
}
hello.doFirst {
    println ‘Hello Venus‘
}
hello.doLast {
    println ‘Hello Mars‘
}
hello << {
    println ‘Hello Jupiter‘
}

Output of gradle -q hello

> gradle -q hello
Hello Venus
Hello Earth
Hello Mars
Hello Jupiter

你可以多次调用 doFirst 和doLast ,它们分别在任务动作列表的开头和结尾增加行为。执行的时候会依序执行,而<<操作符就是doLast的别名.

 

6.8. Shortcut notations快捷符号

你早就注意到了吧,没错,一个任务就是一个脚本的属性,你可以访问它:

Example 6.11. Accessing task as a property of the build script

build.gradle

task hello << {
    println ‘Hello world!‘
}
hello.doLast {
    println "Greetings from the $hello.name task."
}

Output of gradle -q hello

> gradle -q hello
Hello world!
Greetings from the hello task.

对于插件提供的盒子任务。(e.g. compile)这尤其方便

 

6.9. Extra task properties新增属性

可以通过ext增加任务属性值:要增加名为myProperty的熟悉用ext.myProperty 即可。增加后就好像早就定义在里面了一样。

Example 6.12. Adding extra properties to a task

build.gradle

task myTask {
    ext.myProperty = "myValue"
}

task printTaskProperties << {
    println myTask.myProperty
}

Output of gradle -q printTaskProperties

> gradle -q printTaskProperties
myValue
像这样混入属性不仅仅局限于任务,有兴趣看下Section 13.4.2, “Extra properties”.

 

6.10. Using Ant Tasks使用Ant任务

Ant任务是Gradle界的一等公民,Gradle通过Groovy对Ant任务进行了牛逼的整合,因为Groovy有一个牛逼的AntBuilder. Gradle使用Ant任务绝不比Ant使用build.xml差劲甚至还更强大。

Example 6.13. Using AntBuilder to execute ant.loadfile target

build.gradle

task loadfile << {
    def files = file(‘../antLoadfileResources‘).listFiles().sort()
    files.each { File file ->
        if (file.isFile()) {
            ant.loadfile(srcFile: file, property: file.name)
            println " *** $file.name ***"
            println "${ant.properties[file.name]}"
        }
    }
}

Output of gradle -q loadfile

> gradle -q loadfile
*** agile.manifesto.txt ***
Individuals and interactions over processes and tools
Working software over comprehensive documentation
Customer collaboration  over contract negotiation
Responding to change over following a plan
 *** gradle.manifesto.txt ***
Make the impossible possible, make the possible easy and make the easy elegant.
(inspired by Moshe Feldenkrais)

你想怎么干Ant就可以怎么干,只有你有种。更多请看Chapter 17, Using Ant from Gradle.

 

6.11. Using methods方法

Gradle很吊,但要看你怎么写你的脚本逻辑了。对于上面的例子,第一层次就是抽取为方法.

Example 6.14. Using methods to organize your build logic

build.gradle

task checksum << {
    fileList(‘../antLoadfileResources‘).each {File file ->
        ant.checksum(file: file, property: "cs_$file.name")
        println "$file.name Checksum: ${ant.properties["cs_$file.name"]}"
    }
}

task loadfile << {
    fileList(‘../antLoadfileResources‘).each {File file ->
        ant.loadfile(srcFile: file, property: file.name)
        println "I‘m fond of $file.name"
    }
}

File[] fileList(String dir) {
    file(dir).listFiles({file -> file.isFile() } as FileFilter).sort()
}

Output of gradle -q loadfile

> gradle -q loadfile
I‘m fond of agile.manifesto.txt
I‘m fond of gradle.manifesto.txt

以后你就明白:娃是多么幸福,因为多工程构建可以共享方法。如果你还想要复杂,Gradle也提供了组织方法,就怕你不够复杂。我们搞了一整章吓唬你,去看看吧:Chapter 59, Organizing Build Logic.

 

6.12. Default tasks默认任务

默认任务是定义的,可以给你的构建定义一两个。

Example 6.15. Defining a default tasks

build.gradle

defaultTasks ‘clean‘, ‘run‘

task clean << {
    println ‘Default Cleaning!‘
}

task run << {
    println ‘Default Running!‘
}

task other << {
    println "I‘m not a default task!"
}

Output of gradle -q

> gradle -q
Default Cleaning!
Default Running!

这就和执行 gradle clean run 一样样的了。多工程构建中每个工程都可以有自己的默认任务。要是你不定义,就是用父任务定义的默认任务。

 

 

 

6.13. Configure by DAG

我们后面会详细讲(See Chapter 55, The Build Lifecycle) Gradle的配置阶段和执行阶段。配置阶段结束后Gradle了解了所有的要执行的任务(Gradle提供了一个钩子来使用这个信息)。你可以检查要执行的任务里有没有发布任务。这样就可以为不同的变量指不同的值了。

下面这个例子给分发任务和发布任务指了不同的版本号。

Example 6.16. Different outcomes of build depending on chosen tasks

build.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‘
    }
}

Output of gradle -q distribution

> gradle -q distribution
We build the zip with version=1.0-SNAPSHOT

Output of gradle -q release

> gradle -q release
We build the zip with version=1.0
We release now

一旦选定发布任务,在它执行前会影响版本号,即使它有依赖任务。

http://somefuture.iteye.com/blog/2002053

翻译--Gradle之Java工程入门

原文地址 http://www.gradle.org/docs/current/userguide/tutorial_java_projects.html

 

7.1. The Java plugin插件

我们已经看到Gradle是一个通用构建工具,它可以完成相当多的任务,只要你能在脚本里定义好。如果你没写好脚本,它就啥也干不成。

大部分Java项目都是这样的流程:编译源文件,单元测试,打成jar包。你希望不用每次都为每个项目搞下这个。你的小清新到了:Gradle通过插件解决了这个问题。一个插件就是一个Gradle扩展,它以某种方式配置项目。一般会预配置一些任务来一起完成某个目标。Gradle 自带了不少插件,你还可以自己写,并分享给别人。其中的Java插件( Java plugin)可以实现编译测试打包任务。这个插件是基于约定的,它定义了工程的很多默认值,比如源文件位置等。如果就照着它的约定,你就不用怎么改构建脚本了。如果不想或者不能,你可以自己写。实际上,既然对Java项目的支持是一个插件实现的,你完全可以不用插件来构建。

后面我会通过很多例子来讲Java插件的依赖管理和多工程构建等。不过首先我们要看到的是怎么使用Java插件。

7.2. A basic Java project简单的Java工程

要使用Java插件,在脚本里写一句这个(是不是有点像play!框架的插件配置):

Example 7.1. Using the Java plugin

build.gradle

apply plugin: ‘java‘

Note: The code for this example can be found at samples/java/quickstart which is in both the binary and source distributions of Gradle.

这样就增加了Java插件功能,也就配好了一些任务。Gradle默认希望你的源代码在 src/main/java 而测试源代码在src/test/java,资源目录是src/main/resources,测试资源是src/test/resources。输出目录是build 文件夹。需要的jar文件在build/libs下面。

What tasks are available?

 gradle tasks 命令可以列出当前工程的全部任务。你可以看看到底Java插件增加了什么任务。

7.2.1. Building the project开始搞

Java插件会增加一些默认任务,不过你应该不会全都需要。一般需要的任务是 build, 它会完整构建你的项目。运行 gradle build就行:

Example 7.2. Building a Java project

Output of gradle build

> gradle build
:compileJava
:processResources
:classes
:jar
:assemble
:compileTestJava
:processTestResources
:testClasses
:test
:check
:build

BUILD SUCCESSFUL

Total time: 1 secs

其他的任务比如:

clean

删除 build 目录和所有构建文件。

assemble

编译和打包,不进行单元测试。结合其他插件可以实现额外功能。比如加入War插件后这个任务还回生成war包。

check

编译和测试,也可以结合其他插件,比如Code-quality插件会额外执行Checkstyle 

7.2.2. External dependencies外部依赖

要引用外部jar包,就要告诉Gradle它们的位置。Gradle的引用在repository 属性下。比如要引用Maven库的包::

Example 7.3. Adding Maven repository

build.gradle

repositories {
    mavenCentral()
}

Let‘s add some dependencies. 咱们加一些依赖试试,比如我们的发行版有一个commons collections运行时依赖,测试类则依赖的是 junit:

Example 7.4. Adding dependencies

build.gradle

dependencies {
    compile group: ‘commons-collections‘, name: ‘commons-collections‘, version: ‘3.2‘
    testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.+‘
}

更多信息去看看 Chapter 8, Dependency Management Basics.

7.2.3. customizing the project手动配置工程

Java插件增加了一些属性,要修改Java插件属性的默认值也很简单,看一个例子。比如我们要指定版本号,并增加一些manifest属性。

Example 7.5. Customization of MANIFEST.MF

build.gradle

sourceCompatibility = 1.5
version = ‘1.0‘
jar {
    manifest {
        attributes ‘Implementation-Title‘: ‘Gradle Quickstart‘, ‘Implementation-Version‘: version
    }
}

What properties are available?

gradle properties 命令可以列出工程的属性,可以看到Java插件到底增加了什么属性,它们的值是多少。

Java插件增加的都是普通任务,和构建脚本里的一样。所以前面学到的那些机制可以随便用。,比如设置属性值,增加任务行为,修改任务依赖,甚至取代任务。我们配置一个 test 任务,它是Test类型的,让它执行时增加系统属性:

Example 7.6. Adding a test system property

build.gradle

test {
    systemProperties ‘property‘: ‘value‘
}

7.2.4. Publishing the JAR file发布

一般jar需要发布到一个地方,所以你要告诉Gradle发布到哪。默认是发布到库里面。我们让它发布到本地目录,你愿意的话可以发布到远程或者同时多个地方。

Example 7.7. Publishing the JAR file

build.gradle

uploadArchives {
    repositories {
       flatDir {
           dirs ‘repos‘
       }
    }
}

要发布jar包,执行 gradle uploadArchives.

7.2.5. Creating an Eclipse project转成Eclipse项目

要导入到Eclipse里面需要另一个插件:

Example 7.8. Eclipse plugin

build.gradle

apply plugin: ‘eclipse‘

现在执行 gradle eclipse 可以生成Eclipse项目文件。更多Eclipse任务操纵见 Chapter 38, The Eclipse Plugin.

7.2.6. Summary总结

这是完整的构建文件:

Example 7.9. Java example - complete build file

build.gradle

apply plugin: ‘java‘
apply plugin: ‘eclipse‘

sourceCompatibility = 1.5
version = ‘1.0‘
jar {
    manifest {
        attributes ‘Implementation-Title‘: ‘Gradle Quickstart‘, ‘Implementation-Version‘: version
    }
}

repositories {
    mavenCentral()
}

dependencies {
    compile group: ‘commons-collections‘, name: ‘commons-collections‘, version: ‘3.2‘
    testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.+‘
}

test {
    systemProperties ‘property‘: ‘value‘
}

uploadArchives {
    repositories {
       flatDir {
           dirs ‘repos‘
       }
    }
}

 

7.3. Multi-project Java build多工程构建

现在来看多工程构建。下面是工程结构:

Example 7.10. Multi-project build - hierarchical layout

Build layout

multiproject/
  api/
  services/webservice/
  shared/

Note: The code for this example can be found at samples/java/multiproject which is in both the binary and source distributions of Gradle.

我们有三个工程: api 工程用来生成给客户端用的jar文件,这个jar文件可以为XML webservice 提供Java客户端。webservice 是一个web应用,生成 XML。shared 工程包含的是前述两个工程共用的代码。

7.3.1. Defining a multi-project build定义

要定义多工程构建需要一个“设置文件”(settings file),这个文件在项目的根目录下,指明哪些工程要被处理。它的名字叫 settings.gradle. 对于我们的例子来说,对应的设置文件是这样的:

Example 7.11. Multi-project build - settings.gradle file

settings.gradle

include "shared", "api", "services:webservice", "services:shared"

更多信息看56章: Chapter 56, Multi-project Builds.

7.3.2. Common configuration公共配置

多工程构建一般会有配置是各工程都会用到的。比如我们的例子会通过“配置注入( configuration injection)”来实现,在根项目下定义一个公共配置。根项目就像一个容器,子项目会迭代访问它的配置并注入到自己的配置中。这样我们就可以简单的为所有工程定义主配置单了:

Example 7.12. Multi-project build - common configuration

build.gradle

subprojects {
    apply plugin: ‘java‘
    apply plugin: ‘eclipse-wtp‘

    repositories {
       mavenCentral()
    }

    dependencies {
        testCompile ‘junit:junit:4.11‘
    }

    version = ‘1.0‘

    jar {
        manifest.attributes provider: ‘gradle‘
    }
}

我们使用了Java插件,这样所有的工程都有了该特性。所以你可以在跟项目下通过 gradle build 编译、测试、打包了。

7.3.3. Dependencies between projects工程依赖

同一个构建中可以建立工程依赖,这样比如说,一个工程的jar文件就可以给另一个工程使用了。我们给 api 工程的构建增加对  shared 工程jar的依赖。这样Gradle会保证构建api工程之前总要构建shared。

Example 7.13. Multi-project build - dependencies between projects

api/build.gradle

dependencies {
    compile project(‘:shared‘)
}
Section 56.7.1, “Disabling the build of dependency projects”讲了如何停用这个功能。

7.3.4. Creating a distribution发布

要发布到客户端就这样:

Example 7.14. Multi-project build - distribution file

api/build.gradle

task dist(type: Zip) {
    dependsOn spiJar
    from ‘src/dist‘
    into(‘libs‘) {
        from spiJar.archivePath
        from configurations.runtime
    }
}

artifacts {
   archives dist
}

http://somefuture.iteye.com/blog/2002909

翻译:Gradle之依赖管理

原文地址 http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html

 

8.1. What is dependency management?何谓?为何?

依赖管理大致有两块:首先Gradle需要找到你工程需要的东西,这些东西就是所谓的“依赖”。另外Gradle需要构建和上传你工程的产出,这就是所谓的发行。让我们来仔细看看它们:

大部分工程都不太可能完全自给自足,一把你都会用到其他工程的文件。比如我工程需要Hibernate就得把它的类库加进来,比如测试的时候可能需要某些额外jar包(jdbc驱动或者Ehcache jar包)。这些文件就是工程的依赖。Gradle需要你告诉它工程的依赖是什么,它们在哪,然后帮你加入构建中。依赖可能需要去远程库下载,比如Maven 或者 Ivy 库。也可以是本地库,甚至可能是另一个工程。我们称这个过程叫“依赖解决”(dependency resolution).

通常情况下,依赖本身也有依赖。比如Hibernate核心库就依赖一些其他类库。当Gradle测试你工程的时候,它也需要找到这些间接依赖。我们称之为“传递依赖”( transitive dependencies).

大部分工程构建的主要目的是脱离工程使用。比如生成jar包,包括源代码、文档等,然后发布出去。这些个玩意构成了项目的发行内容。Gradle也会替你分忧这个工作。你声明了要发行,Gradle会构建并且发行。你可能会把文件拷贝到本地某处,或者上传到远程 Maven 或 Ivy 库,甚至在其他项目里使用。整个这个都是所谓的发行。

8.2. Declaring your dependencies依赖声明

我们看一下简单的依赖声明脚本:

Example 8.1. Declaring dependencies

build.gradle

apply plugin: ‘java‘

repositories {
    mavenCentral()
}

dependencies {
    compile group: ‘org.hibernate‘, name: ‘hibernate-core‘, version: ‘3.6.7.Final‘
    testCompile group: ‘junit‘, name: ‘junit‘, version: ‘4.+‘
}

这是在搞毛?这段脚本说的是:1,工程在编译时需要Hibernate core 3.6.7.Final。这样Hibernate核心库和它的依赖库都会引入。2,测试的时候需要4.0以上版本的junit。另外就是这些包都要从 Maven central 库里查找。接下来咱们详细看下。

8.3. Dependency configurations依赖配置

Gradle的依赖会分组为“配置”(configurations) 。一个配置就是一套具名依赖,称为“依赖配置”。你可以声明外部依赖,后面我们会看到。

Java插件定义了一些标准配置,形成了插件本身的类路径库。下面列一下,你可以自己去这看:Table 23.5, “Java plugin - dependency configurations”.

compile

该依赖对于编译发行是必须的。

runtime

该依赖对于运行时是必须的,默认包含编译时依赖。

testCompile

该依赖对于编译测试是必须的,默认包含编译产品依赖和编译时依赖。

testRuntime

该依赖对于测试是必须的,默认包含编译、运行时、测试编译依赖。

定制化配置请看: Section 50.3, “Dependency configurations”

8.4. External dependencies外部依赖

依赖类型有多种,现在说一种外部依赖(external dependency)。这种依赖的对象不在工程内,甚至是在远程库里。

定义外部依赖需要如下加入到依赖配置:

Example 8.2. Definition of an external dependency

build.gradle

dependencies {
    compile group: ‘org.hibernate‘, name: ‘hibernate-core‘, version: ‘3.6.7.Final‘
}

外部依赖使用groupname 和version 属性。根据使用库的不同,group 和version可能是可选的。有一种快捷方式声明依赖:

Example 8.3. Shortcut definition of an external dependency

build.gradle

dependencies {
    compile ‘org.hibernate:hibernate-core:3.6.7.Final‘
}

更多信息请看 Section 50.4, “How to declare your dependencies”.

8.5. Repositories库

Gradle是咋定位外部依赖呢?答案是“库”(repository)。一个库其实就是一大堆文件,不过是用groupname 和version组织好的。Gradle支持很多库,比如 Maven 和Ivy;还只存多种方式连接到库,比如本地系统或者 HTTP.

Gradle默认没定义库。你使用前需要至少定义一个,比如Maven central 库:

Example 8.4. Usage of Maven central repository

build.gradle

repositories {
    mavenCentral()
}

或者远程Maven 库:

Example 8.5. Usage of a remote Maven repository

build.gradle

repositories {
    maven {
        url "http://repo.mycompany.com/maven2"
    }
}

或者远程 Ivy 库:

Example 8.6. Usage of a remote Ivy directory

build.gradle

repositories {
    ivy {
        url "http://repo.mycompany.com/repo"
    }
}

你可以自己建个库然后指定:

Example 8.7. Usage of a local Ivy directory

build.gradle

repositories {
    ivy {
        // URL can refer to a local directory
        url "../local-repo"
    }
}

工程里可以制定多个库,Gradle会依序查找,一旦找到就停止。更多信息请看 Section 50.6, “Repositories”.

 

8.6. Publishing artifacts发布文件

依赖配置也用来发布文件,这些文件称为“发布文件”(publication artifacts),或干脆就叫“发件”(artifacts)(不会翻译,谁来纠正一下。。)

一般的,插件会做很多发件定义工作,所以我们不用干什么。不过需要告诉Gradle发件要发布到哪里。方法是把库附在 uploadArchives 任务里。比如我们要发布到远程Ivy库:

Example 8.8. Publishing to an Ivy repository

build.gradle

uploadArchives {
    repositories {
        ivy {
            credentials {
                username "username"
                password "pw"
            }
            url "http://repo.mycompany.com"
        }
    }
}

现在你执行 gradle uploadArchives, Gradle就会构建并上次你的Jar包,同时生成并上传一个ivy.xml 文件.

可以发布到Maven 库,语法有一点点不同。记住要使用Maven插件不然发布不过去。这样Gradle会生成并上传pom.xml文件。

Example 8.9. Publishing to a Maven repository

build.gradle

apply plugin: ‘maven‘

uploadArchives {
    repositories {
        mavenDeployer {
            repository(url: "file://localhost/tmp/myRepo/")
        }
    }
}

更多关于发布的信息请看 Chapter 51, Publishing artifacts.

http://somefuture.iteye.com/blog/2003535

翻译:Gradle之构建脚本编写

原文地址 http://www.gradle.org/docs/current/userguide/writing_build_scripts.html

13.1. The Gradle build language构建语言

Gradle提供了一种“领域专用语言”(domain specific language) 或者 DSL对构建进行描述。这种语言基于Groovy,并加入了其他特性使得描述更简单。

13.2. The Project API

在第七章Chapter 7, Java Quickstart 我们以apply() 方法为例做了描述。咦,这个方法哪来的?当时我们说脚本定义了工程,对于每一个工程Gradle会创建一个 Project 型的实例并将该对象实例和构建脚本进行关联。当构建脚本执行时会配置这个对象:

Getting help writing build scripts

嗨嗨别忘了构建脚本是Groovy代码,用的是Gradle API。而Project 接口就是你最先要了解的访问Gradle API的工具。所以要了解什么标签可用,就得去看Project 接口的文档.

  • 脚本中调用的任何方法如果在基本中无定义,都委托给Project对象。

  • 属性的访问同上。

咱们试着访问name属性看看 。

Example 13.1. Accessing property of the Project object

build.gradle

println name
println project.name

Output of gradle -q check

> gradle -q check
projectApi
projectApi

哇唔,俩println 语句都打出了同一个属性。前者是自动委托了Project对象,对于没在脚本里定义的属性施行。后者使用了工程命来关联Project对象。 如果你定义的属性和Project的成员一样了,那就必须使用工程名进行界定了。

13.2.1. Standard project properties标准属性

Project对象提供了几个标准属性,下面列出了一些常用的:

Table 13.1. Project Properties

Name Type Default Value
project Project The Project instance
name String The name of the project directory.
path String The absolute path of the project.
description String A description for the project.
projectDir File The directory containing the build script.
buildDir File projectDir/build
group Object unspecified
version Object unspecified
ant AntBuilder An AntBuilder instance

13.3. The Script API

脚本执行的时候Gradle会把它编译进实现了 Script接口的一个类中,这样接口中的属性和方法都能直接使用了。

13.4. Declaring variables声明变量

可是声明两种变量:局部变量和额外属性。

13.4.1. Local variables局部变量

使用 def 关键字声明局部变量,它们只能在声明域中访问。局部变量在Groovy中就有。

Example 13.2. Using local variables

build.gradle

def dest = "dest"

task copy(type: Copy) {
    from "source"
    into dest
}

13.4.2. Extra properties额外属性

Gradle域模型中的增强对象都有额外的用户定义属性。这包括但不限于工程、任务、资源集等。通过ext属性可以增加、读取、改变额外属性。也可以使用ext块增加多个额外属性。

Example 13.3. Using extra properties

build.gradle

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 }
}

Output of gradle -q printProperties

> gradle -q printProperties
3.1.0.RELEASE
build@master.org
main
plugin

上例中用 ext 块增加了两个额外属性。另外通过设置ext.purpose为null为每个资源集增加了purpose属性。一旦属性被增加就可以像预定义属性一样访问。

通过要求严格语法,Gradle会在尝试访问不存在是属性命时立即失败。额外属性可以从任何域访问,父工程的额外属性也能被子工程访问。

更多关于额外属性访问: ExtraPropertiesExtension.

13.5. Some Groovy basics 基本Groovy入门

Groovy提供了大量特性来创建DSL,Gradle就利用了这一点。

13.5.1. Groovy JDK

Groovy给JVM类增加了大量好用的方法。比如 Iterable 有一个 each 方法,可以迭代访问元素:

Example 13.4. Groovy JDK methods

build.gradle

// Iterable gets an each() method
configurations.runtime.each { File f -> println f }

详细请看 http://groovy.codehaus.org/groovy-jdk/ 

13.5.2. Property accessors属性访问符

Groovy会自动给属性增加 getter或 setter 方法.

Example 13.5. Property accessors

build.gradle

// Using a getter method
println project.buildDir
println getProject().getBuildDir()

// Using a setter method
project.buildDir = ‘target‘
getProject().setBuildDir(‘target‘)

13.5.3. Optional parentheses on method calls括号可有可无

方法的括号在调用时不需要写

Example 13.6. Method call without parentheses

build.gradle

test.systemProperty ‘some.prop‘, ‘value‘
test.systemProperty(‘some.prop‘, ‘value‘)

13.5.4. List and map literals迭代

Groovy提供了定义 List 和Map的快捷方法:

Example 13.7. List and map literals

build.gradle

// List literal
test.includes = [‘org/gradle/api/**‘, ‘org/gradle/internal/**‘]

List<String> list = new ArrayList<String>()
list.add(‘org/gradle/api/**‘)
list.add(‘org/gradle/internal/**‘)
test.includes = list

// Map literal
apply plugin: ‘java‘

Map<String, String> map = new HashMap<String, String>()
map.put(‘plugin‘, ‘java‘)
apply(map)

13.5.5. Closures as the last parameter in a method闭包作末参数

Gradle DSL随处使用闭包,闭包的概念见here。如果方法的最后一个参数是闭包,则可以把闭包后置 :

Example 13.8. Closure as method parameter

build.gradle

repositories {
    println "in a closure"
}
repositories() { println "in a closure" }
repositories({ println "in a closure" })

13.5.6. Closure delegate闭包委托

每个闭包都有一个delegate 对象,Groovy使用它来查找闭包的非局部变量和引用。Gradle以此来配置闭包,给闭包添加delegate对象。

Example 13.9. Closure delegates

build.gradle

dependencies {
    assert delegate == project.dependencies
    compile(‘junit:junit:4.11‘)
    delegate.compile(‘junit:junit:4.11‘)
}

http://somefuture.iteye.com/blog/2009114

翻译:Gradle之 Java插件

原文地址 http://www.gradle.org/docs/current/userguide/java_plugin.html

23.1. Usage用法

要使用Java插件,在脚本里加入:

Example 23.1. Using the Java plugin

build.gradle

apply plugin: ‘java‘

23.2. Source sets源集

Java插件引入了一个概念:源集(source set),一个源集就是一组被一起编译一起执行的源文件。这些文件可能包括Java源文件,也可以包括资源文件。有些插件增加了源集包含Groovy和Scala源文件的能力。一个源集具有一个相关的编译路径和运行时路径。

 源集的一个作用是将源文件进行逻辑分组来面熟共同的目的。比如可以使用源集定义一个集成测试套件,或用多个源集定义API和实现类。

java插件有两个标准源集:main 和test.。main 源集包含产品源代码,也就是要编译打包成jar的部分。test 源集包含单元测试源代码,编译后用JUnit 或TestNG执行。

23.3. Tasks任务

Java插件会给你的工程增加下面的任务:

Table 23.1. Java plugin - tasks

Task name Depends on Type Description
compileJava All tasks which produce the compile classpath. This includes the jar task for project dependencies included in thecompile configuration. JavaCompile Compiles production Java source files using javac.
processResources - Copy Copies production resources into the production classes directory.
classes compileJava and processResources. Some plugins add additional compilation tasks. Task Assembles the production classes directory.
compileTestJava compile, plus all tasks which produce the test compile classpath. JavaCompile Compiles test Java source files using javac.
processTestResources - Copy Copies test resources into the test classes directory.
testClasses compileTestJava andprocessTestResources. Some plugins add additional test compilation tasks. Task Assembles the test classes directory.
jar compile Jar Assembles the JAR file
javadoc compile Javadoc Generates API documentation for the production Java source, using Javadoc
test compilecompileTest, plus all tasks which produce the test runtime classpath. Test Runs the unit tests using JUnit or TestNG.
uploadArchives The tasks which produce the artifacts in thearchives configuration, including jar. Upload Uploads the artifacts in the archives configuration, including the JAR file.
clean - Delete Deletes the project build directory.
cleanTaskName - Delete Deletes the output files produced by the specified task. For example cleanJar will delete the JAR file created by the jar task, and cleanTest will delete the test results created by the test task.

对于你加入工程的每一个源集,Java插件会增加如下编译任务:

Table 23.2. Java plugin - source set tasks

Task name Depends on Type Description
compileSourceSetJava All tasks which produce the source set‘s compile classpath. JavaCompile Compiles the given source set‘s Java source files using javac.
processSourceSetResources - Copy Copies the given source set‘s resources into the classes directory.
sourceSetClasses compileSourceSetJava and processSourceSetResources. Some plugins add additional compilation tasks for the source set. Task Assembles the given source set‘s classes directory.

Java插件也增加形成工程生命周期的任务:

Table 23.3. Java plugin - lifecycle tasks

Task name Depends on Type Description
assemble All archive tasks in the project, including jar. Some plugins add additional archive tasks to the project. Task Assembles all the archives in the project.
check All verification tasks in the project, including test. Some plugins add additional verification tasks to the project. Task Performs all verification tasks in the project.
build check and assemble Task Performs a full build of the project.
buildNeeded build and build tasks in all project lib dependencies of thetestRuntime configuration. Task Performs a full build of the project and all projects it depends on.
buildDependents build and build tasks in all projects with a project lib dependency on this project in a testRuntime configuration. Task Performs a full build of the project and all projects which depend on it.
buildConfigurationName The tasks which produce the artifacts in configurationConfigurationName. Task Assembles the artifacts in the specified configuration. The task is added by the Base plugin which is implicitly applied by the Java plugin.
uploadConfigurationName The tasks which uploads the artifacts in configurationConfigurationName. Upload Assembles and uploads the artifacts in the specified configuration. The task is added by the Base plugin which is implicitly applied by the Java plugin.

下图是任务间的关系:

Figure 23.1. Java plugin - tasks

技术分享

23.4. Project layout工程布局

Java插件默认你的工程布局如下,任何目录都可以不存在,也可以不包含任何东西。java插件会编译它找到的任何东西,控制任何丢失的。

Table 23.4. Java plugin - default project layout

Directory Meaning
src/main/java Production Java source
src/main/resources Production resources
src/test/java Test Java source
src/test/resources Test resources
src/sourceSet/java Java source for the given source set
src/sourceSet/resources Resources for the given source set

23.4.1. Changing the project layout改变工程布局

可以通过配置合适的源集来配置布局。后面还会详细讨论,下面是一个简单的例子修改了main中的 Java 和resource source 目录.

Example 23.2. Custom Java source layout

build.gradle

sourceSets {
    main {
        java {
            srcDir ‘src/java‘
        }
        resources {
            srcDir ‘src/resources‘
        }
    }
}

23.5. Dependency management依赖管理

Java插件有很多依赖配置,它把这些配置非给各个任务,如compileJava 和 test.

Table 23.5. Java plugin - dependency configurations

Name Extends Used by tasks Meaning
compile - compileJava Compile time dependencies
runtime compile - Runtime dependencies
testCompile compile compileTestJava Additional dependencies for compiling tests.
testRuntime runtime, testCompile test Additional dependencies for running tests only.
archives - uploadArchives Artifacts (e.g. jars) produced by this project.
default runtime - The default configuration used by a project dependency on this project. Contains the artifacts and dependencies required by this project at runtime.

Figure 23.2. Java plugin - dependency configurations

技术分享

对于加入工程的每个源集,Java插件增加如下依赖配置:

Table 23.6. Java plugin - source set dependency configurations

Name Extends Used by tasks Meaning
sourceSetCompile - compileSourceSetJava Compile time dependencies for the given source set
sourceSetRuntime sourceSetCompile - Runtime time dependencies for the given source set

23.6. Convention properties传统属性

Java插件有一些传统属性,如下。你可以直接在脚本中使用这些属性,更多参考 Section 21.3, “Conventions”

Table 23.7. Java plugin - directory properties

Property name Type Default value Description
reportsDirName String reports The name of the directory to generate reports into, relative to the build directory.
reportsDir File (read-only) buildDir/reportsDirName The directory to generate reports into.
testResultsDirName String test-results The name of the directory to generate test result .xml files into, relative to the build directory.
testResultsDir File (read-only) buildDir/testResultsDirName The directory to generate test result .xml files into.
testReportDirName String tests The name of the directory to generate the test report into, relative to the reports directory.
testReportDir File (read-only) reportsDir/testReportDirName The directory to generate the test report into.
libsDirName String libs The name of the directory to generate libraries into, relative to the build directory.
libsDir File (read-only) buildDir/libsDirName The directory to generate libraries into.
distsDirName String distributions The name of the directory to generate distributions into, relative to the build directory.
distsDir File (read-only) buildDir/distsDirName The directory to generate distributions into.
docsDirName String docs The name of the directory to generate documentation into, relative to the build directory.
docsDir File (read-only) buildDir/docsDirName The directory to generate documentation into.
dependencyCacheDirName String dependency-cache The name of the directory to use to cache source dependency information, relative to the build directory.
dependencyCacheDir File (read-only) buildDir/dependencyCacheDirName The directory to use to cache source dependency information.

Table 23.8. Java plugin - other properties

Property name Type Default value Description
sourceSets SourceSetContainer (read-only) Not null Contains the project‘s source sets.
sourceCompatibility JavaVersion. Can also set using a String or a Number, e.g. ‘1.5‘ or1.5. Value of the current used JVM Java version compatibility to use when compiling Java source.
targetCompatibility JavaVersion. Can also set using a String or Number, e.g. ‘1.5‘ or1.5. sourceCompatibility Java version to generate classes for.
archivesBaseName String projectName The basename to use for archives, such as JAR or ZIP files.
manifest Manifest an empty manifest The manifest to include in all JAR files.

这些属性来自传统对象:JavaPluginConventionBasePluginConvention 和 ReportingBasePluginConvention.

23.7. Working with source sets使用源集

通过 sourceSets 属性访问源集,它是工程盛放源集的容器, SourceSetContainer类型的。 还有一个基本块 sourceSets { } ,可以使用闭包来配置源集容器。源集容器工作方式和其他容器完全一样,比如任务tasks.

Example 23.3. Accessing a source set

build.gradle

// Various ways to access the main source set
println sourceSets.main.output.classesDir
println sourceSets[‘main‘].output.classesDir
sourceSets {
    println main.output.classesDir
}
sourceSets {
    main {
        println output.classesDir
    }
}

// Iterate over the source sets
sourceSets.all {
    println name
}

要配置容器,只要用上面的任意一个方法设置源集属性值即可。源集的属性下面会描述。先简单看个例子:

Example 23.4. Configuring the source directories of a source set

build.gradle

sourceSets {
    main {
        java {
            srcDir ‘src/java‘
        }
        resources {
            srcDir ‘src/resources‘
        }
    }
}

23.7.1. Source set properties源集属性

下面是一些常用的重要的属性,更多的参见:SourceSet.

Table 23.9. Java plugin - source set properties

Property name Type Default value Description
name String (read-only) Not null The name of the source set, used to identify it.
output SourceSetOutput (read-only) Not null The output files of the source set, containing its compiled classes and resources.
output.classesDir File buildDir/classes/name The directory to generate the classes of this source set into.
output.resourcesDir File buildDir/resources/name The directory to generate the resources of this source set into.
compileClasspath FileCollection compileSourceSet configuration. The classpath to use when compiling the source files of this source set.
runtimeClasspath FileCollection output + runtimeSourceSetconfiguration. The classpath to use when executing the classes of this source set.
java SourceDirectorySet (read-only) Not null The Java source files of this source set. Contains only .java files found in the Java source directories, and excludes all other files.
java.srcDirs Set<File>. Can set using anything described in Section 16.5, “Specifying a set of input files”. [projectDir/src/name/java] The source directories containing the Java source files of this source set.
resources SourceDirectorySet (read-only) Not null The resources of this source set. Contains only resources, and excludes any.java files found in the resource source directories. Other plugins, such as the Groovy plugin, exclude additional types of files from this collection.
resources.srcDirs Set<File>. Can set using anything described in Section 16.5, “Specifying a set of input files”. [projectDir/src/name/resources] The source directories containing the resources of this source set.
allJava SourceDirectorySet (read-only) java All .java files of this source set. Some plugins, such as the Groovy plugin, add additional Java source files to this collection.
allSource SourceDirectorySet (read-only) resources + java All source files of this source set. This include all resource files and all Java source files. Some plugins, such as the Groovy plugin, add additional source files to this collection.

23.7.2. Defining new source sets新建源集

在 sourceSets { } 块中命名就可以创建源集了:

Example 23.5. Defining a source set

build.gradle

sourceSets {
    intTest
}

新建源集后Java插件会给它增加一些依赖配置,见上文: Table 23.6, “Java plugin - source set dependency configurations”. 你可以用这些配置定义源集的编译和运行时依赖。

Example 23.6. Defining source set dependencies

build.gradle

sourceSets {
    intTest
}

dependencies {
    intTestCompile ‘junit:junit:4.11‘
    intTestRuntime ‘org.ow2.asm:asm-all:4.0‘
}

Java插件会增加一些汇编任务给源集,见上文 Table 23.2, “Java plugin - source set tasks”. 比如对于 intTest的源集可以通过执行 gradle intTestClasses 来编译

Example 23.7. Compiling a source set

Output of gradle intTestClasses

> gradle intTestClasses
:compileIntTestJava
:processIntTestResources
:intTestClasses

BUILD SUCCESSFUL

Total time: 1 secs

23.8. Javadoc

javadoc任务是Javadoc的实例。它支持核心javadoc选项和标准doclet选项(见 reference documentation )。完整信息参考 CoreJavadocOptions and StandardJavadocDocletOptions.

Table 23.10. Java plugin - Javadoc properties

Task Property Type Default Value
classpath FileCollection sourceSets.main.output + sourceSets.main.compileClasspath
source FileTree. Can set using anything described in Section 16.5, “Specifying a set of input files”. sourceSets.main.allJava
destinationDir File docsDir/javadoc
title String The name and version of the project

23.9. Clean

clean 任务是Delete的实例,它会把 dir 属性值对应的目录删掉.

Table 23.11. Java plugin - Clean properties

Task Property Type Default Value
dir File buildDir

23.10. Resources资源

The Java plugin uses the Copy task for resource handling. It adds an instance for each source set in the project. You can find out more about the copy task in Section 16.6, “Copying files”.

Table 23.12. Java plugin - ProcessResources properties

Task Property Type Default Value
srcDirs Object. Can set using anything described in Section 16.5, “Specifying a set of input files”. sourceSet.resources
destinationDir File. Can set using anything described in Section 16.1, “Locating files”. sourceSet.output.resourcesDir

23.11. CompileJava编译

Java插件会给工程中的每个源集增加一个 JavaCompile 类型实例。主要的配置选项如下:

Table 23.13. Java plugin - Compile properties

Task Property Type Default Value
classpath FileCollection sourceSet.compileClasspath
source FileTree. Can set using anything described in Section 16.5, “Specifying a set of input files”. sourceSet.java
destinationDir File. sourceSet.output.classesDir

编译任务委托了Ant的 javac 任务,将options.useAnt设为false可以激活Grass的编译集成从而绕过Ant的任务。以后这会成为默认任务。

默认Java的编译工作在Gradle进程中执行。将options.fork 设为 true 会生成单独的进程。在Ant中这样做会减慢编译速度,而在Gradle中相反,Gradle会尽量重复使用编译进程。优先尊重的选项是options.forkOptions

 

23.13. Jar打包

jar任务会生成jar文件,包括了工程的类文件和资源文件。jar文件是 archives 依赖配置的产出,所以依赖工程可以直接引用。如果要把工程上传到库,jar文件会被声明为依赖描述符的一部分。更多请阅读 Section 16.8, “Creating archives” 和 Chapter 51, Publishing artifacts.

23.13.1. Manifest主配置清单

每个jar或war对象都有一个 manifest 属性,值为 Manifest的单独实例。压缩后对应的MANIFEST.MF文件就写入压缩文件中了。

Example 23.15. Customization of MANIFEST.MF

build.gradle

jar {
    manifest {
        attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
    }
}

可以创建 Manifest 的独立实例,这样就可以在jar中共享主配信息了。

Example 23.16. Creating a manifest object.

build.gradle

ext.sharedManifest = manifest {
    attributes("Implementation-Title": "Gradle", "Implementation-Version": version)
}
task fooJar(type: Jar) {
    manifest = project.manifest {
        from sharedManifest
    }
}

 Manifest 对象可以随便合并,可以是文件路径也可以是主配引用等等。

Example 23.17. Separate MANIFEST.MF for a particular archive

build.gradle

task barJar(type: Jar) {
    manifest {
        attributes key1: ‘value1‘
        from sharedManifest, ‘src/config/basemanifest.txt‘
        from(‘src/config/javabasemanifest.txt‘, ‘src/config/libbasemanifest.txt‘) {
            eachEntry { details ->
                if (details.baseValue != details.mergeValue) {
                    details.value = baseValue
                }
                if (details.key == ‘foo‘) {
                    details.exclude()
                }
            }
        }
    }
}

Manifest会按照from 语句中的顺序进行合并。如果合并中发现相同的值则保持原来的,可以通过增加eachEntry 动作自定义合并行为,在里面要访问 ManifestMergeDetails 的实例。合并并不是立即执行的,而是在生成jar文件或者调用 writeTo 和 effectiveManifest的时候。要把主配写入硬盘很简单:

Example 23.18. Separate MANIFEST.MF for a particular archive

build.gradle

jar.manifest.writeTo("$buildDir/mymanifest.mf")

23.14. Uploading上传

上传请看 Chapter 51, Publishing artifacts.

http://somefuture.iteye.com/blog/2009115

 

 

 

 

 

 

Gradle cookbook(转)

标签:

原文地址:http://www.cnblogs.com/softidea/p/4525764.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!