github 소스 읽고 정리

elastic/elasticsearch를 읽어보자 1: gradlew

Jonchann 2023. 8. 17. 18:46

서론: 간단히 배경

회사에서 2021년 하반기부터 검색 쪽을 맡아서 개발하고 있는데 정작 Elasticsearch(이하 ES)의 내부 구조를 전혀 모르니 읽어봐야겠다.
이 글을 적는 시점의 가장 최신 버전인 8.2가 대상이다.

본론

소스: https://github.com/elastic/elasticsearch

ES는 Gradle로 구축하게끔 되어있다.

$ ./gradlew localDistro

Gradle이란

GroovyKotlin DSL을 사용하는 빌드시스템이다. Kotlin DSL이 가독성이 더 좋고 컴파일 시간을 체크하거나 IDE와 호환이 되기 때문에 많이들 선호하는 편이다.
JVM에서 실행되기 때문에 Java, Kotlin, Scala 등 JVM 계열 언어로 만들어진 모듈을 빌드할 때 사용한다.
build.gradle파일이 있는 폴더를 하나의 프로젝트로 인식한다. build.gradle파일에는 플러그인 등 설정 내용 등을 기재한다. 그 외에는 빌드, 어플리케이션 실행, 테스트 실행, 디플로이 등 개발에 필요한 태스크를 실행하는 스크립트를 적는다.
Maven처럼 프로젝트에서 사용하는 라이브러리를 build.gradle파일에 정의해 종속된 라이브러리까지 함께 다운로드한다.

Gradle을 사용하기 위해서는 JDK 8이상을 사용해야한다. 아래 커맨드로 현재 사용중인 JDK 버전을 확인할 수 있다.

$ java -version

나는 맥북을 사용하니 Homebrew를 이용한다. 아래 커맨드로 Gradle을 설치할 수 있다.

$ brew install gradle
$ gradle -v

프로젝트 폴더 최상단에서 아래 커맨드를 실행해 Gradle을 사용할 준비를 한다.

$ gradle init

 


<Gradle Wrapper란>


Gradle을 적절한 버전으로 실행할 때 Gradle Wrapper(이하 래퍼)를 사용하면 좋다고 한다. 적절한 버전을 사용해주는 것 외에도 사전에 필요한 것들을 알아서 준비하기 때문에 편리하다.


 

위 커맨드로 아래 것들이 생성된다:

  • 래퍼를 사용하기 위한 파일
    • gradle/wrapper/ (래퍼 파일이 놓이는 폴더)
      • gradle-wrapper.jar
      • gradle-wrapper.properties
    • gradlew (래퍼를 사용하기 위한 스크립트)
    • gradlew.bat (래퍼를 사용하기 위한 스크립트)
  • 설정 파일
    • settings.gradle.kts (빌드와 서브프로젝트 이름을 정의하는 파일)
    • buildSrc/build.gradle.kts (의존관계를 정의하는 파일)
    • buildSrc/src/main/kotlin/ (Groovy나 Kotlin DSL로 구현한 컨벤션 플러그인을 보존하는 폴더)
  • 서브 프로젝트를 빌드하기 위한 스크립트
    • app/build.gradle.kts
    • list/build.gradle.kts
    • utilities/build.gradle.kts
  • 소스가 들어있는 폴더
    • app/src/main/
    • list/src/main/
    • utilities/src/main/
  • 테스트를 넣는 폴더
    • app/src/test
    • list/src/test

ES에서 사용하는 Gradle

Github에는 아래 파일이 있었다.

  • build.gradle
  • gradle.properties
  • gradlew (shell스크립트)
  • gradlew.bat (shell스크립트)
  • settings.gradle
  • gradle/
    • wrapper/
      • gradle-wrapper.jar (Gradle 디스트리뷰젼을 다운로드하기 위한 코드가 들은 래퍼 JAR파일)
      • gradle-wrapper.properties (Gradle버전 등 래퍼 실행을 위한 속성값 등을 적은 파일)
    • build.versions.toml
    • verification-metadata.xml

위에 적었다시피(ES의 readme에 나와있듯이) gradlew파일을 실행해서 빌드를 시작한다.

# 리마인드
$ ./gradlew localDistro

app_path./gradlew이다.

# Attempt to set APP_HOME

# Resolve links: $0 may be a link
app_path=$0

daisy-chained symlinks라는 것은 참조의 참조의 참조의... 참조하는 파일과 같이 연속적으로 연결(daisy chain)되어있는 심볼릭 링크(소프트 링크; 특정 파일이나 디렉토리를 지정하는 별도의 파일을 작성해 본체를 참조할 수 있도록 하는 것)를 말하는 것 같다. 만약 실행한 파일이 심볼릭 파일일 때 실제 스크립트 파일의 패스를 취득하고자 하는 것이 아래 코드의 의도인 것 같다.

# Resolve links: $0 may be a link
app_path=$0

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
    [ -h "$app_path" ]
do
    ls=$( ls -ld "$app_path" )
    link=${ls#*' -> '}
    case $link in             #(
      /*)   app_path=$link ;; #(
      *)    app_path=$APP_HOME$link ;;
    esac
done

이렇게 된 김에 shell스크립트 공부도 하고자 하나하나 뜯어보겠다.

quitta블로그를 보면 % %%는 패턴에 일치하는 부분을 가장 오른쪽에서부터 제거해 필요한 부분문자열만 얻는 방법이고 # ##는 패턴에 일치하는 부분을 가장 왼쪽에서부터 제거해 필요한 부분문자열만 얻는 방법이라고 한다.
따라서 아래 ${app_path##*/}는 파일이름만을 얻기 위한 처리이니 gradlew만이 남을것이다.
APP_HOMEgradlew부분을 제거한 디렉토리부분이 될테니 ./이다.

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path

[...]test커맨드의 단축형이고 -h옵션은 해당 패스가 존재하는 심볼릭 링크인지 확인하기 위한 것이다. 심볼릭 링크가 존재하면 True를 존재하지 않으면 False를 반환한다.

# Need this for daisy-chained symlinks.
while
    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
    [ -h "$app_path" ]

ls커맨드는 파일이나 디렉토리의 정보를 리스트로 돌려주는데 -ld옵션을 붙이면 해당 파일이 보존되어있는 디렉토리의 상세 정보를 출력한다. -l이 상세정보를 반환하기 위한 옵션이고 -d가 디렉토리의 정보를 받기 위한 옵션이다.

do
    ls=$( ls -ld "$app_path" )
    link=${ls#*' -> '}
    case $link in             #(
      /*)   app_path=$link ;; #(
      *)    app_path=$APP_HOME$link ;;
    esac
done

->부분을 확인하기 위해 심볼릭 링크를 만들어 실행해봤다. link/*패턴에 맞지 않아 아래 설정으로는 app_path././test가 된다. 아마 보통은 이렇지 않겠지.

$ vim ./test
$ ln -s ./test ./test.symbolic
$ ls -ld ./test.symbolic
lrwxr-xr-x 1 {user_dir} staff 12 Aug 17 18:17 ./test.symbolic -> ./test
# link => ./test

오늘은 여기까지..
Gradle과 shell구문 찾느라 분량에 비해 꽤나 시간을 썼다.