Документация Engee

Распространение двоичных файлов

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

Обратите внимание: хотя код Julia за несколькими исключениями распространяется по лицензии MIT, дистрибутив, созданный описанными здесь способами, будет распространяться по лицензии GPL, так как эта лицензия действует в отношении ряда зависимых библиотек, таких как SuiteSparse. Надеемся, что в будущем будет возможно получить дистрибутив Julia без лицензии GPL.

Управление версиями и Git

Makefile использует файл VERSION, а также хэши и теги фиксаций из репозитория Git для создания файла base/version_git.jl с информацией, которая выводится на начальном экране и в выходных данных функции versioninfo(). Если по какой-либо причине репозиторий Git не будет доступен при сборке, файл base/version_git.jl необходимо создать заранее с помощью следующей команды:

make -C base version_git.jl.phony

Многие зависимости сборки Julia используются в версиях исправления, которые еще не включены в популярные диспетчеры пакетов. Обычно эти зависимости загружаются автоматически при сборке, но если вы хотите выполнить сборку Julia на компьютере без доступа к Интернету, следует создать архив full-source-dist со специальной целью make:

make full-source-dist

В результате будет создан архив julia-version-commit.tar.gz со всеми необходимыми зависимостями.

При компиляции помеченного выпуска в репозитории Git информация о хэше ветви и фиксации на начальном экране не выводится. Вы можете использовать эту строку для отображения описания выпуска длиной до 45 символов. Для задания этой строки необходимо создать файл Make.user, содержащий следующую инструкцию:

override TAGGED_RELEASE_BANNER = "my-package-repository build"

Целевые архитектуры

По умолчанию Julia оптимизирует образ системы под машинную архитектуру компьютера сборки. Обычно это не то, что требуется при сборке пакетов, так как в результате Julia не сможет запускаться на компьютерах с несовместимыми ЦП (в особенности на более старых с ограниченными наборами инструкций).

Поэтому при вызове make мы рекомендуем передавать переменную MARCH, указывая в качестве ее значения целевую базовую архитектуру, которая должна поддерживаться. Она будет определять целевые ЦП как для исполняемого файла и библиотек Julia, так и для образа системы (который также можно указать с помощью JULIA_CPU_TARGET). Для ЦП с архитектурой x86 типичными значениями являются x86-64 и core2 (для 64-разрядных сборок) и pentium4 (для 32-разрядных сборок). К сожалению, ЦП старше Pentium 4 в настоящее время не поддерживаются (см. эту проблему).

Полный список целевых ЦП, поддерживаемых LLVM, можно получить командой llc -mattr=help.

Linux

В Linux команда make binary-dist создает архив Tarball, содержащий полнофункциональную установку Julia. Чтобы создать дистрибутив, например в формате .deb или .rpm, потребуются дополнительные усилия. Пример метаданных, необходимых для создания пакетов .deb для систем на базе Debian и Ubuntu, см. в репозитории julia-debian. Для дистрибутивов на основе RPM см. пакет Fedora. Для создания пакетов Julia для различных дистрибутивов Linux можно использовать программу Alien, хотя мы ее еще не опробовали.

Julia поддерживает переопределение стандартных каталогов установки с помощью prefix и других переменных среды, которые можно передать при вызове make и make install. Их список см. в Make.inc. Для принудительной установки во временный каталог можно также использовать DESTDIR.

По умолчанию Julia загружает $prefix/etc/julia/startup.jl в качестве файла инициализации для всей установки. Диспетчеры дистрибутивов могут использовать его для задания пользовательских путей или кода инициализации. Если для дистрибутива Linux переменной $prefix присвоено значение /usr, каталог /usr/etc отсутствует. Поэтому необходимо изменить путь к частному каталогу etc Julia. Это можно сделать с помощью переменной make sysconfdir во время сборки. Просто передайте sysconfdir=/etc в make при сборке, и Julia сначала проверит файл /etc/julia/startup.jl и лишь затем — $prefix/etc/julia/startup.jl.

OS X

Чтобы создать двоичный дистрибутив в OS X, сначала выполните сборку Julia, а затем перейдите в каталог contrib/mac/app и запустите make с теми же переменными, которые использовались с командой make при сборке Julia. В результате в каталоге contrib/mac/app будет создан файл .dmg с полностью автономным приложением Julia.app.

