코틀린에서 Annotation Processor를 디버깅 하는 방법

https://medium.com/@cafonsomota/debug-annotation-processor-in-kotlin-6eb462e965f8

아래에 나올 내용은 위 링크의 내용을 번역하고, 추가적인 내용을 덧붙인 글입니다.


코틀린 언어를 사용하면서 애노테이션 프로세서 다루는 경우 어떻게 디버깅을 할 수 있는지 알아봅시다.

로그 출력하기

어노테이션 프로세서가 컴파일 타임에 작업을 수행하는 동안 로그를 출력하기기 위해서는 ProcessingEnvironment.getMessager().printMessage(…)를 호출 해야 한다. ProcessingEnvironment 객체는 AbstractProcessor 클래스에 정의되어 있으므로 AbstractProcessor 클래스를 확장하는 클래스를 만드는 경우 이 객체에 쉽게 접근 할 수 있다.

class GenerateProcessor : AbstractProcessor() {

  override fun process(type: MutableSet<out TypeElement>?, 
                       roundEnv: RoundEnvironment?): Boolean {
    processingEnv.messager.printMessage(WARNING, "Processing")
    ...

이 메소드는 몇가지 인자를 취하지만, 일반적으로 두가지만 사용한다.

void printMessage(Diagnostic.Kind kind, CharSequence charSequence);

Kind는 로그 메시지 타입으로, 다음과 같다. (나열된 순서가 위험도 높은순)

  • ERROR
    뭔가 누락되었거나 유효하지 않을 때 사용한다. 만약 이 타입이 정의 되어있다면 컴파일 과정은 취소되고, 콘솔 로그에서 에러를 보게 된다.

  • WARNING
    뭔가 완전히 잘못되지는 않았으나, 컴파일을 중단하기에 충분한 상활 일 때 사용한다.

  • MANDATORY_WARNING
    warning과 비슷하지만 도구 사양에 따라 다르다. 예를 들면 자바 언어에서 특정 unchecked 동작이나 deprecated 된 메소드 사양에 대하여 경고를 띄울 수 있다.

  • NOTE
    유용한 메시지를 출력한다. 예를들면 작업의 시작 또는 끝을 알릴 때 사용할 수 있다.

  • OTHER
    위에 나열된 어떤 카테고리도 사용할 수 없을 때 사용할 수 있다.

디버깅 하기

스마트폰 또는 에뮬레이터에서 실행되는 앱에 디버거를 연결하는 것과 비슷한 방법으로 컴파일 프로세스에도 동일한 작업을 수행할 수 있다. 

 1.리모트(Remote) 디버거를 먼저 하나 만들어야 한다. 그리고 이를 이용해서 나중에 Annotation Processor 코드를 디버깅할 수 있다.

2. 리모트 디버거를 만들기 위해 Edit Configuration을 클릭하여 Run/Debug Configuration 화면으로 진입하자

3. (“+”) 버튼을 눌러서 Remote를 선택한다.

4. Remote 디버거의 이름을 Debugger 또는 원하는 이름으로 짓자. 모든 작업이 끝나면 다음과 같은 화면을 볼 수 있다.

5. gradle.properties 파일에 다음과 같은 내용을 추가하자

kapt.use.worker.api=true

이것을 추가하지 않으면 브레이크포인트(Breakpoint)에서 멈추질 않는다.

6. 이제 모든것이 준비되었으니 Terminal 에서 컴파일을 시작하자. 

./gradlew --no-daemon -Dorg.gradle.debug=true -Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n" :clean assemble

7. 커맨드를 입력하고 입력하면 debugger가 붙을 때까지 프로세스가 기다린다.

8. 아까 만들어 두었던 Remote “Debugger“를 선택하고 debug 버튼을 클릭하자.

9. Daemon이 디버거(Debugger)가 붙은것을 감지하고 나면, 컴파일 작업이 재개되고 지정된 브레이크포인트에서 멈추게 될 것이다.

gardle 명령어 하나씩 살펴보기

Gradle Daemon

-Dorg.gradle.debug=true

그레이들 프로세스가 시작되면 리모트 디버깅이 활성화됨과 동시에 빌드를 수행하게 된다. 5005번 포트를 기본값으로 사용하여 수신 대기하며 이는 다음의 내용을 호출하는것과 동등하다.

-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005

만약 suspend 속성이 true인것을 확인할 수 있는데 이는 디버거가 붙을 때까지 가상머신을 유예시킨다는 의미다.

Kotlin Demon

-Dkotlin.daemon.jvm.options="-Xdebug,-Xrunjdwp:transport=dt_socket\,address=5005\,server=y\,suspend=n"

코틀린은 5005포트에서 디버거를 수신 대기하는 데몬 프로세스를 시작한다.

  • transport
    디버거를 앱에 연결하는데 사용되는 transport의 이름을 설정한다. 필수값이며 기본값은 none이다.

  • address
    연결을 위한 transport 주소다. 만약 디버거의 server 옵션이 n으로 설정되어 있다면 디버거는 해당 address로 연결을 시도하고, y로 설정되었다면 해당 포트에서 연결을 수신대기한다.

  • server
    y면 디버거가 연결될 수 있도록 수신 대기한다. 그렇지 않으면, 지정된 address로 디버거가 연결을 시도한다. 설정하지 않은 경우 기본값은 n이다.

  • suspend
    SUSPEND_ALL 인 경우 VMStartEvent에 사용되는 정책을 정의한다. 그렇지 않으면 SUSPEND_NONE 이다. 설정하지 않은 경우 기본값은 y다.

마지막에 적힌 명령어는 현재 프로젝트를 클린 한 뒤 컴파일 할 수 있도록 한다.

:clean assemble

만약에 특정 모듈만 클린 후 컴파일 하고  싶다면 다음과 같이 바꾸어 사용할 수 있다.

:모듈명:clean :모듈명:assemble

Troubleshooting

jps

jps(Java Virtual Machine Process Status Tool) 라는 명령어 가 있다. 

$ jps

현재 JVM에서 실행되고 있는 모든 프로세스 목록을 나타낸다. 디버거를 붙일 때 Connection Refused( 연결 거부 ) 에러가 자꾸 발생한다면 jps명령어를 통해 5005번 포트를 사용하는 활성화 된 데몬이 남아 있는지 확인할 수 있다.

$ jps
5541 GradleDaemon
294 
5671 Jps

만약 남아있다면 ./gradlew –stop 또는 kill(프로세스 아이디) 를 호출하여 강제로 종료 시킬 수 있다.
위의 경우 프로세스 아이디(pid)가 5541이므로 kill(5541)을 호출하면 종료된다.

com.sun.tools.javac.code.* 클래스가 존재 하지 않을 때

com.sun.tools.javac.code 패키지에 있는 클래스를 사용하는 경우 컴파일 타임에 해당 클래스가 존재하지 않는다는 에러가 발생할 수 있다. 어노테이션 프로세서 모듈에 다음과 같은 내용을 추가하자

dependencies {
    implementation(files("${System.getProperty("java.home")}/../lib/tools.jar"))
}

tools.jar가 없다고 나올 때

tools.jar가 없다고 컴파일 에러가 발생하는 경우가 있다. 여러 버전의 JDK를 사용하는 경우 발생 할 수 있다. 왜냐하면 tools.jar는 Java9에서 삭제된 라이브러리인데 Android Studio는 Java8을 사용하며, 터미널 환경변수는 JDK9 이상을 사용하는 경우에 해당 에러를 볼 수 있다. 이 경우 JDK경로를 JDK8로 설정하도록 하자.

# OSX .bash_profile
export JAVA_HOME=$(/usr/libexec/java_home -v 1.8)

 

 

카테고리: Kotlin

2개의 댓글

개초 · 2020년 12월 12일 5:35 오후

Terminal에서 컴파일시 왜 안되나 했더니 윈도우는 .\gradlew로 해야되네요.. 다들 참고하세요~
찰스님 항상 감사히 잘 보고 있습니다.

    Charlezz · 2020년 12월 13일 8:22 오후

    감사합니다 !

답글 남기기

Avatar placeholder

이메일은 공개되지 않습니다. 필수 입력창은 * 로 표시되어 있습니다.