Перейти к основному содержимому

Язык 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), а так же типы параметров не должны конфликтовать (повторяться).

Атрибуты меню

Меню-запись может иметь несколько атрибутов. Но не все они применимы повсеместно (смотрите синтаксис).

  1. Тип параметра: bool | tristate | string | hex | int

    Все параметры конфигурации должны иметь тип. Есть два базовых типа: tristate и string; все остальные основываются на них. Определение типа также опционально принимает и запрос ввода для пользователя, так что вот эти два примера эквивалетны:

    bool "Networking support"

    и:

    bool
    prompt "Networking support"
  2. Запрос ввода: prompt <prompt> [“if” <expr>]

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

  3. Значение по умолчанию: default <expr> [“if” <expr>]

    Параметр конфигурации может иметь сколько угодно дефолтных значений. Если "видимость" включена у нескольких дефолтов, то использоваться будет тот, который определен раньше. Значения по умолчанию не ограничены в рамках меню-записи, в которой они определены. Это значит, что дефолт может быть определён где-то ещё или быть переопределен более ранним дефолтом. Дефолтные значения назначаются только в случае если пользователь не указал своего (через запрос ввода). Если запрос ввода виден, то дефолтное значение показывается пользователю и может быть измененно им. При необходимости зависимости только для дефолтного значени могут быть добавлены через if.

    Дефолтным значением для значения по умолчанию умышленно установленно "n" во избежание радувания сборки. С несколькими исключениями новые параметры конфигурации не должны менять этого. Смысл в том чтобы make oldconfig вносил как можно меньше изменений в конфигурацию от релиза к релизу.

    к сведению

    default y/m должно быть указано у параметров, которые:

    • Параметр для чего-то, что всегда должно быть включено в сборку, должен иметь default y.

    • "gatekeeping" параметр, который скрывает/показывает другие параметры (но сам не генерирует какой-либо код), должен быть default y чтобы люди могли видеть эти параметры.

    • Параметр реализует поведение вспомогательного драйвера или аналогичные параметры для драйвера, который сам по умолчанию выключен (default n). Это позволяет вам устанавливать разумные дефолты.

    • Взаимодействие с аппаратным обеспечением или инфраструктурой, которое все ожидают. Например: CONFIG_NET или CONFIG_BLOCK. Это редкие исключения.

  4. Определение типа вместе с значением по умолчанию: "def_bool"/"def_tristate" <expr> ["if" <expr>]

    Быстрый способ сразу определить и тип и дефолт к нему. При необходимости зависимости для этого атрибута могут быть добавлены через if.

  5. Зависимости: depends on <expr>

    Этот атрибут определяет зависимости для меню-записи. Если несколько зависимостей определенно – они связываются с помощью логического "И &&". Зависимости также применяются ко всем параметрам в рамках меню-записи (которые также работают с if), поэтому следующие два примера эквивалентны:

    bool "foo" if BAR
    default y if BAR

    и:

    depends on BAR
    bool "foo"
    default y
  6. Обратные зависимости: “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 тоже включено.

  7. Слабые обратные зависимости: imply <symbol> [“if” <expr>]

    Также как и select этот атрибут устанавливает нижний лимит для другого символа за исключением того, что этот символ всё равно может быть установлен в "n" от своей прямой прямой зависимости или от "видимого" пользовательского ввода.

    Пример:

    config FOO
    tristate "foo"
    imply BAZ

    config BAZ
    tristate "baz"
    depends on BAR

    Возможны следующие значения:

    FOOBARдефолты для BAZвыборы для BAZ
    nynN/m/y
    mymM/y/n
    yyyY/m/n
    nmnN/m
    mmmM/n
    ymmM/n
    yn*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> (Будущее такого поведения пока неопределенно).

  8. Ограничение отображение меню блоков: visible if <expr>

    Этот атрибут может использоваться только в меню блоках. Если условие "false", то блок не будет показан пользователю (символы находящиеся в нём всё равно смогут быть "выбраны" другими символами). Поведение идентично prompt'у с условием для меню-записей. Значение по умолчанию для visible – "true".

  9. Числовые диапазоны: range <symbol> <symbol> [“if” <expr>]

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

  10. Вспомогательный текст: help

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

    От авторов перевода

    Пример:

    config EXAMPLE
    bool "Пример параметра"
    help
    Вспомогательный текст.
    Он помогает

    # А этот текст уже не входит в вспомогательный,
    # так как его отступ меньше чем у первой строки вспомогательного.
  11. Атрибут модуля: 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)