Кроме того, Julia можно собрать как фреймворк, вызвав make с целью сборки darwinframework и заданной переменной DARWIN_FRAMEWORK=1. Например, make DARWIN_FRAMEWORK=1 darwinframework.

Windows

Инструкции по созданию дистрибутива Julia в Windows приведены в документации по сборке для Windows.

Примечания касательно BLAS и LAPACK

По умолчанию Julia выполняет сборку OpenBLAS, включая библиотеки BLAS и LAPACK. В 32-разрядных архитектурах Julia выполняет сборку OpenBLAS с использованием 32-битовых целых чисел, а в 64-разрядных архитектурах — с использованием 64-битовых целых чисел (ILP64). Важно, чтобы все функции Julia, вызывающие подпрограммы API BLAS и LAPACK, использовали целые числа правильной ширины.

Большинство дистрибутивов BLAS и LAPACK, входящих в состав дистрибутивов Linux, и даже коммерческие реализации предоставляют библиотеки, использующие 32-разрядные API. Зачастую 64-разрядный API предоставляется в виде отдельной библиотеки.

При использовании библиотек, предоставляемых поставщиком или ОС, в рамках сборки Julia доступен параметр make, который называется USE_BLAS64. При выполнении команды make USE_BLAS64=0 Julia вызывает BLAS и LAPACK, предполагая, что используется 32-разрядный API, где все целые числа имеют ширину 32 бита даже в 64-разрядных архитектурах.

В других библиотеках, используемых Julia, таких как SuiteSparse, также применяются BLAS и LAPACK. Интерфейсы API должны быть согласованными для всех библиотек, которые зависят от BLAS и LAPACK. При сборке Julia все эти библиотеки собираются правильно, но при переопределении значений по умолчанию и использовании системных библиотек необходимо обеспечивать данную согласованность.

Имейте также в виду, что в состав дистрибутивов Linux иногда входят несколько версий OpenBLAS, некоторые из которых поддерживают многопоточность, а другие работают только последовательно. Например, в Fedora версия libopenblasp.so является многопоточной, а libopenblas.so — нет. Для оптимальной производительности мы рекомендуем использовать первый вариант. Чтобы выбрать библиотеку OpenBLAS, отличную от библиотеки по умолчанию (libopenblas.so), передайте в make переменные LIBBLAS=-l$(YOURBLAS) и LIBBLASNAME=lib$(YOURBLAS), заменив $(YOURBLAS) именем своей библиотеки. Вы также можете добавить .so.0 к имени библиотеки, чтобы для работы пакета не требовалась символьная ссылка .so без версии.

Наконец, в состав OpenBLAS входит собственная оптимизированная версия LAPACK. Если вы задаете переменные USE_SYSTEM_BLAS=1 и USE_SYSTEM_LAPACK=1, необходимо также задать LIBLAPACK=-l$(YOURBLAS) и LIBLAPACKNAME=lib$(YOURBLAS). В противном случае будет использоваться эталонная версия LAPACK, и производительность, как правило, будет гораздо ниже.

Начиная с версии Julia 1.7 для выбора другой библиотеки BLAS во время выполнения используется libblastrampoline.

Основы выпуска доработанных версий

Создание доработанного или корректирующего выпуска производится в несколько отдельных шагов.

Фиксации бэкпортов

Некоторые запросы на вытягивание имеют пометку «backport pending x.y» (ожидается бэкпорт x.y), например «backport pending 0.6». Это означает, что следующий выпуск из ветви release-x.y, должен включать фиксации в запросе на вытягивание. После слияния запроса на вытягивание с ветвью master изменения из каждой фиксации должны копироваться в выделенную ветвь, которая в конечном итоге будет слита с release-x.y.

Создание ветви бэкпортов

Сначала создайте ветвь на основе release-x.y. Согласно принятому в Julia соглашению перед именем ветви следует добавлять свои инициалы, если она должна быть личной. Для примера предположим, что автором ветви является пользователь Jane Smith.

git fetch origin
git checkout release-x.y
git rebase origin/release-x.y
git checkout -b js/backport-x.y

