Иногда может возникнуть необходимость использовать какую-то определенную версию Java для вашего проекта. Мы тоже в Surf с таким столкнулись при работе над одним из Flutter приложений-долгожителей. Решение нашлось в виде использования менеджера версий Java. Рассказываем, что за инструмент и как с ним базово работать на MacOS.

Что такое jenv

jenv — это менеджер для удобного управления версиями Java на локальной машине. Он позволяет легко переключаться между различными версиями Java, проводит разделение между глобальной версией Java и локальными версиями для использования в разных проектах, а также позволяет управлять переменными окружения Java в системе.

Возможности jenv

  • Управление версиями Java: jenv позволяет установить и использовать несколько версий Java на одной машине.

  • Поддержка различных ОС: jenv может использоваться на macOS, Linux и Windows;

  • Управление переменными окружения Java: jenv может автоматически установить переменные окружения Java;

  • Управление настройками JVM: jenv позволяет настраивать параметры JVM для каждой версии Java, такие как размер кучи, аргументы командной строки и т. д.

Применение для MacOS

Установка jenv

Устанавливать jenv на macOS рекомендую через brew.

$ brew install jenv

После установки прописываем jenv в ~/.zshrc через команды:

$ echo 'export PATH="$HOME/.jenv/bin:$PATH"' >> ~/.zshrc
$ echo 'eval "$(jenv init -)"' >> ~/.zshrc

После выполнения этих команд надо удостовериться, что в ~/.zshrc есть строки:

export PATH="$HOME/.jenv/bin:$PATH"
eval "$(jenv init -)"

Проверить установку можно через команду:

jenv doctor

Вывод может быть таким:

[OK]	No JAVA_HOME set
[ERROR]	Java binary in path is not in the jenv shims.
[ERROR]	Please check your path, or try using ...
[OK]	Jenv is correctly loaded

Ничего страшного, если есть ошибки, главное: Jenv is correctly loaded.

Установка Java

Получить список всех доступных JVM можно командой:

/usr/libexec/java_home -V

Если есть доступные JVM, то можно увидеть их список:

Matching Java Virtual Machines (6):
20.0.1 (arm64) "Oracle Corporation" - "OpenJDK 20.0.1" /Users/martynov/Library/Java/JavaVirtualMachines/openjdk-20.0.1/Contents/Home
17.0.8 (arm64) "JetBrains s.r.o." - "JBR-17.0.8+7-1000.8-nomod 17.0.8" /Users/martynov/Library/Java/JavaVirtualMachines/jbr-17.0.8/Contents/Home
15.0.10 (arm64) "Azul Systems, Inc." - "Zulu 15.46.17" /Users/martynov/Library/Java/JavaVirtualMachines/azul-15.0.10/Contents/Home
13.0.14 (arm64) "Azul Systems, Inc." - "Zulu 13.54.17" /Users/martynov/Library/Java/JavaVirtualMachines/azul-13.0.14/Contents/Home
11.0.19 (arm64) "Amazon.com Inc." - "Amazon Corretto 11" /Users/martynov/Library/Java/JavaVirtualMachines/corretto-11.0.19/Contents/Home
1.8.0_372 (arm64) "Amazon" - "Amazon Corretto 8" /Users/martynov/Library/Java/JavaVirtualMachines/corretto-1.8.0_372/Contents/Home
/Users/martynov/Library/Java/JavaVirtualMachines/openjdk-20.0.1/Contents/Home

Если список пуст или нет нужной версии Java, то ее можно установить так:

# Установка текущей версии:

brew install openjdk 

# Установка нужной версии:

brew install openjdk@17

После установки, нужно выполнить несколько команд, которые предлагаются в Caveats (предостережениях):

sudo ln -sfn /usr/local/opt/openjdk/libexec/openjdk.jdk /Library/Java/JavaVirtualMachines/openjdk.jdk
echo 'export PATH="/usr/local/opt/openjdk/bin:$PATH"' >> ~/.zshrc
export CPPFLAGS="-I/usr/local/opt/openjdk/include"

Добавление Java в jenv

Для добавления Java в jenv надо выполнить следующую команду (путь можно взять из списка доступных JVM выше):

jenv add /Users/martynov/Library/Java/JavaVirtualMachines/corretto-11.0.19/Contents/Home

Результат с подтверждением добавления:

corretto64-11.0.19 added
11.0.19 added
11.0 added
11.0.19 already present, skip installation

Список всех добавленных JVM можно посмотреть через команду:

Список всех добавленных JVM можно посмотреть через команду:

Результат будет примерно таким:

* system (set by /Users/martynov/.jenv/version)
  11.0
  11.0.19
  15.0
  15.0.10
  17.0
  17.0.8
  corretto64-11.0.19
  jetbrains64-17.0.8
  zulu64-15.0.10

Использование различных версий Java через jenv

Глобальная версия

Для установки глобальной версии java нужно выполнить команду по аналогии с этой:

jenv global 15.0

После чего можно проверить версию командой:

java -version

Которая должна отразить выбранную версию:

openjdk version "15.0.1" 2020-10-20
OpenJDK Runtime Environment (build 15.0.1+9)
OpenJDK 64-Bit Server VM (build 15.0.1+9, mixed mode, sharing)

Локальная версия

Перед установкой нужной версии локально нужно перейти в папку с проектом.Для Flutter проекта это может быть папка /android.

Для установки локальной версии используется команда с нужной версией Java:

jenv local 17.0

Будет добавлен файл android/.java-version с выбранной версией внутри: 17.0. После проверяем установку локальной версии через команду:

jenv versions:
system
  11.0
  11.0.19
  15.0
  15.0.10
* 17.0 (set by /Users/martynov/Surf/flutter-project/android/.java-version)
  17.0.8
  corretto64-11.0.19
  jetbrains64-17.0.8
  zulu64-15.0.10

Еще можно на всякий случай проверить по другому.Перейти в папку в папку android и вызвать команду:

./gradlew -version

Убедиться, что используется нужная версия:

------------------------------------------------------------
Gradle 7.2
------------------------------------------------------------

Build time:   2021-08-17 09:59:03 UTC
Revision:     a773786b58bb28710e3dc96c4d1a7063628952ad

Kotlin:       1.5.21
Groovy:       3.0.8
Ant:          Apache Ant(TM) version 1.10.9 compiled on September 27 2020
JVM:          17.0.8 (JetBrains s.r.o. 17.0.8+7-b1000.8)
OS:           Mac OS X 13.4.1 aarch64

Источник: https://www.jenv.be – официальный сайт jenv.

Больше интересного контента для разработчиков → в телеграм‑канале Surf Flutter Team

П.с там публикуем кейсы, лучшие практики, новости и вакансии Surf, а также проводим прямые эфиры.

