Kconfig 宏語言

概念

基本思想受到 Make 的啟發。當我們檢視 Make 時,我們注意到一種語言中包含兩種語言。一種語言描述了由目標和先決條件組成的依賴關係圖。另一種是用於執行文字替換的宏語言。

兩種語言階段之間有明顯的區別。例如,您可以編寫如下的 makefile

APP := foo
SRC := foo.c
CC := gcc

$(APP): $(SRC)
        $(CC) -o $(APP) $(SRC)

宏語言將變數引用替換為其展開形式,並將其視為原始檔輸入,如下所示

foo: foo.c
        gcc -o foo foo.c

然後,Make 分析依賴關係圖並確定要更新的目標。

Kconfig 中的想法非常相似 - 可以像這樣描述 Kconfig 檔案

CC := gcc

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

Kconfig 中的宏語言將原始檔處理為以下中間狀態

config CC_HAS_FOO
        def_bool y

然後,Kconfig 進入評估階段,以解決符號間的依賴關係,如Kconfig 語言中所述。

變數

與 Make 中一樣,Kconfig 中的變數用作宏變數。宏變數“就地”展開以產生文字字串,然後可以進一步展開。要獲取變數的值,請將變數名括在 $( ) 中。即使對於單字母變數名,也需要括號;$X 是語法錯誤。也不支援花括號形式,如 ${CC}。

有兩種型別的變數:簡單展開的變數和遞迴展開的變數。

簡單展開的變數使用 := 賦值運算子定義。它的右側在從 Kconfig 檔案讀取該行時立即展開。

遞迴展開的變數使用 = 賦值運算子定義。它的右側僅儲存為變數的值,而不以任何方式展開它。相反,展開在變數使用時執行。

還有另一種型別的賦值運算子;+= 用於將文字附加到變數。如果左側最初定義為簡單變數,則 += 的右側立即展開。否則,其評估將被推遲。

變數引用可以採用引數,形式如下

$(name,arg1,arg2,arg3)

您可以將引數化引用視為函式。(更準確地說,是“使用者定義的函式”,與下面列出的“內建函式”相對)。

有用的函式必須在使用時展開,因為如果傳遞不同的引數,則相同的函式會以不同的方式展開。因此,使用者定義的函式使用 = 賦值運算子定義。引數在主體定義中用 $(1)、$(2) 等引用。

事實上,遞迴展開的變數和使用者定義的函式在內部是相同的。(換句話說,“變數”是“零引數的函式”。)當我們廣義地說“變數”時,它包括“使用者定義的函式”。

內建函式

與 Make 類似,Kconfig 提供了幾個內建函式。每個函式都接受特定數量的引數。

在 Make 中,每個內建函式至少接受一個引數。Kconfig 允許內建函式使用零引數,例如 $(filename)、$(lineno)。您可以將這些視為“內建變數”,但這只是我們如何稱呼它的問題。讓我們在這裡說“內建函式”來指代本機支援的功能。

Kconfig 當前支援以下內建函式。

  • $(shell,command)

“shell”函式接受一個引數,該引數被展開並傳遞給子 shell 以執行。然後讀取命令的標準輸出並將其作為函式的值返回。輸出中的每個換行符都替換為空格。任何尾隨換行符都會被刪除。不返回標準錯誤,也不返回任何程式退出狀態。

  • $(info,text)

“info”函式接受一個引數並將其列印到標準輸出。它評估為空字串。

  • $(warning-if,condition,text)

“warning-if”函式接受兩個引數。如果 condition 部分是“y”,則 text 部分將傳送到標準錯誤。文字以當前 Kconfig 檔案的名稱和當前行號為字首。

  • $(error-if,condition,text)

“error-if”函式類似於“warning-if”,但如果 condition 部分是“y”,則它會立即終止解析。

  • $(filename)

“filename”不帶引數,$(filename) 擴充套件為正在解析的檔名。

  • $(lineno)

“lineno”不帶引數,$(lineno) 擴充套件為正在解析的行號。

Make vs Kconfig

Kconfig 採用類似 Make 的宏語言,但函式呼叫語法略有不同。

Make 中的函式呼叫如下所示

$(func-name arg1,arg2,arg3)

函式名和第一個引數之間至少用一個空格分隔。然後,從第一個引數中刪除前導空格,而保留其他引數中的空格。您需要使用一種技巧才能使第一個引數以空格開頭。例如,如果您想讓“info”函式列印“ hello”,您可以這樣寫

empty :=
space := $(empty) $(empty)
$(info $(space)$(space)hello)

Kconfig 僅使用逗號作為分隔符,並保留函式呼叫中的所有空格。有些人喜歡在每個逗號分隔符後放置一個空格

$(func-name, arg1, arg2, arg3)

在這種情況下,“func-name”將接收“ arg1”、“ arg2”、“ arg3”。前導空格的存在可能會影響函式。同樣適用於 Make - 例如,$(subst .c, .o, $(sources)) 是一個典型的錯誤;它將“.c”替換為“ .o”。

在 Make 中,使用者定義的函式透過使用內建函式“call”來引用,如下所示

$(call my-func,arg1,arg2,arg3)

Kconfig 以相同的方式呼叫使用者定義的函式和內建函式。省略“call”使語法更短。

在 Make 中,某些函式將逗號視為字面量而不是引數分隔符。例如,$(shell echo hello, world) 執行命令“echo hello, world”。同樣,$(info hello, world) 將“hello, world”列印到標準輸出。你可以說這是一種_有用的_不一致。

在 Kconfig 中,為了更簡單的實現和語法一致性,出現在 $( ) 上下文中的逗號始終是分隔符。這意味著

$(shell, echo hello, world)

是一個錯誤,因為它傳遞了兩個引數,而“shell”函式僅接受一個引數。要在引數中傳遞逗號,您可以使用以下技巧

comma := ,
$(shell, echo hello$(comma) world)

注意事項

變數(或函式)不能跨標記展開。因此,您不能使用變數作為包含多個標記的表示式的簡寫。以下有效

RANGE_MIN := 1
RANGE_MAX := 3

config FOO
        int "foo"
        range $(RANGE_MIN) $(RANGE_MAX)

但是,以下不起作用

RANGES := 1 3

config FOO
        int "foo"
        range $(RANGES)

變數不能擴充套件為 Kconfig 中的任何關鍵字。以下不起作用

MY_TYPE := tristate

config FOO
        $(MY_TYPE) "foo"
        default y

顯然從設計來看,$(shell command) 在文字替換階段展開。您不能將符號傳遞給“shell”函式。

以下內容無法按預期工作

config ENDIAN_FLAG
        string
        default "-mbig-endian" if CPU_BIG_ENDIAN
        default "-mlittle-endian" if CPU_LITTLE_ENDIAN

config CC_HAS_ENDIAN_FLAG
        def_bool $(shell $(srctree)/scripts/gcc-check-flag ENDIAN_FLAG)

相反,您可以像下面這樣做,以便靜態展開任何函式呼叫

config CC_HAS_ENDIAN_FLAG
        bool
        default $(shell $(srctree)/scripts/gcc-check-flag -mbig-endian) if CPU_BIG_ENDIAN
        default $(shell $(srctree)/scripts/gcc-check-flag -mlittle-endian) if CPU_LITTLE_ENDIAN