Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ jobs:
# shell). Linux/macOS run selftests, compatibility tests, and
# example scripts.
target:
# Python 3.12
- python: '3.12'
os: Linux
builder: ubuntu-24.04
Expand Down Expand Up @@ -59,13 +58,27 @@ jobs:
run: |
python -m pip install --user setuptools wheel pytest

- name: Install GNU toolchain (macOS)
# v6.18 kernel Kconfig probes require GNU Make >= 4.0 and a
# supported linker (GNU ld or LLD). macOS ships Make 3.81
# and Apple ld, neither of which is compatible.
# We add ld.lld to PATH and set LD=ld.lld; reltest forwards LD
# as a make command-line variable to override the kernel
# Makefile's unconditional LD=$(CROSS_COMPILE)ld assignment.
if: ${{ matrix.target.os == 'macOS' }}
run: |
brew install make lld
echo "$(brew --prefix make)/libexec/gnubin" >> $GITHUB_PATH
echo "$(brew --prefix lld)/bin" >> $GITHUB_PATH
echo "LD=ld.lld" >> $GITHUB_ENV

- name: Check out Linux source code
# Skip for Windows (headless-only mode)
if: ${{ matrix.target.headless-only != true }}
uses: actions/checkout@v6
with:
repository: torvalds/linux
ref: v5.4
ref: v6.18

- name: Check out Kconfiglib source code
uses: actions/checkout@v6
Expand Down
12 changes: 12 additions & 0 deletions kconfiglib.py
Original file line number Diff line number Diff line change
Expand Up @@ -1573,6 +1573,9 @@ def _autoconf_contents(self, header):
if not sym._write_to_conf:
continue

if sym.is_transitional:
continue

if sym.orig_type in _BOOL_TRISTATE:
if val == "y":
add("#define {}{} 1\n".format(self.config_prefix, sym.name))
Expand Down Expand Up @@ -1799,6 +1802,9 @@ def _min_config_contents(self, header):
add = chunks.append

for sym in self.unique_defined_syms:
if sym.is_transitional:
continue

# Skip symbols that cannot be changed. Only check
# non-choice symbols, as selects don't affect choice
# symbols.
Expand Down Expand Up @@ -1903,6 +1909,9 @@ def sync_deps(self, path):
# (though it's likely to keep working).
val = sym.str_value

if sym.is_transitional:
continue

# n tristate values do not get written to auto.conf and autoconf.h,
# making a missing symbol logically equivalent to n

Expand Down Expand Up @@ -4815,6 +4824,9 @@ def config_string(self):
if not self._write_to_conf:
return ""

if self.is_transitional:
return ""