Комментарии (12)


  1. BugM
    29.09.2023 14:23
    +1

    Просто запускать нужную джаву по полному пути или по нужному симлинку слишком просто?


    1. avost
      29.09.2023 14:23

      Запустить по полному пути несложно. И даже подменять каждый раз JAVA_HOME и дюжину симлинков не то, чтобы сложно, но изрядно геморройно. А подменять надо, поскольку вы ведь не хотите лезть в кишки того же gradle, чтобы полменить пути там внутри. Ну, вот эта утиль, собственно, и подменяет то, что нужно. Впрочем, довольно хреново - мне так и не удалось нё запинать. jenv пишет, что переключилась на одну версию, а java -v показазывает другую... :(


      1. BugM
        29.09.2023 14:23

        Наверно в самом кровавом энтерпрайзе это еще и нужно. Но где энтерпрайз и где Homebrew. А обычный современный софт давно уже ничего не требует. java -jar service.jar -big_config.yaml и поехали.


        1. avost
          29.09.2023 14:23
          +1

          В энтерпрайзе это не нужно. А вот при разработке в том числе для энтерпрайза версии менять приходится. И кроме вашего java -jar в каталоге JAVA_HOME/bin есть ещё дюжина бинарников, а ещё рантайм, экстёрнал либы, сертификаты. И всё это для разных окружений разное и их тоже надо подменять даже в вашем простейшем хелловорде. И ещё раз внимательнее - я говорил для примера про грэдл. Где у него java -jar service.jar -big_config.yaml ?


          1. BugM
            29.09.2023 14:23

            И ещё раз внимательнее - я говорил для примера про грэдл

            Очень давно не использовал, но первая ссылка Гугла говорит что одной строкой в конфиге все делается.

            есть ещё дюжина бинарников, а ещё рантайм, экстёрнал либы, сертификаты

            Вот это и используется в кровавых энтерпрайзах. Современная разработка перешла на более простые варианты.


            1. avost
              29.09.2023 14:23

              Очень давно не использовал, но первая ссылка Гугла говорит что одной строкой в конфиге все делается.

              Да, разумеется. Одной строкой в конфиге гредла, одной строкой в конфиге мавена, одной строкой в конфиге анта и тд и тп. Возможно, кому-то доставляет удовольствие при каждом переключении вручную менять десятки конфигов, но я слишком ленив для этого. Мне бы одной командой.

              Вот это и используется в кровавых энтерпрайзах. Современная разработка перешла на более простые варианты.

              Ээээ? Вы можете продемонстрировать что именно более простое используется в "современной разработке", чем явский рантайм, явский компилятор, установленный набор сертификатов и набор библиотек? Я, по видимому, отстал от "современной разработки", слишком стар для того, чтобы постоянно менять строчки в десятках конфигов и пропустил момент когда ява отказалась от использования рантаймов, сторонних библиотек и компиляторов.


              1. BugM
                29.09.2023 14:23

                Да, разумеется. Одной строкой в конфиге гредла, одной строкой в конфиге мавена, одной строкой в конфиге анта и тд и тп. Возможно, кому-то доставляет удовольствие при каждом переключении вручную менять десятки конфигов, но я слишком ленив для этого. Мне бы одной командой.

                Профили давно везде есть. Шаблоны и переменные давно поддерживаются во всех конфигах. Все уже решено. Меняется по кнопке. Если вы что-то делаете даже в двух местах, то пора посмотреть подумать и исправить это. Точно есть какое-то стандартное решение.

                Вы можете продемонстрировать что именно более простое используется в "современной разработке", чем явский рантайм, явский компилятор, установленный набор сертификатов и набор библиотек?

                Ну да. Компилятор вызывается из системы сборки, рантайм работает сам просто кого вызовешь. Сертификаты из стандартного хранилища подхватываются сами. Ну вот отсюда вызвал, оно из того же jdk и подхватилось Сторонние библиотеки приезжают в сборку какие укажешь в системе сборки.

                Джава хороша своей огромной экосистемой. Все проблемы о которых вы даже не задумывались решены. И не просто решены, а есть типовое решение. Проверенное всем миром. Никто давно ничего в 10 местах не меняет. Даже в одном менять не стоит. Все делается просто по кнопке.


                1. avost
                  29.09.2023 14:23
                  -1

                  Профили давно везде есть. Шаблоны и переменные давно поддерживаются во всех конфигах. Все уже решено

                  То есть всёртаки не надо менять вручную строчки в конфиге? Ну, надо же! А вы говорили, что запросто меняете каждый раз...

                  Если вы что-то делаете даже в двух местах, то пора посмотреть подумать и исправить это. Точно есть какое-то стандартное решение.

                  Серьёзно? А как же строчка в конфиге? Но я вам даже больше скажу - одно из этих решений называется jenv. Внезапно.

                  Ну да. Компилятор вызывается из системы сборки,

                  В которой надо строчку в конфиге поменять, как вы сами сказали.

                  рантайм работает сам просто кого вызовешь

                  Неа. Вы бы хоть попробовали :)...

                  Сертификаты из стандартного хранилища подхватываются сами

                  Вы же их не используете, они же неудобные. И, нет, это работает иначе.

                  Ну вот отсюда вызвал, оно из того же jdk и подхватилось

                  Niet :)

                  Сторонние библиотеки приезжают в сборку какие укажешь в системе сборки.

                  Это же та которой надо строку в конфиге поменять? Или не надо?

                  Никто давно ничего в 10 местах не меняет. Даже в одном менять не стоит

                  Зачем же вы сказали, что надо поменять в конфиге?

                  Все делается просто по кнопке.

                  (Сходил за попкорном) ну, так назовите уже эту кнопку! "Имя, сестра, имя" (ц)


                1. aleksandy
                  29.09.2023 14:23

                  Все делается просто по кнопке.

                  Так а кнопку-то кто будет настраивать? И, главное, как? Смысл подобных инструментов как раз в том, что они суть есть эта волшебная кнопка.

                  Тут ниже уже написали про sdkman. С ним вообще можно один раз у проект настроить.

                  ~ ❯ mkdir sdkman-test && cd sdkman-test
                  sdkman-test ❯ mkdir j8 && cd j8 && sdk env init && echo java=8.0.372-librca > .sdkmanrc && cd ..
                  .sdkmanrc created.
                  sdkman-test ❯ mkdir j17 && cd j17 && sdk env init && echo java=17.0.7-librca > .sdkmanrc && cd ..
                  .sdkmanrc created.
                  sdkman-test ❯ mkdir j21 && cd j21 && sdk env init && echo java=21-librca > .sdkmanrc && cd ..    
                  .sdkmanrc created.
                  sdkman-test ❯ cd j8 && sdk env && java -version && cd ..
                  
                  Using java version 8.0.372-librca in this shell.
                  openjdk version "1.8.0_372"
                  OpenJDK Runtime Environment (build 1.8.0_372-b07)
                  OpenJDK 64-Bit Server VM (build 25.372-b07, mixed mode)
                  sdkman-test ❯ cd j17 && sdk env && java -version && cd ..
                  
                  Using java version 17.0.7-librca in this shell.
                  openjdk version "17.0.7" 2023-04-18 LTS
                  OpenJDK Runtime Environment (build 17.0.7+7-LTS)
                  OpenJDK 64-Bit Server VM (build 17.0.7+7-LTS, mixed mode, sharing)
                  sdkman-test ❯ cd j21 && sdk env && java -version && cd ..
                  
                  Using java version 21-librca in this shell.
                  openjdk version "21" 2023-09-19 LTS
                  OpenJDK Runtime Environment (build 21+37-LTS)
                  OpenJDK 64-Bit Server VM (build 21+37-LTS, mixed mode, sharing)
                  
                  


  1. b00
    29.09.2023 14:23
    +2

    Выглядит излишне сложно, по сравнению с curl -s "https://get.sdkman.io" | bash

    https://sdkman.io/install

    Да ещё и JVM надо сначала установить отдельно, насколько я понял.


    1. aleksandy
      29.09.2023 14:23
      +1

      А самое главное, что sdkman не ограничивается только jdk.


  1. Lioshik
    29.09.2023 14:23
    +1

    Добрый день, у jenv есть хорошая альтернатива - SDKman - https://sdkman.io. Он по запросу выкачивает и устанавливает как JDK, так и такие инструменты как maven, gradle. Так же можно в разных каталогах использовать разные JVM.

    Не холивара ради а только для разнообразия.