Введение


В данной серии статей я хочу рассмотреть систему сборки дистрибутива buildroot и поделиться опытом её кастомизации. Здесь будет практический опыт создания небольшой ОС с графическим интерфейсом и минимальным функционалом.


Прежде всего, не следует путать систему сборки и дистрибутив. Buildroot может собрать систему из набора пакетов, которые ему предложили. Buildroot построен на make-файлах и поэтому имеет огромные возможности по кастомизации. Заменить пакет на другую версию, добавить свой пакет, поменять правила сборки пакета, кастомизировать файловую систему после установки всех пакетов? Всё это умеет buildroot.


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


Цель работы — собрать дистрибутив с live-загрузкой, интерфейсом icewm и браузером. Целевая платформа — virtualbox.


Зачем собирать свой дистрибутив? Зачастую нужен ограниченный функционал при ограниченных ресурсах. Ещё чаще в автоматизации нужно создавать прошивки. Приспосабливать дистрибутив общего назначения, вычищая лишние пакеты и превращать его в прошивку путь более трудоёмкий, чем собрать новый дистриб. Использование Gentoo тоже имеет свои ограничения.


Buildroot система очень мощная, но она ничего не сделает за вас. Она может лишь дать возможности и автоматизировать процесс сборки.


Альтернативные системы сборки (yocto, open build system и прочие) не рассматриваются и не сравниваются.


Где взять и как начать


Сайт проекта — buildroot.org. Здесь можно скачать актуальную версию и прочитать руководство. Там же можно обратиться к сообществу, есть багтрекер, mail-lists и irc-канал.


Buildroot оперирует defconfig’aми для целевой платы сборки. Defconfig — это конфигурационный файл, хранящий в себе только опции, не имеющими значения по умолчанию. Именно он определяет, что и как будет собрано. При этом можно отдельно настроить конфиги busybox, linux-kernel, uClibc, загрузчиков u-boot и barebox, но все они будут привязаны к целевой плате.
После распаковки скачанного архива или клонировании из git получаем готовый к работе buildroot. Подробно о структуре каталогов можно прочитать в руководстве, расскажу о самых важных:


board — каталог с файлами, специфичными для каждой платы. Это могут быть скрипты формирования образов системы(iso, sdcart, cpio и прочие), каталог overlay, конфиг ядер и прочее
configs — собственно defconfig платы. Defconfig — это неполная конфигурация платы. В нем хранится только отличные от дефолтных настроек параметры
dl — каталог со скачанными исходными кодами/файлами для сборки
output/target — собранная файловая система полученной ОС. В дальнейшем из нее создаются образы для загрузки/установки
output/host — host-утилиты для сборки
output/build — собранные пакеты


Конфигурирование сборки осуществляется через KConfig. Эта же система используется для сборки ядра linux. Список самых часто используемых команд (выполнять в каталоге buildroot):


  • make menuconfig — вызвать настройку сборки. Так же можно с использование графического интерфейса (make nconfig,make xconfig,make gconfig)
  • make linux-menuconfig — вызвать конфигурацию ядра.
  • make clean — очистить результаты сборки (всё что храниться в output)
  • make — собрать систему. При этом не выполняется пересборка уже собранных процессов
  • make defconfig_name — переключить конфигурацию на определенный defconfig
  • make list-defconfigs — показать список defconfig’ов
  • make source — только скачать установочный файлы, без сборки.
  • make help — вывести список возможных команд

Важные замечания и полезные советы


Buildroot не пересобирает уже собранные пакеты! Поэтому может создаться ситуация, когда потребуется полная пересборка.


Можно пересобрать отдельный пакет командой make packagename-rebuild. Например, можно пересобрать ядро linux:


make linux-rebuild

Buildroot хранит состояние любого пакета созданием .stamp-файлов в каталоге output/build/$packagename:



Следовательно, можно пересобрать root-fs и образы без пересборки пакетов:


rm output/build/host-gcc-final-*/.stamp_host_installed;rm -rf output/target;find output/ -name ".stamp_target_installed" |xargs rm -rf ; make

Полезные переменные