if self.orig_type in _BOOL_TRISTATE:
return (
"{}{}={}\n".format(self.kconfig.config_prefix, self.name, val)
Expand Down
24 changes: 8 additions & 16 deletions makefile.patch
Original file line number Diff line number Diff line change
@@ -1,21 +1,14 @@
From 93daf46f309b0c8f86149ef58c4906387d054c22 Mon Sep 17 00:00:00 2001
From: Ulf Magnusson <ulfalizer@gmail.com>
Date: Tue, 9 Jun 2015 13:01:34 +0200
Subject: [PATCH] Kconfiglib scripts/kconfig/Makefile patch

---
scripts/kconfig/Makefile | 29 +++++++++++++++++++++++++++++
1 file changed, 29 insertions(+)

diff --git a/scripts/kconfig/Makefile b/scripts/kconfig/Makefile
index 3f327e21f60e..8b7dd1292005 100644
index fb50bd4..85f47ce 100644
--- a/scripts/kconfig/Makefile
+++ b/scripts/kconfig/Makefile
@@ -27,2 +27,31 @@ gconfig: $(obj)/gconf
@@ -55,6 +55,35 @@ endef

$(foreach c, config menuconfig nconfig gconfig xconfig, $(eval $(call config_rule,$(c))))

+PHONY += scriptconfig iscriptconfig kmenuconfig guiconfig dumpvarsconfig
+
+PYTHONCMD ?= python
+PYTHONCMD ?= python3
+kpython := PYTHONPATH=$(srctree)/Kconfiglib:$$PYTHONPATH $(PYTHONCMD)
+
+ifneq ($(filter scriptconfig,$(MAKECMDGOALS)),)
Expand All @@ -42,7 +35,6 @@ index 3f327e21f60e..8b7dd1292005 100644
+dumpvarsconfig:
+ $(Q)$(kpython) $(srctree)/Kconfiglib/examples/dumpvars.py $(Kconfig)
+
menuconfig: $(obj)/mconf
--
2.20.1

PHONY += localmodconfig localyesconfig
localyesconfig localmodconfig: $(obj)/conf
$(Q)$(PERL) $(src)/streamline_config.pl --$@ $(srctree) $(Kconfig) > .tmp.config
23 changes: 23 additions & 0 deletions tests/Ktransitional
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
config NEW_BOOL
bool "new bool option"
default LEGACY_BOOL

config LEGACY_BOOL
bool "legacy bool (deprecated)"
transitional

config NEW_INT
int "new int option"
default LEGACY_INT

config LEGACY_INT
int "legacy int (deprecated)"
transitional

config NORMAL_BOOL
bool "normal bool"
default y

config NORMAL_INT
int "normal int"
default 42
2 changes: 2 additions & 0 deletions tests/config_transitional
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
CONFIG_LEGACY_BOOL=y
CONFIG_LEGACY_INT=99
9 changes: 7 additions & 2 deletions tests/reltest
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,15 @@

test_script() {
echo -e "\n================= $1 with $py =================\n"
# Forward LD as a make command-line variable so it overrides the
# kernel Makefile's unconditional LD=$(CROSS_COMPILE)ld assignment.
# Environment variables alone do not override Makefile assignments.
local _ld_override=""
[ -n "${LD:-}" ] && _ld_override="LD=$LD"
if (($# == 1)); then
make scriptconfig PYTHONCMD=$py SCRIPT=$1
${MAKE:-make} $_ld_override scriptconfig PYTHONCMD=$py SCRIPT=$1
else
make scriptconfig PYTHONCMD=$py SCRIPT=$1 SCRIPT_ARG="$2"
${MAKE:-make} $_ld_override scriptconfig PYTHONCMD=$py SCRIPT=$1 SCRIPT_ARG="$2"
fi

if (($?)); then
Expand Down
19 changes: 13 additions & 6 deletions tests/test_compat.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,19 +64,21 @@ def kernel_env():
These are referenced inside the kernel Kconfig files and must be present
before any Kconfig object is instantiated.
"""
os.environ["srctree"] = "."
os.environ.setdefault("CC", "gcc")
os.environ.setdefault("LD", "ld")
_make = os.environ.get("MAKE", "make")
_cc = os.environ["CC"]
os.environ["KERNELVERSION"] = (
subprocess.check_output("make kernelversion", shell=True)
subprocess.check_output(f"{_make} kernelversion", shell=True)
.decode("utf-8")
.rstrip()
)
os.environ["CC_VERSION_TEXT"] = (
subprocess.check_output("gcc --version | head -n1", shell=True)
subprocess.check_output(f"{_cc} --version | head -n1", shell=True)
.decode("utf-8")
.rstrip()
)
os.environ["srctree"] = "."
os.environ["CC"] = "gcc"
os.environ["LD"] = "ld"
yield


Expand Down Expand Up @@ -126,7 +128,12 @@ def run_conf_and_compare(script, conf_flag, arch):
"""Run a Kconfiglib script via 'make scriptconfig', then run the C
implementation with *conf_flag*, and compare the resulting .config files.
"""
shell(f"make scriptconfig SCRIPT={script} PYTHONCMD='{sys.executable}'")
_make = os.environ.get("MAKE", "make")
_ld = os.environ.get("LD")
_ld_override = f"LD={_ld}" if _ld else ""
shell(
f"{_make} {_ld_override} scriptconfig SCRIPT={script} PYTHONCMD='{sys.executable}'"
)
shell("mv .config ._config")
shell(f"scripts/kconfig/conf --{conf_flag} Kconfig")
compare_configs(arch)
Expand Down
122 changes: 122 additions & 0 deletions tests/test_transitional.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
"""Tests for the 'transitional' keyword (Linux >= 6.18).

Transitional symbols are read from old .config files to populate new symbol
defaults, but must never appear in generated .config, autoconf.h, or
min-config output.
"""

import os
import tempfile

import kconfiglib

KCONFIG_PATH = "tests/Ktransitional"
CONFIG_PATH = "tests/config_transitional"


def _load(config_path=None):
"""Load the transitional test Kconfig, optionally with a .config."""
kconf = kconfiglib.Kconfig(KCONFIG_PATH, warn=False)
if config_path:
kconf.load_config(config_path)
return kconf


def test_transitional_flag():
"""is_transitional is True for transitional syms, False for normal."""
kconf = _load()

assert kconf.syms["LEGACY_BOOL"].is_transitional is True
assert kconf.syms["LEGACY_INT"].is_transitional is True
assert kconf.syms["NEW_BOOL"].is_transitional is False
assert kconf.syms["NEW_INT"].is_transitional is False
assert kconf.syms["NORMAL_BOOL"].is_transitional is False
assert kconf.syms["NORMAL_INT"].is_transitional is False


def test_transitional_migration():
"""Loading old .config with LEGACY_BOOL=y causes NEW_BOOL to default to y."""
kconf = _load(CONFIG_PATH)

# LEGACY_BOOL=y was loaded, so NEW_BOOL (default LEGACY_BOOL) should be y
assert kconf.syms["NEW_BOOL"].str_value == "y"
# LEGACY_INT=99 was loaded, so NEW_INT (default LEGACY_INT) should be 99
assert kconf.syms["NEW_INT"].str_value == "99"


def test_transitional_write_config():
"""Transitional symbols absent from write_config output; normal symbols present."""
kconf = _load(CONFIG_PATH)

with tempfile.NamedTemporaryFile(mode="r", suffix=".config", delete=False) as f:
tmppath = f.name
try:
kconf.write_config(tmppath)
with open(tmppath) as f:
content = f.read()
finally:
os.unlink(tmppath)

assert "LEGACY_BOOL" not in content
assert "LEGACY_INT" not in content
assert "NEW_BOOL" in content
assert "NORMAL_BOOL" in content
assert "NORMAL_INT" in content


def test_transitional_write_autoconf():
"""Transitional symbols absent from write_autoconf output."""
kconf = _load(CONFIG_PATH)

with tempfile.NamedTemporaryFile(mode="r", suffix=".h", delete=False) as f:
tmppath = f.name
try:
kconf.write_autoconf(tmppath)
with open(tmppath) as f:
content = f.read()
finally:
os.unlink(tmppath)

assert "LEGACY_BOOL" not in content
assert "LEGACY_INT" not in content
# NEW_BOOL=y should produce a #define
assert "NEW_BOOL" in content


def test_transitional_write_min_config():
"""Transitional symbols absent from write_min_config output."""
kconf = _load(CONFIG_PATH)

with tempfile.NamedTemporaryFile(mode="r", suffix=".config", delete=False) as f:
tmppath = f.name
try:
kconf.write_min_config(tmppath)
with open(tmppath) as f:
content = f.read()
finally:
os.unlink(tmppath)

assert "LEGACY_BOOL" not in content
assert "LEGACY_INT" not in content


def test_transitional_config_string():
"""config_string returns '' for transitional symbols."""
kconf = _load(CONFIG_PATH)

assert kconf.syms["LEGACY_BOOL"].config_string == ""
assert kconf.syms["LEGACY_INT"].config_string == ""
# Normal symbols should have non-empty config_string
assert kconf.syms["NORMAL_BOOL"].config_string != ""
assert kconf.syms["NORMAL_INT"].config_string != ""


def test_transitional_repr():
"""repr() includes 'transitional' for flagged symbols."""
kconf = _load()

# Use ", transitional," to avoid false-matching the filename Ktransitional
assert ", transitional," in repr(kconf.syms["LEGACY_BOOL"])
assert ", transitional," in repr(kconf.syms["LEGACY_INT"])
assert ", transitional," not in repr(kconf.syms["NEW_BOOL"])
assert ", transitional," not in repr(kconf.syms["NORMAL_BOOL"])