Выражения перечислены в порядке убывания приоритета.

  1. Переводит символ в выражение. boolean и tristate символы легко переводятся в соответствующие значения выражений. Все остальные типы переводятся в n.
  2. Если значение обоих символов одинаково, то вернётся y, в обратном случае n.
  3. Если значение обоих символов одинаково, то вернётся n, в обратном случае y,
  4. Если значение <symbol1> меньше, больше, меньше или равно, либо больше или равно значению <symbol2>, возвращается 'y', в противном случае — 'n'.
  5. Возвращает значение выражения. Используется для переопределения порядка выполнения операций..
  6. Возвращает результат выражения (2-/expr/).
  7. Возвращает результат выражения min(/expr/, /expr/).
  8. Возвращает результат выражения 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 ...
к сведению

Мы используем существующий параметр конфигурации и избегаем создания новой переменной конфигурации для selectHAVE_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

Добавление функций, требующих поддержки компилятора

Существует несколько функций, требующих поддержки компилятора. Рекомендуемый способ описывать зависимости для таких функций – это использовать depends on с макросом, проверяющим возможности компилятора:

config STACKPROTECTOR
bool "Stack Protector buffer overflow detection"
depends on $(cc-option,-fstack-protector)
...

Если вам нужно использовать способность компилятора в makefile и/или исходных файлах на языке C, рекомендуется использовать префикс CCHAS для опции конфигурации:

config CC_HAS_FOO
def_bool $(success,$(srctree)/scripts/cc-check-foo.sh $(CC))

Собирать только как модуль

Чтобы добавить ограничение компоненту, что теперь он может собираться только как модуль, добавьте в его depends on значение m. Пример:

config FOO
depends on BAR && m

Ограничивает значения FOO до "модуль" (=m) или "выключен" (=n).

Тестирование компиляции

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

От авторов перевода

Например:

config EXOTIC_DRIVER
bool "Support for exotic hardware"
depends on SPECIAL_HW || COMPILE_TEST

Здесь драйвер будет собран, если либо доступно специфическое оборудование (SPECIAL_HW), либо включен режим компиляционных тестов (COMPILE_TEST).

Архитектурные и платформенные зависимости

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

Чтобы не спрашивать пользователя о драйверах, которые нельзя использовать на системе(ах), для которых компилируется ядро, и если это имеет смысл, символы конфигурации, управляющие компиляцией драйвера, должны содержать соответствующие зависимости, ограничивая "видимость" символа (надмножеством) платформ(ы), на которых может использоваться драйвер. Зависимость может быть архитектурной (например, ARM) или платформенной (например, ARCH_OMAP4). Это упрощает жизнь не только владельцам конфигураций дистрибутивов, но и каждому разработчику или пользователю, который настраивает ядро.

Такую зависимость можно "ослабить", объединив ее с описанным выше правилом компиляционного тестирования, что приведет к:

config FOO
bool “Support for foo hardware” depends on ARCH_FOO_VENDOR || COMPILE_TEST
От авторов перевода

Допустим, у нас есть драйвер для устройства, которое поддерживается только на архитектуре ARM и платформе OMAP4.

config MY_DRIVER
tristate "Specific device driver"
depends on ARM && ARCH_OMAP4

Этот конфиг гарантирует, что пользователю будет предложено включить драйвер только если он действительно имеет смысл для его системы (если пользователь собирает ядро для архитектуры ARM и платформы OMAP4).

