タスク
次のコードには、3つのグラードルタスクが表示されます。これらの3つの違いについては、後で説明します。
タスクmytask {println "hello、world!" }タスクmytask {dolast {println "hello、world!" }}タスクmytask << {println "hello、world!" }私の目標は、「Hello、World!」を印刷するタスクを作成することです。それが実行されるとき。私が最初にタスクを作成したとき、私はそれがこのように書かれるべきだと思いました:
タスクmytask {println "hello、world!" }次に、このMyTaskを実行し、コマンドラインにGradle MyTaskを入力して、次のように印刷してみてください。
ユーザー$ gradle mytaskこんにちは、世界! :最新のmytask
このタスクは機能しているように見えます。 「こんにちは、ワールド!」を印刷します。
しかし、それは実際に私たちが期待したものではありません。その理由を見てみましょう。コマンドラインにGradleタスクを入力して、利用可能なすべてのタスクを表示します。
ユーザー$ gradleタスクこんにちは、世界! :タスク---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- init-新しいグラデーションビルドの初期化。 [インキュベーション] .........
待って、なぜ「こんにちは、世界!」印刷された?どのタスクが利用可能であるかを見たかっただけで、カスタムタスクを実行しませんでした!
その理由は実際には非常に単純です。 Gradleタスクには、ライフサイクルに2つの主要な段階があります。構成段階と実行段階です。
たぶん私の言葉はあまり正確ではないかもしれませんが、これは私がタスクを理解するのに本当に役立ちます。
Gradleは、実行する前にタスクを構成する必要があります。問題は、構成プロセス中にタスクのどのコードが実行され、タスクが実行されたときに実行されるコードをどのように知ることができるかということです。答えは、タスクのトップレベルのコードは、次のような構成コードであるということです。
タスクmytask {def name = "pavel" // < - このコード行はprintln "hello、world!" //// <-このコード行も構成段階で実行します}これが、Gradleタスクを実行したときに「Hello、World!」を印刷する理由です。 - 構成コードが実行されるため。しかし、これは私が望む効果ではありません、私は「こんにちは、世界!」が欲しいです私が明示的にmyTaskに電話するときにのみ印刷するために。この効果を達成するために、最も簡単な方法は、タスク#dolast()メソッドを使用することです。
タスクmytask {def text = 'hello、world!' //私のタスクを設定しますdolast {println text //これは私のタスクが呼ばれるときに実行されます}}}さて、「こんにちは、世界!」 Gradle MyTaskを実行するときにのみ印刷されます。かっこいい、今、私はタスクを正しいことをする方法を知っています。別の質問があります。最初の例では、3番目のタスクの<<シンボルはどういう意味ですか?
タスクmytask2 << {println "hello、world!" }これは実際には、ドラストの構文糖バージョンです。次の記述方法と同じ効果があります。
タスクmytask {dolast {println 'hello、world!' //これは私のタスクが呼ばれるときに実行されます}}ただし、この方法でのすべてのコードは実行され、コードの構成部分はないため、構成を必要としない簡単なタスクにより適しています。タスクを構成する必要がある場合は、ドラストバージョンを使用する必要があります。
文法
Gradleスクリプトはグルーヴィーな言語で書かれています。 Groovyの構文はJavaに少し似ています。受け入れることができることを願っています。
すでにGroovyに精通している場合は、この部分をスキップできます。
Groovyには非常に重要な概念があり、閉鎖を理解する必要があります(閉鎖)
閉鎖
閉鎖は、Gradleの理解の鍵です。閉鎖は、パラメーターを受信したり、値を返したり、変数に割り当てることができるコードの個別のブロックです。 JavaとFutureの呼び出し可能なインターフェイスに似ており、理解しやすい関数ポインターのようなものでもあります。 。 。
重要なのは、このコードが作成されたときではなく、呼び出すときに実行されることです。閉鎖の例を参照してください:
def myclosure = {println 'hello world!' } // closingclosure()#出力を実行します:hello world!パラメーターを受信する閉鎖は次のとおりです。
def myclosure = {string str-> println str} // closhtclosureを実行( 'hello world!')#出力:Hello World!閉鎖が1つのパラメーターのみを受信する場合、このパラメーターを参照するために使用できます。
def myclosure = {println it} // closhtclosure( 'hello world!')#出力:hello world!複数のパラメーターを受信する閉鎖:
def myClosure = {string str、int num-> println "$ str:$ num"} // renovationycloserure( 'my string'、21)#output:my string:21さらに、パラメーターのタイプはオプションであり、上記の例は次のように短縮できます。
def myClosure = {str、num-> println "$ str:$ num"} // closingclosure( 'my string'、21)#output:my string:21クールなのは、現在のコンテキストからの変数を閉鎖に使用できることです。デフォルトでは、現在のコンテキストは、閉鎖が作成されたクラスです。
def myvar = 'hello world!' def myclosure = {println myvar} myclosure()#出力:hello world!もう1つのクールなポイントは、閉鎖#setDelegate()を介して閉鎖のコンテキストを変更できることです。この機能は非常に便利です:
def myclosure = {println myvar} // myclass classmyclass m = new myclass()myclosure.setdelegate(m)myclosure()class myclass {def myvar = 'hello from myclass!'}#出力:MyClassからHello!ご覧のとおり、閉鎖を作成するときはMyVarは存在しません。これに問題はありません。なぜなら、閉鎖を実行すると、MyVarが閉鎖の文脈に存在するからです。この例で。閉鎖を実行する前にコンテキストをMに変更したため、MyVarは存在します。
パラメーターとして閉鎖を渡します
閉鎖の利点は、異なる方法に渡すことができることです。これにより、実行ロジックを分離するのに役立ちます。前の例では、クラスのインスタンスに閉鎖を渡す方法を示しました。以下では、閉鎖を受信するパラメーターとしてのさまざまな方法をご覧ください。
1.パラメーターを1つだけ受信し、パラメーターは閉鎖方法です:mymethod(myclosure)
2。メソッドが1つのパラメーターのみを受信する場合、括弧は省略できます:mymethodmyClosure
3.インライン閉鎖を使用できます:mymethod {println 'hello world'}
4。2つのパラメーターを受信する方法:mymethod(arg1、myclosure)
5。4と同様に、特異閉鎖はインラインです:mymethod(arg1、{println 'hello world'})
6.最後のパラメーターが閉鎖である場合、括弧から取り出すことができます:mymethod(arg1){println 'hello world'}
ここでは、3と6の執筆がおなじみのように見えるかどうかを思い出させたいだけですか?
等級の例
基本的な構文を理解したので、Gradleスクリプトでどのように使用しますか?次の例を見てみましょう。
buildscript {repositories {jcenter()}依存関係{classpath 'com.android.tools.build:1.2.3'}} allprojects {repositeries {jcenter()}}}} Groovyの構文を知ったら、上記の例を簡単に理解するのは簡単ですか?
1つ目は、閉鎖を受信するBuildScriptメソッドです。
def buildscript(閉鎖閉鎖)
次は、閉鎖パラメーターも受信するAllProjectsメソッドです。
def allprojects(閉鎖閉鎖)
他は似ています。 。 。
今はずっと簡単に思えますが、私は1つのことを理解していません。つまり、これらの方法はどこに定義されていますか?答えはプロジェクトです
プロジェクト
これは、Gradleスクリプトを理解するための鍵です。
ビルドスクリプトのトップレベルの声明ブロックは、プロジェクトインスタンスに委任されます。これは、プロジェクトが私が探している場所であることを示しています。
プロジェクトのドキュメントページでbuildscriptメソッドを検索すると、buildscript {} scriptブロック(スクリプトブロック)が見つかります。など。スクリプトブロックとは何ですか?ドキュメントによると、
スクリプトブロックは、パラメーターとして閉鎖のみを受信する方法です。 BuildScriptドキュメントを引き続きお読みください。このドキュメントには、BuildScriptのScripThandlerに代表が表示されています。つまり、閉鎖をBuildScriptメソッドに渡し、最終的な実行コンテキストはScripThandlerです。上記の例では、BuildScriptに閉鎖された閉鎖は、リポジトリ(クロージャー)および依存関係(閉鎖)メソッドを呼び出します。閉鎖はScripthandlerに委託されているため、Scripthandlerで依存関係の方法を探します。
ボイド依存関係(閉鎖構成)が見つかりました。ドキュメントによると、依存関係はスクリプト依存関係を構成するために使用されます。依存関係は最終的に依存関係者に委託されました。
グラードルがどれほど広く使用されているかを見ました。委託を理解することは非常に重要です。
スクリプトブロック
デフォルトでは、多くのスクリプトブロックがプロジェクトで事前に定義されていますが、Gradleプラグインを使用すると、新しいスクリプトブロックを定義できます。
これは、ビルドスクリプトの上位レベルに{…}を投稿するが、Gradleのドキュメントにこのスクリプトブロックまたはメソッドを見つけることができない場合、ほとんどの場合、これはプラグインで定義されているスクリプトブロックです。
Androidスクリプトブロック
デフォルトのAndroidアプリ/build.gradleファイルを見てみましょう。
プラグインを適用: 'com.android.application'android {compilesdkversion 22 buildtoolsversion "22.0.1" defaultconfig {applicationId "com.trickyandroid.testapp" minsdkversion 16 Targetsdkversion 22バージョン1バージョン1バージョンname "1.0" getDefaultProguardfile( 'proguard-android.txt')、 'proguard-rules.pro'}}}}タスクオーダー
Gradleを使用するときに遭遇した問題のほとんどがタスクの実行順序に関連していることに気付きました。明らかに、私のタスクが適切なタイミングで実行された場合、私のビルドがうまく機能する場合。タスクの実行順序を変更する方法を詳しく見てみましょう。
deponson
タスクを実行する際の他のタスクに依存する方法を説明する最も直接的な方法は、Depensonメソッドを使用することだと思います。
たとえば、次のシナリオでは、タスクAがすでに存在します。タスクBを追加したいと考えています。その実行は、Aが実行された後でなければなりません。
これは、AとBの定義が次のとおりであると仮定して、非常に単純なシナリオです。
タスクa << {println 'hello from a'} task b << {println 'hello from b'} B.Dependentson Aに電話するだけで問題ありません。
これは、タスクBを実行する限り、タスクAが最初に実行されることを意味します。
Paveldudka $ gradle B:ahello from a:bhello from b
さらに、タスク構成領域で依存関係を宣言できます。
タスクa << {println 'hello from a'}タスクB {dependson a dolast {println 'hello from b'}}}タスクを既存のタスク依存関係に挿入する場合はどうなりますか?
このプロセスは、今のものに似ています。次のタスク依存関係がすでに存在すると仮定します。
タスクa << {println 'hello from a'}タスク新しいタスクに参加してください
タスクb1 << {println 'hello from b1'} b1.dependson bc.dependson b1出力:
Paveldudka $ gradle C:a from A:bhello from B:b1hello from b1:chello from c
Depensonは依存コレクションにタスクを追加するため、複数のタスクに依存することは問題ではないことに注意してください。
タスクb1 << {println 'hello from b1'} b1.dependson bb1.dependson q出力:
Paveldudka $ gradle b1:a from a:bhello from b:qhello from q:b1hello from b1
strunafter
ここで、他の2つのタスクに依存する別のタスクがあるとします。ここでは、2つのタスクがあり、1つの単体テストタスクと1つのUIテストタスクがある実際のシナリオを使用します。すべてのテストを実行するタスクもあり、これは前の2つのタスクに依存します。
タスクユニット<< {println 'hello from unit tests'} task ui << {println 'hello ui tests'}タスクテスト<< {println 'hello from all tests!'} dependson unittests.dependson ui出力:
Paveldudka $ gradleテスト:UIテストからのUihello:ユニットテストからのUnithello:すべてのテストからのテストシェロ!
UnitestおよびUIテストはテストタスクの前に実行されますが、ユニットとUIの実行順序を保証することはできません。アルファベットの順序で実行されていますが、これはGradleの実装に依存し、コードでこの注文に依存してはなりません。
UIテスト時間はユニットテスト時間よりもはるかに長いため、ユニットテストを最初に実行したいと思います。解決策の1つは、UIタスクをユニットタスクに依存させることです。
タスクユニット<< {println 'hello from unit tests'}タスクui << {println 'hello ui tests'} task tests << {println 'hello from All tests!'} testson unittests.dependson uiui.dependson unit // <出力:
Paveldudka $ gradleテスト:ユニットテストからのUnithello:UIテストからのUihello:すべてのテストからのテストシェロ!
これで、ユニットテストはUIテストの前に実行されます。
しかし、ここには非常に嫌な問題があります。私のUIテストは、実際にはユニットテストに依存していません。 UIテストを個別に実行できることを願っていますが、ここではUIテストを実行するたびに、最初にユニットテストを実行します。
ここではstrunafterが必要です。 Mustrunafterは依存関係を追加しません。これは、2つのタスクが同時に存在する場合、実行の優先順位をGradleに伝えるだけです。たとえば、ここでUI.Mustrunafterユニットを指定できます。このようにして、UIタスクとユニットのタスクが同時に存在する場合、Gradleは最初にユニットテストを実行し、Gradle UIのみが実行された場合、ユニットタスクは実行されません。
タスクユニット<< {println 'hello from unit tests'} task ui << {println 'hello ui tests'}タスクテスト<< {println 'hello from All tests!'} dependson unittests.dependson uiui.mustunafter unit出力:
Paveldudka $ gradleテスト:ユニットテストからのUnithello:UIテストからのUihello:すべてのテストからのテストシェロ!
依存関係の関係は次のとおりです。
Mustrunafterは現在、Gradle 2.4の実験機能です。
ファイナル化されています
現在、両方のタスクがテストレポートを出力すると仮定して、ユニットとUIの2つのタスクがあります。ここで、これらの2つのテストレポートを1つに統合したいと思います。
タスクユニット<< {println 'hello from unit tests'} task ui << {println 'hello ui tests'}タスクテスト<< {println 'hello from All tests!'} task mergerports << {println 'Merging Test Reports'} Testsson Unustson Unustson unustson unustson unustson unistssonUIとユニットのテストレポートを取得したい場合は、タスクMergereportsを実行します。
paveldudka $ gradle mergereports:unithello from unitelo from unitelo:uihello from ui tests:すべてのテストからのテストシェロ:mergereportsmergingテストレポート
このタスクは機能しますが、とても愚かに見えます。 Mergereportsは、ユーザーの観点からは特に気分が良くありません。 Mergerportsの存在を知らずにテストレポートを取得するために、テストタスクを実行したいと思います。もちろん、マージロジックをテストタスクに移動することはできますが、テストタスクをあまりにも肥大化したくないので、マージロジックをMergereportsタスクに掲載し続けます。
FinalizeByはシーンを保存するためにここにあります。名前が示すように、finalizebyは、タスクが実行された後に実行されるタスクです。次のようにスクリプトを変更します。
タスクユニット<< {println 'hello from unit tests'} task ui << {println 'hello ui tests'}タスクテスト<< {println 'hello from All tests!'} task mergerports << {println 'Merging Test Reports'} Testsson Unistests.dupendson uie.myui.myui.mmustrungereportss.dentsson unistsson Mergereports次に、テストタスクを実行すると、テストレポートを取得できます。
paveldudka $ gradleテスト:ユニットテストからのUnithello:UIテストからのuihello:すべてのテストからのテストシェロ!:mergereportsmergingテストレポート