openjdk.ruOpenJDK на русском

JEP 138: Autoconf-Based Build System

Система сборки на основе autoconf

ОтветственныйMagnus Ihse Bursie
ТипFeature
ОбластьImplementation
СтатусClosed / Delivered
Выпуск8
Обсуждениеjdk8 dash dev at openjdk dot java dot net
ТрудоёмкостьL
ДлительностьM
БлокируетJEP 161: Compact Profiles
Зависит отJEP 139: Enhance javac to Improve Build Speed
РецензентыMikael Vidstedt
ОдобренMikael Vidstedt
Создан2011/09/15 20:00
Обновлён2015/05/11 14:25
Задача8046128

Аннотация

Ввести настройку сборки на основе autoconf (в стиле ./configure), переработать Makefile, чтобы убрать рекурсию, и задействовать JEP 139: Enhance javac to Improve Build Speed.

Цели

Основные цели, которых мы стремимся достичь:

  1. Радикально ускорить сборку
  2. Упростить исходный код системы сборки (Makefile и т. д.)
  3. Упростить работу разработчиков
  4. Получать точный и воспроизводимый результат сборки
  5. Упростить конфигурацию машин сборки (JPRT и т. д.)

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

  1. Обновить структуру Makefile
  2. Использовать autoconf (скрипт configure)
  3. Добавить поддержку параллельной компиляции Java
  4. Сделать сборку Java инкрементальной

Нам нужно как следует разобраться в существующих рабочих процессах разработчиков, чтобы свести к минимуму влияние этого изменения на всех.

Этот проект — часть более масштабной работы по улучшению инфраструктуры сборки JDK. Мы ожидаем, что вскоре за ним последуют следующие шаги. Разделение на эти шаги в некоторой степени произвольно и сделано лишь для того, чтобы быстро получить пользу от первой работы по улучшению инфраструктуры сборки JDK.

Что не является целью

Поскольку мы обновим Makefile, придав им новую структуру, несколько проблем, которые мы хотим решить в будущем, могут оказаться решёнными сами собой как следствие этого обновления. Однако в рамках этого проекта мы специально этими проблемами не занимаемся, не будем их тестировать и не даём никаких гарантий, что они будут работать правильно. (Тем не менее мы постараемся ничего не сломать из того, что работает.) К этим проблемам относятся:

  • Упростить перенос на новые платформы
  • Сделать возможной разработку JDK без подключения к сети
  • Обеспечить полноценную поддержку кросс-компиляции, включая компиляцию 32-битных двоичных файлов на 64-битных хостах
  • Улучшить обработку предупреждений

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

  • Ускорить компиляцию HotSpot
  • Обновить компиляторы
  • Поддержать проекты IDE
  • Пересмотреть механизм source drop

Критерии успеха

Простота сборки

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

  1. Получить исходный код из репозиториев Mercurial
  2. ./configure
  3. make

Скорость сборки

Скорость сборки зависит от аппаратных факторов, и улучшения будут различаться. Наша целевая конфигурация — компиляция в Linux на 8-процессорной машине. В этом случае время сборки JDK после наших улучшений должно составлять не более 33% от текущего. (Обычно это означает сокращение примерно с 15 минут до примерно 5 минут или меньше.) Амбициозная цель — время сборки JDK не более 20% (около 3 минут).

Обратите внимание, что это относится только к JDK. Сюда не входят сборка HotSpot и создание Javadoc.

Очистка Makefile

Все небольшие (<3 КБ) рекурсивные Makefile в JDK (не считая HotSpot) должны быть удалены, а их функциональность собрана в центральных Makefile. (Небольшое число Makefile само по себе не является целью, однако код, собранный в одном (или нескольких) местах, помогает получить общее представление и разобраться в нём.)

Мотивация

