Язык Kconfig
Введение
База конфигурации это сборник конфигурационных параметров выстроенных в виде древовидной структуры:
+- Code maturity level options
| +- Prompt for development and/or incomplete code/drivers
+- General setup
| +- Networking support
| +- System V IPC
| +- BSD Process Accounting
| +- Sysctl support
+- Loadable module support
| +- Enable loadable module support
| +- Set version information on all module symbols
| +- Kernel module loader
+- ...
Каждая запись имеет свои зависимости. Эти зависимости используются для определения "видимости" записи. Дочерние записи "видимы" только тогда, когда их родительская запись тоже видна.
"Видимость" записи определяет будет ли она предложена пользователю для настройки или нет.
Записи меню
Большинство записей представляют собой какой-либо параметр конфигурации; все остальные существуют для того, чтобы структурировать их. Единичная параметр конфигурации определяется следующим образом:
config MODVERSIONS
bool "Set version information on all module symbols"
depends on MODULES
help
Usually, modules have to be recompiled whenever you switch to a new
kernel. ...
Каждая строка начинается с ключевого слова и далее содержит от одного до нескольких аргументов. config
создает новый параметр конфигурации. Все остальные строчки определяют атрибуты для параметра. Атрибутами могут быть: тип параметра, запрос ввода для пользователя (input prompt), зависимости, вспомогательный текст и значения по умолчанию. Параметр конфигурации может быть определён несколько раз с одним и тем же именем, но каждое определение должно иметь только один атрибут описывающий запрос ввода для пользователя (input prompt), а так же типы параметров не должны конфликтовать (повторяться).
Атрибуты меню
Меню-запись может иметь несколько атрибутов. Но не все они применимы повсеместно (смотрите синтаксис).
-
Тип параметра:
bool
|tristate
|string
|hex
|int
Все параметры конфигурации должны иметь тип. Есть два б азовых типа:
tristate
иstring
; все остальные основываются на них. Определение типа также опционально принимает и запрос ввода для пользователя, так что вот эти два примера эквивалетны:bool "Networking support"
и:
bool
prompt "Networking support" -
Запрос ввода:
prompt <prompt> [“if” <expr>]
Все меню-записи могут иметь максимум один атрибут
prompt
, который отобразится пользователю. При необходимости зависимости только для этого атрибута могут быть добавлены черезif
. -
Значение по умолчанию:
default <expr> [“if” <expr>]
Параметр конфигурации может иметь сколько угодно дефолтных значений. Если "видимость" включена у нескольких дефолтов, то использоваться будет тот, который определен раньше. Значения по умолчанию не ограничены в рамках меню-записи, в которой они определены. Это значит, что дефолт может быть определён где-то ещё или быть переопределен более ранним дефолтом. Дефолтные значения назначаются только в случае если пользователь не указал своего (через запрос ввода). Если запрос в вода виден, то дефолтное значение показывается пользователю и может быть измененно им. При необходимости зависимости только для дефолтного значени могут быть добавлены через
if
.Дефолтным значением для значения по умолчанию умышленно установленно "n" во избежание радувания сборки. С несколькими исключениями новые параметры конфигурации не должны менять этого. Смысл в том чтобы
make oldconfig
вносил как можно меньше изменений в конфигурацию от релиза к релизу.к сведениюdefault y/m
должно быть указано у параметров, которые:-
Параметр для чего-то, что всегда должно быть включено в сборку, должен иметь
default y
. -
"gatekeeping" параметр, который скрывает/показывает другие параметры (но сам не генерирует какой-либо код), должен быть
default y
чтобы люди могли видеть эти параметры. -
Параметр реализует поведение в спомогательного драйвера или аналогичные параметры для драйвера, который сам по умолчанию выключен (
default n
). Это позволяет вам устанавливать разумные дефолты. -
Взаимодействие с аппаратным обеспечением или инфраструктурой, которое все ожидают. Например:
CONFIG_NET
илиCONFIG_BLOCK
. Это редкие исключения.
-
-
Определение типа вместе с значением по умолчанию:
"def_bool"/"def_tristate" <expr> ["if" <expr>]
Быстрый способ сразу определить и тип и дефолт к нему. При необходимости зависимости для этого атрибута могут быть добавлены через
if
. -
Зависимости:
depends on <expr>
Этот атрибут определяет зависимости для меню-записи. Если несколько зависимостей определенно – они связываются с помощью логического "И &&". Зависимости также применяются ко всем параметрам в рамках меню-записи (которые также работают с
if
), поэтому следующие два примера эквивалентны:bool "foo" if BAR
default y if BARи:
depends on BAR
bool "foo"
default y -
Обратные зависимости:
“select” <symbol> [“if” <expr>]
От авторов переводаВ данном контексте слово "symbol" (символ) относится к параметру конфигурации.
Когда обычные зависимости уменьшают верхний предел символа (см. ниже), обратные зависимости могут быть использованы для принудительного установления нижнего лимиты другуг символа (см. ниже). Значение текущего меню символа используется, как минимальное значение, на которое может быть установлен
<symbol>
. Если<symbol>
выбран несколько раз, то предел устанавливается на наибольшее значение из выбранных. Обратные зависимости могут использоваться только сboolean
илиtristate
символами.к сведениюselect
'ом нужно пользоваться аккуратно. Он принудительно назначит символу значение без проверки зависимостей. Злоупотребляяselect
'ом вы можете "выбрать" символ FOO даже если FOO зависит от BAR, который на назначен. Подытожив, используйте select только для "невидимых" символов (не требующих пользовательского ввода) и сомволов без зависимостей. Это ограничит полезность этого атрибута, но и обезопасит позволит избежать "незаконных" конфигураций.Если после
select <symbol>
идётif <expr>
, то<symbol>
будет выбран при логическом И значения текущего меню символа и<expr>
. Это означает, что нижний предел может быть понижен из-за наличияif <expr>
. Такое поведение может показаться странным, но мы на него полагаемся (Будущее такого поведения пока неопределенно).От авторов переводаПример работы
select
:config A
bool "Option A"
select B if C
config B
bool "Option B"
config C
bool "Option C"Если параметр A будет включен пользователем, то он также включит B если C тоже включено.
-
Слабые обратные зависимости:
imply <symbol> [“if” <expr>]
Также как и
select
этот атрибут устанавливает нижний лимит для другого символа за исключением того, что этот символ всё равно может быть установлен в "n" от своей прямой прямой зависимости или от "видимого" пользовательского ввода.Пример:
config FOO
tristate "foo"
imply BAZ
config BAZ
tristate "baz"
depends on BARВозможны следующие значения:
FOO BAR дефолты для BAZ выборы для BAZ n y n N/m/y m y m M/y/n y y y Y/m/n n m n N/m m m m M/n y m m M/n y n * N Это полезно например с несколькими драйверами, которые хотят показать свою возможность подключаться к вторичной подсистеме, позволяя пользователю конфигурировать эту подсистему без нужды отключать эти драйвера.
к сведениюЕсли комбинация FOO=y и BAZ=m приводит к ошибке компоновки, вы можете защитить вызов функции с помощью IS_REACHABLE():
foo_init()
{
if (IS_REACHABLE(CONFIG_BAZ))
baz_register(&foo);
...
}к сведениюЕсли фунционал представляемы BAZ очень желатален для FOO, то FOO должен
imply
не только BAZ, но так же и BAR:config FOO
tristate "foo"
imply BAR
imply BAZк сведениюЕсли
imply <symbol>
указан вместе сif <expr>
, то дефолтное значение<symbol>
будет выбрано при логическом И текущего меню символа и<expr>
(Будущее такого поведения пока неопределенно). -
Ограничение отображение меню блоков:
visible if <expr>
Этот атрибут может использоваться только в меню блоках. Если условие "false", то блок не будет показан пользователю (символы находящиеся в нём всё равно смогут быть "выбраны" другими символами). Поведение идентично
prompt
'у с условием для меню-записей. Значение по умолчанию дляvisible
– "true". -
Числовые диапазоны:
range <symbol> <symbol> [“if” <expr>]
Атрибут позволяет ограничить диапазон возможнных значений полученных от пользовательского ввода для
int
иhex
символов. Пользователь может только ввести значение, которое больше или равно первого символа, и меньше или равно второму. -
Вспомогательный текст:
help
Атрибут назначает вспомогательный текст. Конец текста определяется уровнем отступа, то есть текст заканчивается на первой строке, которая имеет меньший отступ, чем первая строка текста помощи.
От авторов переводаПример:
config EXAMPLE
bool "Пример параметра"
help
Вспомогательный текст.
Он помогает
# А этот текст уже не входит в вспомогательный,
# так как его отступ меньше чем у первой строки вспомогательного. -
Атрибут модуля:
modules
Атрибут объявляет символ, который будет использоваться в качестве символа MODULES, что включает третье модульное состояние для всех символов конфигурации. Не более одного символа может иметь установку опции
modules
.От авторов переводаmodules
позволяет "разблокировать" состояние модуляm
для символов с типомtristate
, что будет означать, что символ выбран для сборки, как модуль.Изначально у
tristate
есть три состояния:y
,n
,m
, но выбрать можно только первые два.
Зависимости меню
Зависимости определяют "видимость" меню-записи и могут также уменьшать диапазон возможных значений, которые пользователь может ввести для tristate
символов. Используемая в выражениях tristate
логика используется на одно состояние больше, чем может использовать boolean
логика, для выражения состояния модуля. Выражения зависимостей имею следующий синтаксис:
<expr> ::= <symbol> (1)
<symbol> '=' <symbol> (2)
<symbol> '!=' <symbol> (3)
<symbol1> '<' <symbol2> (4)
<symbol1> '>' <symbol2> (4)
<symbol1> '<=' <symbol2> (4)
<symbol1> '>=' <symbol2> (4)
'(' <expr> ')' (5)
'!' <expr> (6)
<expr> '&&' <expr> (7)
<expr> '||' <expr> (8)
Выражения перечислены в порядке убывания приоритета.
- Перевод ит символ в выражение.
boolean
иtristate
символы легко переводятся в соответствующие значения выражений. Все остальные типы переводятся вn
. - Если значение обоих символов одинаково, то вернётся
y
, в обратном случаеn
. - Если значение обоих символов одинаково, то вернётся
n
, в обратном случаеy
, - Если значение <symbol1> меньше, больше, меньше или равно, либо больше или равно значению <symbol2>, возвращается 'y', в противном случае — 'n'.
- Возвращает значение выражения. Используется для переопределения порядка выполнения операций..
- Возвращает результат выражения (2-/expr/).
- Возвращает результат выражения min(/expr/, /expr/).
- Возвращает результат выражения max(/expr/, /expr/).
Выражение может иметь значения: n
, m
или y
(или 0, 1, 2 соответственно). Меню-запись становится "видимой", когда выражения принимает значения m
или y
.
Существует два типа символов: константные и не константные символы. Не константные символы наиболее часто используемые и обозначаются ключевым словом config
. Не константные символы полностью состоят из буквенно-цифровых символов или символов подчеркивания. Константные символы используются только как части выражений. Константные символы всегда окружены двойными или одинарными кавычками. Внутри кавычек допускается использование любых других символов, а сами кавычки можно экранировать с помощью ‘'.
Структура меню
Позиция у меню-записи в дереве может быть задана двумя способами. Первый – явное указание:
menu "Network device support"
depends on NET
config NETDEVICES
...
endmenu
Все записи в menu ... endmenu
блоке превращаются в подменю для меню "Network device support". Все подзаписи наследуют зависимости меню-записи, например, это означает, что зависимость "NET" добавляется в список зависимостей параметра конфигурации NETDEVICES
.
Второй способ сгенерировать структуру меню – анализировать зависимости. Если меню-запись каким-то образом зависит от предыдущей записи, то она может превратиться в подменю этой записи. Во-первых, предыдущий (родительский) символ должен быть частью списка зависимостей, и затем одно из следующих двух условий должно быть выполнено:
- дочерняя запись должна стать "невидимой" если родительск ой назначено значение
n
- дочерняя запись должна быть "видимой" только если родительская тоже "видимая":
config MODULES
bool "Enable loadable module support"
config MODVERSIONS
bool "Set version information on all module symbols"
depends on MODULES
comment "module support disabled"
depends on !MODULES
MODVERSIONS
напрямую зависит от MODULES
, это означает, что он "виден" только тогда когда MODULES
имеет отличное от n
значение. С другой стороны комментарий виден только когда значение MODULES
равняется n
.
Синтаксис Kconfig
Конфигурационный файл описывает серии меню-записей, где каждая новая линия начинается с ключевого слова (кроме вспомогательных текстов). Следующие ключевые слова завершают меню-записи:
- config
- menuconfig
- choice/endchoice
- comment
- menu/endmenu
- if/endif
- source
Первые пять также начинают определение меню-записи.
config
:
"config" <symbol>
<config options>
Это определяет конфигурационный символ <symbol>
и принимается все описанные выше отрибуты.
menuconfig
:
"menuconfig" <symbol>
<config options>
Тоже самое, что и обычная конфигурационная запись выше, но также даёт подсказку front end'ам, что все подпараметры должны быть выведены отдельным списком. Для того чтобы убедиться в том, что все подпараметры будут действительно показаны под записью menuconfig
, а не вне её, каждая запись из списка <config options>
должна зависеть от menuconfig
. На практике достичь этого можно двумя способами:
Под front end'ами тут подразумеваются интерфейсы или инструменты, которые пользователь использует для конфигурирования системы.
(1):
menuconfig M
if M
config C1
config C2
endif
(2):
menuconfig M
config C1
depends on M
config C2
depends on M
В следующих прим ерах (3) и (4), C1
и C2
до сих пор имеют зависимость от M
, но они не появятся под menuconfig M
из-за того, что C0
не зависит от M
.
(3):
menuconfig M
config C0
if M
config C1
config C2
endif
(4):
menuconfig M
config C0
config C1
depends on M
config C2
depends on M
choices
:
"choice"
<choice options>
<choice block>
"endchoice"
Определяет группу выбора и принимает любые из указанных выше атрибутов в качестве параметров. Выбор может быть только типа bool
или tristate
. Если тип не указан для выбора, его тип будет определен типом первого элемента выбора в группе или останется неизвестным, если ни у одного из элементов выбора также не указан тип.
В то время как bool choice
позволяет выбрать только одну конфигурационную запись, tristate choice
в дополнение к этому позволяет установить любое количество конфигурационных записей в значение m
. Это может быть полезно, если существует несколько драйверов для одного оборудования, и только один драйвер можно скомпилировать/загрузить в ядро, но все драйверы могут быть скомпилированы как модули.
Пример bool choice
:
choice
bool "Build type"
config DEBUG
bool "Debug build"
config RELEASE
bool "Release build"
endchoice
В этом примере пользователь может выбрать либо DEBUG
, либо RELEASE
. Один из них обязательно будет выбран, а другой будет выключен.
Пример tristate choice
:
choice
tristate "Network driver"
config WIFI
tristate "Wi-Fi driver"
config ETHERNET
tristate "Ethernet driver"
config BLUETOOTH
tristate "Bluetooth driver"
endchoice
В этом примере каждая опция (WIFI
, ETHERNET
, BLUETOOTH
) может быть включена полностью y
, установлена как модуль m
или отключена n
.
comment
:
"comment" <prompt>
<comment options>
Определяет комментарий, который высвечивается пользователю во время процесса конфигурации, а также отражается в выходных файлах. Единственные возможные опции — это зависимости.
menu
:
"menu" <prompt>
<menu options>
<menu block>
"endmenu"
Определяет блок меню (см. "Структура меню" выше для большей информации). Единственные возможные опции – это зависимости и атрибуты "видимости".
if
:
"if" <expr>
<if block>
"endif"
Определяет блок "если". Выражение зависимости <expt>
прикрепляется ко всем вложенным меню-записям.
source
:
"source" <prompt>
Читает указанный конфигурационный файл. Этот файл всегда анализируется (парсится).
mainmenu
:
"mainmenu" <prompt>
Устанавливает заголовок окна программы конфигурации, если она решит его использовать. mainmenu
должно быть размещено в начале конфигурации, перед любыми другими инструкциями.
‘#’ Комментарий в исходном файле Kconfig:
Где-либо находящийся и не окружённый кавычками '#' в исходном файле означает начало комментария. Остальная часть этой строки является комментарием.
Советы по Kconfig
Это сборник советов по Kconfig, большинство из которых не очевидны с первого взгляда и многие из которых стали идиомами в нескольких файлах Kconfig.
Добавление общих функций и возможность настройки их использования.
Распространённой практикой является реализация функции/функциональности, которая актуальна для некоторых архитектур, но не для всех. Рекомендуемый способ сделать это — использование переменной конфигурации с именем HAVE_*
, которая определяется в общем файле Kconfig и выбирается соответствующими архитектурами. Примером может служить общая функциональность IOMAP.
В lib/Kconfig мы можем увидеть следующее:
# Generic IOMAP применяется для ...
config HAVE_GENERIC_IOMAP
config GENERIC_IOMAP
depends on HAVE_GENERIC_IOMAP && FOO
А в lib/Makefile следующее:
obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
Для каждой архитектуры использующей общую функциональность IOMAP мы увидим следующее:
config X86
select ...
select HAVE_GENERIC_IOMAP
select ...
Мы используем существующий параметр конфигурации и избегаем создания новой переменной конфигурации для select
'а HAVE_GENERIC_IOMAP
.
Использование внутренней переменной конфигурации HAVE_GENERIC_IOMAP
введено для преодоления ограничения команды select
, которая принудительно устанавливает параметр конфигурации в значение y
игнорируя при этом его зависимости. В нашем случае зависимости HAVE_GENERIC_IOMAP
переносятся на символ GENERIC_IOMAP
, и это позволяет избежать ситуации, когда команда select
принудительно устанавливает значение символа в y
.
Ещё раз про это только другими словами.
Проблема:
Предположим, что у вас есть опция GENERIC_IOMAP
, которая зависит от нескольких других опций или условий, например:
config GENERIC_IOMAP
bool
depends on ARCH_RISCV64 || ARCH_ARM
default n
Вам необходимо включить этот параметр для архитектуры ARCH_X86
:
config ARCH_X86
bool
select GENERIC_IOMAP
select
установил для GENERIC_IOMAP
значение y
, игнорируя его зависимости: ARCH_RISCV64
и ARCH_ARM
.
Решение:
Для того чтобы избежать такого, создаётся отдельная переменная, которая в свою очередь указывается в зависимостях нужного вам параметра:
config HAVE_GENERIC_IOMAP
bool
config GENERIC_IOMAP
bool
depends on HAVE_GENERIC_IOMAP && (ARCH_X86 || ARCH_ARM)
default HAVE_GENERIC_IOMAP
config ARCH_X86
bool
select HAVE_GENERIC_IOMAP