gradle – خلاصه نکات مهم

اخیرا یک پروژه اندروید جدید رو شروع کردم و به دلایلی که قبلا هم داشتم و اشاره کرده بودم خواستم کامپایل و تشکیلات ساخت اون رو با استفاده از ابزارهای خط فرمان انجام بدم. ولی این بار چون نیاز شد از یک کتابخانه خاص و نسبتا جدید در پروژم استفاده کنم و شرایط کامپایل این کتابخانه عمیقا به gradle وابسته است، مجبور شدم این بار ant رو کنار بذارم و از gradle استفاده کنم. البته هم که gradle نسبت به ant خیلی پیشرفته تر و منعطف تره. با اینکه با دردسر و کوشش زیاد بالاخره موفق شدم پروژه اندروید خودم رو با استفاده از ابزارهای خط فرمان راه اندازی و کامپایل کنم اما فکر کردم مطالعه راهنما و رفرنسی درمورد gradle کار عاقلانه و مفیدیه.
اینجا نکات مهم و خلاصه شده ای از چیزهایی که همگام با مطالعه رفرنس gradle یاد میگیرم رو درج میکنم (این پست به مرور تکمیل میشه) تا برای مراجعات بعدی خودم هم قابل استفاده باشه.

gradle از جاوا و از زبان Groovy استفاده میکنه که بر بستر جاوا اجرا میشه. شما برای استفاده از gradle باید jre یا jdk حداقل نسخه 7 رو روی سیستمتون داشته باشید. gradle کتابخانه Groovy خودش رو همراه خودش داره و نیازی به نصب Groovy روی سیستم نداره.

یک نمونه از تعریف چند task در build.gradle:

task compile {
doLast {
println 'compiling source'
}
}

task compileTest(dependsOn: compile) {
doLast {
println 'compiling unit tests'
}
}

task test(dependsOn: [compile, compileTest]) {
doLast {
println 'running unit tests'
}
}

task dist(dependsOn: [compile, test]) {
doLast {
println 'building the distribution'
}
}

اسم task ها در خط فرمان میتونه بصورت خلاصه نوشته بشه، مثلا compileTest رو میشه بصورت compTest یا حتی cT نوشت.

شناسایی sub-project های پروژه جاری توسط این فرمان:

gradle -q projects

نمایش task های موجود:

gradle -q tasks

البته این فرمان بصورت پیشفرض فقط task هایی رو نشون میده که در یک group هستن.

یک روش اضافه کردن گروه و توصیف به یک task:

dists {
description = 'Builds the distribution'
group = 'build'
}

نمایش تمامی task ها (حتی اونهایی که در یک گروه قرار داده نشدن):

gradle -q tasks --all

نمونه ای از فرمان برای نمایش dependencies:

gradle -q dependencies api:dependencies webapp:dependencies

برای کامپایل/ساخت پروژه های gradle نیازی نیست تا یک نسخه مستقل gradle حتما روی سیستم نصب باشه، بلکه میشه از Gradle Wrapper استفاده کرد. اکثر یا خیلی از پروژه ها Gradle Wrapper رو همراه خودشون دارن، که برنامه ای هست که میشه بجای gradle استفاده کرد، و در واقع در جا میاد و نسخهء مورد نیاز gradle رو دانلود و برای ساخت پروژه استفاده میکنه. اینطوری برای کامپایل و ساخت یک پروژه نیازی به نصب قبلی نسخهء خاصی از gradle روی سیستم کاربر نیست.
اگر پروژه ای حاوی Gradle Wrapper باشه دو فایل رو میشه در ریشهء اون مشاهده کرد، gradlew که شل اسکریپت مورد استفاده برای اجرای wrapper در لینوکس است، و gradlew.bat که بچ فایل برای اجرا روی ویندوز است.
ما میتونیم تمام فرمان هایی رو که به gradle میدیم بجاش به این اسکریپت ها و در نتیجه Gradle Wrapper بدیم تا برامون اجرا کنه.

اگر پروژه از قبل دارای Gradle Wrapper نباشه میشه با این فرمان اون رو بهش اضافه کرد:

gradle wrapper --gradle-version 2.0

با آپشن –gradle-version میتونیم نسخه ای از gradle که میخوایم برای ساخت پروژه دانلود و استفاده بشه رو مشخص کنیم (اگر مشخص نکنیم، از همون نسخهء gradle ای که باهاش این فرمان رو استفاده میکنیم استفاده میشه).