Опциональные зависимости

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

Наиболее распространённый способ выразить эту опциональную зависимость в логике Kconfig использует немного противоречивый подход:

config FOO
tristate "Support for foo hardware"
depends on BAR || !BAR

Это означает, что существует либо зависимость от BAR, которая не позволяет комбинацию FOO=y с BAR=m, либо BAR полностью отключен. Для более формализованного подхода, если есть несколько драйверов с одинаковой зависимостью, можно использовать вспомогательный символ, например:

config FOO
tristate "Support for foo hardware"
depends on BAR_OPTIONAL

config BAR_OPTIONAL
def_tristate BAR || !BAR

Ограничения рекурсивных зависимостей в Kconfig

Если вы столкнулись с ошибкой Kconfig: «выявлена рекурсивная зависимость», у вас возникла проблема рекурсивной зависимости в Kconfig. Рекурсивная зависимость может быть описана как циклическая зависимость. Инструменты kconfig должны убедиться, что файлы Kconfig соответствуют заданным требованиям конфигурации. Для этого kconfig должна определить возможные значения для всех символов Kconfig, что в настоящее время невозможно, если существует циклическая связь между двумя или более символами Kconfig.

Для получения подробной информации обратитесь к подразделу «Простая проблема рекурсивной зависимости Kconfig» ниже. Kconfig не выполняет разрешение рекурсивных зависимостей, что имеет несколько последствий для тех, кто пишет файлы Kconfig. Сначала мы объясним, почему существует эта проблема, а затем приведём пример технического ограничения, которое она налагает на разработчиков Kconfig. Разработчики, желающие попытаться устранить это ограничение, должны прочитать следующие подразделы.

Простая проблема рекурсивной зависимости Kconfig

Читайте: "проблема #1" в Проблемы рекурсии в Kconfig

Проверяйте следующим образом:

make KBUILD_KCONFIG=Documentation/kbuild/Kconfig.recursion-issue-01 allnoconfig

Накопительная проблема рекурсивной зависимости Kconfig

Читайте: "проблема #2" в Проблемы рекурсии в Kconfig

Проверяйте следующим образом:

make KBUILD_KCONFIG=Documentation/kbuild/Kconfig.recursion-issue-02 allnoconfig

Практическое решение для проблем с рекурсией в Kconfig

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

  • a. Удалите все излишние select FOO или depends on FOO
  • b. Соблюдение семантики зависимостей:
    • b1) Заменить все "select FOO" на "depends on FOO" или,
    • b2) Заменить все "depends on FOO" на "select FOO"

Решение пункта "a)" можно протестировать с помощью образца Kconfig файла "Documentation/kbuild/Kconfig.recursion-issue-01" (смотрите Проблемы рекурсии в Kconfig), удалив select CORE из CORE_BELL_A_ADVANCED, так как это уже подразумевается, поскольку CORE_BELL_A зависит от CORE. Иногда может быть невозможно удалить некоторые критерии зависимости; для таких случаев вы можете использовать решение "b)".

Два разных решения для "b)" могут быть протестированы на том же файле-примере "Documentation/kbuild/Kconfig.recursion-issue-02" (смотрите Проблемы рекурсии в Kconfig).

Ниже приведен список примеров предыдущих исправлений для таких типов ошибок с рекурсией; все ошибки, по-видимому, связаны с одним или несколькими операторами "select" и одним или несколькими "depends on".