Сборка всего JDK выполняется неоправданно медленно. Это создаёт дополнительную нагрузку на разработчиков и системы сборки. В результате разработчики извлекают и собирают только часть исходного кода, поскольку сборка продукта целиком занимает слишком много времени.

Текущая реализация системы сборки, в которой более 350 минимальных рекурсивных Makefile разбросаны по всему продукту, затрудняет внесение изменений в систему сборки. Кроме того, при текущем решении иногда приходится обновлять Makefile лишь для того, чтобы добавить новые исходные файлы или каталоги; этого не должно требоваться.

Сегодня система сборки настраивается с помощью нескольких переменных окружения. Это расходится с популярным способом настройки системы сборки с помощью ./configure. Помимо привычности, у этого способа есть несколько преимуществ перед переменными окружения. Аргументы configure проверяются: аргумент с опечаткой приводит к ошибке, а переменная окружения с опечаткой просто игнорируется. ./configure --help выводит список доступных аргументов, тогда как получить полный список всех переменных окружения, влияющих на текущую систему сборки, почти невозможно.

Описание

Эти изменения не приведут ни к каким изменениям в собранном продукте; они затрагивают только внутренний процесс разработки.

Обновить структуру Makefile

Предыстория

Перевод старых Makefile на новую, упрощённую архитектуру станет основой для всей остальной описанной здесь работы.

Реализация

Нынешний стиль рекурсивных Makefile с одним файлом на каталог будет упразднён. Вместо этого Makefile будут находить файлы для компиляции, рекурсивно просматривая каталоги исходного кода. Файлы, которые не должны компилироваться, будут перечислены как явные исключения. Это необходимо, чтобы можно было использовать новый параллельный компилятор javac.

Код, общий для нескольких подсистем, будет храниться в новом каталоге верхнего уровня "common/make". Замысел в том, что эти общие файлы будут представлять собой библиотеку вспомогательных функций, чтобы Makefile отдельных подсистем можно было писать как можно проще и чище. Мы готовы допустить большую сложность кода в этих библиотеках, если это позволит сделать Makefile отдельных подсистем проще.

Поскольку синтаксис Makefile не обеспечивает соблюдение хороших практик программирования автоматически, мы будем особенно внимательно следить за тем, чтобы писать правильный и читаемый код.

В рамках обновления мы подготовим документ с описанием правил написания кода, которые мы сочли полезными и которым следовали в процессе переписывания, чтобы направлять будущие изменения в Makefile. Мы также подготовим документ с описанием общей архитектуры Makefile.

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

  • (пока пуст)

Совмещение старого и нового

Вероятно, можно сохранить старую систему Makefile параллельно с новой, переписанной, чтобы какое-то время у нас было два способа собрать продукт (новый и старый). Это не очень желательно, поскольку грозит дублированием кода и общей путаницей, а также лишит нас преимуществ от удаления старого. Однако сохранение старой системы или наличие простого способа её восстановить помогло бы нам управлять связанными с этим рисками.

Переход

Большинство разработчиков почти не работают с самими Makefile, поэтому больших изменений в рабочем процессе не будет.

Раньше при добавлении или удалении исходных файлов или каталогов иногда требовалось обновлять Makefile. Больше это не понадобится, и об этом нужно сообщить всем разработчикам.

Разработчикам, которые хотят изменять сами Makefile, нужно понимать общую архитектуру и используемые принципы написания кода. Это будет задокументировано, но о существовании этих документов нужно будет сообщить.

Использовать autoconf (скрипт configure)

Предыстория

Основная идея autoconf в том, что единый простой интерфейс берёт на себя «связующие» вопросы между конфигурацией системы пользователя и требованиями Makefile. Этот интерфейс — shell-скрипт ./configure.