با آپشن –gradle-distribution-url میتونیم آدرس دانلود gradle رو مشخص کنیم.

برای تغییر نسخهء gradle نیازی نیست تسک wrapper رو مجددا اجرا کنیم و تغییر distributionUrl در gradle-wrapper.properties کفایت میکنه.

میشه توزیع gradle مورد نظر رو در خود پروژه قرار بدیم و میتونیم آدرس اون رو در gradle-wrapper.properties بصورت نسبی نسبت به مکان gradle-wrapper.properties تعیین کنیم تا نیازی به دانلود نباشه، اما طبیعتا اینطوری حجم پروژه زیاد میشه.

نمونه ای از تعیین dependency (و البته استفاده از پلاگین java و تعیین repository):

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.+'
}

میشه یک dependency رو بصورت خلاصه تر group:name:version هم نوشت:

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

بسته به نوع repo ای که استفاده میکنید ممکنه نیازی به group و version نباشه.

از دو repo از پیش موجود mavenCentral و jcenter میتونیم استفاده کنیم، ولی میتونیم خودمون هم repo تعریف کنیم.

repositories {
jcenter()
}

راستی یه نکته اینکه موارد جدید به هم افزوده میشن نه اینکه موارد قبلی رو بازنویسی کنن. یعنی مثلا یک repo تعریف کنیم و بعد از چند خط و دستور دیگه repo دیگری رو هم تعریف کنیم، repo اول و دوم هر دو به پروژه اضافه میشن. خیلی جاهای دیگر هم همینطوره و میشه تعاریف و دستورات رو بصورت افزایشی در قسمتهای جداگانه قرار داد.

استفاده از یک maven repository سفارشی:

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

همچنین Ivy:

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

استفاده از local repo:

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

برای publish کردن artifact های پروژه میتونیم repositories مورد نظر رو در تسک uploadArchives مشخص کنیم:

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

البته برای maven فعلا سینتاکسش یخورده فرق میکنه و نیاز به لود کردن پلاگین maven هم هست:

apply plugin: 'maven'

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

نمونه ای از اینکه اگر بخوایم task یک subproject خاص رو اجرا کنیم:

gradle :services:webservice:build

نکته: فرمانی مثل این:

gradle test

تسک test رو فقط در دایرکتوری/پروژهء جاری اجرا نمیکنه، بلکه در تمام زیر پروژه های موجود در مکان جاری هم اجرا میکنه (هرکدام که تسک ای به این نام داشته باشن).

اگر یک مسیر با : شروع بشه، اون مسیر نسبت به پروژهء ریشه در نظر گرفته میشه.

مثال:

gradle :services:webservice:tasks

این فرمان تسک های زیر پروژه webservice رو نشون میده.

البته برای مشخص کردن یک subproject باید قبلا اون رو توسط دستور include در settings.gradle معرفی کنیم.
مثال:

include 'subProj'

که در اینجا subProj نام دایرکتوری subproject ما بوده.

یه مثال خوب که نکات مفیدی رو نشون میده:

Closure cl = { task -> println "I'm $task.project.name" }
task('hello').doLast(cl)
project(':bluewhale') {
task('hello').doLast(cl)
}

با allprojects میتونیم چیزی رو برای پروژه جاری و تمام پروژه های زیرمجموعه اش تعریف کنیم:

allprojects {
task hello {
doLast { task ->
println "I'm $task.project.name"
}
}
}

همچنین subprojects وجود داره که باهاش میتونیم چیزهایی رو فقط روی subproject ها اعمال کنیم:

allprojects {
task hello {
doLast { task ->
println "I'm $task.project.name"
}
}
}
subprojects {
hello {
doLast {
println "- I depend on water"
}
}
}

تعریف رفتار خاص برای یک پروژهء مشخص:

project(':bluewhale').hello {
doLast {
println "- I'm the largest animal that has ever lived on this planet."
}
}

نکته: البته معمولا اینطور چیزها رو در build.gradle خود اون پروژه میذارن.

مثال کاربرد configure:

configure(subprojects.findAll {it.name != 'tropicalFish'}) {
hello {
doLast {
println '- I love to spend time in the arctic waters.'
}
}

The configure() method takes a list as an argument and applies the configuration to the projects in this list

—————————————————————————————-

lib dependencies

What if one project needs the jar produced by another project in its compile path, and not just the jar but also the transitive dependencies of this jar? Obviously this is a very common use case for Java multi-project builds

مثال ساختار پروژه:

java/
settings.gradle
build.gradle
api/
src/main/java/
org/gradle/sample/
api/
Person.java
apiImpl/
PersonImpl.java
services/personService/
src/
main/java/
org/gradle/sample/services/
PersonService.java
test/java/
org/gradle/sample/services/
PersonServiceTest.java
shared/
src/main/java/
org/gradle/sample/shared/
Helper.java

We have the projects “shared”, “api” and “personService”. The “personService” project has a lib dependency on the other two projects. The “api” project has a lib dependency on the “shared” project

محتویات settings.gradle:

include 'api', 'shared', 'services:personService'

محتویات build.gradle:

subprojects {
apply plugin: 'java'
group = 'org.gradle.sample'
version = '1.0'
repositories {
mavenCentral()
}
dependencies {
testCompile "junit:junit:4.12"
}
}

project(':api') {
dependencies {
compile project(':shared')
}
}

project(':services:personService') {
dependencies {
compile project(':shared'), project(':api')
}
}

—————————————————————————————-

composite build

my-app/build.gradle:

apply plugin: 'java'
apply plugin: 'application'
apply plugin: 'idea'

group "org.sample"
version "1.0"

mainClassName = "org.sample.myapp.Main"

dependencies {
compile "org.sample:number-utils:1.0"
compile "org.sample:string-utils:1.0"
}

repositories {
jcenter()
}

gradle --include-build ../my-utils run

روش دیگر استفاده از settings.gradle بجای آپشن خط فرمان:

Settings.includeBuild(java.lang.Object)

Defining a separate composite build

One downside of the above approach is that it requires you to modify an existing build, rendering it less useful as a standalone build. One way to avoid this is to define a separate composite build, whose only purpose is to combine otherwise separate builds.

settings.gradle:

rootProject.name='adhoc'

includeBuild '../my-app'
includeBuild '../my-utils'

build.gradle:

task run {
dependsOn gradle.includedBuild('my-app').task(':run')
}

Every included build:
- must have a settings.gradle file.
- must not itself be a composite build.
- must not have a rootProject.name the same as another included build.
- must not have a rootProject.name the same as a top-level project of the composite build.
- must not have a rootProject.name the same as the composite build rootProject.name.

—————————————————————————————-

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

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

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

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

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

You can add your own properties to a task. To add a property named myProperty, set ext.myProperty to an initial value. From that point on, the property can be read and set like a predefined task property.

task myTask {
ext.myProperty = "myValue"
}

task printTaskProperties {
doLast {
println myTask.myProperty
}
}

Default tasks

Gradle allows you to define one or more default tasks that are executed if no other tasks are specified.

defaultTasks 'clean', 'run'

task clean {
doLast {
println 'Default Cleaning!'
}
}

task run {
doLast {
println 'Default Running!'
}
}

task other {
doLast {
println "I'm not a default task!"
}
}

This is equivalent to running gradle clean run. In a multi-project build every subproject can have its own specific default tasks. If a subproject does not specify default tasks, the default tasks of the parent project are used (if defined).

—————————————————————————————-

gradle init --type java-library

gradle init --type java-application

Uses the “application” plugin to produce a command-line application implemented using Java

در gradle معمولا میشه یک کار رو از چندین راه مختلف و با سینتاکس های مختلف انجام داد و آپشن ها و انعطاف زیادی داره میشه از برنامه نویسی Groovy هم بصورت کامل درش استفاده کرد:

project('projectA') {
task taskX(dependsOn: ':projectB:taskY') {
doLast {
println 'taskX'
}
}
}

taskX.dependsOn taskY

taskX.dependsOn {
tasks.findAll { task -> task.name.startsWith('lib') }
}

task copy(type: Copy) {
description 'Copies the resource directory to the target directory.'
from 'resources'
into 'target'
include('**/*.txt', '**/*.xml', '**/*.properties')
}

---------------------------------

task copy(type: Copy)

task copy(overwrite: true) {
doLast {
println('I am the new one.')
}
}

---------------------------------

task hello {
doLast {
println 'hello world'
}
}

hello.onlyIf { !project.hasProperty('skipHello') }

---------------------------------

task disableMe {
doLast {
println 'This should not be printed if the task is disabled.'
}
}
disableMe.enabled = false

If you want to use a (flat) filesystem directory as a repository, simply type:

repositories {
flatDir {
dirs 'lib'
}
flatDir {
dirs 'lib1', 'lib2'
}
}

پاسخ دهید

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *

*

شما می‌توانید از این دستورات HTML استفاده کنید: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>