Это позволяет гарантировать, что перед созданием новой ветви на основе вашей локальной копии release-x.y эта копия актуальна и соответствует исходной ветви.

Копирование изменений в фиксациях

Теперь выполним само бэкпортирование. В веб-интерфейсе GitHub найдите все запросы на вытягивание, для которых выполнено слияние и которые помечены как «backport pending x.y». Для каждого из них прокрутите страницу вниз до надписи «someperson merged commit 123abc into master XX minutes ago» (кто-то выполнил слияние фиксации 123abc с master XX мин назад). Обратите внимание, что имя фиксации представляет собой ссылку: если щелкнуть по ней, будет показано содержимое фиксации. Если на странице показано, что 123abc — это фиксация слияния, вернитесь на страницу запроса на вытягивание: нам нужны не фиксации слияния, а фактические фиксации. Однако если не показано, что это фиксация слияния, значит, слияние запроса на вытягивание было выполнено со сжатием. В этом случае используйте значение git SHA фиксации, указанное рядом с фиксацией на данной странице.

Получив значение SHA фиксации, копируйте ее изменения в ветвь бэкпорта:

git cherry-pick -x -e <sha>

Возможны конфликты, которые потребуется устранить вручную. После устранения конфликтов (если они есть) добавьте ссылку на запрос на вытягивание в GitHub, в котором была добавлена фиксация, в текст сообщения фиксации.

После добавления всех нужных фиксаций в ветвь бэкпорта отправьте эту ветвь в GitHub.

Проверка снижения производительности

Доработанные выпуски не должны приводить к снижению производительности. К счастью, имеющийся в Julia бот тестирования производительности под названием Nanosoldier позволяет проводить тесты для любой ветви, а не только master. В данном случае нам нужно проверить результаты тестирования js/backport-x.y в сравнении с release-x.y. Для этого активируйте бот Nanosoldier с помощью комментария к запросу на вытягивание для бэкпортирования:

@nanosoldier `runbenchmarks(ALL, vs=":release-x.y")`

В результате для release-x.y и js/backport-x.y будут выполнены все зарегистрированные тесты производительности с выводом сводки результатов, в которой отмечены все улучшения и ухудшения.

Если Nanosoldier обнаружит ухудшения производительности, попробуйте провести проверку локально, а затем при необходимости еще раз запустите Nanosoldier. Если похоже, что ухудшения действительно имеют место быть, вам потребуется найти в ветви master фиксацию, с которой они связаны, и бэкпортировать ее. Если же такой фиксации нет, следует определить, что вызывало ухудшение, и отправить исправление в master (или попросить сделать это того, кто разбирается в коде), а затем бэкпортировать фиксацию после слияния. (Если применимо, можно отправить исправление в ветвь бэкпорта напрямую.)

Сборка тестовых двоичных файлов

После слияния запроса на вытягивание для бэкпортирования с ветвью release-x.y обновите локальный клон Julia, а затем получите SHA ветви с помощью следующей команды:

git rev-parse origin/release-x.y

Держите это значение под рукой, так как его нужно будет ввести в поле Revision (Редакция) в пользовательском интерфейсе бота сборки.

Пока же все, что вам нужно, — это двоичные файлы для Linux x86-64, так как они используются для запуска PackageEvaluator. Перейдите на сайт https://buildog.julialang.org, отправьте задачу для nuke_linux64, а затем поставьте в очередь задачу для package_linux64, указав SHA в качестве редакции. Когда задача упаковки завершится, двоичный файл будет отправлен в контейнер julialang2 в AWS. Получите URL-адрес, так как он будет нужен для PackageEvaluator.

Проверка нарушения работы пакетов

Доработанные выпуски не должны приводить к нарушению работы пакетов. Единственным возможным исключением могут быть пакеты, которые производят крайне сомнительные действия с использованием внутренних механизмов модуля Base, которые не предназначены для пользователей. (В таком случае, возможно, следует обсудить ситуацию с автором пакета.)

