Gradle
글
-
Gradle 소스 파헤치기 1: AI와 함께 소스를 읽는 방법과 분석 지도
Gradle이 무엇이고 어떻게 동작하며 어디에 쓰는지를 그림과 3분 실습으로 먼저 보고 시작하는, Gradle 소스 읽기 연재의 첫 글입니다. 어떤 판(master, 9.10.0 개발판)을 읽는지, 같은 커밋을 받아 도커에서 Gradle을 Gradle로 빌드하는 법, 100만 줄짜리 소스를 나눈 플랫폼 지도, gradle 명령 한 번이 클라이언트와 데몬을 지나 태스크 액션에 닿는 큰 길, 그리고 AI가 쓴 노트를 소스로 다시 확인하며 읽는 방법을 정리합니다.
-
Gradle 소스 파헤치기 2: 전체 지도 — 클라이언트와 데몬, 서비스 스코프, 빌드의 세 단계
Gradle 소스를 처음 여는 중급 개발자를 위한 지도입니다. gradle 명령 한 번에 움직이는 프로세스 넷, 데몬 안의 상태 계층(프로세스·세션·빌드 트리·빌드·프로젝트)과 그것에 1:1로 맞물린 서비스 스코프, 초기화·설정·실행 세 단계가 어느 클래스의 어떤 상태인지, 모델(Gradle·Settings·Project·Task)과 실행(실행 계획·노드·실행 엔진)의 큰 그림을 실제 소스와 직접 돌린 예제로 확인하고, 3~12편이 이 지도의 어디를 읽는지 정리합니다.
-
Gradle 소스 파헤치기 3: gradle 명령에서 데몬까지 — 런처, 래퍼, 데몬 프로토콜
터미널에서 gradle을 치면 빌드는 그 프로세스가 아니라 뒤에 숨은 데몬 JVM이 합니다. 셸 스크립트가 띄우는 64MiB짜리 클라이언트, 데몬을 쓸지 정하는 세 갈래와 --no-daemon의 진짜 뜻, registry.bin으로 데몬을 찾고 호환성을 따지는 법, 데몬을 띄우고 연결하는 순서, 데몬 안의 명령 사슬 16개와 Idle·Busy 상태, 데몬이 스스로 죽는 조건, 그리고 gradlew와 IDE가 같은 데몬에 닿는 길까지 기준 커밋의 소스와 도커 예제로 확인합니다.
-
Gradle 소스 파헤치기 4: 서비스 레지스트리 — Gradle 안의 DI 컨테이너
태스크 생성자에 @Inject를 붙이면 FileSystemOperations가 들어오는 것은 누구 덕분일까요. 스코프마다 하나씩 만들어 부모를 따라 서비스를 찾는 DefaultServiceRegistry, @Provides 메서드로 서비스를 선언하는 규칙과 장식(decorator), 처음 찾을 때 만드는 게으른 싱글턴, 서비스가 제 스코프에 있는지 보는 ServiceScopeValidator, 모듈 57개가 스코프마다 서비스를 보태는 GradleModuleServices, 그리고 abstract 게터를 채운 _Decorated 클래스까지, 레지스트리를 빌드 밖에서 직접 써 보고 태스크 생성 스택을 찍어 소스와 함께 확인합니다.
-
Gradle 소스 파헤치기 5: 빌드 생명주기 — 초기화, 설정, 실행은 어디서 갈리나
데몬이 받은 빌드 요청이 실행기 사슬을 지나 빌드 생명주기에 닿는 길, 상태 전이를 지키는 StateTransitionController, init 스크립트와 settings를 읽는 초기화, 프로젝트를 설정하는 설정 단계, 설정 시간 장벽과 작업 그래프, 그리고 buildFinished와 Flow API로 끝나는 마무리까지 기준 커밋의 소스로 따라갑니다. 생명주기 훅을 모두 걸어 순서를 찍고, 빌드 오퍼레이션 나무와 세 단계에서 일부러 낸 실패로 확인합니다.
-
Gradle 소스 파헤치기 6: 프로젝트와 빌드 스크립트 — build.gradle.kts가 실행되기까지
build.gradle.kts 한 파일은 소스에서 무엇이 될까요. settings가 적어 둔 설명서로 DefaultProject를 만드는 곳, 프로젝트 잠금 안에서 도는 평가기 사슬과 beforeEvaluate·afterEvaluate 상태, 파일 이름 끝으로 Groovy와 Kotlin을 고르는 선택기, plugins {}를 먼저 떼어 두 번 컴파일하는 두 DSL, tasks.compileJava를 가능하게 하는 생성된 접근자, settings에서 하위 프로젝트로 이어지는 클래스로더 스코프까지, 같은 스크립트를 두 DSL로 돌리고 스택을 찍어 소스와 함께 확인합니다.
-
Gradle 소스 파헤치기 7: 플러그인 적용 — plugins {} 블록에서 apply()까지
plugins { java } 한 줄은 소스에서 무엇이 될까요. 요청을 모두 먼저 해석하고 나서 한꺼번에 적용하는 DefaultPluginRequestApplicator, 핵심 플러그인·buildSrc·포함된 빌드·플러그인 포털을 차례로 묻는 해석기 사슬과 마커 아티팩트, id를 클래스로 바꾸는 META-INF/gradle-plugins 설명 파일, 같은 플러그인을 두 번 걸어도 한 번만 도는 DefaultPluginManager, withPlugin이 불리는 정확한 시점, 확장이 붙는 자리, java → java-base → base 사슬, buildSrc와 precompiled script plugin까지 직접 만든 플러그인으로 확인합니다.
-
Gradle 소스 파헤치기 8: 태스크와 지연 설정 — DefaultTask, TaskContainer, Provider
tasks.register 한 줄은 태스크를 만들지 않습니다. 이름과 설정 블록만 맡겨 둔 TaskCreatingProvider가 언제 실제 태스크가 되는지, configureEach가 지금 있는 태스크와 나중에 올 태스크에 함께 걸리는 원리, withType { }처럼 모르는 새 모든 태스크를 만들어 버리는 API, TaskFactory와 DefaultTask가 액션 목록을 쥐는 법, 그리고 Provider가 값 대신 "값을 만드는 태스크"를 들고 다녀 dependsOn 없이 순서가 생기는 이유와 Property를 굳히는 세 가지 방법까지, 예제 세 개로 소스와 함께 확인합니다.
-
Gradle 소스 파헤치기 9: 태스크 그래프와 실행 계획 — 무엇을, 어떤 순서로, 몇 개씩
gradle pA 의 pA 가 packageApp 이 되고, dependsOn·mustRunAfter·shouldRunAfter·finalizedBy 가 실행 계획의 어떤 관계가 되어 순서를 정하는지, 실행 중에 다음 노드는 누가 어떻게 고르는지, --parallel 과 --max-workers 가 정확히 무엇을 제한하는지, Worker API 가 태스크 안의 일을 어떻게 나눠 돌리는지를 기준 커밋의 소스와 직접 돌린 예제(whenReady, --dry-run, 스레드 이름, jstack)로 확인합니다.
-
Gradle 소스 파헤치기 10: 실행 엔진과 증분 빌드 — UP-TO-DATE는 어떻게 정해지나
콘솔의 UP-TO-DATE 한 단어는 소스에서 어떻게 정해질까요. 태스크를 엔진에 넘기기 전에 거르는 DefaultTaskNodeExecutor, 태스크를 작업 단위로 감싸는 TaskExecution, 스물다섯 개 스텝이 겹겹이 감싼 실행 엔진, 지난 실행 기록과 지금 상태를 비교하는 ResolveChangesStep과 SkipUpToDateStep, @PathSensitive가 바꾸는 파일 지문, 빌드 사이에 파일 상태를 기억하는 VFS, 바뀐 파일만 넘겨주는 InputChanges까지, 직접 돌린 예제와 함께 소스로 확인합니다.
-
Gradle 소스 파헤치기 11: 빌드 캐시와 설정 캐시 — 결과를 다시 쓰는 두 가지 방법
콘솔의 FROM-CACHE와 Reusing configuration cache.는 소스의 어디서 나올까요. 실행 엔진의 스텝 사슬에 끼어 있는 ResolveMutableCachingStateStep과 BuildCacheStep, MD5로 만드는 캐시 키에 무엇이 들어가는지, @CacheableTask와 캐시하지 않는 이유 목록, 키가 파일 이름이 되는 로컬 디렉터리 캐시와 tar.gz 항목, 그리고 작업 그래프를 통째로 직렬화했다가 첫 빌드에서조차 다시 읽어 실행하는 설정 캐시, 환경 변수 읽기를 바이트코드에서 가로채는 핑거프린트와 문제 보고서까지 기준 커밋의 소스와 도커 예제로 확인합니다.
-
Gradle 소스 파헤치기 12: 의존성 해석 — implementation 한 줄이 jar 파일이 되기까지, 그리고 연재를 마치며
build.gradle.kts의 implementation 한 줄은 소스에서 무엇이 될까요. 생성된 접근자가 부르는 DependencyHandler.add(), 선언·해석·소비로 역할이 나뉜 구성(configuration), 파일 목록을 달라고 할 때까지 미뤄지는 해석, DependencyGraphBuilder가 그래프를 넓혀 가는 반복문, 같은 라이브러리의 두 버전이 부딪칠 때 높은 쪽을 고르는 LatestModuleConflictResolver와 strictly·force가 그 결과를 바꾸는 자리, 캐시를 먼저 보고 저장소로 가는 두 번의 탐색과 --offline·읽기 전용 캐시까지 기준 커밋의 소스와 도커 예제로 확인하고, 12편 연재를 정리합니다.