В buildroot есть набор переменных для удобного конфигурирования


  • $TOPDIR — корневой каталог buildroot
  • $BASEDIR — каталог OUTPUT
  • $HOST_DIR, $STAGING_DIR, $TARGET_DIR — каталоги сборки host fs, staging fs, target fs.
  • $BUILD_DIR — каталог c распакованными и собранными пакетами

Визуализация


В buildroot есть возможность по визуализации.Можно построить схему зависимостей, график времени сборки, график размера пакетов в итоговой системе. Результаты в виде pdf файлов( на выбор есть svn,png) в каталоге output/graph.


Примеры команд визуализации:


  • make graph-depends построить дерево зависимостей
  • make <pkg>-graph-depends построить дерево зависимостей конкретного пакета
  • BR2_GRAPH_OUT=png make graph-build построить график времени сборки с выводом в PNG
  • make graph-size построить график размера пакетов

Полезные скрипты


В каталоге buildroot есть подкаталог utils c полезными скриптами. Например, там есть скрипт, проверяющий корректность описания пакетов. Это может быть полезно при добавлении своих пакетов (я это сделаю позже). В файле utils/readme.txt есть описание этих скриптов.


Соберем cтоковый дистрибутив


Важно напомнить, что все операции ведутся от лица обычного пользователя, не root.
Все команды выполняются в корне buildroot. В поставке buildroot уже есть набор конфигураций под многие распространённые платы и виртуализацию.


Смотрим список конфигураций:



Переключаемся на конфиг qemu_x86_64_defconfig


make qemu_x86_64_defconfig

И запускаем сборку


make

Сборка завершается успешно, смотрим на результаты:



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


qemu-system-x86_64 -kernel output/images/bzImage -hda \   output/images/rootfs.ext2 -append "root=/dev/sda rw" -s -S

Результат — запущенная в qemu система:



Создание конфигурации собственной платы


Добавление файлов платы


Смотрим список конфигураций:



В списке видим pc_x86_64_efi_defconfig. Мы создадим свою плату, скопировав её с конфигурации:


cp configs/pc_x86_64_bios_defconfig configs/my_x86_board_defconfig

Сразу же создадим каталог платы для хранения своих скриптов, rootfs-overlay и прочих нужных файлов:


mkdir board/my_x86_board

Переключаемся на этот defconfig:


make my_x86_board_defconfig

Таким образом, теперь конфиг сборки (хранится в .config в корне каталога buildroot’а) соответствует целевой машине x86-64 legacy(bios) загрузкой.


Скопируем конфигурацию linux-kernel (пригодится в дальнейшем):


cp board/pc/linux.config board/my_x86_board/

Настройка параметров сборки через KConfig


Запускаем настройку:


make menuconfig 

Откроется окно KConfig. Есть возможность конфигурировать с графическим интерфейсом (make nconfig, make xconfig, make gconfig):



Входим в первый раздел Target Options. Здесь можно выбрать целевую архитектуру, под которую будет вестись сборка.



Build options — здесь есть различные настройки сборки. Можно указать каталоги с исходными кодами, количество потоков сборки, зеркала для скачивания исходных кодов и прочие настройки. Оставим настройки по-умолчанию.


Toolchain – здесь настраивается сам инструментарий сборки. О нем подробнее.



Toolchain type – тип используемого тулчейна. Это может быть встроенный в buildroot или внешний тулчейн (можно указать каталог с уже собранным или url для скачивания). Для разных архитектур есть дополнительные опции. Например, для arm можно просто выбрать версию внешнего тулчейна Linaro.


C library – выбор библиотеки С. От этого зависит работа всей системы. Обычно используется glibc, поддерживающая весь возможный функционал. Но она может оказаться слишком большой для встроенной системы, поэтому часто выбирают uClibc или musl. Мы выберем glibc (в дальнейшем это потребуется для использования systemd).


Kernel Headers и Custom Kernel Headers series – должно совпадать с версией ядра, которое будет в собираемой системе. Для kernel headers можно так же указать путь к тарболу или git-репозиторий.


GCC COMPILER VERSIONS – выбор версии компилятора, которая будет использована для сборки
Enable C++ support – выберем для сборки с поддержкой библиотек c++ в системе. В дальнейшем нам это пригодится.


Additional gcc options – можно задать дополнительные опции компилятора. Нам без надобности пока что.