Проверить, не приведут ли изменения в предстоящей версии к нарушению работы пакетов, можно с помощью инструмента PackageEvaluator, который часто сокращенно называют PkgEval. Именно PkgEval присваивает эмблемы статуса в репозиториях GitHub и на сайте pkg.julialang.org. Обычно этот инструмент работает на одном из узлов Nanosoldier, не предназначенных для тестирования производительности, и использует Vagrant для выполнения своих задач на отдельных виртуальных машинах VirtualBox, работающих параллельно.

Настройка PackageEvaluator

Клонируйте PackageEvaluator, создайте ветвь backport-x.y.z и извлеките ее. Имейте в виду, что требуемые изменения довольно сложные и запутанные. Надеемся, в будущей версии PackageEvaluator ситуация изменится. Изменения будут воспроизведены по образцу этой фиксации.

В качестве первого аргумента скрипт настройки принимает версию Julia, а в качестве второго — диапазон имен пакетов (AK для пакетов с именами от A до K, LZ — с именами от L до Z). Наша задача — настроить скрипт так, чтобы запускались только две версии Julia: текущий выпуск x.y и бэкпорт-версия — каждая с тремя диапазонами пакетов.

В сравнении по ссылке указывается, что если второй аргумент имеет значение LZ, следует использовать двоичные файлы, собранные из ветви бэкпорта. В противном случае (AK) следует использовать двоичные файлы ветви выпуска. Затем мы используем первый аргумент для запуска части списка пакетов: A-F для входного значения 0.4, G-N для 0.5 и O-Z для 0.6.

Запуск PackageEvaluator

Для запуска PkgEval подберите достаточно мощную машину (например, узел Nanosoldier 1), а затем выполните следующие команды:

git clone https://github.com/JuliaCI/PackageEvaluator.jl.git
cd PackageEvaluator.jl/scripts
git checkout backport-x.y.z
./runvagrant.sh

В каталоге scripts/ будет создано несколько папок. Имена эти папок и их содержимое приведены ниже.

Имя папки Версия Julia Диапазон пакетов

0.4AK

Выпуск

A-F

0.4LZ

Бэкпорт

A-F

0.5AK

Выпуск

G-N

0.5LZ

Бэкпорт

G-N

0.6AK

Выпуск

O-Z

0.6LZ

Бэкпорт

O-Z

Изучение результатов

После этого с помощью ./summary.sh из того же каталога можно получить сводный отчет по результатам. Мы сделаем это для каждой из папок, чтобы получить совокупные результаты по версии.

