Mill — новий конкурент Maven та Gradle

💡 Усі статті, обговорення, новини про Java — в одному місці. Приєднуйтесь до Java спільноти!

Всім привіт. Я Сергій Моренець, розробник, тренер, викладач, спікер і технічний письменник, хочу розповісти вам про нову перспективну систему збирання Mill, яка на даний момент є найбільш передовою у своїй ніші. У цій статті я опишу її особливості, переваги та недоліки в порівнянні з аналогами, розповім про досвід міграції та також наведу деякі benchmarks. Сподіваюся, що ця стаття буде корисною для всіх, хто хоче більше дізнатися про сучасні технології у світі Java-індустрії.

Передісторія

До недавніх пір Java-спільнота в основному використовувала дві системи збирання — Maven (прийшов на зміну Ant) і Gradle. Серед переваг Maven була його простота, низький поріг входження, використання формату XML, який всім зрозумілий, покращена підтримка з боку IDE та стабільність. У той же час Maven розвивається дуже повільно, поточна версія Maven 3 вийшла в 2010-му році, і 4-я версія все відкладається вже котрий рік. Проте Maven дуже популярний, у тому числі і завдяки стабільності та плагінній архітектурі. Але ці плагіни є його ахіллесова п’ята, тому що їх доводиться використовувати навіть для тривіальних завдань, оскільки за умовчанням Maven вміє лише компілювати/збирати Java код. В результаті скрипти збирання стають занадто великими.

Gradle з’явився в 2010 році і був абсолютною протилежністю Maven. Він пропонував імперативний підхід, вимагав навичок програмування (на Groovy), використовував свій DSL, але зате скрипти збірки виходили більш компактні, а згодом завдяки різноманітним оптимізаціям/кешуванням він перевершив Maven за швидкодією, а після переходу на Kotlin додав собі нових прихильників серед цієї мови програмування. Тому багато нових проектів відразу використовують Gradle, а Maven залишається де-факто лідером серед legacy проектів, які здебільшого перебувають у стадії maintenance.

І ось у липні 2025-го року з’явилася новина, що вийшла стабільна версія 1.0 нової системи збирання Mill. Цей проект стартував ще в 2017-му році і йому знадобилося майже 8 років, щоб випустити першу production-ready версію. З іншого боку, розробники Mill могли врахувати всі проблеми та недоліки існуючих систем, щоб уникнути їх у себе.

Що таке Mill

Mill — це система збирання, написана на Scala, яка дозволяє вам збирати Java/Kotlin/Scala проекти. Крім того, вже зараз є експериментальна підтримка Android, Python та JavaScript. Як запевняють розробники Mill, її головні переваги — це покращена швидкодія (що ми перевіримо в тестах) і простота у використанні. При згадці Scala може виникнути питання. А як же SBT (Simple Build tool), яка й створювалася для складання Scala проектів? Як запевняють автори Mill, він має мінімум три переваги при порівнянні з SBT:

  • Його DSL простіше у розумінні
  • Продуктивність вища завдяки автоматичному кешування
  • Здебільшого немає необхідності використовувати сторонні плагіни

Тому свій продукт вони позиціонують не як альтернативу SBT, а як його заміну. Головний автор Mill — Java/Scala розробник і технічний письменник Лі Хаойі. І при розробці свого проекту він намагався максимально застосувати те, що вже тією чи іншою мірою використовується в існуючих системах збирання:

  1. Кешування
  2. Паралелізм

Якщо взяти Maven, то там кешування практично не використовується, розпаралелювання завдань можливо з додатковими налаштуваннями. У Gradle все набагато просунутіше. І розпаралелювання, і кешування там активно розвивається і застосовується, причому за умовчанням.

У Mill багато фітчі також використовуються за умовчанням і не вимагають додаткового налаштування (або мінімально) — те, що називається convention over configuration. І автори пишаються тим, що вони мають вбудовану підтримку:

  1. Spring Boot/Micronaut
  2. Java annotation processors
  3. Багатомовні збирання
  4. Docker/GraalVM Native image
  5. Інтеграційне тестування та розпаралелювання тестів
  6. Linting (Checkstlye, ErrorProne)

Причому все це зроблено як для Java, так і для Kotlin/Scala технологій. Для управління залежностями використовується coursier, а для компіляції — спеціальний інкрементальний компілятор Zinc. Важко сказати, брали щось автори з Maven/Gradle, як вони самі запевняють, що черпали ідеї з таких проектів як Bazel від Google і системи збірки CBT, що вже не підтримується.