System configuration позволяет задать будущие параметры созданной системы:



Большинство пунктов понятны из названия. Обратим внимание на следующие пункты:
Path to the users tables — таблица с создаваемыми пользователями (https://buildroot.org/downloads/manual/manual.html#makeuser-syntax).


Пример файла. Будет создан пользователь user с паролем admin, автоматически gid/uid, /bin/sh шеллом, группой по-умолчанию user, член группы root, комментарием Foo user


[alexey@alexey-pc buildroot ]$ cat board/my_x86_board/users.txt 
user -1 user -1 =admin /home/user /bin/sh root Foo user

Root filesystem overlay directories — каталог, накладываемый поверх собранной target-fs. Добавляет новые файлы и заменяет имеющиеся.


Custom scripts to run before creating filesystem images — Скрипты, выполняемые непосредственно перед сворачиванием файловой системы в образы. Сам скрипт пока оставим пустым


Перейдём в раздел Kernel



Здесь задаются настройки ядра. Само ядро конфигурируется через make linux-menuconfig.
Задать версию ядра можно по-разному: выбрать из предложенных, ввести версию вручную, указать репозиторий или готовый tarball.


Kernel configuration — путь к конфигу ядра. Можно выбрать конфигурацию по-умолчанию для выбранной архитектуры или defocnfig из Linux. В исходниках Linux есть набор defconfig’ов для разных целевых систем. НАйти нужный можно, глянув напрямую в исходники здесь. Например, для платы beagle bone black можно выбрать конфиг.


Раздел Target packages позволяет выбрать, какие пакеты будут установлены в собираемую систему. Пока оставим без изменений. Позже мы добавим свои пакеты в этот список.
Filesystem images — список образов файловых систем, которые будут собраны. Добавим iso-образ



Bootloaders — выбор собираемых загрузчиков. Выберем isolinix



Конфигурирование Systemd


Systemd становится одним из столбов linux, наравне с kernel и glibc. Поэтому вынес его настройку в отдельный пункт.


Настраивается через make menuconfig, далее Target packages > System tools > systemd. Здесь можно указать, какие службы systemd будут установлены и запущены при старте системы.



Сохранение конфигурации системы


Сохраняем этот конфиг через KConfig.


После чего сохраним наш defconfig:


make savedefconfig

Конфигурирование ядра Linux


Конфигурирование ядра linux вызывается следующей командой:


make linux-menuconfig

Добавим поддержку видеокарты Virtualbox



Добавим Virtualbox Guest integration support



Сохраняем и выходим. ВАЖНО: конфигурация сохранится в output/build/linux-$version/config, но не в board/my_x86_board/linux.config



Поэтому нужно вручную скопировать конфиг в место хранения:


cp output/build/linux-4.19.25/.config board/my_x86_board/linux.config

Этой командой я копирую ПОЛНЫЙ конфиг ядра, что нужно не всегда. Более правильный путь — сохранять defconfig ядра:


linux-update-defconfig

После чего выполним полную пересборку всей системы.Т.к. buildroot не пересобирает уже собранное, то надо вручную указать пакеты для пересборки. Чтобы не терять время и нервы, небольшую систему проще пересобрать целиком):


make clean;make

По завершении сборки запускаем VirtualBox(проверялось на версии 5.2 и 6.0) с загрузкой с cd-диска.Параметры системы:



Запуск с собранного iso:



Список использованных материалов


  1. Buildroot manual

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


  1. Punk_Joker
    19.04.2019 00:25

    output/target — правидьнее будет сказать скелет файловой системы. И стоит тогда упомянуть output/image — тут лежать коечные файлы образов.

    Путь к конфигу ядра можно указать в настройках, и сохранять командый make save-update-defconfig


    1. Boozlachu Автор
      19.04.2019 07:45

      О создании полноценной конфигурации платы, external tree и прочем — в следующей статье. От простого с ложному.
      Команды «save-update-defconfig» не существует. Наверно, имелось ввиду " make linux-update-defconfig"?

      Скелет файловый системы — не хотелось бы путаницы со skeleton. Так-то output/target и не совсем скелет. Это файловая система, в которой отсутвуют только файлы устройств(/dev ) и не настроены правана файлы.


      1. Punk_Joker
        19.04.2019 08:40

        Да, вы правы, комнада make linux-update-defconfig. Но для чего тогда использовать cp, если команда известна?


        1. Boozlachu Автор
          19.04.2019 11:07

          Честно говоря, привык хранить полный конфиг ядра.
          Поправил, добавил использование команды linux-update-defconfig
          Спасибо за комментарий.


  1. jcmvbkbc
    19.04.2019 02:47

    output/target… output/host… output/build… output/image...

    А ещё Makefile понимает параметр O= с помощью которого можно сборку вести в произвольном каталоге, в том числе за пределами buildroot.


  1. gendzo
    19.04.2019 14:00

    Спасибо за статью. Сам хотел что-то подобное написать, но времени не нашлось. Вместо root_overlay, на мой взгляд, лучше наваять скрипт post_build.sh, в котором устанавливать нужные конфиги и файлы с нужными правами с помощью install в target-fs. В Buildroot есть пункты для pre-build, post-build скриптов, которым передает корневую директорию сборки в качестве аргумента.


    1. Punk_Joker
      19.04.2019 15:35

      Можете сказать почему вы пришли к такому выводу?


      1. gendzo
        19.04.2019 16:22

        На мой взгляд, это удобнее и надежнее. Я пробовал оба варианта, скриптами процесс всякой подготовительной работы автоматизируется и переносится из одной версии buildroot в другой.
        В папке board/<ваше_имя_к_конфигам> файлы можно располагать как угодно, в своем порядке, там же держится сам скрипт. В post-build можно не только заменить файлы, но и права назначить и всяческие проверки добавить, которые устанавливают файлы в зависимости от наличия тех, или иных пакетов в итоговой сборке.
        Также можно с помощью средств шелла менять нужные строчки в дефолтных системных конфигах.
        В комментарии выше я наврал — скрипту из buildroot передается target-fs, а не корневая директория, прошу прощения.
        Ну и пример:

        install -m 0644 $BOARD_DIR/inittab $1/etc/
        

        Где: $BOARD_DIR — переменная билдрута. $1 — путь к target-fs, передается билдрутом в качестве аргумента скрипту.


        1. Boozlachu Автор
          19.04.2019 22:43
          +1

          мой опыт:
          1.roofs_overlay. Удобно когда нужно иметь файл гарантированного содержания. Который не поменяется с обновлением версий.Наглядно, понятно, новый в проекте человек сходу всё видит и понимает. Ну и порядок- сразу видно, какой файл куда попадёт.
          2. Вариант со скриптами. Удобно, что можно прогнать sed/awk и внести изменения в любой конфиг, который может поменяться с обновлением пакета.Я тоже так делаю.

          Насчет обновления buildroot, удобно хранить в external_tree свои наработки. Это избавляет лишней работы.

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

          В итоге я использую оба метода. Они оба хороши по-своему.


          1. Punk_Joker
            19.04.2019 23:55

            Да, права на каталоги выставляются нормально, как и владелецс группой.
            Можно все ложить в оверлей, а уже в пост скрипте изменить его в зависимости от условий


          1. gendzo
            20.04.2019 07:27

            Спасибо Вам за ваш труд, жду следующих статей!

            Насчет root_overlay — дело вкуса, мне кажется. Конкретно против external_tree ничего против не имею, саму строчку в конфиге buildroot считаю лишней. Особенно потому, что она заменяется строчкой cp -frP <путь_к_external_tree> $1 в скрипте. Плюсы — можно держать несколько external_tree и гибко их менять. Так и использую.

            Мне кажется, мы с Вами немного разные цели преследуем. У меня нет новичков, работаю в небольшой компании. А когда им был — скрипт для меня был понятнее :). Там и задокументировать все можно. А наработки у меня в своей системе сборки раскладываются в зависимости от платформы по соответствующим папкам в дереве проекта. Платформ несколько, и они отличаются. Мне проще в зависимости от платформы забрать свежие наработки из проекта, чем из проекта копировать в одно и то же место, создавая путаницу.


  1. Boozlachu Автор
    20.04.2019 22:12

    Резюмирую, сказав так: возможностей много, весь вопрос задач конкретного проекта и личных предпочтений.