152 lines
4.9 KiB
Python
Executable File
152 lines
4.9 KiB
Python
Executable File
#!/usr/bin/python
|
|
"""Iterate through KConfig options, enabling them one by one."""
|
|
import argparse
|
|
import pathlib
|
|
import re
|
|
import subprocess
|
|
import sys
|
|
import termcolor
|
|
import typing
|
|
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument("-q", "--quick", action="store_true", dest="quick",
|
|
help="Only compile the subdirectory "
|
|
"containing the Kconfig file")
|
|
parser.add_argument("kconfig", metavar="Kconfig", nargs=1,
|
|
help="Path to Kconfig file")
|
|
parser.add_argument("makeargs", metavar="Make Arguments", nargs="*",
|
|
help="Options to pass to make")
|
|
|
|
Args = parser.parse_args()
|
|
Kconfig = pathlib.Path(Args.kconfig[0])
|
|
Config = pathlib.Path("scripts/config").absolute()
|
|
Make = pathlib.Path("~/.local/bin/makelinux.zsh").expanduser()
|
|
MakeArg = Args.makeargs + ([f"{str(Kconfig)}/"] if Args.quick else [])
|
|
Options = dict()
|
|
Colors = {"n": "dark_grey", "y": "green", "m": "cyan"}
|
|
Attrs = {"y": ["bold"], "m": ["bold"]}
|
|
NameLen = 0
|
|
|
|
if Kconfig.is_dir():
|
|
Kconfig = Kconfig / "Kconfig"
|
|
|
|
|
|
for path in [Kconfig, Make, Config]:
|
|
if not path.exists():
|
|
print(f"Error: {path} does not exist!")
|
|
sys.exit(1)
|
|
|
|
|
|
class Option:
|
|
"""Represents a single CONFIG_* KConfig Option."""
|
|
|
|
def __init__(self, opt: str):
|
|
"""Initialize our Option class."""
|
|
lines = opt.splitlines()
|
|
self.name = lines[0].strip()
|
|
self.type = lines[1].strip().split()[0]
|
|
self.state = "y"
|
|
self.res = None
|
|
|
|
if search := re.search(r"\sdepends on (.*?)\n", opt):
|
|
depends = search.group(1).strip()
|
|
depends = [d.strip() for d in re.split("[&&|=m|=y|=n]", depends)]
|
|
self.depends = set([d for d in depends if len(d) > 0])
|
|
else:
|
|
self.depends = set()
|
|
|
|
def __repr__(self) -> str:
|
|
"""Return a string representation of this Option."""
|
|
return f"{self.name}:{self.type}"
|
|
|
|
def __lt__(self, rhs: typing.Self) -> bool:
|
|
"""Check if this option is less than the `rhs` option."""
|
|
if self.name.endswith("DEBUG"):
|
|
return False
|
|
if rhs.name.endswith("DEBUG"):
|
|
return True
|
|
return self.name in rhs.dependencies()
|
|
|
|
def dependencies(self) -> set[typing.Self]:
|
|
"""Get a set of dependencies for this Option."""
|
|
ret = self.depends.copy()
|
|
for dep in self.depends:
|
|
if (opt := Options.get(dep)) is not None:
|
|
ret.update(opt.dependencies())
|
|
return ret
|
|
|
|
def disable(self) -> None:
|
|
"""Disable this Option."""
|
|
subprocess.run([Config] + ["--disable", self.name]).check_returncode()
|
|
self.state = "n"
|
|
|
|
def enable(self) -> None:
|
|
"""Enable this Option."""
|
|
how = "--module" if self.type == "tristate" else "--enable"
|
|
subprocess.run([Config] + [how, self.name]).check_returncode()
|
|
self.state = "m" if self.type == "tristate" else "y"
|
|
|
|
def print(self) -> None:
|
|
"""Print out this option (in color!)."""
|
|
print(" ", end="")
|
|
termcolor.cprint(self.name, Colors.get(self.state),
|
|
attrs=["bold", "dark"], end="")
|
|
termcolor.cprint("=", "white", attrs=["bold"], end="")
|
|
termcolor.cprint(self.state, Colors.get(self.state),
|
|
attrs=["bold", "dark"])
|
|
|
|
def print_result(self) -> None:
|
|
"""Print out this option and result."""
|
|
attrs = [] if self.res is None else ["bold"]
|
|
|
|
match self.res:
|
|
case True: result = ("Success", "green")
|
|
case False: result = ("Failed", "red")
|
|
case _: result = ("Not Run", "yellow")
|
|
|
|
termcolor.cprint(" " + self.name + " ", Colors.get(self.state),
|
|
attrs=attrs, end="")
|
|
termcolor.cprint(" " * (NameLen - len(self.name)), "grey",
|
|
attrs=["underline"], end="")
|
|
termcolor.cprint(" " + result[0], result[1], attrs=attrs)
|
|
|
|
|
|
with open(Kconfig) as f:
|
|
regex = re.compile("\nconfig")
|
|
opts = [Option(opt) for opt in regex.split(f.read()) if opt[0] != "#"]
|
|
Options = {opt.name: opt for opt in opts
|
|
if opt.type in ["bool", "tristate"]}
|
|
|
|
options = list(sorted(Options.values()))
|
|
|
|
|
|
def make_opt(option: Option) -> None:
|
|
"""Enable and build a single option."""
|
|
option.enable()
|
|
|
|
print()
|
|
for o in options:
|
|
o.print()
|
|
|
|
option.res = False
|
|
subprocess.run([Make] + ["olddefconfig"]).check_returncode()
|
|
subprocess.run([Make] + MakeArg).check_returncode()
|
|
option.res = True
|
|
|
|
|
|
try:
|
|
for opt in options:
|
|
NameLen = max(NameLen, len(opt.name) + 2)
|
|
opt.disable()
|
|
for opt in options:
|
|
make_opt(opt)
|
|
except Exception as e:
|
|
print(str(e))
|
|
finally:
|
|
print()
|
|
termcolor.cprint("Kconfig Results:", "yellow", attrs=["bold", "underline"])
|
|
for opt in options:
|
|
opt.print_result()
|
|
if False in [opt.res for opt in options]:
|
|
sys.exit(1)
|