OCaml을 위한 더 나은 빌드 시스템, Dune이 되기까지
(blog.janestreet.com)- Jane Street는 OMake에 대한 불만에서 Jenga를 만들었지만 외부 채택이 약했고, 호환용으로 만든 Jbuilder가 예상 밖으로 OCaml 커뮤니티의 기반 도구로 커짐
- Jbuilder의 초기 매력은 기능보다 속도와 이식성이었고, Jane Street 프로젝트에서는 OCamlbuild보다 약 5배 빠른 컴파일이 채택을 이끎
- Jbuilder는 OCaml Labs와 협력하며 완전한 빌드 시스템으로 발전했고, 기존 Borland Java IDE와의 이름 충돌 때문에 커뮤니티 논의를 거쳐 Dune이 됨
- Jane Street는 한동안 Jenga와 Dune을 함께 유지했지만, 코드베이스가 65M 줄 OCaml과 5M 줄 Python까지 커지며 내부 이전 난도가 크게 올라감
- 1년 넘는 집중 작업 끝에 Jane Street의 코드베이스는 Dune으로 빌드되며, 캐시가 대부분 채워진 빌드는 2~3배 빨라짐
OMake 불만에서 시작된 Jenga
- 빌드 시스템은 여러 소스 파일에서 실행 가능한 프로그램을 만들고, 컴파일러 호출과 테스트 실행까지 맡는 개발자의 일상 도구임
- Jane Street는 2012년경 OCaml의 표준 빌드 시스템 중 하나였던 OMake에 불만을 느껴 자체 빌드 시스템 Jenga를 만듦
- 내부에서는 Jenga가 잘 동작했기 때문에, Jane Street는 커뮤니티도 유용하게 쓸 수 있을 것으로 보고 공개를 결정함
- 외부 사용자가 Jenga를 써보고 기여하기를 기대함
- Jane Street 코드의 오픈소스 공개도 쉬워질 것으로 봄
- 실제 외부 채택은 기대에 미치지 못함
- Windows에서 동작하지 않았음
- Jenga를 채택하려면 사실상 OCaml을 빌드하는 Jane Street 방식 전체를 받아들여야 했음
- 외부 사용이 늘지 않자 Jane Street는 Jenga를 다시 비공개로 돌렸고, 오픈소스 코드 빌드 문제는 남아 있었음
호환 계층으로 만든 Jbuilder
- 2016년 Jane Street는 외부 사용자가 Jenga 전체를 받아들이지 않고도 자사 코드를 빌드할 수 있는 단순한 크로스플랫폼 도구 Jbuilder를 만들기로 함
- 이 도구는 당시 OCaml 프로젝트 빌드의 새 표준으로 떠오르던 OCamlbuild에 맞춰 빌드를 다시 작성해야 하는 부담도 줄이려는 목적이 있었음
- Jbuilder는 Jenga가 빌드 설정에 쓰던
jbuild파일을 이해하고, 필요한 컴파일 명령을 위상 정렬 순서로 실행함 - 초기 Jbuilder는 일반적인 의미의 빌드 시스템과 거리가 있었음
- 입력이 바뀐 명령만 골라 다시 실행하지 않았음
- 매번 모든 명령을 다시 실행하는 방식이었음
Jbuilder가 Dune으로 바뀐 과정
- Jbuilder는 원래 작은 호환 계층으로 의도됐지만, 사용자들은 Jane Street 패키지를 넘어 자기 프로젝트 빌드에도 쓰기 시작함
- 채택을 이끈 가장 큰 요인은 속도였음
- Jane Street 프로젝트 컴파일에서 OCamlbuild보다 약 5배 빠름
- 이식성이 좋았음
- 구조가 단순해 수정하기 쉬웠음
- Jane Street는 OCaml Labs, 현재의 Tarides와 협력해 Jbuilder를 더 완전한 빌드 시스템으로 발전시키기 시작함
- 이름은 별도 문제가 됨
- 이미 Borland의 Java IDE인 JBuilder가 존재했음
- 해당 시스템은 오래전에 사실상 사라졌지만, 저작권 소유자에게 이름 사용 의사를 문의해도 허락을 받지 못함
- 커뮤니티 논의 끝에 새 이름으로 Dune이 선택됨
- 관련 논의는 새 Jbuilder 이름 제안 스레드에서 진행됨
두 빌드 시스템을 함께 유지한 비용
- Dune의 인기가 커지면서 Jane Street는 자체적으로 만든 두 개의 완전한 빌드 시스템, Jenga와 Dune을 동시에 유지·지원하게 됨
- Dune은 다시 설계된 구조, 대부분 사용자 빌드에서의 빠른 성능, 더 넓은 채택, 더 나은 API와 사용자 경험을 갖춘 시스템으로 자리 잡음
- 내부를 언제 Dune으로 옮길지가 자연스럽게 과제가 됐지만, 빌드 시스템 팀은 커지는 코드베이스를 따라가는 일만으로도 바빴음
- 2016년 Dune이 시작될 당시 OCaml 코드는 4M 줄이었음
- 현재는 65M 줄의 OCaml과 5M 줄의 Python이 있음
- Dune 이전은 부담이 커서 본격적으로 착수하지 못했고, “앞으로 6~12개월 안에 가능할 수 있다”는 희망적 추정만 반복됨
- 지난해 Jane Street는 빌드 시스템 팀을 풀타임 엔지니어 5명으로 키운 뒤, 장기간 미뤄온 이전 작업을 시작함
Jane Street 내부에서 Jenga를 대체한 Dune
- 가장 큰 작업 중 하나는 Dune이 Jane Street의 거대한 코드베이스에 맞게 확장되도록 만드는 일이었음
- 외부에서 Dune이 빨랐던 이유 중 하나는 대부분의 사용자가 Jane Street의 70M 줄 저장소보다 훨씬 작은 프로젝트를 빌드했기 때문임
- Jenga도 10년 동안 그대로 멈춰 있지는 않았음
- Jane Street는 코드베이스 성장에 맞춰 Jenga 구현을 계속 개선해왔음
- Jenga는 대규모 모노레포 요구사항에 맞게 상당히 최적화된 상태였음
- 이 최적화 작업의 상당 부분을 Dune으로 옮겨야 했음
- 일상적인 통합 작업도 만만치 않았음
- 빌드 시스템은 여러 워크플로에서 호출됨
- Vim, Emacs, VSCode 세 에디터가 각각 Jenga용 커스텀 통합을 갖고 있었음
- 각 통합을 Dune으로 하나씩 이전해야 했음
- 1년 넘는 집중 작업 끝에 Jane Street의 코드베이스는 이제 Dune으로 빌드됨
- 전환 시점의 Dune 성능은 전반적으로 Jenga와 같거나 더 좋았음
- 일부 경우에는 훨씬 더 나은 성능을 보임
- 대부분의 빌드 작업이 이미 캐시에 있는 경우, 빌드가 2~3배 빨라짐
단일 시스템 위에서 가능한 다음 개선
- Jane Street는 Dune 성능 개선 작업의 상당 부분을 오픈소스로 공개할 계획이며, 일부는 이미 공개됨
- Jane Street용 Dune 포크와 외부 Dune이라는 두 시스템이 다시 생기지 않도록, 가능한 변경 사항을 업스트림에 반영하는 데 신경 쓰고 있음
- Dune은 새 기능을 만들기 위한 기반으로 더 적합해짐
- 코드베이스가 더 단순하고 작업하기 쉬움
- 하나의 시스템에 집중할 수 있음
- 다음 기능들도 더 가까워짐
-
분산 빌드
- 얕은 빌드, 즉 “builds without the bytes”
- 빌드 그래프 자체의 캐시 로딩
- 빌드 시스템 팀은 뉴욕, 런던, 싱가포르의 풀타임 엔지니어 12명으로 커졌고, Dune 개선 속도도 올라감
-
댓글과 토론
Hacker News 의견들
-
Dune의 크래시 메시지:
세그폴트를 내지 않으리라. 불확실성은 정신을 죽이는 자다. 예외는 완전한 소멸을 부르는 작은 죽음이다. 나는 모든 경우를 완전히 표현하리라. 실행은 나를 지나고 나를 통과하리라. 그것이 지나간 뒤에는 그 경로를 따라 스택을 되감으리라. 경우들이 처리된 곳에는 아무것도 없을 것이다. 오직 나만이 남으리라
- 와, 진짜였네
https://github.com/ocaml/dune/issues/2767 - 뭔가에 입문당한 느낌임
- 와, 진짜였네
-
Jane Street의 기술 팟캐스트 Signals and Threads에서 2020년에 빌드 시스템을 다룬 에피소드가 있었고 강력 추천함
다른 에피소드들도 좋고, 특히 Richard Eisenberg가 나온 The Future of Programming, Anil Madhavapeddy가 나온 What is an Operating System?도 추천함
¥https://signalsandthreads.com/build-systems/
°https://signalsandthreads.com/future-of-programming/
§https://signalsandthreads.com/what-is-an-operating-system/- 이 각주 표기 방식이 흥미로움. 그냥 재미와 미감 때문인지, 더 깊은 유래나 목적이 있는지 궁금함
- 좋은 추천이고, Multicast and the Markets도 정말 좋았음
https://signalsandthreads.com/multicast-and-the-markets/
-
Jane Street이 HM 타입 함수형 프로그래밍도 충분히 공을 들이면 프로덕션 준비가 되고 매우 빠를 수 있음을 증명한 점은 존중할 만함
아직도 더 학술적인 스타일의 함수형 언어는 프로덕션에서 못 쓴다는 얘기를 가끔 듣지만, OCaml은 고성능 요구사항이 있어도 충분히 가능하다는 걸 보여줌- Mercury도 백엔드에 Haskell을 씀
https://mercury.com/ - 맞음. 프로덕션에서 F# 도 꽤 많이 쓰임
기존 시스템과 폭넓게 상호운용하면서 고품질 프로그램을 만들기에 훌륭한 선택지임 - NoRedInk도 백엔드 일부에 Haskell을 쓰고, 프런트엔드 대부분에는 Elm을 씀
몇 년 전 블로그 글에 따르면 Roc도 다뤄본 적이 있는데, 지금 쓰는지는 모르겠음
[0] https://blog.noredink.com - pandoc도 Haskell로 작성됨. Facebook도 한때 스팸 방지에 Haskell을 썼고, 지금도 그런지는 모르겠음
- 타입 시스템이 실행 속도에 영향을 준다는 뜻인가? 아는 한 타입 시스템은 그냥 정적 분석이라 런타임 속도에는 영향이 없음
- Mercury도 백엔드에 Haskell을 씀
-
Janestreet 블로그 글을 볼 때마다, Jane에서 금융 업계 일을 하는 게 이른바 기술 업계 직업의 90%보다 더 재미있고 보상도 좋을 것 같아 속으로 웃게 됨
- Jane Street과 몇몇 엘리트 회사는 금융 업계 기술 인력 중 아주 작은 비율일 뿐임
금융권 기술직에는 Jane Street은커녕 평균적인 기술 회사만큼도 재미있거나 보수가 좋지 않은 일이 훨씬 많음 - 남의 떡이 더 커 보이는 법임
금융권의 많은 일은 20년 된 Java 코드를 고치거나, 맞춤 보고서를 위해 Excel 파일에 데이터를 넣고 빼는 새 방법을 찾는 것임 - 증명은 안 되는 일화지만, 동료 한 명은 JS에서 6개월 일한 뒤 일이 너무 지루해서 FAANG으로 옮겼음. 연봉은 더 높았는데도 그랬음
그리고 JS에 들어가는 게 Google/Meta보다 훨씬 어렵다고 들었음
나도 예전에 지원했는데 면접관이 꽤 불쾌했고, 빅테크 면접에서는 그런 적이 없어서 좋은 인상은 아니었음 - 정말 그럴까? 근무 시간은 길고 기준은 매우 빡빡하며, 결국 이익을 위해 숫자를 조작하는 일이라 대부분에게는 그다지 의미 있는 일은 아님
그래도 사람들이 광고를 클릭하게 만드는 일보다는 높게 평가함 - Jane Street이나 비슷하게 선별적인 고빈도 거래 회사에서 하는 일을 사람들이 너무 과대평가함
다른 회사들처럼 흥미로운 프로젝트와 지루한 일이 섞여 있음
- Jane Street과 몇몇 엘리트 회사는 금융 업계 기술 인력 중 아주 작은 비율일 뿐임
-
복잡한 코드베이스보다 더 나쁜 건 복잡한 빌드 시스템뿐임. CMake는 혐오스러움
“이 언어를 컴파일하려면 또 다른 새 언어를 배워라!”라는 꼴임- 복잡한 빌드 시스템보다 더 나쁜 건, 필요한 걸 빌드하지 못하는 단순한 빌드 시스템임 :(
- “이 형편없는 새 언어를 배워라”에 가까움
- CMake가 싫지는 않음. 끔찍하긴 하지만 이미 C나 C++을 쓰고 있으니 기준이 꽤 낮게 잡혀 있음
제대로 하는 일도 몇 가지 있고, 잘못하는 것 중 일부는 관리 가능함
사람이 편집할 수 있는 설정 파일 한 벌로 여러 플랫폼의 빌드 절차를 얻을 수 있다면 어느 정도 고통은 감수할 수 있음 - 현대적인 타깃 지향 CMake에 담긴 아이디어들은 가치가 있음
끔찍한 스크립트 언어가 발목을 잡지만, 충분한 동기가 있으면 개선될 수 있음
-
예전에 Lisp 이후 첫 함수형 경험으로 Haskell 쪽에서 몇 년을 보냈음
OCaml은 잘 와닿지 않았고, 드문 형태의 세미콜론 알레르기가 있어서 Haskell이 훨씬 보기 좋았음
그런데 최근 Reason을 써봤더니 아주 좋았고, 갑자기 OCaml 관련 모든 것이 흥미로워짐- F# 은 OCaml의 더 흥미로운 기능 일부, 예를 들어 모듈 시스템의 표현력은 빠져 있지만, 들여쓰기 민감 문법이라 대부분의 경우 세미콜론을 포함한 많은 구조물을 덜어냄
https://learn.microsoft.com/en-us/dotnet/fsharp/language-ref... - “파일을 스크립트로 컴파일하거나 해석하려는 목적이라면 OCaml에서 이중 세미콜론은 피할 수 있고 피해야 한다. 남겨둬도 오류는 나지 않지만 쓸모는 없다. 컴파일러가 이를 허용하는 이유는 UTop에서 파일로 복사해 붙여넣을 때 제거하지 않아도 되게 하기 위해서다.”
https://ocaml.org/docs/toplevel-introduction#loading-librari...
https://reasonml.github.io/는 멋져 보이고, JavaScript 느낌의 OCaml 같음 - 재밌는 건, Haskell에서 항상 꺼려졌던 부분은 구조를 알아볼 시각적 단서 없이 공백과 식별자 흐름처럼 느껴진다는 점이었음
그 선을 깨달으면 괜찮겠지만, Lisp처럼 그 구조로 생각하는 법을 배워야 하는 것 같음. 그래도 Haskell 코드가 화면 가득 있으면 겁이 남
그중에서는 SML/NJ가 가장 읽기 쉬웠음 - Reason 문법이 기본 문법이었으면 좋겠음
- F# 은 OCaml의 더 흥미로운 기능 일부, 예를 들어 모듈 시스템의 표현력은 빠져 있지만, 들여쓰기 민감 문법이라 대부분의 경우 세미콜론을 포함한 많은 구조물을 덜어냄
-
Jane Street의 오픈소스 OCaml 라이브러리와 도구를 어느 정도 진지한 프로젝트에 써본 사람이 있는지 궁금함
잠깐 살펴본 인상으로는 생태계를 만들고 코드를 오픈소스로 공개하는 데 많은 노력을 들이긴 했지만, Jane Street 외부 사람들이 쓰기에 “준비된” 상태는 아닌 것 같았음- 쓰고 있음. 처음 OCaml을 배운 프로젝트에서 그들의 표준 라이브러리 대체품을 썼고, 그래서 계속 사용해 왔음
무언가를 찾으려면 함수 시그니처를 꽤 능숙하게 읽어야 하지만, 그 외에는 불만이 없음. 예를 들어 모든 기본 타입에 대해 s-표현식으로 변환하거나 되돌리는 함수는 존재를 알고 미리 계획하면 시간을 엄청 아껴줌
다른 라이브러리도 써봤고, 로깅과 단위 테스트 ppx는 OCaml 세계에서 그런 게 가능하다면 사실상 표준에 가까움. 코드 포매터, 테스트 프레임워크 하나, 고급 자료구조 구현도 써봤음
가끔은 다른 댓글처럼 정말 동작하지 않는 것도 만남. 따로 배포된 문서화 안 된 무언가에 의존하거나, 비밀 설정이 필요하거나 하는 식임. 악의는 아니고, 이슈를 열고 1~2년 뒤 돌아오면 동작하는 경우가 많음
좌절이 전혀 없는 건 아니지만, 더 적은 릴리스를 테스트하고 다듬는 데 자원을 쓰기보다 전부 공개하는 접근은 고맙게 봄. 코드 품질은 대체로 매우 높고, 직접 동작시키지 못해도 엄밀하고 검증된 예제 구현이 됨 - 진지한 프로젝트는 아니지만, 개인용 정적 사이트 생성기에 그들의 stdlib을 씀
표준 stdlib보다 API가 훨씬 낫다고 봄 - magic-trace 도구를 써보려 했는데, 사소한 dotnet 프로그램에서도 최소 4가지 다른 크래시가 났음
- Bloomberg에서 사용했음
- 복잡한 수학 연구 프로젝트의 테스트를 돌리는 중인데, 실행 시간은 놀랍게도 지금까지 Lean 4 << Haskell << OCaml 순서임
Lean 4를 좋아하지만, AI에게 도움받기는 쉽지 않음. 오늘 진행 중인 작업은 참고 매뉴얼을 200K 컨텍스트 창 안에 잘 들어가도록 소화시키는 것임. 도움이 될지 볼 예정임
- 쓰고 있음. 처음 OCaml을 배운 프로젝트에서 그들의 표준 라이브러리 대체품을 썼고, 그래서 계속 사용해 왔음
-
자체 새 빌드 시스템을 개발하고 거기로 마이그레이션하는 게, buck2나 bazel 같은 기성 도구를 채택하는 것보다 여전히 말이 된다는 점이 꽤 놀라움
이런 고급 빌드 도구를 도입하기 쉽다는 뜻은 아니지만, 직접 만드는 것과 비교하면 어떤가? dune을 많이 다르게 만드는 특별한 점이 있는지 궁금함- 구체적인 내용은 경험 많은 사람들이 더 잘 말해줄 수 있겠지만, JS가 2012년에 자체 도구를 만들기 시작했을 때는 buck도 bzl도 없었음
Bazel의 첫 릴리스는 2015년, Buck은 2013년임
JS에는 NIH 문화가 좀 있긴 하지만, 여기서 그게 작용했는지는 모르겠음. 당시에는 좋은 빌드 도구가 많지 않았고, 특히 비정통적인 기술 스택을 쓰는 회사에는 더 그랬음 - OCaml을 특별히 잘 아는 건 아니지만, 언급된 것 중 실제로 OCaml을 올바르게 컴파일할 의미론을 가진 빌드 시스템은 buck2뿐이라고 이해하고 있음
특히 미리 ocamldep를 실행할 수 있어야 하거나 두 번 컴파일해야 하는 것으로 알고 있는데, buck2는 가능하고 Bazel은 기억상 해킹이 필요함 - 우리 팀은 큰 OCaml 코드베이스와 이중 빌드 시스템, 즉 buck2와 dune을 갖고 있음
둘은 대략 비슷함. OCaml 빌드 도구를 호출하는 순수 속도는 dune이 빠르고, 많은 것을 다시 빌드해야 하며 분산 빌드 시스템을 사용할 때는 buck2가 이김
큰 고통은 LSP 통합임. LSP 서버가 파일의 의존성을 알 수 있는 유일한 방법이 빌드이기 때문에, 빌드 시스템과 밀접하게 묶여야 함. dune에서는 모든 것이 깔끔하게 제공됨. buck2로도 어떻게든 엮어놓긴 했지만 그만큼 좋지는 않음
- 구체적인 내용은 경험 많은 사람들이 더 잘 말해줄 수 있겠지만, JS가 2012년에 자체 도구를 만들기 시작했을 때는 buck도 bzl도 없었음
-
Dune은 빌드 시스템 이름으로는 단연 최고이고, 근처에 오는 것도 없음
- 맞음. “빌드 시스템을 지배하는 자가 우주를 지배한다”
- 소프트웨어가 움직이는 모래 위에 지어져서 그런가?
-
더 나은 빌드 시스템을 만들려고 시작한 뒤 “우연히” 더 나은 빌드 시스템을 만들었다니, 그런 우연이 생기는 방식은 참 이상함