commitfixметка
06b718c01208select A -> depends on A
c22eacfe82f9depends on A -> depends on B
6a91e854442cselect A -> depends on A
118c565a8f2eselect A -> select B
f004e5594705select A -> depends on A
c7861f37b4c6depends on A -> (null)
80c69915e5fbselect A -> (null)(1)
c2218e26c0d0select A -> depends on A(1)
d6ae99d04e1cselect A -> depends on A
95ca19cf8cbfselect A -> depends on A
8f057d7bca54depends on A -> (null)
8f057d7bca54depends on A -> select A
a0701f04846eselect A -> depends on A
0c8b92f7f259depends on A -> (null)
e4e9e0540928select A -> depends on A(2)
7453ea886e87depends on A > (null)(1)
7b1fff7e4fdfselect A -> depends on A
86c747d2a4f0select A -> depends on A
d9f9ab51e55eselect A -> depends on A
0c51a4d8abd6depends on A -> select A(3)
e98062ed6dc4select A -> depends on A(3)
91e5d284a7f1select A -> (null)
  1. Частичная (или нет) цитата ошибки.
  2. Кажется, это суть данного исправления.
  3. Та же ошибка.
От авторов перевода

Перевод этих пунктов может быть некорректный, поэтому вот они в оригинале:

  1. Partial (or no) quote of error.
  2. That seems to be the gist of that fix.
  3. Same error.

Будущая работа по Kconfig

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

Дальнейшая работа в обоих этих направлениях приветствуется в Kconfig. Мы подробнее остановимся на каждом из них в следующих двух подразделах.

Семантика Kconfig

Использование Kconfig широко распространено, и Linux теперь лишь один из пользователей Kconfig: одно исследование завершило широкий анализ использования Kconfig в 12 проектах [0]. Несмотря на его широкое использование, и хотя этот документ хорошо описывает базовый синтаксис Kconfig, более точное определение семантики Kconfig было бы полезным. Один проект вывел семантику Kconfig с помощью конфигуратора xconfig [1]. Следует провести работу по подтверждению, соответствует ли выведенная семантика нашим целям проектирования Kconfig. Другой проект формализовал денотационную семантику базового подмножества языка Kconfig [10].

Иметь четко определенную семантику может быть полезно для инструментов для практической оценки зависимостей. Например, одним из таких случаев была работа по выражению в булевой абстракции выведенной семантики Kconfig для перевода логики Kconfig в булевые формулы и запуска SAT-солвера для поиска мертвого кода/функционала (всегда неактивного), 114 мертвых функций были найдены в Linux с использованием этой методологии [1] (Раздел 8: Угрозы достоверности). Инструмент kismet, основанный на семантике из [10], находит злоупотребления обратными зависимостями и привел к десяткам исправлений, внесенных в файлы Linux Kconfig [11].

Подтверждение этого могло бы оказаться полезным, так как Kconfig является одним из ведущих промышленных языков моделирования вариативности [1] [2]. Его изучение помогло бы оценить практическое использование таких языков, поскольку их применение ранее было только теоретическим, и реальные требования не были хорошо поняты. На данный момент, однако, для выведения семантики из языков моделирования вариативности, таких как Kconfig, использовались только методы обратного инжиниринга [3].

Полный SAT-солвер для KConfig

Хотя SAT-солверы [4] еще не использовались напрямую в Kconfig, как отмечено в предыдущем подразделе, однако была проведена работа по выражению выведенной семантики Kconfig в булевой абстракции для перевода логики Kconfig в булевые формулы и запуска SAT-солвера для их анализа [5]. Другой известный связанный проект — CADOS [6] (бывший VAMOS [7]) и инструменты, главным образом "undertaker" [8], который был впервые представлен с [9]. Основная концепция "undertaker" заключается в извлечении моделей вариативности из Kconfig и объединении их с пропозициональной формулой, извлеченной из CPP #ifdef и правил сборки, в SAT-солвер для поиска мертвого кода, мертвых файлов и мертвых символов. Если использование SAT-солвера в Kconfig желательно, одним из подходов было бы оценить возможность адаптации таких усилий для Kconfig. Существует достаточный интерес со стороны наставников существующих проектов, чтобы не только помочь в интеграции этой работы в основной проект, но и способствовать ее долгосрочному поддержанию. Заинтересованным разработчикам следует посетить: https://kernelnewbies.org/KernelProjects/kconfig-sat.