Таким образом, у использования autoconf две стороны: создание скрипта ./configure и его использование. Скрипт configure генерируется инструментами autoconf из исходного кода в configure.ac (и сопутствующих вспомогательных файлах), написанного с использованием макросов M4. Из этого исходного кода генерируется shell-скрипт configure. Этот скрипт (хотя он и генерируется) хранится в репозитории. При каждом изменении исходного кода configure.ac скрипт configure нужно сгенерировать заново и обновить в репозитории. Чтобы заново сгенерировать configure, в системе должны быть установлены инструменты autoconf.

Однако обычному пользователю делать этого не придётся. Поскольку configure хранится в репозитории, ему достаточно запустить ./configure. Для этого инструменты autoconf не нужны. В результате получается файл config.spec в синтаксисе Makefile, который определяет детали сборки и подключается в Makefile.

Реализация autoconf

У скрипта configure три основные задачи:

  1. Убедиться, что все зависимости сборки присутствуют.
  2. Проанализировать известные различия между платформами и определить, какие из них применимы в текущей ситуации.
  3. Применить переданные пользователем аргументы, чтобы настроить сборку.

Хотя фреймворк autoconf помогает со всеми этими задачами, каждую из них нужно явно запрограммировать с учётом особенностей OpenJDK. Это означает, что нам нужно чётко понимать, какие зависимости сборки у нас на самом деле есть, какие различия нужно определять и каким образом пользователь может влиять на результат сборки.

Зависимости сборки раньше описывались в файле README.

Известные различия раньше были закодированы в Makefile или относились к «общеизвестному».

Исторически пользователь влиял на сборку с помощью переменных окружения, а их проверка находилась в Makefile.

Скрипт configure может работать как «обёртка» для старых Makefile и задавать в config.spec те же переменные, которые использовались в Makefile. В этом случае для Makefile будет почти незаметно, что переменные пришли из скрипта configure, а не от пользователя. Однако во многих случаях, вероятно, лучше выводить более «чистую» переменную и переписывать соответствующие части Makefile.

Чтобы использовать autoconf, нам нужно включить три файла из autoconf в репозиторий исходного кода JDK 8. Это файлы pkg.m4, config.guess и config.sub. Запрошено юридическое разрешение на включение этих файлов в OpenJDK. Мы считаем, что проблем быть не должно, поскольку лицензия autoconf явно составлена так, чтобы поддерживать этот сценарий (по сути, она позволяет нам распространять эти файлы как угодно, если они используются как часть скрипта configure).

Переход

Сейчас процесс сборки OpenJDK в основном выглядит так:

  1. Получить исходный код из репозитория
  2. Задать множество переменных окружения
  3. Запустить make
  4. Повторять шаги 2 и 3 каждый раз, когда нужна повторная сборка

Многие участники команды написали для этого собственные shell-скрипты и похожие решения.

Новый порядок работы со скриптами configure будет таким:

  1. Получить исходный код из репозитория
  2. Запустить ./configure, возможно, с уточняющими аргументами
  3. Запустить make
  4. Повторять шаг 3 каждый раз, когда нужна повторная сборка

Шаг 3 настолько прост, что для повторной сборки shell-скрипты не понадобятся. Однако если пользователь сильно настроил своё окружение под себя, он может захотеть написать скрипты, которые помогут запускать configure с правильными аргументами.

Нам следует подготовить таблицу соответствия старых переменных окружения новым аргументам configure.

Для обсуждения: Возможно, стоит при запуске configure/make проверять некоторые часто используемые переменные окружения старого образца и предупреждать пользователя?

Ускорение javac с помощью серверного режима с поддержкой параллельной компиляции

В рамках JEP 139: Enhance javac to Improve Build Speed мы напишем расширение javac, которое будет поддерживать параллельную компиляцию. Чтобы его использовать, нужно добавить его поддержку в makefile-файлы.

Переход

Переход компиляции Java на сервер javac не будет заметен для разработчика (кроме, конечно, значительного ускорения). План перехода не нужен.

Инкрементальная сборка Java-кода за счёт вывода зависимостей в javac

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