Декларативна конфігурація

Якщо при роботі з Maven/Gradle ви можете завантажити весь дистрибутив і його налаштувати (або використовувати Wrapper), то тут рекомендується завантажити виконуваний bootstrap скрипт (наприклад, mill.bat або .mill), який встановить останню версію Mill і налаштує її.

Почнемо із найпростішого Java проекту без залежностей. І ось тут відразу видно дві фундаментальні відмінності Mill від Maven/Gradle, де ви можете створити проект без модулів, а можете і багатомодульний. У Gradle для цього потрібно написати окремий файл settings.gradle. У Mill такого поділу немає. Там завжди є як мінімум один модуль, і він буде розташовуватися в окремій підпапці, яка збігатиметься з ім’ям модуля.

Друга відмінність від Maven/Gradle — там вихідні файли знаходяться у папках src/main та src/test. У Mill вони перебувають у папках src і test/src. Що робити, якщо у вас вже є існуючий проект (а швидше за все, так і є)? Не хвилюйтеся, Mill підтримує і стандартний Maven layout, про це ми поговоримо трохи згодом.

Третя відмінність — Mill спочатку підтримував імперативний підхід (як Gradle), але з версії 1.1 додали підтримку і для декларативного підходу (як у Maven), але на базі не XML, а YAML. Таким чином, яку б систему збирання ви б не використовували для свого проекту, ви можете перейти на Mill та зберегти існуючий підхід. Це робить Mill унікальним у сфері систем збирання.

Для знайомства та тестування використовуватимемо останню версію 1.1.2. Створимо подпапку starter/src (це і буде єдиним модулем), в яку помістимо Java клас Runner. Почнемо з декларативного підходу, як зрозумілішого. У корені проекту створимо порожній файл build.mill.yaml, а в папці starter package.mill.yaml:

extends: JavaModule

і для компіляції всього проекту достатньо команди:

mill starter.compile

Якщо подивитися на її результати, то видно ще відмінність Mill від решти систем збирання — вона створює папку out у корені проекту та всі артефакти (навіть для модулів) будуть там. Крім того, Mill зберігає там службову інформацію (метадані зборки) у JSON форматі якраз для подальшого кешування.

Як ви бачите, розмір скрипта складання мінімальний (порівняно і з аналогічним Maven, і Gradle). Що таке JavaModule? Перші дві системи збирання побудовані на плагінній архітектурі. Якщо вам потрібна нова функціональність, ви просто додаєте новий плагін і його конфігурацію. У Mill роль плагіна виконують модулі. Це можна назвати аналогом плагінів, але з більшими можливостями. З точки зору програмування, це traits в Scala (щось середнє між інтерфейсами та класом з можливістю множинного успадкування):