./summary.sh 0.4AK/*.json > summary_release.txt
./summary.sh 0.5AK/*.json >> summary_release.txt
./summary.sh 0.6AK/*.json >> summary_release.txt
./summary.sh 0.4LZ/*.json > summary_backport.txt
./summary.sh 0.5LZ/*.json >> summary_backport.txt
./summary.sh 0.6LZ/*.json >> summary_backport.txt

Теперь у нас есть два файла, summary_release.txt и summary_backport.txt, с результатами тестов PackageEvaluator (пройден или не пройден) для каждого пакета в двух версиях.

Чтобы их было легче передать в Julia, мы преобразуем их в файлы CSV, а затем используем пакет DataFrames для обработки результатов. Для преобразования в формат CSV скопируйте каждый файл TXT в соответствующий файл CSV, а затем войдите в Vim и выполните ggVGI"<esc>, а затем :%s/\.json /",/g. (Использовать Vim необязательно — это лишь один из способов.) Теперь обработайте результаты с помощью кода Julia наподобие следующего.

using DataFrames

release = readtable("summary_release.csv", header=false, names=[:package, :release])
backport = readtable("summary_backport.csv", header=false, names=[:package, :backport])

results = join(release, backport, on=:package, kind=:outer)

for result in eachrow(results)
    a = result[:release]
    b = result[:backport]
    if (isna(a) && !isna(b)) || (isna(b) && !isna(a))
        color = :yellow
    elseif a != b && occursin("pass", b)
        color = :green
    elseif a != b
        color = :red
    else
        continue
    end
    printstyled(result[:package], ": Release ", a, " -> Backport ", b, "\n", color=color)
end

В результате в stdout будут выведены строки с цветовой кодировкой. Все красные строки следует изучить, так как они указывают на потенциальные нарушения, вызванные бэкпорт-версией. На желтые строки также следует обратить внимание: они означают, что пакет выполняется в одной версии, но по какой-то причине не выполняется в другой. Если вы обнаружили, что из-за бэкпортированной ветви возникают нарушения, определите проблемные фиксации с помощью команды git bisect, выполните команду git revert для этих фиксаций и повторите процедуру.

Слияние бэкпортов с ветвью выпуска

Если выполняются все следующие условия:

  • бэкпортированные фиксации проходят все модульные тесты Julia;

  • из-за бэкпортированных фиксаций производительность не снижается по сравнению с ветвью выпуска;

  • бэкпортированные фиксации не нарушают работу зарегистрированных пакетов;

ветвь бэкпорта готова к слиянию с release-x.y. После слияния пройдите по всем запросам на вытягивание с бэкпортированными фиксациями и удалите метку «backport pending x.y». Не снимайте эту метку с запросов на вытягивание, которые не были бэкпортированы.

Ветвь release-x.y теперь должна содержать все новые фиксации. Последнее, что нужно сделать с ветвью, — подкорректировать номер версии. Для этого отправьте в release-x.y запрос на вытягивание, который изменяет файл VERSION так, что из номера версии удаляется элемент -pre. После его слияния можно переходить к добавлению тега.

Добавление тега к выпуску

Время пришло! Извлеките ветвь release-x.y и убедитесь в том, что ее локальная копия синхронизирована с удаленной. В командной строке выполните следующие команды:

git tag v$(cat VERSION)
git push --tags

В результате тег будет создан локально и отправлен в GitHub.

После добавления тега к выпуску отправьте в release-x.y еще один запрос на вытягивание, чтобы обновить номер исправления и снова добавить в конце элемент -pre. Это означает, что состояние ветви соответствует предварительной версии следующего доработанного выпуска в серии x.y.

Следуйте дальнейшим указаниям в Makefile.

Подписывание двоичных файлов

Для некоторых из описанных ниже действий потребуются защищенные пароли. Чтобы получить их, обратитесь к Эллиоту Саба (Elliot Saba, staticfloat) или Алексу Арслану (Alex Arslan, ararslan). Обратите внимание, что подписывание кода для каждой платформы должно производиться именно на этой платформе (например, подписывание для Windows должно выполняться в Windows и т. д.).

Linux

В Linux код должен подписываться вручную, но делается это достаточно легко. Сначала получите файл julia.key из папки CodeSigning в контейнере AWS juliasecure. Добавьте его в свой набор ключей GnuPG с помощью следующей команды:

gpg --import julia.key

При этом потребуется ввести пароль, который следует получить у Эллиота или Алекса. Далее задайте для ключа максимальный уровень доверия. Сначала войдите в сеанс gpg:

gpg --edit-key julia

В командной строке введите trust, а затем, когда будет запрошен уровень доверия, укажите максимально возможное значение (скорее всего, 5). Выйдите из GnuPG.

Теперь для каждого архива Tarball Linux, созданного ботами сборки, введите следующую команду:

gpg -u julia --armor --detach-sig julia-x.y.z-linux-<arch>.tar.gz

В результате для каждого архива Tarball будет создан соответствующий файл ASC. Вот и все!

macOS

Боты сборки macOS должны подписывать код автоматически. Однако нужно проверить, все ли прошло успешно. В системе или на виртуальной машине с macOS скачайте файл DMG, созданный ботами сборки. Для примера предположим, что файл DMG называется julia-x.y.z-osx.dmg. Выполните следующие команды:

mkdir ./jlmnt
hdiutil mount -readonly -mountpoint ./jlmnt julia-x.y.z-osx.dmg
codesign -v jlmnt/Julia-x.y.app

На этапе подключения запомните имя подключаемого диска! Допустим, это диск disk3. Если проверка подписывания кода была завершена успешно, на шаге codesign никаких выходных данных не будет. Если это так, файл DMG можно отключить:

hdiutil eject /dev/disk3
rm -rf ./jlmnt

Если же вы получаете сообщение наподобие следующего:

Julia-x.y.app: code object is not signed at all

код необходимо подписать вручную.

Чтобы подписать код вручную, сначала получите сертификаты OS X из папки CodeSigning в контейнере AWS juliasecure. Добавьте файл P12 в свой набор ключей с помощью Keychain.app. За паролем к ключу обратитесь к Эллиоту Саба (Elliot Saba, staticfloat) или Алексу Арслану (Alex Arslan, ararslan). Теперь выполните следующие команды.

hdiutil convert julia-x.y.z-osx.dmg -format UDRW -o julia-x.y.z-osx_writable.dmg
mkdir ./jlmnt
hdiutil mount -mountpoint julia-x.y.z-osx_writable.dmg
codesign -s "AFB379C0B4CBD9DB9A762797FC2AB5460A2B0DBE" --deep jlmnt/Julia-x.y.app

Может появиться следующее сообщение об ошибке:

Julia-x.y.app: resource fork, Finder information, or similar detritus not allowed

В этом случае необходимо удалить лишние атрибуты:

xattr -cr jlmnt/Julia-x.y.app

После этого еще раз попытайтесь подписать код. Если ошибок нет, повторите проверку. Если теперь все в порядке, отключите доступный для записи файл DMG и снова сделайте его доступным только для чтения:

hdiutil eject /dev/disk3
rm -rf ./jlmnt
hdiutil convert julia-x.y.z-osx_writable.dmg -format UDZO -o julia-x.y.z-osx_fixed.dmg

Проверьте, исправлен ли в результате файл DMG, дважды щелкнув по нему. Если проблем не наблюдается, извлеките его и уберите суффикс _fixed из имени. Вот и все!

Windows

В Windows код подписывается вручную. Сначала получите пакет SDK для Windows 10 с необходимыми средствами подписывания с веб-сайта Microsoft. Нам потребуется программа SignTool, которая должна быть установлена по пути наподобие C:\Program Files (x86)\Windows Kits\10\App Certification Kit. Получите файлы сертификатов Windows из CodeSigning в juliasecure и поместите их в каталог с исполняемыми файлами. Откройте окно CMD в Windows, перейдите (cd) в каталог со всеми файлами и выполните следующие команды:

set PATH=%PATH%;C:\Program Files (x86)\Windows Kits\10\App Certification Kit;
signtool sign /f julia-windows-code-sign_2017.p12 /p "PASSWORD" ^
   /t http://timestamp.verisign.com/scripts/timstamp.dll ^
   /v julia-x.y.z-win32.exe

Обратите внимание, что ^ — это символ продолжения строки в Windows CMD, а PASSWORD — заполнитель пароля для сертификата. Как обычно, за паролями обращайтесь к Эллиоту или Алексу. Если ошибок нет, значит все в порядке!

Отправка двоичных файлов

Теперь, когда код подписан, нужно отправить двоичные файлы в AWS. Можно воспользоваться такой программой, как Cyberduck, или программой командной строки aws. Двоичные файлы необходимо поместить в соответствующие папки в контейнере julialang2. Например, для Linux x86-64 это julialang2/bin/linux/x.y. Не забудьте удалить текущий файл julia-x.y-latest-linux-<arch>.tar.gz и заменить его копией julia-x.y.z-linux-<arch>.tar.gz.

Кроме того, нужно отправить контрольные суммы для всех собранных компонентов, включая архивы Tarball с исходным кодом и двоичные файлы выпуска. Сделать это несложно.

shasum -a 256 julia-x.y.z* | grep -v -e sha256 -e md5 -e asc > julia-x.y.z.sha256
md5sum julia-x.y.z* | grep -v -e sha256 -e md5 -e asc > julia-x.y.z.md5

Имейте в виду, если эти команды выполняются в macOS, выходные данные будут немного другими. Переформатировать их можно на основе существующего файла. Кроме того, пользователям Mac потребуется использовать md5 -r вместо md5sum. Отправьте файлы MD5 и SHA256 в julialang2/bin/checksums в AWS.

Для всех отправленных файлов в AWS должно быть задано разрешение «Все: ЧТЕНИЕ».

Для каждого отправленного файла необходимо очистить кэш Fastly, чтобы ссылки на веб-сайте указывали на отправленные файлы. Пример:

curl -X PURGE https://julialang-s3.julialang.org/bin/checksums/julia-x.y.z.sha256

Иногда можно обойтись и без этого, но лишним это не будет.