В рамках JEP 139: Enhance javac to Improve Build Speed мы напишем расширение javac, которое позволит выполнять инкрементальную сборку Java-кода. Чтобы его использовать, нужно добавить его поддержку в makefile-файлы.

Переход

Инкрементальная сборка будет доступна разработчикам без каких-либо специальных действий. Теоретически единственным заметным для разработчика отличием должна стать более высокая скорость перекомпиляции. Однако если генерация зависимостей завершится ошибкой или запутается, сборка может оказаться некорректной, и понадобится полная пересборка. Это очень маловероятно, однако полезно будет сообщить разработчикам об этой возможной проблеме и объяснить, как выполнить полную пересборку.

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

Альтернативы

Вместо того чтобы сделать javac по-настоящему параллельным, можно было бы параллельно запускать несколько однопоточных компиляций разных независимых Java-пакетов. Это не потребовало бы изменений в javac, но правильно написать Makefile-файлы было бы гораздо труднее, и ускорение было бы не таким значительным.

Можно было бы не переписывать Makefile-файлы, но вносить такие изменения, не приведя сначала Makefile-файлы в порядок, было бы сложной и долгой задачей.

Тестирование

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

Однако нужно убедиться, что обещание не менять итоговые бинарные файлы выполнено. В рамках этого проекта следует создать инструмент сравнения сборок, который сможет сравнить результат сборки старой системой с результатом сборки новой системой по всем значимым параметрам. Эта задача сложнее, чем кажется: две последовательные сборки, даже одной и той же системой сборки, не будут побитово идентичны из-за случайных и несущественных факторов. Чтобы такой инструмент был полезен, он должен игнорировать несущественные различия и сосредоточиться на том, что не должно меняться.

Этот инструмент следует запускать на разных платформах и для разных типов сборки, сравнивая старую и новую системы.

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

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

Для обсуждения: Следует изучить возможность добавить хотя бы какое-то базовое тестирование Makefile-файлов. Одним из видов таких тестов может быть проверка инкрементальных сборок на специально подготовленных «вредных» зависимостях. Есть ли готовый набор тестов javac, в который можно добавить такие тесты?

Риски и допущения

Удаление элементов, не относящихся к сборке

  • Риск: по ошибке удалить поддержку порядка работы или процесса, который нужен каким-то группам
  • План снижения риска: связаться со всеми группами, собрать требования
  • Резервный план: немедленно заново реализовать поддержку этого порядка работы

Проблемы на редких платформах

  • Риск: в некоторых редких случаях новая система сборки не будет работать
  • План снижения риска: до развёртывания протестировать множество сценариев (разное оборудование и ПО, для разных групп); обеспечить возможность при необходимости использовать новую и старую системы параллельно
  • Резервный план: сохранить старую систему, чтобы обе системы можно было использовать параллельно

Итоговый продукт некорректен

  • Риск: из-за изменений в сборке собираются некорректные бинарные файлы
  • План снижения риска: тщательно протестировать итоговую сборку
  • Резервный план: сохранить старую систему и использовать её, пока проблема не будет решена

Зависимости

Как уже отмечалось, этот JEP зависит от JEP 139: Enhance javac to Improve Build Speed.

Этот JEP внесёт значительные изменения в код, который также изменяется в рамках порта BSD/MacOS X. Изменения сборки, скорее всего, попадут в JDK 8 раньше, чем результаты того проекта, поэтому нам придётся учесть внесённые им изменения. Однако большинство изменений будет касаться HotSpot, который в этом проекте не рассматривается.

Будущие JEP будут опираться на этот JEP, чтобы улучшить процессы сборки HotSpot и Javadoc.

Влияние

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

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

  • Переносимость: нужно убедиться, что новая система сборки корректно работает на всех поддерживаемых платформах. По возможности её следует писать так, чтобы свести к минимуму трудозатраты на перенос на новые системы.

  • Документация: существующую документацию (например, README по сборке) нужно обновить.