trait MavenModule extends JavaModule { outer =>
 private[mill] def sourcesFolders0 = Seq(os.sub / "src/main/java")
 override def sourcesFolders = sourcesFolders0

І якщо вам потрібна якась нова функціональність, ви просто у своєму модулі успадкуєте той чи інший існуючий модуль, тобто тут усе побудовано на об’єктно-орієнтованому підході. Тепер розглянемо найцікавіший варіант програмної конфігурації.

Програмна конфігурація

Для написання скриптів збірки на Scala потрібно мати мінімальні знання в цій мові, в іншому тут нічого складного, тому що в документації є безліч прикладів.

Створимо порожній файл build.mill, який і буде скриптом збирання. Ви можете створювати скрипти складання в кожному модулі, а можете написати конфігурацію в кореневому build.mill, що ми зробимо. У нас один модуль starter, тому скрипт build.mill буде дуже простим:

package build
import mill.*, javalib.*
object starter extends JavaModule {
}

Тут ми вказуємо, що наш модуль starter буде використовувати JavaModule і всі його можливості (в тому числі tasks).

Ще одна відмінність Mill скриптів від Gradle — нам потрібно явно писати імпорти всіх типів, які ми використовуємо. Для компіляції проекту можна запустити:

mill starter.compile

Для складання та створення jar-файлу використовується команда:

mill starter.jar

А для запуску самого додатку:

mill starter.run

Що відбувається під час збирання? Ми запускаємо так званий Mill launcher, який у свою чергу запускає Mill демон, який і виконує основну роботу (тут Mill схожий на Gradle). Один із показників гнучкості та функціональності систем складання — це те, наскільки легко вам створити Task (завдання) для ваших цілей. У Mill це робиться досить просто:

object starter extends Module {
 def hello() = Task.Command { println("Hello world") } 
}

А викликати її можна через команду mill starter.hello.

Що, якщо у вас є Maven залежності? У Mill також є аналог scope (як Maven) і конфігурації (як у Gradle), але вони чітко розділені, між залежностями на інші ваші модулі:

def moduleDeps = Seq(`common`
)

На compile залежності (як Maven):

def mvnDeps = Seq(
mvn"jakarta.validation:jakarta.validation-api:3.0.2"
)

provided залежності:

def compileMvnDeps = Seq(
mvn"org.projectlombok:lombok:1.18.42"
)

Та runtime залежності:

def runMvnDeps = Seq(
mvn"commons-logging:commons-logging:1.2"
)

Також можна окремо виділяти версії залежностей в окремий клас-синглтон:

object versions {
   val elVersion="3.0.0"
   val metricsVersion="4.2.17"
}

Для подальшого використання:

mvn"io.dropwizard.metrics:metrics-jakarta-servlets:${versions.metricsVersion}"

Якщо у вас великий проект і багато модулів, то, швидше за все, якась частина залежностей і конфігурації буде загальною. У Maven/Gradle все це зазвичай виносять у кореневий скрипт збирання. У Mill ситуація аналогічна, але тут за рахунок ООП це виглядає більш просто та природно. Наприклад, можна створити такий спільний модуль:

trait ParentModule extends MavenModule {
      def slf4jVersion = "2.0.6"
      def javacOptions = Seq("—release", "21")
      def compileMvnDeps = Seq(
           mvn"org.projectlombok:lombok:1.18.42"
      )
      def mvnDeps = Seq(
            mvn"org.slf4j:slf4j-api:$slf4jVersion",
      )
}

І потім його використовувати в підмодулях в їх package.mill:

package build.` client`
import mill.*, javalib.*,publish.*
object `package` extends build.ParentModule {
    def mvnDeps = Seq(
        mvn"jakarta.validation:jakarta.validation-api:3.0.2",
    )
    object test extends MavenTests, TestModule.Junit6 {
    }
}

Ще одна важлива відмінність Mill — у ньому тестова та production розділені, щоб уникнути помилок та зробити скрипт збирання більш структурованим. Тому об’єкт test вже містить всю необхідну конфігурацію для запуску Junit 6 тестів.

Міграція з Maven на Mill

Зараз ми створили найпростіший Java-проект із усіх можливих. Що якщо у нас вже є складний багатомодульний проект із залежностями та плагінами? Як просто перейти на Mill для існуючого проекту? Як стверджують автори проекту, це може зайняти від кількох годин для невеликих проектів до тижня для серйозних комерційних додатків. У Mill є підтримка автоматичної міграції, яка конвертує конфігурацію підпроектів і залежності для Maven/Gradle/Sbt.

При цьому самі автори визнають, що за міграції можуть бути дві фундаментальні проблеми.:

  1. Відсутність альтернативних плагінів (тобто вам доведеться писати свої)
  2. Якщо у вас є код у скриптах збірки, який використовує DSL, вам теж доведеться заміняти його на DSL від Mill

Тому для того, щоб краще розібратися в особливостях Mill, спробуємо перевести на нього два проекти, один із яких використовує Maven, інший — Gradle. Почнемо з Maven проекту. Для міграції до Mill є команда init, яка створює декларативні скрипти збірки(якщо вам потрібна програмна конфігурація, необхідно додати опції —declarative false) . Запустимо міграцію:

mill init

Міграція займає 74 секунди. Крім кореневого build.mill.yaml були створені скрипти package.mill.yaml у кожному модулі. Спробуємо розібрати їх детальніше, розпочавши build.mill.yaml:

mill-version: 1.1.1-16-8f959f
mill-jvm-version: system
extends: [JavaModule, BomModule, PublishModule]
bomMvnDeps:
— org.testcontainers:testcontainers-bom:1.20.4
sources: []
resources: []
pomSettings:
organization: demo
publishVersion: 1.0.0

Таким чином, кожна з фітч/особливостей pom.xml стала базовим типом для кореневого проекту — BomModule і PublishModule. Ну а BOM залежності просто передаються одним рядком у властивість bomMvnDeps.

Тепер розберемо package.mill.yaml (він буде дано у спрощеному варіанті):

extends: [millbuild.ProjectBaseModule, mill.javalib.errorprone.ErrorProneModule]
moduleDeps: [common]
mvnDeps:
— org.slf4j:slf4j-api:2.0.6
— org.apache.commons:commons-lang3:3.12.0
compileMvnDeps:
— org.projectlombok:lombok:1.18.42
bomMvnDeps:
— org.testcontainers:testcontainers-bom:1.20.4
javacOptions: [—release, 21, —should-stop=ifError=FLOW]
errorProneDeps:
— com.google.errorprone:error_prone_core:2.42.0
— org.projectlombok:lombok:1.18.42
errorProneOptions: ["-Xep:EmptyBlockTag:OFF", , "-Xep:JavaTimeDefaultTimeZone:OFF"]
artifactName: user-service-client
object test:
extends: ProjectBaseTests
mvnDeps:
— org.junit.jupiter:junit-jupiter:6.0.1
compileMvnDeps:
— org.projectlombok:lombok:1.18.42

Тут можна відзначити кілька моментів:

