작업
다음 코드는 세 개의 Gradle 작업을 보여 주며 나중에이 세 가지의 차이점을 설명합니다.
작업 mytask {println "안녕하세요, 세계!" } task mytask {dolast {println "hello, world!" }} task mytask << {println "안녕하세요, 세계!" }저의 목표는 "안녕하세요, 세계!"을 인쇄하는 작업을 만드는 것입니다. 실행할 때. 처음으로 작업을 만들었을 때 다음과 같이 작성해야한다고 추측합니다.
작업 mytask {println "안녕하세요, 세계!" }이제이 mytask를 실행하고 명령 줄에 Gradle mytask를 입력하고 다음과 같이 인쇄하십시오.
사용자 $ gradle Mytask Hello, World! : mytask
이 작업은 작동하는 것처럼 보입니다. "안녕하세요, 세상!"인쇄.
그러나 실제로 우리가 기대 한 것은 아닙니다. 이유를 살펴 보겠습니다. 사용 가능한 모든 작업을 보려면 명령 줄에 Gradle 작업을 입력하십시오.
사용자 $ gradle 작업 안녕하세요, 세계! :tasks ------------------------------------------------------------ All tasks runnable from root project ------------------------------------------------------------ Build Setup tasks ----------------- init - Initializes a new Gradle build. [인큐베이션] ..........
잠깐, 왜 "안녕하세요, 세상!" 인쇄 되었습니까? 어떤 작업을 사용할 수 있는지보고 싶었고 사용자 정의 작업을 실행하지 않았습니다!
그 이유는 실제로 매우 간단합니다. Gradle 작업에는 수명주기에 구성 단계 및 실행 단계의 두 가지 주요 단계가 있습니다.
어쩌면 내 말이 정확하지는 않지만 이것은 실제로 작업을 이해하는 데 도움이 될 수 있습니다.
Gradle은 작업을 실행하기 전에 작업을 구성해야합니다. 문제는 구성 프로세스 중에 작업에서 어떤 코드가 실행되고 작업이 실행될 때 어떤 코드가 실행되는지 어떻게 알 수 있습니까? 대답은 작업의 최상위 레벨의 코드가 다음과 같은 구성 코드라는 것입니다.
task mytask {def name = "pavel"// <-이 코드 줄은 println "hello, world!"//// <-이 코드 줄에서도 구성 단계에서 실행됩니다}}이것이 Gradle 작업을 실행할 때 "안녕하세요, 세계!"을 인쇄하는 이유입니다. - 구성 코드가 실행되기 때문에. 그러나 이것은 내가 원하는 효과가 아닙니다. "안녕하세요, 세상!" MyTask에 명시 적으로 전화 할 때만 인쇄하려면 인쇄합니다. 이 효과를 달성하기 위해 가장 쉬운 방법은 작업#dolast () 메소드를 사용하는 것입니다.
작업 mytask {def text = 'hello, world!' // 내 작업 구성 DOLAST {println text // 이것은 내 작업이}}이라고 할 때 실행됩니다.}자, "안녕하세요, 세상!" Gradle mytask를 실행할 때만 인쇄됩니다. 쿨, 이제 작업을 구성하고 작업을 올바르게 수행하는 방법을 알고 있습니다. 또 다른 질문이 있습니다. 초기 예에서 세 번째 작업의 << 기호는 무엇을 의미합니까?
작업 mytask2 << {println "안녕하세요, 세계!" }이것은 실제로 Dolast의 구문 설탕 버전 일뿐입니다. 다음 글쓰기 방법과 동일한 효과가 있습니다.
작업 mytask {dolast {println 'hello, world!' // 이것은 내 작업이}}이라고 불리는 경우 실행됩니다.그러나 이러한 방식으로 모든 코드가 실행되며 코드의 구성 부분이 없으므로 구성이 필요하지 않은 간단한 작업에 더 적합합니다. 작업을 구성해야 할 경우 여전히 Dolast 버전을 사용해야합니다.
문법
Gradle 스크립트는 그루비 언어로 작성됩니다. Groovy의 구문은 Java와 약간 비슷합니다. 수락 할 수 있기를 바랍니다.
이미 그루비에 익숙하다면이 부분을 건너 뛸 수 있습니다.
그루비에는 폐쇄를 이해해야한다는 매우 중요한 개념이 있습니다 (폐쇄)
폐쇄
폐쇄는 Gradle에 대한 우리의 이해의 열쇠입니다. 클로저는 매개 변수, 반환 값을 받거나 변수에 할당 될 수있는 별도의 코드 블록입니다. Java 및 Future의 Callable 인터페이스와 유사하며 이해하기 쉬운 기능 포인터와도 유사합니다. . .
핵심은이 코드가 만들 때가 아니라 호출 할 때 실행된다는 것입니다. 폐쇄의 예를 참조하십시오.
def myclosure = {println 'Hello World!' } // ClosingClosure ()#출력 실행 : Hello World!다음은 매개 변수를 수신하는 클로저입니다.
def myclosure = {String str-> println str} // 폐쇄 클로저 ( 'Hello World!')#출력 : Hello World!Closure가 하나의 매개 변수 만 수신되면이 매개 변수를 참조하여 다음을 참조 할 수 있습니다.
def myclosure = {println it} // 폐쇄 클로저 ( 'Hello World!')#출력 : Hello World!여러 매개 변수를 수신하는 폐쇄 :
def myClosure = {String str, int num-> println "$ str : $ num"} // 리노베이션 클로저를 실행합니다 ( 'my string', 21) #output : my string : 21또한, 매개 변수의 유형은 선택 사항이며, 위의 예는 다음과 같이 축약 될 수 있습니다.
def myClosure = {str, num-> println "$ str : $ num"} // ClosingClosure ( 'my String', 21) #output : 21멋진 것은 현재 컨텍스트의 변수를 폐쇄에 사용할 수 있다는 것입니다. 기본적으로 현재 컨텍스트는 폐쇄가 생성 된 클래스입니다.
def myvar = 'Hello World!'def myClosure = {println myvar} myClosure ()#출력 : Hello World!또 다른 쿨 포인트는 클로저#setDelegate ()를 통해 폐쇄의 컨텍스트를 변경할 수 있다는 것입니다. 이 기능은 매우 유용합니다.
def myclosure = {println myvar} // myclass classmyclass m = new myclass () myclass.setdelegate (m) myclosure () class myclass {hello from myclass! '}#output : hello myclass에서 myvar를 참조하고 있습니다.보시다시피, 클로저를 만들 때 Myvar는 존재하지 않습니다. 우리가 폐쇄를 실행할 때, Myvar가 폐쇄의 맥락에 존재하기 때문에 이것에 문제가 없습니다. 이 예에서. 폐쇄를 실행하기 전에 맥락을 M으로 변경했기 때문에 Myvar가 존재합니다.
매개 변수로 폐쇄를 통과합니다
폐쇄의 장점은 다른 방법으로 전달 될 수 있다는 것입니다. 이는 실행 로직을 분리하는 데 도움이 될 수 있습니다. 이전 예에서는 클래스 인스턴스로 폐쇄를 통과하는 방법을 보여주었습니다. 아래에서는 매개 변수로 폐쇄를받는 다양한 방법을 살펴 봅니다.
1. 하나의 매개 변수 만 받고 매개 변수는 Closure 메소드입니다 : myMethod (myClosure).
2. 메소드가 하나의 매개 변수 만 수신하는 경우 괄호 안에서 생략 할 수 있습니다. MyMethod myClosure
3. 인라인 클로저를 사용할 수 있습니다 : MyMethod {println 'Hello World'}}
4. 두 가지 매개 변수를 수신하는 방법 : myMethod (arg1, myClosure)
5. 4와 마찬가지로, 단일 폐쇄는 인라인입니다 : myMethod (arg1, {println 'Hello World'})
6. 마지막 매개 변수가 폐쇄되면 괄호에서 꺼낼 수 있습니다 : MyMethod (arg1) {println 'Hello World'}
여기서 나는 단지 3과 6의 글이 친숙한지 여부를 상기시키고 싶습니다.
Gradle 예제
이제 기본 구문을 이해 했으므로 Gradle 스크립트에서 어떻게 사용합니까? 다음 예를 살펴 보겠습니다.
빌드 스크립트 {repositories {jcenter ()} 종속성 {classpath 'com.android.tools.build: gradle:1.2.3'}} allProjects {repositories {jcenter ()}} 그루비의 구문을 알면 위의 예를 쉽게 이해하기 쉽습니까?
첫 번째는 빌드 스크립트 방법으로 폐쇄를받습니다.
DEF 빌드 스크립트 (폐쇄 폐쇄)
다음은 allprojects 메소드이며, 또한 클로저 매개 변수를 수신합니다.
DEF AllProjects (폐쇄 폐쇄)
다른 것들은 비슷합니다. . .
지금은 훨씬 쉬워 보이지만 한 가지를 이해하지 못합니다. 즉, 이러한 방법은 어디에 정의되어 있습니까? 대답은 프로젝트입니다
프로젝트
이것은 Gradle 스크립트를 이해하는 열쇠입니다.
빌드 스크립트의 최상위 레벨의 명령문 블록은 프로젝트 인스턴스로 위임되며 프로젝트가 내가 찾고있는 곳임을 보여줍니다.
프로젝트의 문서 페이지에서 빌드 스크립트 메소드를 검색하면 BuildScript {} 스크립트 블록 (스크립트 블록)이 있습니다. 등. 도대체 스크립트 블록은 무엇입니까? 문서에 따르면 :
스크립트 블록은 매개 변수로만 클로저를 수신하는 메소드입니다. 빌드 스크립트 문서를 계속 읽으십시오. 이 문서는 대의원이 다음과 같이 말합니다. 즉, 폐쇄를 빌드 스크립트 방법으로 전달하고 최종 실행 컨텍스트는 Scripthandler입니다. 위의 예에서, 우리의 폐쇄는 빌드 스크립트로 전달되어 리포지토리 (폐쇄) 및 종속성 (폐쇄) 메소드를 호출합니다. 클로저가 Scripthandler에 위임되었으므로 Scripthandler의 종속성 방법을 찾을 것입니다.
void 종속성 (폐쇄 구성)이 발견되었습니다. 문서에 따르면 종속성은 스크립트 종속성을 구성하는 데 사용됩니다. 종속성은 결국 종속성 핸들러에 위임되었습니다.
나는 gradles가 얼마나 널리 사용되는지 보았다. 위탁을 이해하는 것은 매우 중요합니다.
스크립트 블록
기본적으로 많은 스크립트 블록이 프로젝트에 사전 정의되어 있지만 Gradle 플러그인을 사용하면 새 스크립트 블록을 스스로 정의 할 수 있습니다!
즉, 빌드 스크립트의 최상위 레벨에 {…}을 게시하면 Gradle의 문서 에서이 스크립트 블록이나 메소드를 찾을 수 없다면 대부분의 경우 플러그인에 정의 된 스크립트 블록입니다.
안드로이드 스크립트 블록
기본 Android 앱/build.gradle 파일을 살펴 보겠습니다.
플러그인을 적용 : 'com.android.application'android {compilesdkversion 22 buildToolSversion "22.0.1"defaultConfig {ApplicationId "com.trickyandroid.testapp"minsdKversion 16 TargetSdKversion 22 버전 코드 1 버전 이름 "1.0"} release {릴리스 {minification false proguardfiles getDefaultProguardFile ( 'proguard-1-droid.txt'), 'proguard-rules.pro'}}}작업 순서
Gradle을 사용할 때 발생하는 대부분의 문제는 작업의 실행 순서와 관련이 있음을 알았습니다. 내 작업이 적시에 실행되면 내 빌드가 더 잘 작동하는 경우 분명히. 작업의 실행 순서를 변경하는 방법을 자세히 살펴 보겠습니다.
부양 가족
작업을 실행할 때 다른 작업에 의존하는 방식을 설명하는 가장 직접적인 방법은 Dependson 메소드를 사용하는 것입니다.
예를 들어, 다음 시나리오에서는 작업 A가 이미 존재합니다. 작업 B를 추가하려고하고 A가 실행 된 후에 실행해야합니다.
이것은 A와 B의 정의가 다음과 같습니다.
작업 a << {println 'hello from a'} 작업 b << {println 'hello from b'}} 단순히 B.dependentson에 전화하십시오. 괜찮습니다.
이는 작업 B를 실행하는 한 작업 A가 먼저 실행된다는 것을 의미합니다.
Paveldudka $ Gradle B : A : Bhello의 Ahello B.
또한 작업 구성 영역에서 종속성을 선언 할 수 있습니다.
작업 a << {println 'hello from a'} task b {dependson a dolast {println 'hello from b'}}} 작업을 기존 작업 종속성에 삽입하려면 어떻게해야합니까?
프로세스는 지금과 비슷합니다. 다음 작업 종속성이 이미 존재한다고 가정합니다.
작업 a << {println 'hello from a'} 작업 b << {println 'hello from b'} task c << {println 'hello from c'} b.dependentson ac.dependentson b새로운 작업에 참여하십시오
작업 b1 << {println 'hello from b1'} b1.dependson bc.dependson b1산출:
Paveldudka $ Gradle C : AHello의 A : BHELLO FROM B : B1HELLO B1 : CHELLO FROM COM
Dependson은 종속 컬렉션에 작업을 추가하므로 여러 작업에 의존하는 것은 문제가되지 않습니다.
작업 b1 << {println 'hello from b1'} b1.dependson bb1.dependson q산출:
Paveldudka $ Gradle B1 : A : A : Bhello의 Bhello : Qhello Q : B1Hello B1에서 B1Hello
MUSTRUNAFTER
이제 다른 두 가지 작업에 따라 다른 작업이 있다고 가정합니다. 여기에는 두 가지 작업, 하나의 단위 테스트 작업 및 하나의 UI 테스트 작업이있는 실제 시나리오를 사용합니다. 이전 두 작업에 따라 모든 테스트를 실행하는 작업도 있습니다.
작업 단위 << {println 'hello from Unit Tests'} task ui << {println 'hello from ui tests'} task tests << {println 'hello hello hello!'} tests.dependson unittests.dependson ui산출:
Paveldudka $ Gradle 테스트 : UI 테스트의 Uihello : 단위 테스트에서 Unithello : 모든 테스트에서 TestShello!
테스트 작업 전에 Unitest 및 UI 테스트가 실행되지만 장치 및 UI의 실행 순서는 보장 할 수 없습니다. 지금은 알파벳 순서대로 실행되지만 이는 Gradle의 구현에 따라 다르며 코드 에서이 주문에 의존해서는 안됩니다.
UI 테스트 시간은 단위 테스트 시간보다 훨씬 길기 때문에 장치 테스트를 먼저 실행하기를 원합니다. 한 가지 해결책은 UI 작업이 장치 작업에 의존하는 것입니다.
작업 단위 << {println 'hello from Unit Tests'} task ui << {println 'hello from ui tests'} task tests << {println 'hello hello!'} tests.dependson unittests.dependson uiui.dependson init // <- 나는이 의존성을 추가했습니다.산출:
Paveldudka $ Gradle 테스트 : 단위 테스트의 Unithello : UI 테스트의 Uihello : 모든 테스트에서 TestShello!
이제 UI 테스트 전에 단위 테스트가 실행됩니다.
그러나 여기에는 매우 역겨운 문제가 있습니다. 내 UI 테스트는 실제로 단위 테스트에 의존하지 않습니다. UI 테스트를 별도로 실행할 수 있기를 희망하지만 UI 테스트를 실행할 때마다 단위 테스트를 먼저 실행합니다.
여기서는 MUTTRUNAFTER가 필요합니다. MUSTRUNAFTER는 종속성을 추가하지 않으며 두 작업이 동시에 존재하면 Gradle에 실행 우선 순위를 알려줍니다. 예를 들어, 여기에서 UI.MustrUnafter 단위를 지정할 수 있습니다. 이러한 방식으로 UI 작업 및 단위 작업이 동시에 존재하면 Gradle은 먼저 장치 테스트를 실행하고 Gradle UI 만 실행되면 장치 작업이 실행되지 않습니다.
작업 단위 << {println 'hello from Unit Tests'} task ui << {println 'hello from ui tests'} 작업 테스트 << {println 'hello hello!'} tests.dependson unittests.dependson uiui.mustrunafter init산출:
Paveldudka $ Gradle 테스트 : 단위 테스트의 Unithello : UI 테스트의 Uihello : 모든 테스트에서 TestShello!
의존성 관계는 다음과 같습니다.
MUSTRUNAFTER는 현재 Gradle 2.4에서 실험적인 기능입니다.
마무리
이제 두 작업이 두 가지 작업이 테스트 보고서를 출력한다고 가정 할 때 두 가지 작업의 두 가지 작업이 있습니다. 이제이 두 테스트 보고서를 하나로 병합하고 싶습니다.
작업 단위 << {println 'hello from Unit Tests'} task ui << {println 'hello from ui tests'} 작업 테스트 << {println 'hello hello!'} task mergereports << {println '} tests.dependson unittests.dependson trests.이제 UI 및 Unit에 대한 테스트 보고서를 받으려면 Task Mergereports를 실행하십시오.
Paveldudka $ Gradle Mergereports : Unithello의 Unithello : UI 테스트의 Uihello : 모든 테스트에서 TestShello! : MergereportSmerging Test Reports
이 작업은 작동하지만 너무 바보처럼 보입니다. Mergereports는 사용자의 관점에서 특히 기분이 좋지 않습니다. Mergereports의 존재를 알지 못하고 테스트 보고서를 실행하고 싶습니다. 물론 합병 로직을 테스트 작업으로 옮길 수는 있지만 테스트 작업을 너무 부풀게 만들고 싶지 않으므로 MergeReports 작업에 Merge Logic을 계속 배치합니다.
Finalizeby는 장면을 저장하기 위해 여기에 있습니다. 이름에서 알 수 있듯이 Finalizeby는 작업이 실행 된 후 실행해야 할 작업입니다. 다음과 같이 스크립트를 수정하십시오.
작업 단위 << {println 'hello from Unit Tests'} task ui << {println 'hello from ui tests'} task tests << {println 'hello hello!'} task mergereports << {println '} tests.dependson tests.faterports.fernergports.fernepport.fernepports.fintergeports. Mergereports이제 테스트 작업을 실행하면 테스트 보고서를 얻을 수 있습니다.
Paveldudka $ gradle 테스트 : 단위 테스트의 Unithello : UI 테스트의 Uihello : 모든 테스트의 TestShello! : MergereportSmerging Test Reports