11# Kbuild Toolchain Functions
22
3- Kconfiglib implements Kbuild toolchain detection functions used by the Linux kernel since version 4.18.
3+ Kconfiglib implements Kbuild toolchain detection functions used by the Linux kernel since version 4.18,
4+ plus the portable ` $(python,...) ` built-in for cross-platform boolean checks.
45These preprocessor functions enable runtime detection of compiler, assembler, and linker capabilities,
56allowing kernel configurations to adapt to different toolchain versions.
67
@@ -26,6 +27,39 @@ For comprehensive Kconfig syntax documentation, see the
2627` $(failure,command) `
2728: Returns ` n ` if command succeeds, ` y ` otherwise. Inverse of ` success ` .
2829
30+ ### Portable In-Process Checks
31+
32+ ` $(python,code) `
33+ : Evaluates a Python code string in-process via ` exec() ` . Returns ` y ` if
34+ execution succeeds without exception, ` n ` otherwise. No subprocess, no shell,
35+ no PATH dependency. Use ` assert ` for boolean checks.
36+
37+ The exec namespace provides pre-imported modules and a shell-free subprocess helper:
38+
39+ | Name | Type | Purpose |
40+ | ------------| ----------| --------------------------------------------|
41+ | ` os ` | module | env vars, paths, file ops |
42+ | ` sys ` | module | platform, version, ` sys.executable ` path |
43+ | ` shutil ` | module | ` which() ` for tool detection |
44+ | ` platform ` | module | machine/system/architecture |
45+ | ` run ` | function | shell-free subprocess, returns bool |
46+
47+ ` run(*argv) ` executes a command as an argument list (` shell=False ` ) and returns
48+ ` True ` if it exits with code 0, ` False ` otherwise.
49+
50+ Each ` exec() ` call receives a fresh copy of the namespace so assignments in one
51+ ` $(python,...) ` invocation do not leak into subsequent calls.
52+
53+ ` SystemExit ` is handled specially: ` SystemExit(0) ` and ` SystemExit(None) ` map
54+ to ` y ` ; non-zero/non-empty codes map to ` n ` . ` AssertionError ` maps to ` n `
55+ silently (expected for boolean checks via ` assert ` ). All other exceptions
56+ (` NameError ` , ` SyntaxError ` , etc.) map to ` n ` and emit a Kconfig warning
57+ with the exception type and message, aiding diagnosis of typos in code strings.
58+
59+ Trust model: ` $(python,...) ` has the same trust level as ` $(shell,...) ` .
60+ Kconfig files are trusted code (like Makefiles). The restricted globals provide
61+ scope isolation (no parser internals visible), not security sandboxing.
62+
2963### Compiler Detection
3064
3165` $(cc-option,flag[,fallback]) `
@@ -99,6 +133,48 @@ config HAS_32BIT
99133 def_bool "$(m32-flag)" != ""
100134```
101135
136+ ### Portable Checks with $(python,...)
137+
138+ ```
139+ # Boolean checks (in-process, no subprocess)
140+ config PYTHON_AVAILABLE
141+ def_bool $(python,)
142+
143+ config HAS_CC
144+ def_bool $(python,assert os.environ.get('CC'))
145+
146+ config IS_LINUX
147+ def_bool $(python,assert sys.platform == 'linux')
148+
149+ config IS_X86_64
150+ def_bool $(python,assert platform.machine() == 'x86_64')
151+
152+ config HAS_GCC
153+ def_bool $(python,assert shutil.which('gcc'))
154+
155+ # Shell-free subprocess checks
156+ config CC_IS_CLANG
157+ def_bool $(python,assert run(sys.executable, 'scripts/detect-compiler.py', '--is', 'Clang'))
158+
159+ config HAVE_SDL2
160+ def_bool $(python,assert run('pkg-config', '--exists', 'sdl2'))
161+ ```
162+
163+ Commas inside ` run(...) ` are safe: the Kconfig preprocessor tracks parenthesis
164+ depth and only splits on commas at the top level of the function call.
165+
166+ Quoted strings are also safe: the preprocessor tracks single (` ' ` ), double (` " ` ),
167+ triple-single (` ''' ` ), and triple-double (` """ ` ) quoted regions. Commas and
168+ parentheses inside quotes are treated as literal characters, not argument
169+ separators or nesting markers. Backslash escapes (` \" ` , ` \' ` ) inside quoted
170+ regions are handled correctly.
171+
172+ Use semicolons instead of commas for multi-statement code:
173+ ` $(python,import os; assert os.path.isfile('Makefile')) ` .
174+
175+ For string-valued results (e.g., getting the compiler type name), ` $(shell,...) `
176+ remains the right tool. ` $(python,...) ` only returns ` y ` or ` n ` .
177+
102178## Implementation
103179
104180### Design
@@ -110,6 +186,28 @@ Functions are implemented in `kconfiglib.py` following these principles:
110186- Python 3.6+ using standard library only
111187- Graceful error handling (missing tools return ` n ` )
112188
189+ ### Shell-Free Toolchain Functions
190+
191+ Toolchain functions (` cc-option ` , ` ld-option ` , ` as-instr ` , ` as-option ` ,
192+ ` cc-option-bit ` , ` rustc-option ` ) use ` subprocess.Popen ` with argument lists
193+ (` shell=False ` ) and ` os.devnull ` instead of Unix shell syntax. This
194+ eliminates shell injection from environment variables and Kconfig-supplied
195+ options, and makes the functions portable to Windows.
196+
197+ Internal helpers:
198+
199+ ` _run_argv(argv, stdin_data=None) `
200+ : Runs a command as an argument list. Returns ` True ` if exit code is 0.
201+ Used by all toolchain functions.
202+
203+ ` _run_cmd(command) `
204+ : Runs a command via shell (` shell=True ` ). Used by ` success ` , ` failure ` ,
205+ and ` if-success ` , which accept user-supplied shell commands by design.
206+
207+ ` _run_helper(*argv) `
208+ : Shell-free subprocess for ` $(python,...) ` code strings. Exposed as ` run() `
209+ in the exec namespace.
210+
113211### Environment Variables
114212
115213Functions respect standard build variables:
@@ -119,33 +217,34 @@ Functions respect standard build variables:
119217
120218### Performance
121219
122- Functions execute shell commands during Kconfig parsing, which can be slow.
123- For applications that parse configurations repeatedly, consider implementing
124- caching or using ` allow_empty_macros=True ` to skip toolchain detection.
220+ Toolchain functions spawn subprocesses during Kconfig parsing, which can be
221+ slow. ` $(python,...) ` checks that don't call ` run() ` execute in-process with
222+ no subprocess overhead. For applications that parse configurations repeatedly,
223+ consider implementing caching or using ` allow_empty_macros=True ` to skip
224+ toolchain detection.
125225
126226## Testing
127227
128- Four test suites validate the implementation :
228+ Tests live in ` tests/test_preprocess.py ` (part of the pytest suite) :
129229
130- ` test_issue111.py `
131- : Validates basic toolchain function parsing.
230+ ` test_kbuild_functions `
231+ : Verifies toolchain functions (` cc-option ` , ` as-instr ` , etc.) and
232+ ` $(python,...) ` via Kconfig parsing. Exercises the full preprocessor path.
132233
133- ` test_issue109.py `
134- : Tests nested function calls and complex expressions.
234+ ` test_success_failure_fns `
235+ : Tests ` success ` , ` failure ` , and ` if-success ` directly in Python using
236+ ` sys.executable ` as a portable true/false replacement.
135237
136- ` test_kbuild_complete.py `
137- : Comprehensive suite with 35+ test cases covering all functions, edge cases, and error conditions.
238+ ` test_python_fn_isolation `
239+ : Verifies that variable assignments in one ` $(python,...) ` call do not
240+ leak into subsequent calls.
138241
139- ` test_kernel_compat.py `
140- : Real-world kernel Kconfig snippets from init/Kconfig, arch/x86/Kconfig, etc .
242+ ` test_python_fn_system_exit `
243+ : Verifies ` SystemExit ` handling: ` exit(0) ` maps to ` y ` , non-zero to ` n ` .
141244
142- Run all tests :
245+ Run:
143246``` bash
144- python3 test_basic_parsing.py && \
145- python3 test_issue111.py && \
146- python3 test_issue109.py && \
147- python3 test_kbuild_complete.py && \
148- python3 test_kernel_compat.py
247+ python3 -m pytest tests/test_preprocess.py -v
149248```
150249
151250## Compatibility
@@ -165,6 +264,30 @@ Tested with:
165264- binutils 2.31+
166265- rustc 1.60+ (optional)
167266
267+ ## Portability
268+
269+ ### Unix vs Windows
270+
271+ | Unix shell idiom | Portable replacement |
272+ | ---| ---|
273+ | ` $(shell,cmd 2>/dev/null && echo y \|\| echo n) ` | ` $(python,assert run('cmd', 'arg')) ` |
274+ | ` $(shell,test -n "$CC" && echo y \|\| echo n) ` | ` $(python,assert os.environ.get('CC')) ` |
275+ | ` $(shell,scripts/foo.py --flag ...) ` | ` $(python,assert run(sys.executable, 'scripts/foo.py', '--flag')) ` |
276+ | ` $(shell,pkg-config --exists lib && echo y \|\| echo n) ` | ` $(python,assert run('pkg-config', '--exists', 'lib')) ` |
277+ | ` $(success,true) ` | ` $(python,) ` |
278+ | ` $(failure,false) ` | ` $(python,assert False) ` |
279+
280+ ` $(shell,...) ` remains necessary for string-valued output (e.g., compiler
281+ type name, version strings). For boolean checks, prefer ` $(python,...) `
282+ on cross-platform projects.
283+
284+ ### Toolchain functions
285+
286+ ` cc-option ` , ` ld-option ` , ` as-instr ` , ` as-option ` , ` cc-option-bit ` , and
287+ ` rustc-option ` are portable by default. They use ` subprocess.Popen ` with
288+ argument lists internally -- no shell involvement, no ` /dev/null ` path
289+ dependency (` os.devnull ` is used instead).
290+
168291## Real-World Examples
169292
170293From ` arch/x86/Kconfig.cpu ` :
@@ -191,6 +314,18 @@ config SHADOW_CALL_STACK
191314 depends on $(cc-option,-fsanitize=shadow-call-stack -ffixed-x18)
192315```
193316
317+ From a cross-platform project (Mado):
318+ ```
319+ config CC_IS_CLANG
320+ def_bool $(python,assert run(sys.executable, 'scripts/detect-compiler.py', '--is', 'Clang'))
321+
322+ config HAVE_SDL2
323+ def_bool $(python,assert run('pkg-config', '--exists', 'sdl2'))
324+
325+ config CROSS_COMPILE_ENABLED
326+ def_bool $(python,assert os.environ.get('CROSS_COMPILE'))
327+ ```
328+
194329## See Also
195330
196331- [ Kconfig Language] ( https://docs.kernel.org/kbuild/kconfig-language.html ) - Complete syntax specification
0 commit comments