  1. Для модулів вказується повний шлях (package name)
  2. Залежності розбиваються по scope (compile/bom/annotation processor)
  3. Для тестів вказується окрема конфігурація зі своїми залежностями
  4. Є підтримка ErrorProne конфігурації

Крім того, під час тестування було знайдено й помилки міграції. У вихідному проекті опції компілятора виглядали так:

<compilerArgs>
      <arg>-XDcompilePolicy=simple</arg>
      <arg>-Xplugin:ErrorProne -Xep:EmptyBlockTag:OFF -Xep:JavaTimeDefaultTimeZone:OFF</arg>
       <arg>—should-stop=ifError=FLOW</arg>
</compilerArgs>

Mill спробував відокремити опції для компілятора і для Error Prone (що в принципі непогано) і перетворив це на:

javacOptions: [—release, 21, —should-stop=ifError=FLOW]
errorProneOptions: ["-Xep:EmptyBlockTag:OFF", , "-Xep:JavaTimeDefaultTimeZone:OFF"]

Але з якоїсь причини він вважав, що —should-stop — це опція компілятора (хоча це опція Error Prone). Крім того, зайва прогалина в рядку призвела до того, що Mill поставив дві коми поспіль в errorProneOptions, що неправильно. Я створив тикет на ці помилки. Ще одна проблема, серйозніша, полягає в тому, як Mill імпортував загальну конфігурацію. У проектах Maven загальна конфігурація виноситься в кореневий pom.xml, після чого вона буде доступна всім модулям. Тут же Mill скопіював загальну конфігурацію в кожен package.mill.yaml, що не має особливого сенсу. У випадку Maven ми змінюємо загальну конфігурацію один раз, у нашому випадку доведеться змінювати її в кожному модулі. Це недолік саме декларативного підходу, якщо для вас це серйозна проблема, то використовуйте програмну конфігурацію

Тепер запустимо Mill-збирання згенерованої конфігурації. І відразу ж отримуємо помилку:

build.mill.yaml-68] [error] admin/package.mill.yaml:14:24
build.mill.yaml-68] trait package_ extends millbuild.ProjectBaseModule, mill.javalib.errorprone.ErrorProneModule, AutoOverride[_root_.mill.T[?]] {
build.mill.yaml-68] ^^^^^^^^^
build.mill.yaml-68] Not found: millbuild

З цього повідомлення видно, що Mill перетворив YAML-файли на звичайні скрипти на Sca-la, ось при їх компіляції і виникає проблема. І швидше за все проблема в цьому рядку в YAML:

extends: [millbuild.ProjectBaseModule, mill.javalib.errorprone.ErrorProneModule]

Я додав тикет, але нічого не заважає мені виправити помилку самостійно. Справа в тому, що офіційна документація говорить, що якщо у вас проект Maven, то ви можете вказати базовим модулем MavenModule:

extends: [MavenModule, mill.javalib.errorprone.ErrorProneModule]

Тепер колишня помилка зникає, але з’являється нова:

build.mill.yaml-68] [error] admin/package.mill.yaml:20:23
build.mill.yaml-68] object test extends ProjectBaseTests, AutoOverride[_root_.mill.T[?]] {
Швидше за все, помилка виникає через цей рядок:
object test:
extends: ProjectBaseTests

І тут знову на допомогу приходить офіційна документація, де йдеться, що в Maven проектах, якщо у вас тести на базі Junit 6, то ви маєте використовувати базовий модуль TestModule.Junit6, що ми і можемо зробити:

object test:
extends: MavenTests, TestModule.Junit6

І тільки тепер збирання виходить успішним. Але при цьому команда mill assembly не генерує jar-файли (як це робить команда mvn package). Її аналогом є mill jar, а точніше mill _.jar, оскільки у нас багатомодульний проект. І тут нова помилка при обробці кожного модуля:

publishVersion configuration missing in package.mill.yaml

Я створив тикет на цю помилку, але поки що над ним працюють, доводиться в кожному модулі в кожен package.mill.yaml додати рядок:

publishVersion: 1.0.0

Аналіз згенерованих YAML виявив ще одну проблему. Якщо ви використовуєте Maven для збирання, то навіть не замислюєтеся над тим, що левова частка загальної конфігурації знаходиться в parent залежностях (особливо це помітно для Spring Boot проектів). Якщо ж ви мігруєте на Mill, то йому доводиться імпортувати всі налаштування у ваш локальний скрипт збирання. Це стосується і dependency management конфігурації. У результаті одного з модулів розмір pom.xml був 8 кб, а package.mill.yaml — 61 кб.

Ще одна проблема, притаманна для декларативної конфігурації — в ній відсутній такий важливий елемент як властивості (properties) в Maven. Тому якщо в Maven ви виносили версії в окремий блок:

<properties>
     <lombok.version>1.18.38</lombok.version>
     <junit.version>5.11.3</junit.version>
</properties>

То в згенерованих YAML файлах всі версії вказані прямо в блоці залежностей, що ускладнює їх підтримку та зміну.

Міграція з Gradle на Mill

Тепер візьмемо схожий з першим проектом Gradle проект і спробуємо також конвертувати його в Mill формат. Для цього використовуємо стандартну команду mill init, яка несподівано завершується помилкою:

Caused by: java.lang.IllegalArgumentException: Unsupported class file major version 69
 at groovyjarjarasm.asm.ClassReader.<init>(ClassReader.java:200)

Я створив тикет на цю проблему, а полягає вона в тому, що Mill з якоїсь причини завантажує і запускає Gradle 8.11.1 для конвертації, а ця версія несумісна зі встановленою на комп’ютері JDK 25. Але поки ця проблема не вирішена, є workaround — можна додати Gradle wrapper, вказати. Тому додамо папку gradle/wrapper та файл gradle-wrapper.properties:

distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-9.3.1-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists

Тепер Mill використовує Gradle 9.3.1, але при конвертації виникає ще одна помилка:

Caused by: org.gradle.api.internal.artifacts.configurations.DefaultConfiguration$IllegalResolutionException:
Resolution of the configuration ’:demo-service:nativeImageTestClasspathInternal’ was attempted without an exclusive lock. This is unsafe and not allowed .

Я додав тикет на цю проблему, але схоже, що вона пов’язана з конфігурацією модуля Micronaut. Видалимо його з settings.gradle.kts. Вона зайняла 92 секунди та пройшла успішно, хоча багато плагінів так і не були перенесені через те, що вони не підтримуються або не існують для Mill.

Продуктивність

Як стверджують автори Mill, їх продукт перевершує за продуктивністю як Maven:

Так i Gradle:

На наших власних проектах перевірити це не вийде через різні помилки/обмеження або відсутність відповідних плагінів, спробуємо більш простий варіант, але надійний варіант. Згенеруємо проект на базі Spring Boot Initializer, де виберемо дефолтний варіант без додаткових залежностей на основі JDK 21:

  1. Maven проект
  2. Gradle-Kotlin проект

І з ними виконаємо 4 операції:

  1. Збирання проекту з нуля
  2. Інкрементальне збирання (без змін)
  3. Збирання проекту після зміни вихідного коду
  4. Запуск тестів

Крім того, порівнюватимуть розміри скриптів складання. Для Mill спочатку буде перевірена декларативна конфігурація, яка отримана після міграції, потім оптимізована в ручному режимі і врешті-решт програмна конфігурація.

І для Maven, і для Gradle будуть використані стандартні налаштування. В результаті отримаємо для Maven/Gradle:

Maven 3.9.12

Gradle 9.3.1

Збирання з нуля, сек

0.85

0.55

Інкрементальне збирання (без змін), сек

0.52

0.41

Збирання проекту після зміни вихідного коду, сек

0.94

0.64

Запуск тестів, сек

1.26

1.0

Розмір скрипта збирання, кб

1

0.6

И для самого Mill (1.1.2):

Декларативний (після міграції)

Декларативний (оптимізований)

Програмна конфігурація

Збирання з нуля, сек

6

5

5

Інкрементальне збирання (без змін), сек

0.05

0.05

0.05

Збирання проекту після зміни вихідного коду, сек

0.1

0.1

0.15

Запуск тестів, сек

0.61

0.59

0.63

Розмір скрипта збирання, кб

33

0.26

0.34

Аналіз тестів показує, що дійсно Gradle в 1.3 — 1.7 рази швидше Maven в базових операціях, а Mill випереджає Gradle за всіма показниками, крім створення вихідної конфігурації (метаданих проекту), яка займає 5-6 секунд. Але потім базові операції виконуються на порядок швидше. Те ж саме і зі скриптами збірки — у Mill найкращий показник розміру серед усіх технологій, що тестуються. Крім того, виявилося, що немає особливої ​​різниці у продуктивності між програмною та декларативною конфігурацією.

Обмеження та проблеми

Під час роботи та тестування Mill були знайдені несподівані обмеження або помилки, для яких немає workarounds. Насамперед це стосується плагінів. Але в Mill досі немає вбудованого модуля для WAR плагіна з Maven/Gradle. Немає навіть стороннього модуля, який можна було б використати. Я вже мовчу про підтримку таких плагінів, як AsciiDoctor та інших. Є й несподіваніші відкриття. Наприклад, якщо після редагування скриптів збирання в Mill можна отримати таку помилку:

build.mill-42] [error] parseBuildFiles
admin/package.mill:23:5
mvn"jakarta.enterprise:jakarta.enterprise.cdi-api:4.1.0",
^
Incompatible combinations of tabs and spaces in indentation prefixes.
Previous indent : 8 spaces
Latest indent : 3 spaces, 1 tab

