В разделах, описывающих детали применения системы сборки, мы уже дали краткое представление о процессе разработки, средствах и принципах, которые позволяют ускорить сборку целевого продукта. Здесь мы постараемся представить весь цикл производства в обобщенном, схематичном виде так, чтобы пользователи системы сборки могли организовать собственный процесс или просто ответили себе на вопрос, с чего именно надо начинать.
Все средства и принципы, о которых мы будем говорить далее, приводятся в качестве примеров, позволяющих увидеть производственный цикл в качестве простой схемы. Какие именно средства необходимо задействовать и каких принципов придерживаться в собственном производстве, пользователям предстоит решать самостоятельно.
Development Infrastructure
На следующей схеме (рис.1) показаны основные ресурсы, которые необходимы для разработки. Здесь нас не интересует физическая организация сети, поскольку все может быть организовано с помощью различных аппаратных средств. Если речь идет о единственном разработчике, то все необходимое можно развернуть на одной персональной машине, если же речь пойдет о масштабном проекте, то, наверное, понадобится несколько серверов и, схему, показанную на рис.1, надо будет прорисовывать более детально, добавив к ней сборочные серверы и рабочие машины персонала.
Главное, что необходимо подчеркнуть, это наличие трех типов репозиториев исходного кода, а также FTP сервера, служащего для хранения исходных архивов, toolchain-ов и готовых продуктов. Для начала, рассмотрим первые два вида репозиториев, а именно группу репозиториев 3pp mirrors, которая содержит зеркала сторонних продуктов, и репозиторий sources.git.
Разработка скриптов для получения исходных архивов из сетевых источников, а также, действий по приготовлению необходимых срезов сторонних репозиториев, как удаленных, так и представленных в виде зеркал в собственной сети, ведется в репозитории sources.git. На FTP сервере находится лишь клон репозитория sources.git. На рис.1 красная стрелка с названием pull демонстрирует единственную операцию, которая необходима для обновления скриптов получения готовых к работе исходных архивов сторонних продуктов. На FTP сервере выполняются лишь две команды:
$ git pull $ make
Первая команда служит для обновления скриптов, а вторая, – для загрузки архивов или приготовления срезов репозиториев, которые, в свою очередь, должны остаться на сервере как готовые к работе исходные архивы. Приготовление срезов сторонних репозиториев происходит автоматически согласно номерам ревизий, указанным в Make-файлах. Данный процесс представлен на схеме с помощью стрелки синего цвета (take snapshot).
Здесь важно отметить, что никакие изменения в стороннем коде, на данном этапе, не производятся и, архивы содержат оригинальный авторский код.
В разделе Build System ≫ Download мы уже говорили о том, что содержание собственного хранилища сторонних архивов позволяет значительно сократить время плучения исходных текстов, непосредственно перед началом сборки продукта. Более того, постоянные изменения в сети, требуют периодического пересмотра скриптов загрузки. Поэтому процесс отслеживания новостей от сторонних разработчиков лучше всего проводить отдельно от сборки продукта. Если же говорить об автоматизации данного процесса, то можно заметить следующее. Многие проекты позволяют автоматизировать составление и обновление скриптов загрузки. Например, такие, хорошо структурированные архивы, как GNOME, KDE и X.org позволяют не только генерировать скрипты загрузки пакетов, но и автоматизировать процесс обновления версий, загружаемых пакетов. Однако в большинстве случаев, обновления приходится делать вручную.
То обстоятельство, что на FTP сервере представлены не только исходные архивы, но и Make-файлы, с помощью которых происходит загрузка архивов, весьма полезно для того, чтобы пользователь всегда имел возможность узнать, из какого источника получен архив и, какая версия исходного продукта содержится в данном архиве. Это особенно важно для архивов, содержащих срезы сторонних репозиториев, поскольку вести отдельный реестр ревизий продуктов, особенно если они представлены sha1 суммами, слишком обременительно, а здесь достаточно взглянуть на содержимое Make-файла, чтобы получить исчерпывающую информацию об архиве.
Поскольку мы заговорили о FTP сервере, необходимо сказать, что каталог toolchains, показанный на схеме (рис.1), служит для хранения toolchaon-ов, которые система сборки может загрузить автоматически пред началом работы, а каталог releases предназначен для размещения готовых продуктов.
Третий вид репозиториев, о которых мы говорили ранее, не требует специального рассмотрения, поскольку к нему относятся репозитории исходных кодов выпускаемых продуктов. На схеме (рис.1) показаны три основных репозитория: toolchains, build-system и platform. Первый содержит скрипты сборки toolchain-ов, второй, – исходные коды системы сборки и, наконец, третий представляет коды сборки дистрибутива.
Справа, на схеме (рис.1), перечислены средства, которые могут быть использованы для автоматизации процесса разработки, распространения знаний внутри коллектива разработчиков, а также, для представления результатов работы широкому кругу пользователей.
Operation Cycle
Рабочий цикл производства, как правило, состоит из подготовительных операций, работ по созданию или усовершенствованию продукта, а также работ, связанных с выпуском очередных версий продукта. Если речь идет об отдельно взятом инженере, то весь рабочий цикл может состоять из трех основных этапов:
- получение локальной копии определенной ветки исходного кода и настройки рабочего окружения;
- непосредственная работа, связанная, например, с добавлением в состав продукта очередного компонента или усовершенствование уже имеющегося;
- и, наконец, передача готового продукта на тестирование.
В своей работе мы придерживаемся правила, которое можно выразить следующими словами.
После получения локальной копии ветки репозитория, например, с помощью команды
$ svn co -r370 svn://radix.pro/platform/branches/radix-1.1 radix-1.1
всё необходимое окружение должно быть готово к работе. То есть разработчику не надо совершать никаких дополнительных действий по установке переменных окружения или загрузке дополнительных утилит на рабочую машину, включая SDK (Software Development Kit).
Начальное состояние SDK представляет собой toolchain, компиляторы которого уже готовы компоновать программы пользователя с библиотекой GNU Libc, находящейся в составе toolchain-а. Далее, по мере сборки компонентов продукта, SDK формируется и развивается в каталоге dist. Мы говорили о данном процессе во вводной статье.
Таким образом, перед тем как добавлять в состав дистрибутива новый пакет, разработчик должен, либо собрать текущий дистрибутив целиком, либо собрать поддерево пакетов, от которых зависит новый пакет.
Здесь необходимо озвучить еще один базовый принцип исходных текстов платформы Radix.Linux, состоящий в том, что перейдя в любой из каталогов и выполнив команду
$ make
мы, фактически, запускаем процесс сборки всех пакетов, от которых зависит пакет, сценарий сборки которого находится в выбранном нами каталоге.
Сценарии сборки полного набора пакетов, как правило, размещены в каталоге products. Например, Make-файл products/base/Makefile содержит сценарий сборки базового набора, достаточного для получения рабочей системы без X сервера и каких-либо оконных менеджеров. Если рассмотреть данный Make-файл, то в нем не окажется никаких действий, а лишь декларация списка зависимостей. Если сравнивать подобные Make-файлы с другими сисемами сборки, то первыми кандидатами для сравнения будут OpenEmbedded layers, если же говорить о пакетах других дистрибутивов, то, наверное, можно вспомнить о metapackages, которые, не являясь самостоятельными пакетами, представляют целые наборы.
Что касается системы сборки, то и здесь, от пользователя не требуется никаких подготовительных операций, поскольку перед тем как начинать процесс сборки, система сама инициализирует все, что необходимо в каталоге build-system.
В тех случаях, когда пользователю необходимо самостоятельно инициализировать систему сборки и определить перечень целевых устройств, отличный от того, что задан по умолчанию, он может выполнить комаду Make в каталоге build-system так, как описано в разделе Choose Hardware Targets.
Относительно получения toolchain-ов, необходимых для сборки, мы также предусмотрели частичную автоматизацию их загрузки, о которой говорится в разделе Getting Toolchains.
Итак, предположим, что разработчику необходимо добавить очередной сценарий сборки какого-либо пакета и, при этом, еще не известен полный список зависимостей добавляемого пакета. В этом случае, наверное, лучшим решением будет сборка всего дистрибутива с целью получения наиболее полного SDK в каталоге dist. По окончании сборки, разработчик сможет приступить к своей непосредственной работе и далее уточнить список зависимостей нового пакета на практике.
На схеме (рис.2) показан полный цикл работы, начиная с получения локальной копии ветки и заканчивая выпуском готового продукта.
Рассмотрим теперь действия, которые необходимы для интеграции нового сценария в систему, полагая, что на FTP сервере уже приготовлен исходный архив пакета. Напомним здесь, что хранилище исходных архивов не обязательно должно располагаться на FTP сервере. Для указания общего источника архивов служит переменная DOWNLOAD_SERVER, о которой мы говорили в разделе, посвященном получению системы сборки в собственное распоряжение.
Прежде всего, необходимо обеспечить загрузку исходного архива и, при необходимости, обеспечить создание patch-файлов. Для этого, в каталоге sources, необходимо определить новый сценарий загрузки. Сделать это не трудно, достаточно выбрать подходящий, уже готовый сценарий, скопировать его в выбранный каталог и отредактировать на предмет указания точного адреса архива на FTP сервере.
После этого, воспользовавшись тем же принципом copy-paste, создать сценарий сбрки пакета и указать путь к исходному архиву в списке SOURCE_REQUIRES. Более детально ознакомиться с процессом приготовления сценария сборки пакета можно в разделе User's Makefiles, а информацию о структуре самого пакета и процессе его инсталляции, как во временную корневую файловую систему, так и на целевую машину, можно получить в разделе Package Tools.
Поскольку здесь мы говорим об упрощенном представлении рабочего цикла, останавливаться на деталях, наверное, не надо, а вот напомнить об общих действиях, связанных с процессом разработки и отладки приложений, необходимо.
Обычно, работая непосредственно с исходным пакетом, пользователи применяют следующие команды
$ ./configure $ make $ make install
для того, чтобы инсталлировать пакет в систему, и команду
$ make uninstall
для деинсталляции пакета.
Кроме того, существует возможность создания архива исходного пакета, для его отчуждения:
$ make dist
Если же пользователю необходимо привести исходные тексты в исходное состояние, которое пакет имел сразу после распаковки до выполнения команды
$ ./configure
то ему необходимо воспользоваться командой
$ make distclean
Одним словом, существует стандартный набор действий, к которому уже привыкли миллионы польователей и разработчиков открытых программных продуктов.
Работая с системой сборки Radix.Linux, пользователи остаются в привычном окружении и имеют, по сути, привычный набор комманд, с помощью которых осуществляется сборка и последующая инсталляция пакетов.
Разумеется, существуют определенные отличия, связанные с тем, что работа идет в кросс-окружении и целевой системой является не машина разработчика, а операционная среда, образ которой создается в отдельном каталоге.
Так действия, совершаемые с помощью привычной последовательности комманд:
$ ./configure $ make $ make install
попадают в категорию BUILD_TARGETS (см. раздел Main Targets) и описываются разработчиком в Make-файле, представляющем собой сценарий создания пакета. При этом последняя команда
$ make install
предназначена для инсталляции результатов сборки не в целевую файловую систему, а во временный каталог, который предназначен для формирования отчуждаемого пакета. Пример такой инсталляции можно видеть в разделе User's Makefile ≫ Installation.
Кроме того, разработчик самостоятельно решает как доставить необходимые компоненты в SDK (cм. применение функции install-into-devenv в разделе User's Makefile ≫ Installation), развивающийся в каталоге dist.
Далее, после создания отчуждаемого пакета, вступают в силу действия, попадающие в категории PRODUCT_TARGETS и ROOTFS_TARGETS, последняя из которых, связана с инсталляцией готового пакета во временный каталог, в котором разворачивается образ целевой файловой системы. Инсталляция пакета, при этом, осуществляется с помощью тех же утилит, и таким же образом, как если бы мы делали это непосредственно на работающей машине.
Цель PRODUCT_TARGETS, по сути, предназначена для простого копирования пакета в каталог, предназначенный для выпуска всего продукта.
Во время работы сценария, описанного выше, разумеется, никакой интерактивности нет и система сборки не предоставляет явного механизма пошагового исполнения сценария. Однако во время отладки, разработчик может остановить процесс в любом месте сценария, добавив команду
$ exit 1
в любую последовательность команд, отвечающую за выполнение той или иной цели, в понимании языка GNU Make.
По окончании процесса сборки, разработчику могут понадобиться средства по деинсталляции пакета и очистки SDK от любых следов, оставленных во время инсталляции.
Именно для этих целей, система сборки предусматривает команды
$ make local_dist_clean $ make local_rootfs_clean
первая из которых очищает SDK и удаляет созданный пакет из поставки, размещаемой в каталоге dist/products (см. раздел Target Products Directory), а вторая служит для деинсталляции пакета из временной корневой файловой системы, находящейся в каталоге dist/rootfs (cм. раздел Target Root File System Directory).
Таким образом, в распоряжении разработчика имеются все необходимые средства для отладки создаваемого пакета.
Следует отметить, что в любом каталоге, где присутствует Make-файл, пользователь может выполнить команду
$ make help
которая предоставит информацию о доступных действиях и, кроме того, покажет список имен целевых устройств, для которых предназначен данный сценарий.
По завершению всех работ и окончательной сборки всего дистрибутива, в каталоге dist/products будет находиться готовый продукт, отчуждение которого можно осуществить простым копированием содержимого на FTP сервер так, как показано на схеме рис.2 с помощью стрелки синего цвета под названием upload release.
Porting
В данном разделе мы рассмотрим процесс портирования систем, основанных на ядре Linux, поскольку в случае создания программного обеспечения микроконтроллеров, основанного на собственном ядре, данный процесс будет представлять собой совершенно другой набор действий, целиком зависящий от идеологии создаваемого продукта.
Портирование платформы Radix.Linux на новое устройство можно разделить на три этапа:
- создание и подключение toolchain-а к системе сборки;
- приготовление ядра и загрузчика;
- перенос всех пакетов, входящих в сборку.
Последние два этапа можно производить независимо друг от друга и в любом порядке.
Так же, к вопросу портирования можно отнести переход на новую версию компилятора и библиотеки GNU Libc, и этот процесс будет похож на третий этап портирования, где осуществляется настройка сценариев сборки пакетов на новую архитектуру или устройство.
Допустим, что toolchain уже собран и подключен к системе сборки, так как описано в разделе Connection of the New Toolchain to Build System и рассмотрим процесс переноса пакетов на новую архитектуру, не затрагивая вопросы сборки ядра и загрузчика.
Base Packages
В первую очередь, необходимо собрать пакет base/pkgtool, который представляет собой копию утилит управления пакетами и необходим для работы на целевой машине. Практически все пакеты платформы зависят от пакета base/pkgtool, однако существуют пакеты, которые должны быть собраны до начала сборки base/pkgtool.
К таким пакетам относится пакет base/radix-system представляющий скрипты старта и останова операционной системы. Кроме того, для упрощения инсталляции системы на целевой носитель, подключенный к машине разработчика, собирается пакет base/install-tools. Этот пакет, помимо инсталляции в каталог /usr/share в качестве дополнительного средства, копируется напрямую в каталоги dist/progucts/$(TOOLCHAIN)/$(HARDWARE). В разделе Install Tools дана инструкция по применению утилит, входящих в состав данного пакета.
Еще одним специальным пакетом, является пакет base/init-devices. Этот пакет не инсталлируется в систему, он используется системным инсталлятором для создания файлов устройств в каталоге /dev, которые необходимы до того, как будет развернута файловая система /dev. Назначение пакета base/init-devices, более детально, рассматривается в разделе Devices Table.
Итак, приготовление базовых пакетов для новой архитектуры заключается в том, чтобы включить в список COMPONENT_TARGETS новое устройство. Проделать это нужно во всех Make-файлах каталога base. Например, для включения платы Nit6Q в списки COMPONENT_TARGETS, необходимо добавить следующую строку в начало каждого Make-файла.
COMPONENT_TARGETS += $(HARDWARE_NIT6Q)
Разумеется, если новое устройство подразумевает наличие дополнительных файлов устройств в каталоге /dev или имеет некоторые особенности инициализации системы, нам необходимо уточнить процедуры создания пакетов base/init-devices и base/radix-system.
После того, как все необходимые изменения будут сделаны, можно собрать базовые пакеты. Для этого надо перейти в каталог base/pkgtool и выполнить команду Make
$ make
которая, в отсутствие дополнительных аргументов, приведет к сборке базовых пакетов для всех устройств. Напомним, что для сборки пакетов на одно выбранное устройство, необходимо указать явно имя этого устройства
$ HARDWARE=nit6q make
так, например, как описано в разделе First Package.
GNU Libc
Перед тем как начинать сборку GNU Libc, необходимо инсталлировать библиотеки компилятора и заголовочные файлы ядра Linux в кросс-окружение. Для этого служат пакеты dev/gcc-runtime и сценарий dev/kernel-headers, для которого надо создать новый пакет, например, dev/kernel/nit6q/3.14.28-20160304-headers. Создание последнего достаточно просто, если воспользоваться примерами, находящимися в соседних каталогах.
Портирование библиотеки GNU Libc, в общем случае, осуществляется также, путем добавления новой цели в начало Make-файлов. Здесь необходимо проследить цепочку межпакетных зависимостей от пакета zoneinfo до, непосредственно, пакета glibc определенной версии. Для этого, например, надо рассмотреть Make-файлы в каталогах
libs/glibc/2.23 libs/glibc/2.23-i18n libs/glibc/2.23-x86_32 libs/glibc/2.23-zoneinfo
Поскольку все параметры, необходимые для настройки на целевую машину уже определены при подключении toolchain-а к системе сборки, процесс портирования библиотеки GNU Libc не представляет особых сложностей, и пользователи могут самостоятельно осуществить необходимые действия.
В заключение, необходимо собрать библиотеки
libs/zlib libs/gmp libs/mpfr libs/mpc libs/isl
на новую архитектуру точно так же, добавляя в соответствующие Make-файлы новое целевое устройство. Разумеется, порядок сборки надо сверять со списками REQUIRES, в начале каждого Make-файла.
GNU Compiler Collection
Если мы хотим, чтобы в целевой системе присутствовали средства разработки программ, то следующим шагом будет сборка GNU Binutils и компиляторов, входящих в GCC.
Здесь, в отличие от предыдущего опыта, нам понадобится немного б'ольшая внимательность.
Сценарии сборки dev/binutils и dev/gcc, помимо списка целевых устройств в начале Make-файла, содержат списки управлений, применяемых на этапе конфигурирования, которые должны соответствовать управлениям, которые были применены на этапе создания toolchain-а. Условные операторы, напрмер,
ifeq ($(TOOLCHAIN),$(TOOLCHAIN_IMX6_GLIBC)) . . . endif
ограничивают списки управлений, соответствующих определенным toolchain-ам.
В каталоге dev/gcc начиная с версии 5.3.0, из состава собираемой коллекции, исключен компилятор языка Java. Обусловлено это тем, что на данной стадии сборки системы, мы еще не имеем библиотеки Gtk и, следовательно, не можем обеспечить полную функциональность комплекта Java. В дальнейшем, после сборки библиотеки Gtk, недостающий пакет мы будем собирать в каталоге dev/gcc-java.
All Packages
В предыдущих параграфах мы описали последовательность сборки тех пакетов, которые требуют детальной проработки. Остальные пакеты, входящие в состав дистрибутива, в большинстве своем, не требуют такого ручного переноса, если, конечно, нет необходимости вмешательства в их настройку. Поэтому перенос остальных пакетов можно автоматизировать, а их отладку производить по мере сборки всей системы.
Отметим здесь несколько пакетов, которые требуют более внимательного отношения. К таким пакетам, например, относится app/mpg123, где некоторые части кода могут выбираться исходя из целевой архитектуры и, их выбор определяется специальными параметрами скрипта configure. Кроме того, следует обратить внимание на цепочки пакетов, представляющих драйверы устройств или библиотеки, взаимодействующие с аппаратными средствами, такими, например, как 3D ускорители. Как правило, такие пакеты расположены в каталоге hal (Hardware Abstraction Layer) или непосредственно взаимодействуют с пакетами, находящимися в каталоге hal. К числу таких пакетов можно отнести драйверы и библиотеки, сборка которых осуществляется в каталогах
hal/drivers/ci20/sgx-ddk/3.18 X11/libs/ci20-sgx-um
и которые поставляют средства для использования графического ускорителя PowerVR на плате MIPS Creator CI20. Разумеется, подобные Make-файлы нельзя редактировать в автоматическом режиме.
Остальные сценарии позволяют автоматизировать процесс портирования. Так, например, для того чтобы добавить новое целевое устройство Nit6Q в список COMPONENT_TARGETS всех Make-файлов платформы, можно воспользоваться поточным редактором sed, как показано на следующем листинге.
#!/bin/sh # # Add following TARGETS to each Makefile # # COMPONENT_TARGETS += $(HARDWARE_NIT6Q) # # before OMAP5UEVM. # # NOTE: this script works only if OMAP5UEVM defined and defined by += assign. # for file in `find . -iname 'Makefile' -exec echo {} \;` ; do already_done="`cat $file | grep -n '^COMPONENT_TARGETS[ \t]*+=[ \t]*\$(HARDWARE_NIT6Q)' | cut -f2 -d':'`" if [ -z "$already_done" ] ; then line="`cat $file | grep -n '^COMPONENT_TARGETS[ \t]*+=[ \t]*\$(HARDWARE_OMAP5UEVM)' | cut -f2 -d':'`" lnum="`cat $file | grep -n '^COMPONENT_TARGETS[ \t]*+=[ \t]*\$(HARDWARE_OMAP5UEVM)' | cut -f1 -d':'`" if [ ! -z "$line" ] ; then echo "FOUND: $file: $lnum: $line" sed -i "$lnum i\ COMPONENT_TARGETS += \$(HARDWARE_NIT6Q)" $file fi fi done
Данный скрипт добавляет строку
COMPONENT_TARGETS += $(HARDWARE_NIT6Q)
в начало каждого Make-файла исходного дерева, разумеется, при условии, что данная строка изначально не была представлена в данном файле. Кроме того, строка добавляется лишь в те Make-файлы, которые содержат строку
COMPONENT_TARGETS += $(HARDWARE_OMAP5UEVM)
что предостерегает нас от добавления нового устройства в те файлы, которые в принципе не предназначены для сбоки интересующих нас пакетов. В основном это касается Make-файлов каталога sources, однако, в любом случае, применять подобные скрипты надо с особой осторожностью, чтобы в последствии не пришлось просматривать большое количество Make-файлов с целью поиска ошибочных нововведений.
В заключение, важно отметить, что мы рассмотрели лишь схему или просто порядок работ по портированию платформы. Вопросы, связанные с портированием отдельных пакетов, например такие, как состав пакета на целевой системе, настройка кросс-окружения после копирования компонентов в каталоги dist/.$(TOOLCHAIN)/$(HARDWARE) и другие, подобные тем, что рассматривались в разделе Installation, нами сознательно опущены, поскольку требуют более детального рассмотрения и более строгой привязки к отдельно взятым пакетам и, более того, зависят от требований, предъявляемых к операционной системе, в целом.
В большинстве случаев, пользователям будет необходимо самостоятельно решать данные вопросы исходя из собственных требований к целевой системе и кросс-окружению. Мы здесь можем лишь сослаться на то, что в системе Radix.Linux уже накоплен достаточный опыт, и пользователи могут сверять свои действия с примерами уже готовых Make-файлов.