Тобто Mill стежить, щоб вирівнювання збігалося в програмної конфігурації. Якщо ви переходите з Gradle на Mill, досить зручно копіювати залежність разом з версіями. Але іноді це не працює, якщо у вас використовується classifier:

140] [error] admin.mvnDeps
java.lang.Exception: Unable to parse signature: [org.primefaces:primefaces:15.0.10:jakarta]
mill.javalib.Dep$.parse(Dep.scala:136)
mill.javalib.package$DepSyntax$.mvn$extension(package.scala:13)

У Mill потрібно використовувати ось таку конструкцію:

mvn"org.primefaces:primefaces:15.0.10;classifier=jakarta",

Висновки

Mill — досить просунута система збирання, яка підтримує Java/Kotlin/Scala проекти та пропонує декларативну/програмну конфігурацію. Серед її переваг — можливість використовувати ООП підхід, застосування конвенцій, що скорочує розмір скриптів, підтримка сучасних технологій, більш простий DSL (у порівнянні з тим же Gradle), поділ конфігурації на production і тестову, а також швидкодію.

Крім того, Mill дозволяє вам мігрувати ваш Maven/Gradle проект на декларативну/програмну конфігурацію, і практика показує, що міграція переносить до 90% вмісту скрипту збірки, решту потрібно додавати вручну і ще бажано оптимізувати отриманий результат.

Серед недоліків — треба знати Scala на мінімальному рівні, якщо ви хочете писати програмну конфігурацію та відсутність підтримки популярних плагінів. Також серед відносних мінусів проекту — невелике охоплення аудиторії. Якщо по Maven/Gradle ви знайдете десятки тисяч питань/відповідей на Stackoverflow, то у Mill їх всього 43, основна дискусія йде на GitHub. Але це лише питання часу.

Сподобалась стаття? Підписуйтесь на автора, щоб отримувати сповіщення про нові публікації на пошту.

👍ПодобаєтьсяСподобалось11
До обраногоВ обраному2
LinkedIn
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Дозволені теги: blockquote, a, pre, code, ul, ol, li, b, i, del.
Ctrl + Enter
Серед недоліків — треба знати Scala на мінімальному рівні

пропіхували-пропіхували скалу без мила, і здрасьтє, знову.

Цікаво було б його порівняти з sbt2, особливо для Java проектів. Обіцяли теж неабияку швидкість, натхненну Bazel

Mill uses Java zulu:21 by default, regardless of what is installed as your system java.

все логічно 😉 це зроблено для того, щоб з одного боку не жанглювати Java-версіями в різних проєктах через sdkman чи щось подібне, а з іншого боку, щоб не зав’язувати сам білд тул на легасі версії jvm. версію javac можна вказати окремо через jvmVersion.

до речі, jvm яку вигористовує сам mill теж можна змінити, але, здається, в останніх версіях мінімальна вимога — Java 17.

Добре, що не 26

> file ~/.cache/mill/download/1.1.5-native-linux-amd64

~/.cache/mill/download/1.1.5-native-linux-amd64: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=fdbf076aa6349dcf30ed5345574b67ea3a90c793, for GNU/Linux 3.2.0, not stripped
все логічно 😉

Звісно

щоб не зав’язувати сам білд тул на легасі версії jvm.

Які легасі версі??? Воно через graalvm зібране.

версію javac можна вказати окремо через jvmVersion.

Куди вказати? Ось воно впало

coursier.cache.ArtifactError$DownloadError: download error: Caught java.net.ConnectException (Connection refused) while downloading cdn.azul.com/...​k21.0.10-linux_x64.tar.gz
...
Mill launcher failed. See out/mill-launcher/log for details.

Ви будете ще щось комусь вказувати, замість того, щоб цю хрінь видалити та забути?
Але воно вже скачало сміття на ~1G, яке ще треба знайти та видалити, перш ніж забути.

Серед недоліків — треба знати Scala на мінімальному рівні, якщо ви хочете писати програмну конфігурацію та відсутність підтримки популярних плагінів. Також серед відносних мінусів проекту — невелике охоплення аудиторії. Якщо по Maven/Gradle ви знайдете десятки тисяч питань/відповідей на Stackoverflow, то у Mill їх всього 43, основна дискусія
йде на GitHub.

Лол, 43 питання)
Щоб зібрати джава проект треба вчити скала.
Хто таке взагалі до себе в проект затягне)

лол 😅 користуватись Groovy для використання Gradle.

розкрию вам маленку таємницю 🤫 mill з версії 1.1 підтримує declarative configuration (спрощено yaml build file). тобто для маленьких проєктів або типових задач цього вистачить з головою.

Most Tasks that you can define programmatically in a build.mill file can be specified in a build.mill.yaml, as long as they involve simple configuration (strings, lists, maps, etc.) and do not need Custom Build Logic. For example, you can use the various configuration methods discussed in Java Library Dependencies: runMvnDeps, compileMvnDeps, unmanagedClasspath, etc. For more flexibility, e.g. if you need to generate sources or resource files at build time, you can instead use Programmable Modules.

mill-build.org/...​avalib/module-config.html

Mill — це система збирання, написана на Scala

Далі не читав, вже зробили одну таку огидну потвору, нашо повторювати?

Якщо щось написано на Scala, то одразу погано? Візьміть Kafka, яка стала де-факто стандартом для високопродуктивних систем обміну повідомленнями

А де топікстартер вказує, що все написане на Skala — це погано?

топікстартер робить нічим непідкріплені коментарі. хай він спочатку розкриє свою «експертну» думку і аргументує її, а потім поговоримо. а зараз це просто його 💩 в інеті.

Колись давно слухав gAmUssA, він казав що на Scala там вже не багато залишилось, все мігрує на Java.
Spark думаю був би кращим прикладом, хоча дефакто стандартом став PySpark.
Не кажу що Scala погана, але для нових проєктів не дуже хороший вибір.

понабирають всяких москаликів, а потім Scala зникає з проєктів 🚬🚬🚬

але насправді там зараз доволі цікава ситуація. відцоток скали в Apache Kafka дійсно малий. менше 10% в trunk зараз, але треба зауважити, що, окрім Scala KafkaStreams API, їх левова доля припадає на core project, який не доступний, як публічний API. тобто проглядається цікава закономірність що на Java перевели лише «публічні» проєкти, а не core. тепер живіть з цим 😅

Це називається «чув дзвін, та не знає, де він» 🤦‍♂️🤦‍♂️🤦‍♂️

Підписатись на коментарі